Refining code and adding tests
Some checks failed
benchmarks / benchmarks (macos-13, 3.12) (push) Has been cancelled
benchmarks / benchmarks (macos-14, 3.12) (push) Has been cancelled
benchmarks / benchmarks (ubuntu-latest, 3.12) (push) Has been cancelled
benchmarks / benchmarks (windows-latest, 3.12) (push) Has been cancelled
Upload coverage reports to Codecov / run (push) Has been cancelled
pylint / lint (3.10) (push) Has been cancelled
Run type checker / typecheck (3.10) (push) Has been cancelled
Run type checker / typecheck (3.13) (push) Has been cancelled
Wheel building and publishing / Build wheel on ubuntu-latest (push) Has been cancelled
tests / tests (macos-13, 3.10) (push) Has been cancelled
tests / tests (macos-13, 3.13) (push) Has been cancelled
tests / tests (macos-14, 3.10) (push) Has been cancelled
tests / tests (macos-14, 3.13) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.10) (push) Has been cancelled
tests / tests (ubuntu-latest, 3.13) (push) Has been cancelled
tests / tests (windows-latest, 3.10) (push) Has been cancelled
tests / tests (windows-latest, 3.13) (push) Has been cancelled
Wheel building and publishing / upload_pypi (push) Has been cancelled

This commit is contained in:
gumyr 2025-09-13 14:17:04 -04:00
parent 872c62c645
commit f0f79fccd4
5 changed files with 172 additions and 207 deletions

View file

@ -55,13 +55,13 @@ __all__ = [
"Intrinsic",
"Keep",
"Kind",
"LengthConstraint",
"Sagitta",
"LengthMode",
"MeshType",
"Mode",
"NumberDisplay",
"PageSize",
"PositionConstraint",
"Tangency",
"PositionMode",
"PrecisionMode",
"Select",

View file

@ -254,8 +254,8 @@ class FontStyle(Enum):
return f"<{self.__class__.__name__}.{self.name}>"
class LengthConstraint(Enum):
"""Length Constraint for sagitta selection"""
class Sagitta(Enum):
"""Sagitta selection"""
SHORT = 0
LONG = -1
@ -320,8 +320,8 @@ class PageSize(Enum):
return f"<{self.__class__.__name__}.{self.name}>"
class PositionConstraint(Enum):
"""Position Constraint for edge selection"""
class Tangency(Enum):
"""Tangency constraint for solvers edge selection"""
UNQUALIFIED = GccEnt_unqualified
ENCLOSING = GccEnt_enclosing

View file

@ -70,7 +70,7 @@ from OCP.gp import (
)
from OCP.TopoDS import TopoDS_Edge
from build123d.build_enums import LengthConstraint, PositionConstraint
from build123d.build_enums import Sagitta, Tangency
from build123d.geometry import TOLERANCE, Vector, VectorLike
from .zero_d import Vertex
from .shape_core import ShapeList
@ -113,7 +113,7 @@ def _forward_delta(u1: float, u2: float, first: float, period: float) -> float:
# Core helpers
# ---------------------------
def _edge_to_qualified_2d(
edge: TopoDS_Edge, position_constaint: PositionConstraint
edge: TopoDS_Edge, position_constaint: Tangency
) -> tuple[Geom2dGcc_QualifiedCurve, Geom2d_Curve, float, float]:
"""Convert a TopoDS_Edge into 2d curve & extract properties"""
@ -153,9 +153,7 @@ def _param_in_trim(u: float, first: float, last: float, h2d: Geom2d_Curve) -> bo
return (u >= first - TOLERANCE) and (u <= last + TOLERANCE)
def _as_gcc_arg(
obj: Edge | Vertex | VectorLike, constaint: PositionConstraint
) -> tuple[
def _as_gcc_arg(obj: Edge | Vertex | VectorLike, constaint: Tangency) -> tuple[
Geom2dGcc_QualifiedCurve | Geom2d_CartesianPoint,
Geom2d_Curve | None,
float | None,
@ -229,9 +227,9 @@ def _qstr(q) -> str:
def _make_2tan_rad_arcs(
*tangencies: tuple[Edge, PositionConstraint] | Edge | Vector, # 2
*tangencies: tuple[Edge, Tangency] | Edge | Vector, # 2
radius: float,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
edge_factory: Callable[[TopoDS_Edge], TWrap],
) -> list[Edge]:
"""
@ -259,8 +257,7 @@ def _make_2tan_rad_arcs(
# Unpack optional per-edge qualifiers (default UNQUALIFIED)
tangent_tuples = [
t if isinstance(t, tuple) else (t, PositionConstraint.UNQUALIFIED)
for t in tangencies
t if isinstance(t, tuple) else (t, Tangency.UNQUALIFIED) for t in tangencies
]
# Build inputs for GCC
@ -310,21 +307,21 @@ def _make_2tan_rad_arcs(
# )
# Build BOTH sagitta arcs and select by LengthConstraint
if sagitta_constraint == LengthConstraint.BOTH:
if sagitta == Sagitta.BOTH:
solutions.extend(_two_arc_edges_from_params(circ, u_circ1, u_circ2))
else:
arcs = _two_arc_edges_from_params(circ, u_circ1, u_circ2)
arcs = sorted(
arcs, key=lambda e: GCPnts_AbscissaPoint.Length_s(BRepAdaptor_Curve(e))
)
solutions.append(arcs[sagitta_constraint.value])
solutions.append(arcs[sagitta.value])
return ShapeList([edge_factory(e) for e in solutions])
def _make_2tan_on_arcs(
*tangencies: tuple[Edge, PositionConstraint] | Edge | Vector, # 2
*tangencies: tuple[Edge, Tangency] | Edge | Vector, # 2
center_on: Edge,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
edge_factory: Callable[[TopoDS_Edge], TWrap],
) -> ShapeList[Edge]:
"""
@ -338,8 +335,7 @@ def _make_2tan_on_arcs(
# Unpack optional per-edge qualifiers (default UNQUALIFIED)
tangent_tuples = [
t if isinstance(t, tuple) else (t, PositionConstraint.UNQUALIFIED)
for t in tangencies
t if isinstance(t, tuple) else (t, Tangency.UNQUALIFIED) for t in tangencies
]
# Build inputs for GCC
@ -351,7 +347,7 @@ def _make_2tan_on_arcs(
# Build center locus ("On") input
_, h_on2d, e_first[2], e_last[2], adapt_on = _edge_to_qualified_2d(
center_on.wrapped, PositionConstraint.UNQUALIFIED
center_on.wrapped, Tangency.UNQUALIFIED
)
is_edge[2] = True
@ -409,7 +405,7 @@ def _make_2tan_on_arcs(
continue
# Build sagitta arc(s) and select by LengthConstraint
if sagitta_constraint == LengthConstraint.BOTH:
if sagitta == Sagitta.BOTH:
solutions.extend(_two_arc_edges_from_params(circ, u_circ1, u_circ2))
else:
arcs = _two_arc_edges_from_params(circ, u_circ1, u_circ2)
@ -418,14 +414,14 @@ def _make_2tan_on_arcs(
arcs = sorted(
arcs, key=lambda e: GCPnts_AbscissaPoint.Length_s(BRepAdaptor_Curve(e))
)
solutions.append(arcs[sagitta_constraint.value])
solutions.append(arcs[sagitta.value])
return ShapeList([edge_factory(e) for e in solutions])
def _make_3tan_arcs(
*tangencies: tuple[Edge, PositionConstraint] | Edge | Vector, # 3
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
*tangencies: tuple[Edge, Tangency] | Edge | Vector, # 3
sagitta: Sagitta = Sagitta.SHORT,
edge_factory: Callable[[TopoDS_Edge], TWrap],
) -> ShapeList[Edge]:
"""
@ -433,14 +429,13 @@ def _make_3tan_arcs(
The circle is determined by the three tangency constraints; the returned arc(s)
are trimmed between the two tangency points corresponding to `tangencies[0]` and
`tangencies[1]`. Use `sagitta_constraint` to select the shorter/longer (or both) arc.
`tangencies[1]`. Use `sagitta` to select the shorter/longer (or both) arc.
Inputs must be representable on Plane.XY.
"""
# Unpack optional per-edge qualifiers (default UNQUALIFIED)
tangent_tuples = [
t if isinstance(t, tuple) else (t, PositionConstraint.UNQUALIFIED)
for t in tangencies
t if isinstance(t, tuple) else (t, Tangency.UNQUALIFIED) for t in tangencies
]
# Build inputs for GCC
@ -491,7 +486,7 @@ def _make_3tan_arcs(
continue
# Build arc(s) between u_circ1 and u_circ2 per LengthConstraint
if sagitta_constraint == LengthConstraint.BOTH:
if sagitta == Sagitta.BOTH:
out_topos.extend(_two_arc_edges_from_params(circ, u_circ1, u_circ2))
else:
arcs = _two_arc_edges_from_params(circ, u_circ1, u_circ2)
@ -501,13 +496,13 @@ def _make_3tan_arcs(
arcs,
key=lambda e: GCPnts_AbscissaPoint.Length_s(BRepAdaptor_Curve(e)),
)
out_topos.append(arcs[sagitta_constraint.value])
out_topos.append(arcs[sagitta.value])
return ShapeList([edge_factory(e) for e in out_topos])
def _make_tan_cen_arcs(
tangency: tuple[Edge, PositionConstraint] | Edge | Vector,
tangency: tuple[Edge, Tangency] | Edge | Vector,
*,
center: VectorLike | Vertex,
edge_factory: Callable[[TopoDS_Edge], TWrap],
@ -529,7 +524,7 @@ def _make_tan_cen_arcs(
if isinstance(tangency, tuple):
object_one, obj1_qual = tangency
else:
object_one, obj1_qual = tangency, PositionConstraint.UNQUALIFIED
object_one, obj1_qual = tangency, Tangency.UNQUALIFIED
# ---------------------------
# Build fixed center (gp_Pnt2d)
@ -590,7 +585,7 @@ def _make_tan_cen_arcs(
def _make_tan_on_rad_arcs(
tangency: tuple[Edge, PositionConstraint] | Edge | Vector,
tangency: tuple[Edge, Tangency] | Edge | Vector,
*,
center_on: Edge,
radius: float,
@ -615,7 +610,7 @@ def _make_tan_on_rad_arcs(
if isinstance(tangency, tuple):
object_one, obj1_qual = tangency
else:
object_one, obj1_qual = tangency, PositionConstraint.UNQUALIFIED
object_one, obj1_qual = tangency, Tangency.UNQUALIFIED
# --- build tangency input (point/edge) ---
q_o1, h_e1, e1_first, e1_last, is_edge1 = _as_gcc_arg(object_one, obj1_qual)
@ -627,7 +622,7 @@ def _make_tan_on_rad_arcs(
# Project the center locus Edge to 2D (XY)
_, h_on2d, on_first, on_last, adapt_on = _edge_to_qualified_2d(
on_obj.wrapped, PositionConstraint.UNQUALIFIED
on_obj.wrapped, Tangency.UNQUALIFIED
)
gcc = Geom2dGcc_Circ2dTanOnRad(q_o1, adapt_on, radius, TOLERANCE)

View file

@ -198,8 +198,8 @@ from build123d.build_enums import (
GeomType,
Keep,
Kind,
LengthConstraint,
PositionConstraint,
Sagitta,
Tangency,
PositionMode,
Side,
)
@ -1588,11 +1588,11 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
@classmethod
def make_constrained_arcs(
cls,
tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_one: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
*,
radius: float,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
) -> ShapeList[Edge]:
"""
Create all planar circular arcs of a given radius that are tangent/contacting
@ -1602,7 +1602,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
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
sagitta (LengthConstraint, optional): returned arc selector
(i.e. either the short, long or both arcs). Defaults to
LengthConstraint.SHORT.
@ -1614,11 +1614,11 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
@classmethod
def make_constrained_arcs(
cls,
tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_one: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
*,
center_on: Edge,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
) -> ShapeList[Edge]:
"""
Create all planar circular arcs whose circle is tangent to two objects and whose
@ -1629,7 +1629,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
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
sagitta (LengthConstraint, optional): returned arc selector
(i.e. either the short, long or both arcs). Defaults to
LengthConstraint.SHORT.
@ -1641,11 +1641,11 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
@classmethod
def make_constrained_arcs(
cls,
tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_three: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_one: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
tangency_two: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
tangency_three: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
*,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
) -> ShapeList[Edge]:
"""
Create planar circular arc(s) on XY tangent to three provided objects.
@ -1655,7 +1655,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
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
sagitta (LengthConstraint, optional): returned arc selector
(i.e. either the short, long or both arcs). Defaults to
LengthConstraint.SHORT.
@ -1667,7 +1667,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
@classmethod
def make_constrained_arcs(
cls,
tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_one: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
*,
center: VectorLike,
) -> ShapeList[Edge]:
@ -1689,7 +1689,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
@classmethod
def make_constrained_arcs(
cls,
tangency_one: tuple[Edge, PositionConstraint] | Edge | Vertex | VectorLike,
tangency_one: tuple[Edge, Tangency] | Edge | Vertex | VectorLike,
*,
radius: float,
center_on: Edge,
@ -1706,7 +1706,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
Geometric entity to be contacted/touched by the circle(s)
radius (float): arc radius
center_on (Edge): center must lie on this edge
sagitta_constraint (LengthConstraint, optional): returned arc selector
sagitta (LengthConstraint, optional): returned arc selector
(i.e. either the short, long or both arcs). Defaults to
LengthConstraint.SHORT.
@ -1718,7 +1718,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
def make_constrained_arcs(
cls,
*args,
sagitta_constraint: LengthConstraint = LengthConstraint.SHORT,
sagitta: Sagitta = Sagitta.SHORT,
**kwargs,
) -> ShapeList[Edge]:
@ -1738,22 +1738,22 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
if kwargs:
raise TypeError(f"Unexpected argument(s): {', '.join(kwargs.keys())}")
tangencies_raw = [
tangency_args = [
t for t in (tangency_one, tangency_two, tangency_three) if t is not None
]
tangencies = []
for tangency_raw in tangencies_raw:
if (
isinstance(tangency_raw, tuple)
and not isinstance(tangency_raw[0], Edge)
) or not isinstance(tangency_raw, Edge):
for tangency_arg in tangency_args:
if isinstance(tangency_arg, Edge):
tangencies.append(tangency_arg)
continue
if isinstance(tangency_arg, tuple) and isinstance(tangency_arg[0], Edge):
tangencies.append(tangency_arg)
continue
# if not Edges or constrained Edges convert to Vectors
try:
tangency = Vector(tangency_raw)
except:
raise TypeError("Invalid tangency")
else:
tangency = tangency_raw
tangencies.append(tangency)
tangencies.append(Vector(tangency_arg))
except Exception as exc:
raise TypeError(f"Invalid tangency: {tangency_arg!r}") from exc
# Sort the tangency inputs so points are always last
tangent_tuples = [t if isinstance(t, tuple) else (t, None) for t in tangencies]
@ -1780,7 +1780,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
return _make_2tan_rad_arcs(
*tangencies,
radius=radius,
sagitta_constraint=sagitta_constraint,
sagitta=sagitta,
edge_factory=cls,
)
if (
@ -1792,13 +1792,11 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
return _make_2tan_on_arcs(
*tangencies,
center_on=center_on,
sagitta_constraint=sagitta_constraint,
sagitta=sagitta,
edge_factory=cls,
)
if tan_count == 3 and radius is None and center is None and center_on is None:
return _make_3tan_arcs(
*tangencies, sagitta_constraint=sagitta_constraint, edge_factory=cls
)
return _make_3tan_arcs(*tangencies, sagitta=sagitta, edge_factory=cls)
if (
tan_count == 1
and center is not None

View file

@ -35,11 +35,12 @@ from build123d.objects_curve import (
IntersectingLine,
ThreePointArc,
)
from build123d.objects_sketch import Rectangle
from build123d.topology import Edge, Solid, Vertex
from build123d.geometry import Axis, Vector
from build123d.build_enums import PositionConstraint, LengthConstraint, LengthMode
from build123d.topology import Edge, Solid, Vertex, Wire, topo_explore_common_vertex
from build123d.geometry import Axis, Vector, TOLERANCE
from build123d.build_enums import Tangency, Sagitta, LengthMode
from OCP.BRep import BRep_Tool
from OCP.GeomAbs import GeomAbs_C1
from OCP.LocalAnalysis import LocalAnalysis_CurveContinuity
radius = 0.5
e1 = Line((-2, 0), (2, 0))
@ -51,13 +52,13 @@ e1.color = "Grey"
e2.color = "Red"
def test_constrained_arcs_0():
def test_constrained_arcs_arg_processing():
"""Test input error handling"""
with pytest.raises(TypeError):
Edge.make_constrained_arcs(Solid.make_box(1, 1, 1), (1, 0), radius=0.5)
with pytest.raises(TypeError):
Edge.make_constrained_arcs(
(Vector(0, 0), PositionConstraint.UNQUALIFIED), (1, 0), radius=0.5
(Vector(0, 0), Tangency.UNQUALIFIED), (1, 0), radius=0.5
)
with pytest.raises(TypeError):
Edge.make_constrained_arcs(pnt1=(1, 1, 1), pnt2=(1, 0), radius=0.5)
@ -69,24 +70,26 @@ def test_constrained_arcs_0():
Edge.make_constrained_arcs((0, 0), (0, 0.5), radius=-0.5)
def test_constrained_arcs_1():
def test_tan2_rad_arcs_1():
"""2 edges & radius"""
e1 = Line((-2, 0), (2, 0))
e2 = Line((0, -2), (0, 2))
tan2_rad_edges = Edge.make_constrained_arcs(
e1,
e2,
radius=0.5,
sagitta_constraint=LengthConstraint.BOTH,
e1, e2, radius=0.5, sagitta=Sagitta.BOTH
)
assert len(tan2_rad_edges) == 8
tan2_rad_edges = Edge.make_constrained_arcs(e1, e2, radius=0.5)
assert len(tan2_rad_edges) == 4
tan2_rad_edges = Edge.make_constrained_arcs(
(e1, Tangency.UNQUALIFIED), (e2, Tangency.UNQUALIFIED), radius=0.5
)
assert len(tan2_rad_edges) == 4
def test_constrained_arcs_2():
def test_tan2_rad_arcs_2():
"""2 edges & radius"""
e1 = CenterArc((0, 0), 1, 0, 90)
e2 = Line((1, 0), (2, 0))
@ -95,7 +98,7 @@ def test_constrained_arcs_2():
assert len(tan2_rad_edges) == 1
def test_constrained_arcs_3():
def test_tan2_rad_arcs_3():
"""2 points & radius"""
tan2_rad_edges = Edge.make_constrained_arcs((0, 0), (0, 0.5), radius=0.5)
assert len(tan2_rad_edges) == 2
@ -111,129 +114,98 @@ def test_constrained_arcs_3():
assert len(tan2_rad_edges) == 2
# tan2_rad_edges = Edge.make_constrained_arcs(
# (e1, PositionConstraint.OUTSIDE),
# (e2, PositionConstraint.UNQUALIFIED),
# radius=radius,
# sagitta_constraint=LengthConstraint.SHORT,
# )
def test_tan2_center_on_1():
"""2 tangents & center on"""
c1 = PolarLine((0, 0), 4, -20, length_mode=LengthMode.HORIZONTAL)
c2 = Line((4, -2), (4, 2))
c3_center_on = Line((3, -2), (3, 2))
tan2_on_edge = Edge.make_constrained_arcs(
(c1, Tangency.UNQUALIFIED),
(c2, Tangency.UNQUALIFIED),
center_on=c3_center_on,
)
assert len(tan2_on_edge) == 1
# # 2 lines & radius
# # 2 points & radius
# p1 = Vector(0, 0, 0)
# p2 = Vector(3, 0, 0)
# tan2_rad_pnts = Edge().make_constrained_arcs(p1, p2, radius=3)
# #
# # 2 tangents & center on
# c1 = PolarLine((0, 0), 4, -20, length_mode=LengthMode.HORIZONTAL)
# c2 = Line((4, -2), (4, 2))
# c3_center_on_this_line = Line((3, -2), (3, 2))
# c4 = Line((0, 0), (0, 10))
# for c in (c1, c2, c3_center_on_this_line, c4):
# c.color = "LightGrey"
# tan2_on_edge = Edge.make_constrained_arcs(
# (c1, PositionConstraint.UNQUALIFIED),
# (c2, PositionConstraint.UNQUALIFIED),
# center_on=c3_center_on_this_line,
# )[0]
# l1 = Line(tan2_on_edge @ 0, (0, 0))
# l2 = JernArc(tan2_on_edge @ 1, tan2_on_edge % 1, tan2_on_edge.radius, 45)
# l3 = IntersectingLine(l2 @ 1, l2 % 1, c4)
# #
# # tangent & center
# c5 = PolarLine((0, 0), 4, 60)
# center1 = Vector(2, 1)
# tan_center = Edge.make_constrained_arcs(
# (c5, PositionConstraint.UNQUALIFIED), center=center1
# )
# #
# # point & center
# p3 = Vector(-2.5, 1.5)
# center2 = Vector(-2, 1)
# pnt_center = Edge.make_constrained_arcs(p3, center=center2)
# #
# # tangent, radius, center on
# # tan_rad_on = Edge.make_constrained_arcs(
# # (c1, PositionConstraint.UNQUALIFIED), radius=1, center_on=c3_center_on_this_line
# # )
# tan_rad_on = Edge.make_constrained_arcs(c1, radius=1, center_on=c3_center_on_this_line)
# print(f"{len(tan_rad_on)=}")
# objects = [
# (c1, PositionConstraint.ENCLOSED),
# (Vector(1, 2, 3), None),
# (Edge.make_line((0, 0), (1, 0)), PositionConstraint.UNQUALIFIED),
# ]
# s = sorted(objects, key=lambda t: not issubclass(type(t[0]), Edge))
# print(f"{objects=},{s=}")
# #
# # 3 tangents
# c6 = PolarLine((0, 0), 4, 40)
# c7 = CenterArc((0, 0), 4, 0, 90)
# tan3 = Edge.make_constrained_arcs(
# (c5, PositionConstraint.UNQUALIFIED),
# (c6, PositionConstraint.UNQUALIFIED),
# (c7, PositionConstraint.UNQUALIFIED),
# )
# tan3 = Edge.make_constrained_arcs(c5, c6, c7)
# # v = Vertex(1, 2, 0)
# # v.color = "Teal"
# # show(e1, e2, tan2_rad, v)
# r_left, r_right = 0.75, 1.0
# r_bottom, r_top = 6, 8
# con_circle_left = CenterArc((-2, 0), r_left, 0, 360)
# con_circle_right = CenterArc((2, 0), r_right, 0, 360)
# for c in [con_circle_left, con_circle_right]:
# c.color = "LightGrey"
# # for con1, con2 in itertools.product(PositionConstraint, PositionConstraint):
# # try:
# # egg1 = Edge.make_constrained_arcs(
# # (c8, con1),
# # (c9, con2),
# # radius=10,
# # )
# # except:
# # print(f"{con1},{con2} failed")
# # else:
# # print(f"{con1},{con2} {len(egg1)=}")
# egg_bottom = Edge.make_constrained_arcs(
# (con_circle_right, PositionConstraint.OUTSIDE),
# (con_circle_left, PositionConstraint.OUTSIDE),
# radius=r_bottom,
# ).sort_by(Axis.Y)[0]
# egg_top = Edge.make_constrained_arcs(
# (con_circle_right, PositionConstraint.ENCLOSING),
# (con_circle_left, PositionConstraint.ENCLOSING),
# radius=r_top,
# ).sort_by(Axis.Y)[-1]
# egg_right = ThreePointArc(
# egg_bottom.vertices().sort_by(Axis.X)[-1],
# con_circle_right @ 0,
# egg_top.vertices().sort_by(Axis.X)[-1],
# )
# egg_left = ThreePointArc(
# egg_bottom.vertices().sort_by(Axis.X)[0],
# con_circle_left @ 0.5,
# egg_top.vertices().sort_by(Axis.X)[0],
# )
# egg_plant = Wire([egg_left, egg_top, egg_right, egg_bottom])
def test_tan_center_on_1():
"""1 tangent & center on"""
c5 = PolarLine((0, 0), 4, 60)
tan_center = Edge.make_constrained_arcs((c5, Tangency.UNQUALIFIED), center=(2, 1))
assert len(tan_center) == 1
assert tan_center[0].is_closed
# make_constrained_arcs
def test_pnt_center_1():
"""pnt & center"""
pnt_center = Edge.make_constrained_arcs((-2.5, 1.5), center=(-2, 1))
assert len(pnt_center) == 1
assert pnt_center[0].is_closed
# class TestConstrainedArcs(unittest.TestCase):
# def test_close(self):
# self.assertAlmostEqual(
# Edge.make_circle(1, end_angle=180).close().length, math.pi + 2, 5
# )
# self.assertAlmostEqual(Edge.make_circle(1).close().length, 2 * math.pi, 5)
def test_tan_rad_center_on_1():
"""tangent, radius, center on"""
c1 = PolarLine((0, 0), 4, -20, length_mode=LengthMode.HORIZONTAL)
c3_center_on = Line((3, -2), (3, 2))
tan_rad_on = Edge.make_constrained_arcs(
(c1, Tangency.UNQUALIFIED), radius=1, center_on=c3_center_on
)
assert len(tan_rad_on) == 1
assert tan_rad_on[0].is_closed
def test_tan3_1():
"""3 tangents"""
c5 = PolarLine((0, 0), 4, 60)
c6 = PolarLine((0, 0), 4, 40)
c7 = CenterArc((0, 0), 4, 0, 90)
tan3 = Edge.make_constrained_arcs(
(c5, Tangency.UNQUALIFIED),
(c6, Tangency.UNQUALIFIED),
(c7, Tangency.UNQUALIFIED),
)
assert len(tan3) == 1
assert not tan3[0].is_closed
def test_eggplant():
"""complex set of 4 arcs"""
r_left, r_right = 0.75, 1.0
r_bottom, r_top = 6, 8
con_circle_left = CenterArc((-2, 0), r_left, 0, 360)
con_circle_right = CenterArc((2, 0), r_right, 0, 360)
egg_bottom = Edge.make_constrained_arcs(
(con_circle_right, Tangency.OUTSIDE),
(con_circle_left, Tangency.OUTSIDE),
radius=r_bottom,
).sort_by(Axis.Y)[0]
egg_top = Edge.make_constrained_arcs(
(con_circle_right, Tangency.ENCLOSING),
(con_circle_left, Tangency.ENCLOSING),
radius=r_top,
).sort_by(Axis.Y)[-1]
egg_right = ThreePointArc(
egg_bottom.vertices().sort_by(Axis.X)[-1],
con_circle_right @ 0,
egg_top.vertices().sort_by(Axis.X)[-1],
)
egg_left = ThreePointArc(
egg_bottom.vertices().sort_by(Axis.X)[0],
con_circle_left @ 0.5,
egg_top.vertices().sort_by(Axis.X)[0],
)
egg_plant = Wire([egg_left, egg_top, egg_right, egg_bottom])
assert egg_plant.is_closed
egg_plant_edges = egg_plant.edges().sort_by(egg_plant)
common_vertex_cnt = sum(
topo_explore_common_vertex(egg_plant_edges[i], egg_plant_edges[(i + 1) % 4])
is not None
for i in range(4)
)
assert common_vertex_cnt == 4
# C1 continuity
assert all(
(egg_plant_edges[i] % 1 - egg_plant_edges[(i + 1) % 4] % 0).length < TOLERANCE
for i in range(4)
)