From c3603a7a0e3fba17e98df212553ee5f0f8e8ae05 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Fri, 30 Jan 2026 23:02:04 +0100 Subject: [PATCH 01/13] add PointOnObject constraint and test --- cadquery/occ_impl/sketch_solver.py | 30 +++++++++++++++++++++++++++++ tests/test_sketch.py | 31 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/cadquery/occ_impl/sketch_solver.py b/cadquery/occ_impl/sketch_solver.py index 1f4e10c50..657a8860b 100644 --- a/cadquery/occ_impl/sketch_solver.py +++ b/cadquery/occ_impl/sketch_solver.py @@ -30,6 +30,7 @@ "Radius", "Orientation", "ArcAngle", + "PointOnObject", ] ConstraintInvariants = { # (arity, geometry types, param type, conversion func) @@ -47,6 +48,7 @@ "Radius": (1, ("CIRCLE",), Real, None), "Orientation": (1, ("LINE",), Tuple[Real, Real], None), "ArcAngle": (1, ("CIRCLE",), Real, radians), + "PointOnObject": (2, ("CIRCLE", "LINE"), Optional[Real], None), } Constraint = Tuple[Tuple[int, Optional[int]], ConstraintKind, Optional[Any]] @@ -219,6 +221,33 @@ def arc_angle_cost(x, t, x0, val): return rv +def point_on_object_cost(x1, t1, x10, x2, t2, x20, val): + + if t1 == "LINE" and val == None: + raise invalid_args(t1, val) + p = [.0, .0] + if t1 == "LINE": + p = line_point(x1, val) + elif t1 == "CIRCLE" and val == None: + p = x1[:2] + elif t1 == "CIRCLE": + p = arc_point(x1, val) + + if t2 == "LINE": + start = x2[:2] + end = x2[2:] + v = end - start + l = norm(v) + if l == 0: + return norm(p - start) + else: + d = p - start + return (v[0] * d[1] - v[1] * d[0]) / l + elif t2 == "CIRCLE": + c = x2[:2] + radius = x2[2] + return absolute(norm(p - c) - radius) + raise invalid_args(t2) # dictionary of individual constraint cost functions costs: Dict[str, Callable[..., float]] = dict( @@ -231,6 +260,7 @@ def arc_angle_cost(x, t, x0, val): Radius=radius_cost, Orientation=orientation_cost, ArcAngle=arc_angle_cost, + PointOnObject=point_on_object_cost, ) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index a75241d40..e0d2382b8 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -744,6 +744,37 @@ def test_constraint_solver(): assert s7._faces.isValid() + s8 = ( + Sketch() + .segment((-1, 0), (1, 0), "base") + .segment((1, 0), (0, 1), "side1") + .segment((0, 1), (-1, 0), "side2") + .segment((0, 0.9), (0, 0.01), "height", True) + ) + height = sqrt(2 * 2 - 1) + s8.constrain("base", "side1", "Coincident", None) + s8.constrain("side1", "side2", "Coincident", None) + s8.constrain("side2", "base", "Coincident", None) + s8.constrain("height", "base", "Angle", -90) + s8.constrain("height", "base", "PointOnObject", 1) + s8.constrain("base", "FixedPoint", 1) + s8.constrain("base", "Orientation", (1, 0)) + s8.constrain("side1", "height", "Coincident", None) + s8.constrain("side1", "base", "Angle", 120) + s8.constrain("height", "Length", height) + s8.solve() + + assert s8._solve_status["status"] == 4 + + s8.assemble() + + assert s8._faces.isValid() + + assert s8._tags["base"][0].Length() == approx(2) + assert s8._tags["side1"][0].Length() == approx(2) + assert s8._tags["side2"][0].Length() == approx(2) + assert s8._faces.Area() == approx(height) + def test_dxf_import(): From 8ea10b8eae64fceead3bf41aedffa895dcc2fa99 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sat, 31 Jan 2026 19:07:49 +0100 Subject: [PATCH 02/13] add PointOnObject --- doc/sketch.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/sketch.rst b/doc/sketch.rst index 6dcc84b7f..918630eec 100644 --- a/doc/sketch.rst +++ b/doc/sketch.rst @@ -187,6 +187,10 @@ Following constraints are implemented. Arguments are passed in as one tuple in : - Arc - `angle` - Specified entity is fixed angular span + * - PointOnObject + - 2 + - All + - `None` for arc center or `0..1` for point on segment/arc Workplane integration From 0136a814c8a3c53de33eda04531fc072e8274041 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sat, 31 Jan 2026 23:03:12 +0100 Subject: [PATCH 03/13] fully constrain s8 --- tests/test_sketch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index e0d2382b8..efa31cc0b 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -761,6 +761,7 @@ def test_constraint_solver(): s8.constrain("base", "Orientation", (1, 0)) s8.constrain("side1", "height", "Coincident", None) s8.constrain("side1", "base", "Angle", 120) + s8.constrain("side2", "base", "Angle", -120) s8.constrain("height", "Length", height) s8.solve() From 5da3cf88b45895bdb850cd7c5744cd4d4af02389 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 02:15:34 +0100 Subject: [PATCH 04/13] add test for circle points on line --- tests/test_sketch.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index efa31cc0b..e00a5dc60 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -776,6 +776,24 @@ def test_constraint_solver(): assert s8._tags["side2"][0].Length() == approx(2) assert s8._faces.Area() == approx(height) + s9 = ( + Sketch() + .segment((1, 0), (-1, 0), "segment1", True) + .arc((-.5, .0), 0.8, 180, -180, "arc1") + .arc((.5, .0), 0.8, -180, 180, "arc2") + ) + s9.constrain("segment1", "Fixed", None) + s9.constrain("arc1", "segment1", "PointOnObject", 1) + s9.constrain("arc1", "segment1", "PointOnObject", None) + s9.constrain("arc2", "segment1", "PointOnObject", None) + s9.constrain("arc1", "arc2", "Coincident", None) + s9.constrain("arc2", "segment1", "Coincident", None) + s9.constrain("segment1", "arc1", "Coincident", None) + s9.solve() + + assert s9._solve_status["status"] == 4 + + assert s9._tags["arc1"][0].radius() == approx(.5) def test_dxf_import(): From 418d92ee6590c2820679beb6c2537234a273d4ab Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 12:17:48 +0100 Subject: [PATCH 05/13] move PointOnObject test to separate function --- tests/test_sketch.py | 81 +++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index e00a5dc60..8229468ae 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -744,7 +744,9 @@ def test_constraint_solver(): assert s7._faces.isValid() - s8 = ( + +def test_point_on_object(): + s1 = ( Sketch() .segment((-1, 0), (1, 0), "base") .segment((1, 0), (0, 1), "side1") @@ -752,48 +754,49 @@ def test_constraint_solver(): .segment((0, 0.9), (0, 0.01), "height", True) ) height = sqrt(2 * 2 - 1) - s8.constrain("base", "side1", "Coincident", None) - s8.constrain("side1", "side2", "Coincident", None) - s8.constrain("side2", "base", "Coincident", None) - s8.constrain("height", "base", "Angle", -90) - s8.constrain("height", "base", "PointOnObject", 1) - s8.constrain("base", "FixedPoint", 1) - s8.constrain("base", "Orientation", (1, 0)) - s8.constrain("side1", "height", "Coincident", None) - s8.constrain("side1", "base", "Angle", 120) - s8.constrain("side2", "base", "Angle", -120) - s8.constrain("height", "Length", height) - s8.solve() - - assert s8._solve_status["status"] == 4 - - s8.assemble() - - assert s8._faces.isValid() - - assert s8._tags["base"][0].Length() == approx(2) - assert s8._tags["side1"][0].Length() == approx(2) - assert s8._tags["side2"][0].Length() == approx(2) - assert s8._faces.Area() == approx(height) + s1.constrain("base", "side1", "Coincident", None) + s1.constrain("side1", "side2", "Coincident", None) + s1.constrain("side2", "base", "Coincident", None) + s1.constrain("height", "base", "Angle", -90) + s1.constrain("height", "base", "PointOnObject", 1) + s1.constrain("base", "FixedPoint", 1) + s1.constrain("base", "Orientation", (1, 0)) + s1.constrain("side1", "height", "Coincident", None) + s1.constrain("side1", "base", "Angle", 120) + s1.constrain("side2", "base", "Angle", -120) + s1.constrain("height", "Length", height) + s1.solve() - s9 = ( + assert s1._solve_status["status"] == 4 + + s1.assemble() + + assert s1._faces.isValid() + + assert s1._tags["base"][0].Length() == approx(2) + assert s1._tags["side1"][0].Length() == approx(2) + assert s1._tags["side2"][0].Length() == approx(2) + assert s1._faces.Area() == approx(height) + + s2 = ( Sketch() .segment((1, 0), (-1, 0), "segment1", True) - .arc((-.5, .0), 0.8, 180, -180, "arc1") - .arc((.5, .0), 0.8, -180, 180, "arc2") + .arc((-0.5, 0.0), 0.8, 180, -180, "arc1") + .arc((0.5, 0.0), 0.8, -180, 180, "arc2") ) - s9.constrain("segment1", "Fixed", None) - s9.constrain("arc1", "segment1", "PointOnObject", 1) - s9.constrain("arc1", "segment1", "PointOnObject", None) - s9.constrain("arc2", "segment1", "PointOnObject", None) - s9.constrain("arc1", "arc2", "Coincident", None) - s9.constrain("arc2", "segment1", "Coincident", None) - s9.constrain("segment1", "arc1", "Coincident", None) - s9.solve() - - assert s9._solve_status["status"] == 4 - - assert s9._tags["arc1"][0].radius() == approx(.5) + s2.constrain("segment1", "Fixed", None) + s2.constrain("arc1", "segment1", "PointOnObject", 1) + s2.constrain("arc1", "segment1", "PointOnObject", None) + s2.constrain("arc2", "segment1", "PointOnObject", None) + s2.constrain("arc1", "arc2", "Coincident", None) + s2.constrain("arc2", "segment1", "Coincident", None) + s2.constrain("segment1", "arc1", "Coincident", None) + s2.solve() + + assert s2._solve_status["status"] == 4 + + assert s2._tags["arc1"][0].radius() == approx(0.5) + def test_dxf_import(): From 2803fe8e6c8d81baa1f2e1ed6f0732a1aa251b6d Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 14:00:02 +0100 Subject: [PATCH 06/13] remove absolute value --- cadquery/occ_impl/sketch_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadquery/occ_impl/sketch_solver.py b/cadquery/occ_impl/sketch_solver.py index 657a8860b..ce521ee96 100644 --- a/cadquery/occ_impl/sketch_solver.py +++ b/cadquery/occ_impl/sketch_solver.py @@ -246,7 +246,7 @@ def point_on_object_cost(x1, t1, x10, x2, t2, x20, val): elif t2 == "CIRCLE": c = x2[:2] radius = x2[2] - return absolute(norm(p - c) - radius) + return norm(p - c) - radius raise invalid_args(t2) # dictionary of individual constraint cost functions From 6fd87478b26bf8ce6af93af343a0953ff6aa7f30 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 14:04:07 +0100 Subject: [PATCH 07/13] black format --- cadquery/occ_impl/sketch_solver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cadquery/occ_impl/sketch_solver.py b/cadquery/occ_impl/sketch_solver.py index ce521ee96..294ac56ba 100644 --- a/cadquery/occ_impl/sketch_solver.py +++ b/cadquery/occ_impl/sketch_solver.py @@ -221,11 +221,12 @@ def arc_angle_cost(x, t, x0, val): return rv + def point_on_object_cost(x1, t1, x10, x2, t2, x20, val): if t1 == "LINE" and val == None: raise invalid_args(t1, val) - p = [.0, .0] + p = [0.0, 0.0] if t1 == "LINE": p = line_point(x1, val) elif t1 == "CIRCLE" and val == None: @@ -249,6 +250,7 @@ def point_on_object_cost(x1, t1, x10, x2, t2, x20, val): return norm(p - c) - radius raise invalid_args(t2) + # dictionary of individual constraint cost functions costs: Dict[str, Callable[..., float]] = dict( Fixed=fixed_cost, From e9636be578c97267de0a6cdedc9090ae314140a9 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 14:05:12 +0100 Subject: [PATCH 08/13] add more PointOnObject tests --- tests/test_sketch.py | 65 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index 8229468ae..2ade28d47 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -746,6 +746,8 @@ def test_constraint_solver(): def test_point_on_object(): + + # equilateral triangle defined by height s1 = ( Sketch() .segment((-1, 0), (1, 0), "base") @@ -778,6 +780,7 @@ def test_point_on_object(): assert s1._tags["side2"][0].Length() == approx(2) assert s1._faces.Area() == approx(height) + # s-shaped wire defined by horizontal length s2 = ( Sketch() .segment((1, 0), (-1, 0), "segment1", True) @@ -797,6 +800,68 @@ def test_point_on_object(): assert s2._tags["arc1"][0].radius() == approx(0.5) + # circle inscribed equilateral triangle + s3 = ( + Sketch() + .arc((0, 0), 2, 0, 180, "arc1", True) + .segment((-1, -1), (1, -1), "segment1") + .segment((1, -1), (0, 2), "segment2") + .segment((0, 2), (-1, -1), "segment3") + ) + s3.constrain("arc1", "FixedPoint", None) + s3.constrain("arc1", "ArcAngle", 180) + s3.constrain("arc1", "Radius", 2) + + s3.constrain("segment1", "Orientation", (0, 1)) + s3.constrain("segment2", "segment1", "Angle", 120) + s3.constrain("segment3", "segment1", "Angle", -120) + s3.constrain("segment1", "segment2", "Coincident", None) + s3.constrain("segment2", "segment3", "Coincident", None) + s3.constrain("segment3", "segment1", "Coincident", None) + s3.constrain("segment1", "arc1", "PointOnObject", 1) + s3.constrain("segment2", "arc1", "PointOnObject", 1) + s3.constrain("segment3", "arc1", "PointOnObject", 1) + + s3.solve() + + assert s3._solve_status["status"] == 4 + + s3.assemble() + + s3._faces.isValid() + + assert s3._tags["segment1"][0].Length() == approx(2 * sqrt(3)) + assert s3._tags["segment2"][0].Length() == approx(2 * sqrt(3)) + assert s3._tags["segment3"][0].Length() == approx(2 * sqrt(3)) + + # circle inscribed in square + s4 = ( + Sketch() + .arc((0, 0), 0.2, 0, 360, "arc1") + .segment((-0.5, -0.5), (0.5, -0.5), "segment1", True) + .segment((0.5, -0.5), (0.5, 0.5), "segment2", True) + .segment((0.5, 0.5), (-0.5, 0.5), "segment3", True) + .segment((-0.5, 0.5), (-0.5, -0.5), "segment4", True) + ) + + s4.constrain("segment1", "Fixed", None) + s4.constrain("segment1", "segment2", "Coincident", None) + s4.constrain("segment2", "segment3", "Coincident", None) + s4.constrain("segment3", "segment4", "Coincident", None) + s4.constrain("segment4", "segment1", "Coincident", None) + s4.constrain("segment2", "segment1", "Angle", 90) + s4.constrain("segment3", "segment2", "Angle", 90) + s4.constrain("segment4", "segment3", "Angle", 90) + s4.constrain("arc1", "segment2", "PointOnObject", 1) + s4.constrain("arc1", "FixedPoint", None) + + s4.solve() + + assert s4._solve_status["status"] == 4 + + s4.assemble() + assert s4._tags["arc1"][0].radius() == approx(0.5) + def test_dxf_import(): From ad7ac1861bbbb7d2f269846ceec1622a13d836a3 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Sun, 1 Feb 2026 17:41:48 +0100 Subject: [PATCH 09/13] fully constrain s4 --- tests/test_sketch.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index 2ade28d47..d2d098e7f 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -834,17 +834,22 @@ def test_point_on_object(): assert s3._tags["segment2"][0].Length() == approx(2 * sqrt(3)) assert s3._tags["segment3"][0].Length() == approx(2 * sqrt(3)) - # circle inscribed in square + # arc inscribed in square s4 = ( Sketch() - .arc((0, 0), 0.2, 0, 360, "arc1") + .arc((0, 0), 0.2, 0, 180, "arc1") + .segment((0, 0), (0, 0.2), "arcdir", True) .segment((-0.5, -0.5), (0.5, -0.5), "segment1", True) .segment((0.5, -0.5), (0.5, 0.5), "segment2", True) .segment((0.5, 0.5), (-0.5, 0.5), "segment3", True) .segment((-0.5, 0.5), (-0.5, -0.5), "segment4", True) ) + s4.constrain("arcdir", "arc1", "Coincident", None) + s4.constrain("arcdir", "Orientation", (0.2, 0)) + s4.constrain("arcdir", "FixedPoint", 0) s4.constrain("segment1", "Fixed", None) + s4.constrain("arc1", "ArcAngle", 180) s4.constrain("segment1", "segment2", "Coincident", None) s4.constrain("segment2", "segment3", "Coincident", None) s4.constrain("segment3", "segment4", "Coincident", None) @@ -852,7 +857,8 @@ def test_point_on_object(): s4.constrain("segment2", "segment1", "Angle", 90) s4.constrain("segment3", "segment2", "Angle", 90) s4.constrain("segment4", "segment3", "Angle", 90) - s4.constrain("arc1", "segment2", "PointOnObject", 1) + s4.constrain("arc1", "segment2", "PointOnObject", 0) + s4.constrain("arc1", "segment4", "PointOnObject", 1) s4.constrain("arc1", "FixedPoint", None) s4.solve() From 3029abe710d9138b4880c4eb4ebe9f6ec23fb514 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Mon, 2 Feb 2026 00:29:07 +0100 Subject: [PATCH 10/13] fix Codecov --- cadquery/occ_impl/sketch_solver.py | 8 ++------ tests/test_sketch.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/cadquery/occ_impl/sketch_solver.py b/cadquery/occ_impl/sketch_solver.py index 294ac56ba..0398c398b 100644 --- a/cadquery/occ_impl/sketch_solver.py +++ b/cadquery/occ_impl/sketch_solver.py @@ -239,16 +239,12 @@ def point_on_object_cost(x1, t1, x10, x2, t2, x20, val): end = x2[2:] v = end - start l = norm(v) - if l == 0: - return norm(p - start) - else: - d = p - start - return (v[0] * d[1] - v[1] * d[0]) / l + d = p - start + return (v[0] * d[1] - v[1] * d[0]) / l elif t2 == "CIRCLE": c = x2[:2] radius = x2[2] return norm(p - c) - radius - raise invalid_args(t2) # dictionary of individual constraint cost functions diff --git a/tests/test_sketch.py b/tests/test_sketch.py index d2d098e7f..f849f026d 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -1,5 +1,7 @@ import os +import OCP + from cadquery.sketch import Sketch, Vector, Location from cadquery.selectors import LengthNthSelector from cadquery import Edge, Vertex @@ -565,6 +567,21 @@ def test_constraint_validation(): with raises(ValueError): Sketch().segment(1.0, 1.0, "s").constrain("s", "Fixed", 1) + with raises(ValueError): + Sketch().segment((1, 0), (0, 0), "s1").segment((0, 1), (0, 0), "s2").constrain( + "s1", "s2", "PointOnObject", None + ).solve() + + with raises(ValueError): + Sketch().spline([(1.0, 1.0), (2.0, 1.0), (0.0, 0.0)], "s").segment( + (1, 0), (0, 0), "s1" + ).constrain("s1", "s", "PointOnObject", 1) + + with raises(ValueError): + Sketch().spline([(1.0, 1.0), (2.0, 1.0), (0.0, 0.0)], "s").segment( + (1, 0), (0, 0), "s1" + ).constrain("s", "s1", "PointOnObject", 1) + def test_constraint_solver(): @@ -868,6 +885,12 @@ def test_point_on_object(): s4.assemble() assert s4._tags["arc1"][0].radius() == approx(0.5) + # test that degenerate segments cannot exist and prevent division by zero + with raises(OCP.StdFail.StdFail_NotDone): + Sketch().segment((0, 1), (0, 0.2), "segment").segment( + (0, 0), (0, 0), "degenerate" + ) + def test_dxf_import(): From 483eaac874c46454cd594cb7a669ea55a4e96828 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Mon, 2 Feb 2026 00:56:34 +0100 Subject: [PATCH 11/13] add OCP.Standard.Standard_Failure for MacOSX --- tests/test_sketch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index f849f026d..b2e329f27 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -886,7 +886,7 @@ def test_point_on_object(): assert s4._tags["arc1"][0].radius() == approx(0.5) # test that degenerate segments cannot exist and prevent division by zero - with raises(OCP.StdFail.StdFail_NotDone): + with raises((OCP.StdFail.StdFail_NotDone, OCP.Standard.Standard_Failure)): Sketch().segment((0, 1), (0, 0.2), "segment").segment( (0, 0), (0, 0), "degenerate" ) From 78d1b68173c03641d310e041f4d38966f8027bd9 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Mon, 2 Feb 2026 13:25:25 +0100 Subject: [PATCH 12/13] add circle on circle test --- tests/test_sketch.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index b2e329f27..e56328ee4 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -885,6 +885,31 @@ def test_point_on_object(): s4.assemble() assert s4._tags["arc1"][0].radius() == approx(0.5) + R = 2 + s5 = ( + Sketch() + .arc((0, 0), R, 45, -270, "major") + .arc((0, R + 0.1), 1, 180 + 45, -270, "minor") + .segment((0, 0), (0, 1), "arcdir", True) + ) + + # lobes: circle on circle + s5.constrain("major", "Fixed", None) + s5.constrain("arcdir", "Fixed", None) + s5.constrain("minor", "major", "Coincident", None) + s5.constrain("major", "minor", "Coincident", None) + s5.constrain("minor", "major", "PointOnObject", None) + s5.constrain("minor", "arcdir", "PointOnObject", None) + s5.solve() + + assert s5._solve_status["status"] == 4 + s5.assemble() + assert s5._faces.isValid() + s5.export("/tmp/s5.dxf") + + r = sqrt((R / sqrt(2)) ** 2 + (R - R / sqrt(2)) ** 2) + assert s5._tags["minor"][0].radius() == approx(r) + # test that degenerate segments cannot exist and prevent division by zero with raises((OCP.StdFail.StdFail_NotDone, OCP.Standard.Standard_Failure)): Sketch().segment((0, 1), (0, 0.2), "segment").segment( From f1ba7156203a2ef5ab9d87ab15095bd0e45edaa3 Mon Sep 17 00:00:00 2001 From: Matteo Scalia Date: Mon, 2 Feb 2026 18:43:17 +0100 Subject: [PATCH 13/13] remove debug export --- tests/test_sketch.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_sketch.py b/tests/test_sketch.py index e56328ee4..074eb6545 100644 --- a/tests/test_sketch.py +++ b/tests/test_sketch.py @@ -905,7 +905,6 @@ def test_point_on_object(): assert s5._solve_status["status"] == 4 s5.assemble() assert s5._faces.isValid() - s5.export("/tmp/s5.dxf") r = sqrt((R / sqrt(2)) ** 2 + (R - R / sqrt(2)) ** 2) assert s5._tags["minor"][0].radius() == approx(r)