mirror of
https://github.com/gumyr/build123d.git
synced 2026-01-05 16:51:45 -08:00
Fixed Issue #843 added is_forward to Edge parameter methods
This commit is contained in:
parent
739368c417
commit
6590df1e65
4 changed files with 141 additions and 27 deletions
|
|
@ -506,7 +506,7 @@ class Mixin1D(Shape):
|
|||
Note that circles may have identical start and end points.
|
||||
"""
|
||||
curve = self.geom_adaptor()
|
||||
umax = curve.LastParameter()
|
||||
umax = curve.LastParameter() if self.is_forward else curve.FirstParameter()
|
||||
|
||||
return Vector(curve.Value(umax))
|
||||
|
||||
|
|
@ -546,6 +546,12 @@ class Mixin1D(Shape):
|
|||
"""
|
||||
curve = self.geom_adaptor()
|
||||
|
||||
if not self.is_forward:
|
||||
if position_mode == PositionMode.PARAMETER:
|
||||
distance = 1 - distance
|
||||
else:
|
||||
distance = self.length - distance
|
||||
|
||||
if position_mode == PositionMode.PARAMETER:
|
||||
param = self.param_at(distance)
|
||||
else:
|
||||
|
|
@ -595,7 +601,9 @@ class Mixin1D(Shape):
|
|||
)
|
||||
loc = Location(TopLoc_Location(transformation))
|
||||
|
||||
if self.is_forward:
|
||||
return loc
|
||||
return -loc
|
||||
|
||||
def locations(
|
||||
self,
|
||||
|
|
@ -822,8 +830,12 @@ class Mixin1D(Shape):
|
|||
curve = self.geom_adaptor()
|
||||
|
||||
if position_mode == PositionMode.PARAMETER:
|
||||
if not self.is_forward:
|
||||
distance = 1 - distance
|
||||
param = self.param_at(distance)
|
||||
else:
|
||||
if not self.is_forward:
|
||||
distance = self.length - distance
|
||||
param = self.param_at(distance / self.length)
|
||||
|
||||
return Vector(curve.Value(param))
|
||||
|
|
@ -1120,7 +1132,7 @@ class Mixin1D(Shape):
|
|||
Note that circles may have identical start and end points.
|
||||
"""
|
||||
curve = self.geom_adaptor()
|
||||
umin = curve.FirstParameter()
|
||||
umin = curve.FirstParameter() if self.is_forward else curve.LastParameter()
|
||||
|
||||
return Vector(curve.Value(umin))
|
||||
|
||||
|
|
@ -1169,6 +1181,12 @@ class Mixin1D(Shape):
|
|||
Returns:
|
||||
Vector: tangent value
|
||||
"""
|
||||
if not self.is_forward:
|
||||
if position_mode == PositionMode.PARAMETER:
|
||||
position = 1 - position
|
||||
else:
|
||||
position = self.length - position
|
||||
|
||||
if isinstance(position, (float, int)):
|
||||
curve = self.geom_adaptor()
|
||||
if position_mode == PositionMode.PARAMETER:
|
||||
|
|
@ -1198,7 +1216,9 @@ class Mixin1D(Shape):
|
|||
res = gp_Vec()
|
||||
curve.D1(parameter, tmp, res)
|
||||
|
||||
if self.is_forward:
|
||||
return Vector(gp_Dir(res))
|
||||
return Vector(gp_Dir(res)) * -1
|
||||
|
||||
def vertex(self) -> Vertex | None:
|
||||
"""Return the Vertex"""
|
||||
|
|
@ -2089,9 +2109,20 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
|
|||
projected_edges = [w.edges()[0] for w in projected_wires]
|
||||
return projected_edges
|
||||
|
||||
def reversed(self) -> Edge:
|
||||
"""Return a copy of self with the opposite orientation"""
|
||||
def reversed(self, reconstruct: bool = False) -> Edge:
|
||||
"""reversed
|
||||
|
||||
Return a copy of self with the opposite orientation.
|
||||
|
||||
Args:
|
||||
reconstruct (bool, optional): rebuild edge instead of setting OCCT flag.
|
||||
Defaults to False.
|
||||
|
||||
Returns:
|
||||
Edge: reversed
|
||||
"""
|
||||
reversed_edge = copy.deepcopy(self)
|
||||
if reconstruct:
|
||||
first: float = self.param_at(0)
|
||||
last: float = self.param_at(1)
|
||||
curve = BRep_Tool.Curve_s(self.wrapped, first, last)
|
||||
|
|
@ -2099,6 +2130,8 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]):
|
|||
last = curve.ReversedParameter(last)
|
||||
topods_edge = BRepBuilderAPI_MakeEdge(curve.Reversed(), last, first).Edge()
|
||||
reversed_edge.wrapped = topods_edge
|
||||
else:
|
||||
reversed_edge.wrapped = downcast(self.wrapped.Reversed())
|
||||
return reversed_edge
|
||||
|
||||
def to_axis(self) -> Axis:
|
||||
|
|
@ -2805,10 +2838,18 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]):
|
|||
|
||||
def order_edges(self) -> ShapeList[Edge]:
|
||||
"""Return the edges in self ordered by wire direction and orientation"""
|
||||
ordered_edges = [
|
||||
e if e.is_forward else e.reversed() for e in self.edges().sort_by(self)
|
||||
]
|
||||
return ShapeList(ordered_edges)
|
||||
|
||||
sorted_edges = self.edges().sort_by(self)
|
||||
ordered_edges = ShapeList([sorted_edges[0]])
|
||||
|
||||
for edge in sorted_edges[1:]:
|
||||
last_edge = ordered_edges[-1]
|
||||
if abs(last_edge @ 1 - edge @ 0) < TOLERANCE:
|
||||
ordered_edges.append(edge)
|
||||
else:
|
||||
ordered_edges.append(edge.reversed())
|
||||
|
||||
return ordered_edges
|
||||
|
||||
def param_at_point(self, point: VectorLike) -> float:
|
||||
"""Parameter at point on Wire"""
|
||||
|
|
|
|||
|
|
@ -415,20 +415,32 @@ class TestRevolve(unittest.TestCase):
|
|||
def test_revolve_size(self):
|
||||
"""Verify revolution result matches revolution_arc size and direction"""
|
||||
ax = Axis.X
|
||||
profile = RegularPolygon(10, 4, align=(Align.CENTER, Align.MIN))
|
||||
full_volume = revolve(profile, ax, 360).volume
|
||||
sizes = [30, 90, 150, 180, 200, 360, 500, 720, 750]
|
||||
sizes = [x * -1 for x in sizes[::-1]] + [0] + sizes
|
||||
for size in sizes:
|
||||
profile = RegularPolygon(10, 4, align=(Align.CENTER, Align.MIN))
|
||||
solid = revolve(profile, axis=ax, revolution_arc=size)
|
||||
|
||||
# Find any rotation edge and and the start tangent normal to the profile
|
||||
edge = solid.edges().filter_by(GeomType.CIRCLE).sort_by(Edge.length)[-1]
|
||||
# Create a rotation edge and and the start tangent normal to the profile
|
||||
edge = Edge.make_circle(
|
||||
1,
|
||||
Plane.YZ,
|
||||
0,
|
||||
size % 360,
|
||||
(
|
||||
AngularDirection.COUNTER_CLOCKWISE
|
||||
if size > 0
|
||||
else AngularDirection.CLOCKWISE
|
||||
),
|
||||
)
|
||||
sign = (edge % 0).Z
|
||||
|
||||
expected = size % (sign * 360)
|
||||
expected = sign * 360 if expected == 0 else expected
|
||||
result = edge.length / edge.radius / pi * 180 * sign
|
||||
|
||||
self.assertAlmostEqual(solid.volume, full_volume * abs(expected) / 360)
|
||||
self.assertAlmostEqual(expected, result)
|
||||
|
||||
# Invalid test
|
||||
|
|
@ -506,10 +518,10 @@ class TestThicken(unittest.TestCase):
|
|||
outer_sphere = thicken(non_planar, amount=0.1)
|
||||
self.assertAlmostEqual(outer_sphere.volume, (4 / 3) * pi * (1.1**3 - 1**3), 5)
|
||||
|
||||
wire = JernArc((0, 0), (-1, 0), 1, 180).edge().reversed() + JernArc(
|
||||
(0, 0), (1, 0), 2, -90
|
||||
)
|
||||
part = thicken(sweep((wire ^ 0) * RadiusArc((0, 0), (0, -1), 1), wire), 0.4)
|
||||
wire = JernArc((0, -2), (-1, 0), 1, -180) + JernArc((0, 0), (1, 0), 2, -90)
|
||||
|
||||
surface = sweep((wire ^ 0) * RadiusArc((0, 0), (0, -1), 1), wire)
|
||||
part = thicken(surface, 0.4)
|
||||
self.assertAlmostEqual(part.volume, 2.241583787221904, 5)
|
||||
|
||||
part = thicken(
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ license:
|
|||
import math
|
||||
import unittest
|
||||
|
||||
from build123d.build_enums import AngularDirection, GeomType, Transition
|
||||
from build123d.build_enums import AngularDirection, GeomType, PositionMode, Transition
|
||||
from build123d.geometry import Axis, Plane, Vector
|
||||
from build123d.objects_curve import CenterArc, EllipticalCenterArc
|
||||
from build123d.objects_sketch import Circle, Rectangle, RegularPolygon
|
||||
|
|
@ -282,6 +282,9 @@ class TestEdge(unittest.TestCase):
|
|||
e2r = e2.reversed()
|
||||
self.assertAlmostEqual((e2 @ 0.1).X, -(e2r @ 0.1).X, 5)
|
||||
|
||||
e2r = e2.reversed(reconstruct=True)
|
||||
self.assertAlmostEqual((e2 @ 0.1).X, -(e2r @ 0.1).X, 5)
|
||||
|
||||
def test_init(self):
|
||||
with self.assertRaises(TypeError):
|
||||
Edge(direction=(1, 0, 0))
|
||||
|
|
@ -294,6 +297,66 @@ class TestEdge(unittest.TestCase):
|
|||
self.assertEqual(len(inside_edges), 5)
|
||||
self.assertTrue(all(e.geom_type == GeomType.ELLIPSE for e in inside_edges))
|
||||
|
||||
def test_position_at(self):
|
||||
line = Edge.make_line((1, 1), (2, 2))
|
||||
self.assertEqual(line @ 0, Vector(1, 1, 0))
|
||||
self.assertEqual(line @ 1, Vector(2, 2, 0))
|
||||
self.assertEqual(line.reversed() @ 0, Vector(2, 2, 0))
|
||||
self.assertEqual(line.reversed() @ 1, Vector(1, 1, 0))
|
||||
|
||||
self.assertEqual(
|
||||
line.position_at(1, position_mode=PositionMode.LENGTH),
|
||||
Vector(1, 1) + Vector(math.sqrt(2) / 2, math.sqrt(2) / 2),
|
||||
)
|
||||
self.assertEqual(
|
||||
line.reversed().position_at(1, position_mode=PositionMode.LENGTH),
|
||||
Vector(2, 2) - Vector(math.sqrt(2) / 2, math.sqrt(2) / 2),
|
||||
)
|
||||
|
||||
def test_tangent_at(self):
|
||||
arc = Edge.make_circle(1, start_angle=0, end_angle=180)
|
||||
self.assertEqual(arc % 0, Vector(0, 1, 0))
|
||||
self.assertEqual(arc % 1, Vector(0, -1, 0))
|
||||
self.assertEqual(arc.reversed() % 0, Vector(0, 1, 0))
|
||||
self.assertEqual(arc.reversed() % 1, Vector(0, -1, 0))
|
||||
self.assertEqual(arc.reversed() @ 0, Vector(-1, 0, 0))
|
||||
self.assertEqual(arc.reversed() @ 1, Vector(1, 0, 0))
|
||||
|
||||
self.assertEqual(
|
||||
arc.tangent_at(math.pi, position_mode=PositionMode.LENGTH), Vector(0, -1, 0)
|
||||
)
|
||||
self.assertEqual(
|
||||
arc.reversed().tangent_at(math.pi / 2, position_mode=PositionMode.LENGTH),
|
||||
Vector(1, 0, 0),
|
||||
)
|
||||
|
||||
def test_location_at(self):
|
||||
arc = Edge.make_circle(1, start_angle=0, end_angle=180)
|
||||
self.assertEqual(arc.location_at(0).position, Vector(1, 0, 0))
|
||||
self.assertEqual(arc.location_at(1).position, Vector(-1, 0, 0))
|
||||
self.assertEqual(arc.location_at(0).z_axis.direction, Vector(0, 1, 0))
|
||||
self.assertEqual(arc.location_at(1).z_axis.direction, Vector(0, -1, 0))
|
||||
|
||||
self.assertEqual(arc.reversed().location_at(0).position, Vector(-1, 0, 0))
|
||||
self.assertEqual(arc.reversed().location_at(1).position, Vector(1, 0, 0))
|
||||
self.assertEqual(
|
||||
arc.reversed().location_at(0).z_axis.direction, Vector(0, 1, 0)
|
||||
)
|
||||
self.assertEqual(
|
||||
arc.reversed().location_at(1).z_axis.direction, Vector(0, -1, 0)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
arc.location_at(math.pi, position_mode=PositionMode.LENGTH).position,
|
||||
Vector(-1, 0, 0),
|
||||
)
|
||||
self.assertEqual(
|
||||
arc.reversed()
|
||||
.location_at(math.pi, position_mode=PositionMode.LENGTH)
|
||||
.position,
|
||||
Vector(1, 0, 0),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -182,8 +182,6 @@ class TestWire(unittest.TestCase):
|
|||
]
|
||||
)
|
||||
ordered_edges = w1.order_edges()
|
||||
self.assertFalse(all(e.is_forward for e in w1.edges()))
|
||||
self.assertTrue(all(e.is_forward for e in ordered_edges))
|
||||
self.assertAlmostEqual(ordered_edges[0] @ 0, (0, 0, 0), 5)
|
||||
self.assertAlmostEqual(ordered_edges[1] @ 0, (1, 0, 0), 5)
|
||||
self.assertAlmostEqual(ordered_edges[2] @ 0, (1, 1, 0), 5)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue