mirror of
https://github.com/gumyr/build123d.git
synced 2026-03-15 11:10:50 -07:00
Merge pull request #50 from bernhard-42/location_changes
Location changes
This commit is contained in:
commit
ee8ed88cd9
2 changed files with 124 additions and 35 deletions
|
|
@ -148,6 +148,7 @@ from OCP.gp import (
|
|||
gp_Dir2d,
|
||||
gp_Elips,
|
||||
gp_EulerSequence,
|
||||
gp_Quaternion,
|
||||
gp_GTrsf,
|
||||
gp_Lin,
|
||||
gp_Pln,
|
||||
|
|
@ -1021,42 +1022,58 @@ class Location:
|
|||
|
||||
@overload
|
||||
def __init__(self) -> None: # pragma: no cover
|
||||
# Empty location with not rotation or translation with respect to the original location.
|
||||
"Empty location with not rotation or translation with respect to the original location."
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, translation: VectorLike) -> None: # pragma: no cover
|
||||
# Location with translation with respect to the original location.
|
||||
def __init__(self, location: "Location") -> None: # pragma: no cover
|
||||
"Location with another given location."
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self, translation: VectorLike, angle: float = 0
|
||||
) -> None: # pragma: no cover
|
||||
"""Location with translation with respect to the original location.
|
||||
If angle != 0 then the location includes a rotation around z-axis by angle"""
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self, translation: VectorLike, rotation: RotationLike = None
|
||||
) -> None: # pragma: no cover
|
||||
"""Location with translation with respect to the original location.
|
||||
If rotation is not None then the location includes the rotation (see also Rotation class)"""
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, plane: Plane) -> None: # pragma: no cover
|
||||
# Location corresponding to the location of the Plane.
|
||||
"Location corresponding to the location of the Plane."
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self, plane: Plane, plane_offset: VectorLike
|
||||
) -> None: # pragma: no cover
|
||||
# Location corresponding to the angular location of the Plane with translation plane_offset.
|
||||
"Location corresponding to the angular location of the Plane with translation plane_offset."
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, top_loc: TopLoc_Location) -> None: # pragma: no cover
|
||||
# Location wrapping the low-level TopLoc_Location object t
|
||||
"Location wrapping the low-level TopLoc_Location object t"
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(self, gp_trsf: gp_Trsf) -> None: # pragma: no cover
|
||||
# Location wrapping the low-level gp_Trsf object t
|
||||
"Location wrapping the low-level gp_Trsf object t"
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self, translation: VectorLike, axis: VectorLike, angle: float
|
||||
) -> None: # pragma: no cover
|
||||
# Location with translation t and rotation around axis by angle
|
||||
# with respect to the original location."""
|
||||
"""Location with translation t and rotation around axis by angle
|
||||
with respect to the original location."""
|
||||
...
|
||||
|
||||
def __init__(self, *args):
|
||||
|
|
@ -1065,6 +1082,7 @@ class Location:
|
|||
|
||||
if len(args) == 0:
|
||||
pass
|
||||
|
||||
elif len(args) == 1:
|
||||
translation = args[0]
|
||||
|
||||
|
|
@ -1078,6 +1096,9 @@ class Location:
|
|||
)
|
||||
transform.SetTransformation(coordinate_system)
|
||||
transform.Invert()
|
||||
elif isinstance(args[0], Location):
|
||||
self.wrapped = translation.wrapped
|
||||
return
|
||||
elif isinstance(translation, TopLoc_Location):
|
||||
self.wrapped = translation
|
||||
return
|
||||
|
|
@ -1085,15 +1106,33 @@ class Location:
|
|||
transform = translation
|
||||
else:
|
||||
raise TypeError("Unexpected parameters")
|
||||
|
||||
elif len(args) == 2:
|
||||
translation, origin = args
|
||||
coordinate_system = gp_Ax3(
|
||||
Vector(origin).to_pnt(),
|
||||
translation.z_dir.to_dir(),
|
||||
translation.x_dir.to_dir(),
|
||||
)
|
||||
transform.SetTransformation(coordinate_system)
|
||||
transform.Invert()
|
||||
if isinstance(args[0], (Vector, tuple)):
|
||||
if isinstance(args[1], (Vector, tuple)):
|
||||
rotation = [radians(a) for a in args[1]]
|
||||
q = gp_Quaternion()
|
||||
q.SetEulerAngles(gp_EulerSequence.gp_Intrinsic_XYZ, *rotation)
|
||||
transform.SetRotation(q)
|
||||
elif isinstance(args[0], (Vector, tuple)) and isinstance(
|
||||
args[1], (int, float)
|
||||
):
|
||||
angle = radians(args[1])
|
||||
q = gp_Quaternion()
|
||||
q.SetEulerAngles(gp_EulerSequence.gp_Intrinsic_XYZ, 0, 0, angle)
|
||||
transform.SetRotation(q)
|
||||
|
||||
# set translation part after setting rotation (if exists)
|
||||
transform.SetTranslationPart(Vector(args[0]).wrapped)
|
||||
else:
|
||||
translation, origin = args
|
||||
coordinate_system = gp_Ax3(
|
||||
Vector(origin).to_pnt(),
|
||||
translation.z_dir.to_dir(),
|
||||
translation.x_dir.to_dir(),
|
||||
)
|
||||
transform.SetTransformation(coordinate_system)
|
||||
transform.Invert()
|
||||
else:
|
||||
translation, axis, angle = args
|
||||
transform.SetRotation(
|
||||
|
|
@ -1123,9 +1162,11 @@ class Location:
|
|||
rot = transformation.GetRotation()
|
||||
|
||||
rv_trans = (trans.X(), trans.Y(), trans.Z())
|
||||
rv_rot = rot.GetEulerAngles(gp_EulerSequence.gp_Extrinsic_XYZ)
|
||||
rv_rot = [
|
||||
degrees(a) for a in rot.GetEulerAngles(gp_EulerSequence.gp_Intrinsic_XYZ)
|
||||
]
|
||||
|
||||
return rv_trans, rv_rot
|
||||
return rv_trans, tuple(rv_rot)
|
||||
|
||||
def __repr__(self):
|
||||
"""To String
|
||||
|
|
@ -1136,7 +1177,7 @@ class Location:
|
|||
Location as String
|
||||
"""
|
||||
position_str = ", ".join((f"{v:.2f}" for v in self.to_tuple()[0]))
|
||||
orientation_str = ", ".join((f"{180*v/pi:.2f}" for v in self.to_tuple()[1]))
|
||||
orientation_str = ", ".join((f"{v:.2f}" for v in self.to_tuple()[1]))
|
||||
return f"(p=({position_str}), o=({orientation_str}))"
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -1148,7 +1189,7 @@ class Location:
|
|||
Location as String
|
||||
"""
|
||||
position_str = ", ".join((f"{v:.2f}" for v in self.to_tuple()[0]))
|
||||
orientation_str = ", ".join((f"{180*v/pi:.2f}" for v in self.to_tuple()[1]))
|
||||
orientation_str = ", ".join((f"{v:.2f}" for v in self.to_tuple()[1]))
|
||||
return f"Location: (position=({position_str}), orientation=({orientation_str}))"
|
||||
|
||||
|
||||
|
|
@ -1160,14 +1201,16 @@ class Rotation(Location):
|
|||
self.about_y = about_y
|
||||
self.about_z = about_z
|
||||
|
||||
# Compute rotation matrix.
|
||||
rot_x = gp_Trsf()
|
||||
rot_x.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), radians(about_x))
|
||||
rot_y = gp_Trsf()
|
||||
rot_y.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), radians(about_y))
|
||||
rot_z = gp_Trsf()
|
||||
rot_z.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radians(about_z))
|
||||
super().__init__(rot_x * rot_y * rot_z)
|
||||
q = gp_Quaternion()
|
||||
q.SetEulerAngles(
|
||||
gp_EulerSequence.gp_Intrinsic_XYZ,
|
||||
radians(about_x),
|
||||
radians(about_y),
|
||||
radians(about_z),
|
||||
)
|
||||
t = gp_Trsf()
|
||||
t.SetRotationPart(q)
|
||||
super().__init__(t)
|
||||
|
||||
|
||||
#:TypeVar("RotationLike"): Three tuple of angles about x, y, z or Rotation
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from OCP.gp import (
|
|||
gp_Trsf,
|
||||
gp_Ax1,
|
||||
gp_Dir,
|
||||
gp_Quaternion,
|
||||
gp_EulerSequence,
|
||||
)
|
||||
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
|
||||
|
||||
|
|
@ -414,19 +416,65 @@ class TestCadObjects(unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
Location("xy_plane")
|
||||
|
||||
# Test that the computed rotation matrix and intrinsic euler angles return the same
|
||||
|
||||
about_x = uniform(-2 * math.pi, 2 * math.pi)
|
||||
about_y = uniform(-2 * math.pi, 2 * math.pi)
|
||||
about_z = uniform(-2 * math.pi, 2 * math.pi)
|
||||
|
||||
rot_x = gp_Trsf()
|
||||
rot_x.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), about_x)
|
||||
rot_y = gp_Trsf()
|
||||
rot_y.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), about_y)
|
||||
rot_z = gp_Trsf()
|
||||
rot_z.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), about_z)
|
||||
loc1 = Location(rot_x * rot_y * rot_z)
|
||||
|
||||
q = gp_Quaternion()
|
||||
q.SetEulerAngles(
|
||||
gp_EulerSequence.gp_Intrinsic_XYZ,
|
||||
about_x,
|
||||
about_y,
|
||||
about_z,
|
||||
)
|
||||
t = gp_Trsf()
|
||||
t.SetRotationPart(q)
|
||||
loc2 = Location(t)
|
||||
|
||||
self.assertTupleAlmostEquals(loc1.to_tuple()[0], loc2.to_tuple()[0], 6)
|
||||
self.assertTupleAlmostEquals(loc1.to_tuple()[1], loc2.to_tuple()[1], 6)
|
||||
|
||||
loc1 = Location((1, 2), 34)
|
||||
self.assertTupleAlmostEquals(loc1.to_tuple()[0], (1, 2, 0), 6)
|
||||
self.assertTupleAlmostEquals(loc1.to_tuple()[1], (0, 0, 34), 6)
|
||||
|
||||
rot_angles = (-115.00, 35.00, -135.00)
|
||||
loc2 = Location((1, 2, 3), rot_angles)
|
||||
self.assertTupleAlmostEquals(loc2.to_tuple()[0], (1, 2, 3), 6)
|
||||
self.assertTupleAlmostEquals(loc2.to_tuple()[1], rot_angles, 6)
|
||||
|
||||
loc3 = Location(loc2)
|
||||
self.assertTupleAlmostEquals(loc3.to_tuple()[0], (1, 2, 3), 6)
|
||||
self.assertTupleAlmostEquals(loc3.to_tuple()[1], rot_angles, 6)
|
||||
|
||||
def test_location_repr_and_str(self):
|
||||
self.assertEqual(
|
||||
repr(Location()), "(p=(0.00, 0.00, 0.00), o=(0.00, -0.00, 0.00))"
|
||||
repr(Location()), "(p=(0.00, 0.00, 0.00), o=(-0.00, 0.00, -0.00))"
|
||||
)
|
||||
self.assertEqual(
|
||||
str(Location()),
|
||||
"Location: (position=(0.00, 0.00, 0.00), orientation=(0.00, -0.00, 0.00))",
|
||||
"Location: (position=(0.00, 0.00, 0.00), orientation=(-0.00, 0.00, -0.00))",
|
||||
)
|
||||
loc = Location((1, 2, 3), (33, 45, 67))
|
||||
self.assertEqual(
|
||||
str(loc),
|
||||
"Location: (position=(1.00, 2.00, 3.00), orientation=(33.00, 45.00, 67.00))",
|
||||
)
|
||||
|
||||
def test_location_inverted(self):
|
||||
loc = Location(Plane.XZ)
|
||||
self.assertTupleAlmostEquals(
|
||||
loc.inverse().orientation.to_tuple(), (-math.pi / 2, 0, 0), 6
|
||||
loc.inverse().orientation.to_tuple(), (-90, 0, 0), 6
|
||||
)
|
||||
|
||||
def test_edge_wrapper_radius(self):
|
||||
|
|
@ -1288,9 +1336,7 @@ class TestAxis(unittest.TestCase):
|
|||
x_location = Axis.X.to_location()
|
||||
self.assertTrue(isinstance(x_location, Location))
|
||||
self.assertTupleAlmostEquals(x_location.position.to_tuple(), (0, 0, 0), 5)
|
||||
self.assertTupleAlmostEquals(
|
||||
x_location.orientation.to_tuple(), (-math.pi, -math.pi / 2, 0), 5
|
||||
)
|
||||
self.assertTupleAlmostEquals(x_location.orientation.to_tuple(), (0, 90, 180), 5)
|
||||
|
||||
def test_axis_to_plane(self):
|
||||
x_plane = Axis.X.to_plane()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue