mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-15 07:10:25 -08:00
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
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:
parent
872c62c645
commit
f0f79fccd4
5 changed files with 172 additions and 207 deletions
|
|
@ -55,13 +55,13 @@ __all__ = [
|
|||
"Intrinsic",
|
||||
"Keep",
|
||||
"Kind",
|
||||
"LengthConstraint",
|
||||
"Sagitta",
|
||||
"LengthMode",
|
||||
"MeshType",
|
||||
"Mode",
|
||||
"NumberDisplay",
|
||||
"PageSize",
|
||||
"PositionConstraint",
|
||||
"Tangency",
|
||||
"PositionMode",
|
||||
"PrecisionMode",
|
||||
"Select",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue