Deprecated to_axis Issue #155
Some checks are pending
benchmarks / benchmarks (macos-13, 3.12) (push) Waiting to run
benchmarks / benchmarks (macos-14, 3.12) (push) Waiting to run
benchmarks / benchmarks (ubuntu-latest, 3.12) (push) Waiting to run
benchmarks / benchmarks (windows-latest, 3.12) (push) Waiting to run
Upload coverage reports to Codecov / run (push) Waiting to run
pylint / lint (3.10) (push) Waiting to run
Run type checker / typecheck (3.10) (push) Waiting to run
Run type checker / typecheck (3.13) (push) Waiting to run
Wheel building and publishing / Build wheel on ubuntu-latest (push) Waiting to run
Wheel building and publishing / upload_pypi (push) Blocked by required conditions
tests / tests (macos-13, 3.10) (push) Waiting to run
tests / tests (macos-13, 3.13) (push) Waiting to run
tests / tests (macos-14, 3.10) (push) Waiting to run
tests / tests (macos-14, 3.13) (push) Waiting to run
tests / tests (ubuntu-latest, 3.10) (push) Waiting to run
tests / tests (ubuntu-latest, 3.13) (push) Waiting to run
tests / tests (windows-latest, 3.10) (push) Waiting to run
tests / tests (windows-latest, 3.13) (push) Waiting to run

This commit is contained in:
gumyr 2025-05-18 10:18:08 -04:00
parent 67115111e2
commit 2efd21ff58
8 changed files with 94 additions and 59 deletions

View file

@ -159,7 +159,7 @@ class Hinge(Compound):
for hole, hole_location in enumerate(hole_locations): for hole, hole_location in enumerate(hole_locations):
CylindricalJoint( CylindricalJoint(
label="hole" + str(hole), label="hole" + str(hole),
axis=hole_location.to_axis(), axis=Axis(hole_location),
linear_range=(-2 * CM, 2 * CM), linear_range=(-2 * CM, 2 * CM),
angular_range=(0, 360), angular_range=(0, 360),
) )

View file

@ -1,6 +1,7 @@
""" """
Experimental Joint development file Experimental Joint development file
""" """
from build123d import * from build123d import *
from ocp_vscode import * from ocp_vscode import *
@ -72,9 +73,9 @@ swing_arm_hinge_edge: Edge = (
.sort_by(Axis.X)[-2:] .sort_by(Axis.X)[-2:]
.sort_by(Axis.Y)[0] .sort_by(Axis.Y)[0]
) )
swing_arm_hinge_axis = swing_arm_hinge_edge.to_axis() swing_arm_hinge_axis = Axis(swing_arm_hinge_edge)
base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1] base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1]
base_hinge_axis = base_corner_edge.to_axis() base_hinge_axis = Axis(base_corner_edge)
j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180)) j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180))
j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location) j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location)
base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90) base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90)
@ -86,7 +87,7 @@ slider_arm = JointBox(4, 1, 2, 0.2)
s1 = LinearJoint( s1 = LinearJoint(
"slide", "slide",
base, base,
axis=Edge.make_mid_way(*base_top_edges, 0.67).to_axis(), axis=Axis(Edge.make_mid_way(*base_top_edges, 0.67)),
linear_range=(0, base_top_edges[0].length), linear_range=(0, base_top_edges[0].length),
) )
s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0))) s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0)))
@ -111,7 +112,7 @@ j5.connect_to(j6, position=-1, angle=90)
j7 = LinearJoint( j7 = LinearJoint(
"slot", "slot",
base, base,
axis=Edge.make_mid_way(*base_top_edges, 0.33).to_axis(), axis=Axis(Edge.make_mid_way(*base_top_edges, 0.33)),
linear_range=(0, base_top_edges[0].length), linear_range=(0, base_top_edges[0].length),
) )
pin_arm = JointBox(2, 1, 2) pin_arm = JointBox(2, 1, 2)

View file

@ -62,9 +62,9 @@ swing_arm_hinge_edge = (
.sort_by(Axis.X)[-2:] .sort_by(Axis.X)[-2:]
.sort_by(Axis.Y)[0] .sort_by(Axis.Y)[0]
) )
swing_arm_hinge_axis = swing_arm_hinge_edge.to_axis() swing_arm_hinge_axis = Axis(swing_arm_hinge_edge)
base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1] base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1]
base_hinge_axis = base_corner_edge.to_axis() base_hinge_axis = Axis(base_corner_edge)
j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180)) j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180))
j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location) j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location)
base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90) base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90)
@ -77,7 +77,7 @@ slider_arm = JointBox(4, 1, 2, 0.2)
s1 = LinearJoint( s1 = LinearJoint(
"slide", "slide",
base, base,
axis=Edge.make_mid_way(*base_top_edges, 0.67).to_axis(), axis=Axis(Edge.make_mid_way(*base_top_edges, 0.67)),
linear_range=(0, base_top_edges[0].length), linear_range=(0, base_top_edges[0].length),
) )
s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0))) s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0)))
@ -102,7 +102,7 @@ j5.connect_to(j6, position=-1, angle=90)
j7 = LinearJoint( j7 = LinearJoint(
"slot", "slot",
base, base,
axis=Edge.make_mid_way(*base_top_edges, 0.33).to_axis(), axis=Axis(Edge.make_mid_way(*base_top_edges, 0.33)),
linear_range=(0, base_top_edges[0].length), linear_range=(0, base_top_edges[0].length),
) )
pin_arm = JointBox(2, 1, 2) pin_arm = JointBox(2, 1, 2)

View file

@ -593,6 +593,7 @@ class Axis(metaclass=AxisMeta):
origin (VectorLike): start point origin (VectorLike): start point
direction (VectorLike): direction direction (VectorLike): direction
edge (Edge): origin & direction defined by start of edge edge (Edge): origin & direction defined by start of edge
location (Location): location to convert to axis
Attributes: Attributes:
position (Vector): the global position of the axis origin position (Vector): the global position of the axis origin
@ -603,49 +604,56 @@ class Axis(metaclass=AxisMeta):
_dim = 1 _dim = 1
@overload @overload
def __init__(self, gp_ax1: gp_Ax1): # pragma: no cover def __init__(self, gp_ax1: gp_Ax1):
"""Axis: point and direction""" """Axis: point and direction"""
@overload @overload
def __init__(self, origin: VectorLike, direction: VectorLike): # pragma: no cover def __init__(self, location: Location):
"""Axis from location"""
@overload
def __init__(self, origin: VectorLike, direction: VectorLike):
"""Axis: point and direction""" """Axis: point and direction"""
@overload @overload
def __init__(self, edge: Edge): # pragma: no cover def __init__(self, edge: Edge):
"""Axis: start of Edge""" """Axis: start of Edge"""
def __init__(self, *args, **kwargs): def __init__(
self, *args, **kwargs
): # pylint: disable=too-many-branches, too-many-locals
gp_ax1 = kwargs.pop("gp_ax1", None) gp_ax1 = kwargs.pop("gp_ax1", None)
origin = kwargs.pop("origin", None) origin = kwargs.pop("origin", None)
direction = kwargs.pop("direction", None) direction = kwargs.pop("direction", None)
edge = kwargs.pop("edge", None) edge = kwargs.pop("edge", None)
location = kwargs.pop("location", None)
# Handle unexpected kwargs # Handle unexpected kwargs
if kwargs: if kwargs:
raise ValueError(f"Unexpected argument(s): {', '.join(kwargs.keys())}") raise ValueError(f"Unexpected argument(s): {', '.join(kwargs.keys())}")
# Handle positional arguments
if len(args) == 1: if len(args) == 1:
if isinstance(args[0], gp_Ax1): arg = args[0]
gp_ax1 = args[0] if isinstance(arg, gp_Ax1):
elif ( gp_ax1 = arg
hasattr(args[0], "wrapped") elif isinstance(arg, Location):
and args[0].wrapped is not None location = arg
and isinstance(args[0].wrapped, TopoDS_Edge) elif hasattr(arg, "wrapped") and isinstance(arg.wrapped, TopoDS_Edge):
): edge = arg
edge = args[0] elif isinstance(arg, (Vector, tuple)):
origin = arg
else: else:
origin = args[0] raise ValueError(f"Unrecognized single argument: {arg}")
elif len(args) == 2: elif len(args) == 2:
origin, direction = args origin, direction = args
# Handle edge-based construction
if edge is not None: if edge is not None:
if ( if not (hasattr(edge, "wrapped") and isinstance(edge.wrapped, TopoDS_Edge)):
hasattr(edge, "wrapped") raise ValueError(f"Invalid edge argument: {edge}")
and edge.wrapped is not None
and isinstance(edge.wrapped, TopoDS_Edge)
):
# Extract the start point and tangent
topods_edge: TopoDS_Edge = edge.wrapped # type: ignore[annotation-unchecked] topods_edge: TopoDS_Edge = edge.wrapped # type: ignore[annotation-unchecked]
curve = BRep_Tool.Curve_s(topods_edge, float(), float()) curve = BRep_Tool.Curve_s(topods_edge, float(), float())
param_min, _ = BRep_Tool.Range_s(topods_edge) param_min, _ = BRep_Tool.Range_s(topods_edge)
@ -654,24 +662,26 @@ class Axis(metaclass=AxisMeta):
curve.D1(param_min, origin_pnt, tangent_vec) curve.D1(param_min, origin_pnt, tangent_vec)
origin = Vector(origin_pnt) origin = Vector(origin_pnt)
direction = Vector(gp_Dir(tangent_vec)) direction = Vector(gp_Dir(tangent_vec))
else:
raise ValueError(f"Invalid argument {edge}")
if gp_ax1 is not None: # Convert location to axis
if not isinstance(gp_ax1, gp_Ax1): if location is not None:
raise ValueError(f"Invalid Axis parameter {gp_ax1}") gp_ax1 = Axis.Z.located(location).wrapped
self.wrapped: gp_Ax1 = gp_ax1 # type: ignore[annotation-unchecked]
else: # Construct self.wrapped from gp_ax1 or origin/direction
if gp_ax1 is None:
try: try:
origin_vector = Vector(origin) origin_vector = Vector(origin)
direction_vector = Vector(direction) direction_vector = Vector(direction)
except TypeError as exc: gp_ax1 = gp_Ax1(
raise ValueError("Invalid Axis parameters") from exc
self.wrapped = gp_Ax1(
origin_vector.to_pnt(), origin_vector.to_pnt(),
gp_Dir(*tuple(direction_vector.normalized())), gp_Dir(*tuple(direction_vector.normalized())),
) )
except Exception as exc:
raise ValueError("Invalid Axis parameters") from exc
elif not isinstance(gp_ax1, gp_Ax1):
raise ValueError(f"Invalid Axis parameter: {gp_ax1}")
self.wrapped: gp_Ax1 = gp_ax1 # type: ignore[annotation-unchecked]
@property @property
def position(self): def position(self):
@ -1425,7 +1435,9 @@ class Location:
"""Location with translation t and rotation around direction by angle """Location with translation t and rotation around direction by angle
with respect to the original location.""" with respect to the original location."""
def __init__(self, *args, **kwargs): def __init__(
self, *args, **kwargs
): # pylint: disable=too-many-branches, too-many-locals, too-many-statements
position = kwargs.pop("position", None) position = kwargs.pop("position", None)
orientation = kwargs.pop("orientation", None) orientation = kwargs.pop("orientation", None)
ordering = kwargs.pop("ordering", None) ordering = kwargs.pop("ordering", None)
@ -1751,6 +1763,12 @@ class Location:
def to_axis(self) -> Axis: def to_axis(self) -> Axis:
"""Convert the location into an Axis""" """Convert the location into an Axis"""
warnings.warn(
"to_axis is deprecated and will be removed in a future version. "
"Use 'Axis(Location)' instead.",
DeprecationWarning,
stacklevel=2,
)
return Axis.Z.located(self) return Axis.Z.located(self)
def to_tuple(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]: def to_tuple(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]:

View file

@ -1563,7 +1563,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
Returns: Returns:
Edge: linear Edge between two Edges Edge: linear Edge between two Edges
""" """
flip = first.to_axis().is_opposite(second.to_axis()) flip = Axis(first).is_opposite(Axis(second))
pnts = [ pnts = [
Edge.make_line( Edge.make_line(
first.position_at(i), second.position_at(1 - i if flip else i) first.position_at(i), second.position_at(1 - i if flip else i)
@ -2184,6 +2184,12 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
def to_axis(self) -> Axis: def to_axis(self) -> Axis:
"""Translate a linear Edge to an Axis""" """Translate a linear Edge to an Axis"""
warnings.warn(
"to_axis is deprecated and will be removed in a future version. "
"Use 'Axis(Edge)' instead.",
DeprecationWarning,
stacklevel=2,
)
if self.geom_type != GeomType.LINE: if self.geom_type != GeomType.LINE:
raise ValueError( raise ValueError(
f"to_axis is only valid for linear Edges not {self.geom_type}" f"to_axis is only valid for linear Edges not {self.geom_type}"

View file

@ -33,7 +33,7 @@ import unittest
import numpy as np import numpy as np
from OCP.gp import gp_Ax1, gp_Dir, gp_Pnt from OCP.gp import gp_Ax1, gp_Dir, gp_Pnt
from build123d.geometry import Axis, Location, Plane, Vector from build123d.geometry import Axis, Location, Plane, Vector
from build123d.topology import Edge from build123d.topology import Edge, Vertex
class AlwaysEqual: class AlwaysEqual:
@ -65,10 +65,18 @@ class TestAxis(unittest.TestCase):
self.assertAlmostEqual(test_axis.position, (1, 2, 3), 5) self.assertAlmostEqual(test_axis.position, (1, 2, 3), 5)
self.assertAlmostEqual(test_axis.direction, (0, 0, 1), 5) self.assertAlmostEqual(test_axis.direction, (0, 0, 1), 5)
with self.assertRaises(ValueError):
Axis("one")
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
Axis("one", "up") Axis("one", "up")
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
Axis(one="up") Axis(one="up")
with self.assertRaises(ValueError):
bad_edge = Edge()
bad_edge.wrapped = Vertex(0, 1, 2).wrapped
Axis(edge=bad_edge)
with self.assertRaises(ValueError):
Axis(gp_ax1=Edge.make_line((0, 0), (1, 0)))
def test_axis_from_occt(self): def test_axis_from_occt(self):
occt_axis = gp_Ax1(gp_Pnt(1, 1, 1), gp_Dir(0, 1, 0)) occt_axis = gp_Ax1(gp_Pnt(1, 1, 1), gp_Dir(0, 1, 0))
@ -100,6 +108,11 @@ class TestAxis(unittest.TestCase):
self.assertAlmostEqual(y_axis.position, (0, 0, 1), 5) self.assertAlmostEqual(y_axis.position, (0, 0, 1), 5)
self.assertAlmostEqual(y_axis.direction, (0, 1, 0), 5) self.assertAlmostEqual(y_axis.direction, (0, 1, 0), 5)
def test_from_location(self):
axis = Axis(Location((1, 2, 3), (-90, 0, 0)))
self.assertAlmostEqual(axis.position, (1, 2, 3), 6)
self.assertAlmostEqual(axis.direction, (0, 1, 0), 6)
def test_axis_to_plane(self): def test_axis_to_plane(self):
x_plane = Axis.X.to_plane() x_plane = Axis.X.to_plane()
self.assertTrue(isinstance(x_plane, Plane)) self.assertTrue(isinstance(x_plane, Plane))

View file

@ -270,10 +270,11 @@ class TestLocation(unittest.TestCase):
self.assertAlmostEqual(loc1.position, loc3.position.to_tuple(), 6) self.assertAlmostEqual(loc1.position, loc3.position.to_tuple(), 6)
self.assertAlmostEqual(loc1.orientation, loc3.orientation.to_tuple(), 6) self.assertAlmostEqual(loc1.orientation, loc3.orientation.to_tuple(), 6)
def test_to_axis(self): # deprecated
axis = Location((1, 2, 3), (-90, 0, 0)).to_axis() # def test_to_axis(self):
self.assertAlmostEqual(axis.position, (1, 2, 3), 6) # axis = Location((1, 2, 3), (-90, 0, 0)).to_axis()
self.assertAlmostEqual(axis.direction, (0, 1, 0), 6) # self.assertAlmostEqual(axis.position, (1, 2, 3), 6)
# self.assertAlmostEqual(axis.direction, (0, 1, 0), 6)
def test_equal(self): def test_equal(self):
loc = Location((1, 2, 3), (4, 5, 6)) loc = Location((1, 2, 3), (4, 5, 6))

View file

@ -94,10 +94,6 @@ class TestProjection(unittest.TestCase):
self.assertAlmostEqual(projection[0].position_at(0), (0, 1, 0), 5) self.assertAlmostEqual(projection[0].position_at(0), (0, 1, 0), 5)
self.assertAlmostEqual(projection[0].arc_center, (0, 0, 0), 5) self.assertAlmostEqual(projection[0].arc_center, (0, 0, 0), 5)
def test_to_axis(self):
with self.assertRaises(ValueError):
Edge.make_circle(1, end_angle=30).to_axis()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()