From 3b11f40d9debc629b692f3e99b24b62ecd21db9a Mon Sep 17 00:00:00 2001 From: gumyr Date: Thu, 11 Sep 2025 10:09:56 -0400 Subject: [PATCH] Moved edge/point ordering to make_constrained_arcs --- src/build123d/topology/constrained_lines.py | 50 ++++++++------------- src/build123d/topology/one_d.py | 42 ++++++++++------- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/src/build123d/topology/constrained_lines.py b/src/build123d/topology/constrained_lines.py index cb25f72..39cc35f 100644 --- a/src/build123d/topology/constrained_lines.py +++ b/src/build123d/topology/constrained_lines.py @@ -264,11 +264,11 @@ def _make_2tan_rad_arcs( if isinstance(object_1, tuple): object_one, object_one_constraint = object_1 else: - object_one, object_one_constraint = object_1, None + object_one, object_one_constraint = object_1, PositionConstraint.UNQUALIFIED if isinstance(object_2, tuple): object_two, object_two_constraint = object_2 else: - object_two, object_two_constraint = object_2, None + object_two, object_two_constraint = object_2, PositionConstraint.UNQUALIFIED # --------------------------- # Build inputs and GCC @@ -280,10 +280,6 @@ def _make_2tan_rad_arcs( object_two, object_two_constraint ) - # Put the Edge arg first when exactly one is an Edge (improves robustness) - if is_edge1 ^ is_edge2: - q_o1, q_o2 = (q_o1, q_o2) if is_edge1 else (q_o2, q_o1) - gcc = Geom2dGcc_Circ2d2TanRad(q_o1, q_o2, radius, TOLERANCE) if not gcc.IsDone() or gcc.NbSolutions() == 0: raise RuntimeError("Unable to find a tangent arc") @@ -349,25 +345,18 @@ def _make_2tan_on_arcs( Notes ----- - - `center_on` is treated as a **center locus** (not a tangency target). For a line - locus this uses Geom2dGcc_Circ2d2TanOn; for other 2D curves it uses the *Geo variant*. - - A point is NOT a valid center locus for the 2TanOn solver; use the TanCen variant - (fixed center) for that case. + - `center_on` is treated as a **center locus** (not a tangency target). """ - # Unpack optional qualifiers on the two tangency args - object_one_constraint = PositionConstraint.UNQUALIFIED - object_two_constraint = PositionConstraint.UNQUALIFIED - if isinstance(object_1, tuple): object_one, object_one_constraint = object_1 else: - object_one = object_1 + object_one, object_one_constraint = object_1, PositionConstraint.UNQUALIFIED if isinstance(object_2, tuple): object_two, object_two_constraint = object_2 else: - object_two = object_2 + object_two, object_two_constraint = object_2, PositionConstraint.UNQUALIFIED # --------------------------- # Build tangency inputs @@ -379,17 +368,6 @@ def _make_2tan_on_arcs( object_two, object_two_constraint ) - # Prefer "edge-first" ordering when exactly one arg is an Edge - if is_edge1 ^ is_edge2: - q_o1, q_o2 = (q_o1, q_o2) if is_edge1 else (q_o2, q_o1) - h_e1, h_e2 = (h_e1, h_e2) if is_edge1 else (h_e2, h_e1) - e1_first, e1_last, e2_first, e2_last = ( - (e1_first, e1_last, e2_first, e2_last) - if is_edge1 - else (e2_first, e2_last, e1_first, e1_last) - ) - is_edge1, is_edge2 = (True, False) if is_edge1 else (False, True) - # --------------------------- # Build center locus ("On") input # --------------------------- @@ -404,7 +382,10 @@ def _make_2tan_on_arcs( guesses.append((e2_last - e2_first) / 2 + e2_first) guesses.append((on_last - on_first) / 2 + on_first) - gcc = Geom2dGcc_Circ2d2TanOn(q_o1, q_o2, adapt_on, TOLERANCE, *guesses) + if is_edge1 or is_edge2: + gcc = Geom2dGcc_Circ2d2TanOn(q_o1, q_o2, adapt_on, TOLERANCE, *guesses) + else: + gcc = Geom2dGcc_Circ2d2TanOn(q_o1, q_o2, adapt_on, TOLERANCE) if not gcc.IsDone() or gcc.NbSolutions() == 0: raise RuntimeError("Unable to find a tangent arc with center_on constraint") @@ -510,10 +491,15 @@ def _make_3tan_arcs( q_o2, h_e2, e2_first, e2_last, is_edge2 = _as_gcc_arg(object_two, obj2_qual) q_o3, h_e3, e3_first, e3_last, is_edge3 = _as_gcc_arg(object_three, obj3_qual) - guesses = [ - (l - f) / 2 + f - for f, l in [(e1_first, e1_last), (e2_first, e2_last), (e3_first, e3_last)] - ] + # Provide initial guess parameters for all of the lines + guesses = [] + if is_edge1: + guesses.append((e1_last - e1_first) / 2 + e1_first) + if is_edge2: + guesses.append((e2_last - e2_first) / 2 + e2_first) + if is_edge3: + guesses.append((e3_last - e3_first) / 2 + e3_first) + # For 3Tan we keep the user-given order so the arc endpoints remain (arg1,arg2) gcc = Geom2dGcc_Circ2d3Tan(q_o1, q_o2, q_o3, TOLERANCE, *guesses) if not gcc.IsDone() or gcc.NbSolutions() == 0: diff --git a/src/build123d/topology/one_d.py b/src/build123d/topology/one_d.py index bf076ef..ac47101 100644 --- a/src/build123d/topology/one_d.py +++ b/src/build123d/topology/one_d.py @@ -1588,8 +1588,8 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): @classmethod def make_constrained_arcs( cls, - tangency_one: tuple[Edge, PositionConstraint] | Vertex | VectorLike, - tangency_two: tuple[Edge, PositionConstraint] | Vertex | VectorLike, + tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, + tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, *, radius: float, sagitta_constraint: LengthConstraint = LengthConstraint.SHORT, @@ -1598,8 +1598,8 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): Create all planar circular arcs of a given radius that are tangent/contacting the two provided objects on the XY plane. Args: - tangency_one (tuple[Edge, PositionConstraint] | Vertex | VectorLike): - tangency_two (tuple[Edge, PositionConstraint] | Vertex | VectorLike): + tangency_one (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): + tangency_two (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): Geometric entities to be contacted/touched by the circle(s) radius (float): arc radius sagitta_constraint (LengthConstraint, optional): returned arc selector @@ -1614,8 +1614,8 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): @classmethod def make_constrained_arcs( cls, - tangency_one: tuple[Edge, PositionConstraint] | Vertex | VectorLike, - tangency_two: tuple[Edge, PositionConstraint] | Vertex | VectorLike, + tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, + tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, *, center_on: Edge, sagitta_constraint: LengthConstraint = LengthConstraint.SHORT, @@ -1625,8 +1625,8 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): CENTER lies on a given locus (line/circle/curve) on the XY plane. Args: - tangency_one (tuple[Edge, PositionConstraint] | Vertex | VectorLike): - tangency_two (tuple[Edge, PositionConstraint] | Vertex | VectorLike): + tangency_one (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): + tangency_two (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): Geometric entities to be contacted/touched by the circle(s) center_on (Edge): center must lie on this edge sagitta_constraint (LengthConstraint, optional): returned arc selector @@ -1641,9 +1641,9 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): @classmethod def make_constrained_arcs( cls, - tangency_one: tuple[Edge, PositionConstraint] | Vertex | VectorLike, - tangency_two: tuple[Edge, PositionConstraint] | Vertex | VectorLike, - tangency_three: tuple[Edge, PositionConstraint] | Vertex | VectorLike, + tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, + tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, + tangency_three: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, *, sagitta_constraint: LengthConstraint = LengthConstraint.SHORT, ) -> ShapeList[Edge]: @@ -1651,9 +1651,9 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): Create planar circular arc(s) on XY tangent to three provided objects. Args: - tangency_one (tuple[Edge, PositionConstraint] | Vertex | VectorLike): - tangency_two (tuple[Edge, PositionConstraint] | Vertex | VectorLike): - tangency_three (tuple[Edge, PositionConstraint] | Vertex | VectorLike): + tangency_one (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): + tangency_two (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): + tangency_three (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): Geometric entities to be contacted/touched by the circle(s) sagitta_constraint (LengthConstraint, optional): returned arc selector (i.e. either the short, long or both arcs). Defaults to @@ -1667,7 +1667,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): @classmethod def make_constrained_arcs( cls, - tangency_one: tuple[Edge, PositionConstraint] | Vertex | VectorLike, + tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, *, center: VectorLike, ) -> ShapeList[Edge]: @@ -1677,7 +1677,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): a single object. Args: - tangency_one (tuple[Edge, PositionConstraint] | Vertex | VectorLike): + tangency_one (tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike): Geometric entity to be contacted/touched by the circle(s) center (VectorLike): center position @@ -1689,7 +1689,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): @classmethod def make_constrained_arcs( cls, - tangency_one: tuple[Edge, PositionConstraint] | Vertex | VectorLike, + tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike, *, radius: float, center_on: Edge, @@ -1742,6 +1742,14 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): tangencies = [ t for t in (tangency_one, tangency_two, tangency_three) if t is not None ] + + # Sort the tangency inputs so points are always last + tangent_tuples = [t if isinstance(t, tuple) else (t, None) for t in tangencies] + tangent_tuples = sorted( + tangent_tuples, key=lambda t: not issubclass(type(t[0]), Edge) + ) + tangencies = [t[0] if t[1] is None else t for t in tangent_tuples] + tan_count = len(tangencies) if not (1 <= tan_count <= 3): raise TypeError("Provide 1 to 3 tangency targets.")