refactored loft at direct api level

This commit is contained in:
Romain FERRU 2024-11-07 20:38:31 +01:00
parent f3fa230c52
commit e28ffb04c2
2 changed files with 313 additions and 682 deletions

View file

@ -296,9 +296,7 @@ class TestAxis(DirectApiTestCase):
def test_axis_angle_between(self):
self.assertAlmostEqual(Axis.X.angle_between(Axis.Y), 90, 5)
self.assertAlmostEqual(
Axis.X.angle_between(Axis((1, 1, 1), (-1, 0, 0))), 180, 5
)
self.assertAlmostEqual(Axis.X.angle_between(Axis((1, 1, 1), (-1, 0, 0))), 180, 5)
def test_axis_reverse(self):
self.assertVectorAlmostEquals(Axis.X.reverse().direction, (-1, 0, 0), 5)
@ -426,9 +424,7 @@ class TestBoundBox(DirectApiTestCase):
def test_bounding_box_repr(self):
bb = Solid.make_box(1, 1, 1).bounding_box()
self.assertEqual(
repr(bb), "bbox: 0.0 <= x <= 1.0, 0.0 <= y <= 1.0, 0.0 <= z <= 1.0"
)
self.assertEqual(repr(bb), "bbox: 0.0 <= x <= 1.0, 0.0 <= y <= 1.0, 0.0 <= z <= 1.0")
def test_center_of_boundbox(self):
self.assertVectorAlmostEquals(
@ -719,22 +715,16 @@ class TestColor(DirectApiTestCase):
def test_hex(self):
c = Color(0x996692)
self.assertTupleAlmostEquals(
tuple(c), (0x99 / 0xFF, 0x66 / 0xFF, 0x92 / 0xFF, 1), 5
)
self.assertTupleAlmostEquals(tuple(c), (0x99 / 0xFF, 0x66 / 0xFF, 0x92 / 0xFF, 1), 5)
c = Color(0x006692, 0x80)
self.assertTupleAlmostEquals(
tuple(c), (0, 0x66 / 0xFF, 0x92 / 0xFF, 0x80 / 0xFF), 5
)
self.assertTupleAlmostEquals(tuple(c), (0, 0x66 / 0xFF, 0x92 / 0xFF, 0x80 / 0xFF), 5)
c = Color(0x006692, alpha=0x80)
self.assertTupleAlmostEquals(tuple(c), (0, 102 / 255, 146 / 255, 128 / 255), 5)
c = Color(color_code=0x996692, alpha=0xCC)
self.assertTupleAlmostEquals(
tuple(c), (153 / 255, 102 / 255, 146 / 255, 204 / 255), 5
)
self.assertTupleAlmostEquals(tuple(c), (153 / 255, 102 / 255, 146 / 255, 204 / 255), 5)
c = Color(0.0, 0.0, 1.0, 1.0)
self.assertTupleAlmostEquals(tuple(c), (0, 0, 1, 1), 5)
@ -770,9 +760,7 @@ class TestCompound(DirectApiTestCase):
arc = Edge.make_three_point_arc((-50, 0, 0), (0, 20, 0), (50, 0, 0))
text = Compound.make_text("test", 10, text_path=arc)
self.assertEqual(len(text.faces()), 4)
text = Compound.make_text(
"test", 10, align=(Align.MAX, Align.MAX), text_path=arc
)
text = Compound.make_text("test", 10, align=(Align.MAX, Align.MAX), text_path=arc)
self.assertEqual(len(text.faces()), 4)
def test_fuse(self):
@ -810,9 +798,7 @@ class TestCompound(DirectApiTestCase):
]
)
self.assertVectorAlmostEquals(test_compound.center(CenterOf.MASS), (1, 0, 0), 5)
self.assertVectorAlmostEquals(
test_compound.center(CenterOf.BOUNDING_BOX), (4.25, 0, 0), 5
)
self.assertVectorAlmostEquals(test_compound.center(CenterOf.BOUNDING_BOX), (4.25, 0, 0), 5)
with self.assertRaises(ValueError):
test_compound.center(CenterOf.GEOMETRY)
@ -885,9 +871,7 @@ class TestCompound(DirectApiTestCase):
class TestEdge(DirectApiTestCase):
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, end_angle=180).close().length, math.pi + 2, 5)
self.assertAlmostEqual(Edge.make_circle(1).close().length, 2 * math.pi, 5)
def test_make_half_circle(self):
@ -931,13 +915,9 @@ class TestEdge(DirectApiTestCase):
)
self.assertVectorAlmostEquals(spline.end_point(), (2, 0, 0), 5)
with self.assertRaises(ValueError):
Edge.make_spline(
points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)], parameters=[0.0, 1.0]
)
Edge.make_spline(points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)], parameters=[0.0, 1.0])
with self.assertRaises(ValueError):
Edge.make_spline(
points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)], tangents=[(1, 1, 0)]
)
Edge.make_spline(points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)], tangents=[(1, 1, 0)])
def test_spline_approx(self):
spline = Edge.make_spline_approx([(0, 0), (1, 1), (2, 1), (3, 0)])
@ -1019,12 +999,8 @@ class TestEdge(DirectApiTestCase):
def test_trim(self):
line = Edge.make_line((-2, 0), (2, 0))
self.assertVectorAlmostEquals(
line.trim(0.25, 0.75).position_at(0), (-1, 0, 0), 5
)
self.assertVectorAlmostEquals(
line.trim(0.25, 0.75).position_at(1), (1, 0, 0), 5
)
self.assertVectorAlmostEquals(line.trim(0.25, 0.75).position_at(0), (-1, 0, 0), 5)
self.assertVectorAlmostEquals(line.trim(0.25, 0.75).position_at(1), (1, 0, 0), 5)
with self.assertRaises(ValueError):
line.trim(0.75, 0.25)
@ -1041,9 +1017,7 @@ class TestEdge(DirectApiTestCase):
e2_trim.position_at(0), Vector(10, 0, 0).rotate(Axis.Z, 45), 5
)
e3 = Edge.make_spline(
[(0, 10, 0), (-4, 5, 2), (0, 0, 0)], tangents=[(-1, 0), (1, 0)]
)
e3 = Edge.make_spline([(0, 10, 0), (-4, 5, 2), (0, 0, 0)], tangents=[(-1, 0), (1, 0)])
e3_trim = e3.trim_to_length(0, 7)
self.assertAlmostEqual(e3_trim.length, 7, 5)
@ -1088,9 +1062,7 @@ class TestEdge(DirectApiTestCase):
def test_find_tangent(self):
circle = Edge.make_circle(1)
parm = circle.find_tangent(135)[0]
self.assertVectorAlmostEquals(
circle @ parm, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 5
)
self.assertVectorAlmostEquals(circle @ parm, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 5)
line = Edge.make_line((0, 0), (1, 1))
parm = line.find_tangent(45)[0]
self.assertAlmostEqual(parm, 0, 5)
@ -1156,9 +1128,7 @@ class TestFace(DirectApiTestCase):
def test_center(self):
test_face = Face(Wire.make_polygon([(0, 0), (1, 0), (1, 1), (0, 0)]))
self.assertVectorAlmostEquals(
test_face.center(CenterOf.MASS), (2 / 3, 1 / 3, 0), 1
)
self.assertVectorAlmostEquals(test_face.center(CenterOf.MASS), (2 / 3, 1 / 3, 0), 1)
self.assertVectorAlmostEquals(
test_face.center(CenterOf.BOUNDING_BOX),
(0.5, 0.5, 0),
@ -1171,18 +1141,14 @@ class TestFace(DirectApiTestCase):
def test_chamfer_2d(self):
test_face = Face.make_rect(10, 10)
test_face = test_face.chamfer_2d(
distance=1, distance2=2, vertices=test_face.vertices()
)
test_face = test_face.chamfer_2d(distance=1, distance2=2, vertices=test_face.vertices())
self.assertAlmostEqual(test_face.area, 100 - 4 * 0.5 * 1 * 2)
def test_chamfer_2d_reference(self):
test_face = Face.make_rect(10, 10)
edge = test_face.edges().sort_by(Axis.Y)[0]
vertex = edge.vertices().sort_by(Axis.X)[0]
test_face = test_face.chamfer_2d(
distance=1, distance2=2, vertices=[vertex], edge=edge
)
test_face = test_face.chamfer_2d(distance=1, distance2=2, vertices=[vertex], edge=edge)
self.assertAlmostEqual(test_face.area, 100 - 0.5 * 1 * 2)
self.assertAlmostEqual(test_face.edges().sort_by(Axis.Y)[0].length, 9)
self.assertAlmostEqual(test_face.edges().sort_by(Axis.X)[0].length, 8)
@ -1191,9 +1157,7 @@ class TestFace(DirectApiTestCase):
test_face = Face.make_rect(10, 10)
edge = test_face.edges().sort_by(Axis.Y)[0]
vertex = edge.vertices().sort_by(Axis.X)[0]
test_face = test_face.chamfer_2d(
distance=2, distance2=1, vertices=[vertex], edge=edge
)
test_face = test_face.chamfer_2d(distance=2, distance2=1, vertices=[vertex], edge=edge)
self.assertAlmostEqual(test_face.area, 100 - 0.5 * 1 * 2)
self.assertAlmostEqual(test_face.edges().sort_by(Axis.Y)[0].length, 8)
self.assertAlmostEqual(test_face.edges().sort_by(Axis.X)[0].length, 9)
@ -1236,8 +1200,7 @@ class TestFace(DirectApiTestCase):
mount = Solid.make_loft(
[
Rectangle((1 + 16 + 4), 20, align=(Align.MIN, Align.CENTER)).wire(),
Pos(1, 0, 4)
* Rectangle(16, 20, align=(Align.MIN, Align.CENTER)).wire(),
Pos(1, 0, 4) * Rectangle(16, 20, align=(Align.MIN, Align.CENTER)).wire(),
],
)
self.assertTrue(all(f.is_planar for f in mount.faces()))
@ -1304,10 +1267,7 @@ class TestFace(DirectApiTestCase):
def test_bezier_surface(self):
points = [
[
(x, y, 2 if x == 0 and y == 0 else 1 if x == 0 or y == 0 else 0)
for x in range(-1, 2)
]
[(x, y, 2 if x == 0 and y == 0 else 1 if x == 0 or y == 0 else 0) for x in range(-1, 2)]
for y in range(-1, 2)
]
surface = Face.make_bezier_surface(points)
@ -1316,9 +1276,7 @@ class TestFace(DirectApiTestCase):
self.assertVectorAlmostEquals(bbox.max, (+1, +1, +1), 1)
self.assertLess(bbox.max.Z, 1.0)
weights = [
[2 if x == 0 or y == 0 else 1 for x in range(-1, 2)] for y in range(-1, 2)
]
weights = [[2 if x == 0 or y == 0 else 1 for x in range(-1, 2)] for y in range(-1, 2)]
surface = Face.make_bezier_surface(points, weights)
bbox = surface.bounding_box()
self.assertVectorAlmostEquals(bbox.min, (-1, -1, 0), 3)
@ -1361,14 +1319,10 @@ class TestFace(DirectApiTestCase):
circumference = 2 * math.pi * radius
hex_diagonal = 4 * (circumference / 10) / 3
cylinder = Solid.make_cylinder(radius, hex_diagonal * 5)
cylinder_wall: Face = cylinder.faces().filter_by(GeomType.PLANE, reverse=True)[
0
]
cylinder_wall: Face = cylinder.faces().filter_by(GeomType.PLANE, reverse=True)[0]
with BuildSketch(Plane.XZ.offset(radius)) as hex:
with Locations((0, hex_diagonal)):
RegularPolygon(
hex_diagonal * 0.4, 6, align=(Align.CENTER, Align.CENTER)
)
RegularPolygon(hex_diagonal * 0.4, 6, align=(Align.CENTER, Align.CENTER))
hex_wire_vertical: Wire = hex.sketch.faces()[0].outer_wire()
projected_wire: Wire = hex_wire_vertical.project_to_shape(
@ -1483,9 +1437,7 @@ class TestFace(DirectApiTestCase):
if platform.system() != "Darwin":
with self.assertRaises(RuntimeError):
Face.make_surface(
[Edge.make_circle(50)], surface_points=[(0, 0, -50), (0, 0, 50)]
)
Face.make_surface([Edge.make_circle(50)], surface_points=[(0, 0, -50), (0, 0, 50)])
with self.assertRaises(RuntimeError):
Face.make_surface(
@ -1529,9 +1481,7 @@ class TestFace(DirectApiTestCase):
def test_normal_at(self):
face = Face.make_rect(1, 1)
self.assertVectorAlmostEquals(face.normal_at(0, 0), (0, 0, 1), 5)
self.assertVectorAlmostEquals(
face.normal_at(face.position_at(0, 0)), (0, 0, 1), 5
)
self.assertVectorAlmostEquals(face.normal_at(face.position_at(0, 0)), (0, 0, 1), 5)
with self.assertRaises(ValueError):
face.normal_at(0)
with self.assertRaises(ValueError):
@ -1680,19 +1630,13 @@ class TestLocation(DirectApiTestCase):
T = loc5.wrapped.Transformation().TranslationPart()
self.assertTupleAlmostEquals((T.X(), T.Y(), T.Z()), (0, 0, 1), 6)
angle5 = (
loc5.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
angle5 = loc5.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
self.assertAlmostEqual(15, angle5)
angle6 = (
loc6.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
angle6 = loc6.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
self.assertAlmostEqual(30, angle6)
angle7 = (
loc7.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
angle7 = loc7.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
self.assertAlmostEqual(30, angle7)
# Test error handling on creation
@ -1766,9 +1710,7 @@ class TestLocation(DirectApiTestCase):
Location(Intrinsic.XYZ)
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))"
)
self.assertEqual(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))",
@ -2219,9 +2161,7 @@ class TestMixin1D(DirectApiTestCase):
self.assertVectorAlmostEquals(loc.position, (0, 1, 0), 5)
self.assertVectorAlmostEquals(loc.orientation, (0, -90, -90), 5)
loc = Edge.make_circle(1).location_at(
math.pi / 2, position_mode=PositionMode.LENGTH
)
loc = Edge.make_circle(1).location_at(math.pi / 2, position_mode=PositionMode.LENGTH)
self.assertVectorAlmostEquals(loc.position, (0, 1, 0), 5)
self.assertVectorAlmostEquals(loc.orientation, (0, -90, -90), 5)
@ -2247,9 +2187,7 @@ class TestMixin1D(DirectApiTestCase):
def test_project2(self):
target = Cylinder(1, 10).faces().filter_by(GeomType.PLANE, reverse=True)[0]
square = Wire.make_rect(1, 1, Plane.YZ).locate(Location((10, 0, 0)))
projections: list[Wire] = square.project(
target, direction=(-1, 0, 0), closest=False
)
projections: list[Wire] = square.project(target, direction=(-1, 0, 0), closest=False)
self.assertEqual(len(projections), 2)
def test_is_forward(self):
@ -2268,10 +2206,7 @@ class TestMixin1D(DirectApiTestCase):
self.assertEqual(len(offset_wire.edges().filter_by(GeomType.CIRCLE)), 2)
offset_wire_right = base_wire.offset_2d(0.1, side=Side.RIGHT)
self.assertAlmostEqual(
offset_wire_right.edges()
.filter_by(GeomType.CIRCLE)
.sort_by(SortBy.RADIUS)[-1]
.radius,
offset_wire_right.edges().filter_by(GeomType.CIRCLE).sort_by(SortBy.RADIUS)[-1].radius,
0.5,
4,
)
@ -2361,9 +2296,7 @@ class TestMixin3D(DirectApiTestCase):
def test_chamfer_too_high_length(self):
box = Solid.make_box(1, 1, 1)
face = box.faces
self.assertRaises(
ValueError, box.chamfer, 2, None, box.edges().sort_by(Axis.Z)[-1:]
)
self.assertRaises(ValueError, box.chamfer, 2, None, box.edges().sort_by(Axis.Z)[-1:])
def test_chamfer_edge_not_part_of_face(self):
box = Solid.make_box(1, 1, 1)
@ -2383,9 +2316,7 @@ class TestMixin3D(DirectApiTestCase):
def test_dprism(self):
# face
f = Face.make_rect(0.5, 0.5)
d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism(
None, [f], additive=False
)
d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism(None, [f], additive=False)
self.assertTrue(d.is_valid())
self.assertAlmostEqual(d.volume, 1 - 0.5**2, 5)
@ -2408,9 +2339,7 @@ class TestMixin3D(DirectApiTestCase):
# wire
w = Face.make_rect(0.5, 0.5).outer_wire()
d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism(
None, [w], additive=False
)
d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism(None, [w], additive=False)
self.assertTrue(d.is_valid())
self.assertAlmostEqual(d.volume, 1 - 0.5**2, 5)
@ -2503,12 +2432,8 @@ class TestPlane(DirectApiTestCase):
p_from_named_loc = Plane(location=loc)
for p in [p_from_loc, p_from_named_loc]:
self.assertVectorAlmostEquals(p.origin, (0, 0, 0), 6)
self.assertVectorAlmostEquals(
p.x_dir, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6
)
self.assertVectorAlmostEquals(
p.y_dir, (-math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6
)
self.assertVectorAlmostEquals(p.x_dir, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6)
self.assertVectorAlmostEquals(p.y_dir, (-math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6)
self.assertVectorAlmostEquals(p.z_dir, (0, 0, 1), 6)
self.assertVectorAlmostEquals(loc.position, p.location.position, 6)
self.assertVectorAlmostEquals(loc.orientation, p.location.orientation, 6)
@ -2518,12 +2443,8 @@ class TestPlane(DirectApiTestCase):
p = Plane(loc)
self.assertVectorAlmostEquals(p.origin, (0, 2, -1), 6)
self.assertVectorAlmostEquals(p.x_dir, (1, 0, 0), 6)
self.assertVectorAlmostEquals(
p.y_dir, (0, math.sqrt(2) / 2, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(
p.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(p.y_dir, (0, math.sqrt(2) / 2, math.sqrt(2) / 2), 6)
self.assertVectorAlmostEquals(p.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6)
self.assertVectorAlmostEquals(loc.position, p.location.position, 6)
self.assertVectorAlmostEquals(loc.orientation, p.location.orientation, 6)
@ -2537,13 +2458,9 @@ class TestPlane(DirectApiTestCase):
self.assertVectorAlmostEquals(p.origin, (1, 2, 3), 6)
self.assertVectorAlmostEquals(p.x_dir, (math.sqrt(2) / 2, 0.5, 0.5), 6)
self.assertVectorAlmostEquals(p.y_dir, (-math.sqrt(2) / 2, 0.5, 0.5), 6)
self.assertVectorAlmostEquals(
p.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(p.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6)
self.assertVectorAlmostEquals(f.location.position, p.location.position, 6)
self.assertVectorAlmostEquals(
f.location.orientation, p.location.orientation, 6
)
self.assertVectorAlmostEquals(f.location.orientation, p.location.orientation, 6)
# from a face with x_dir
f = Face.make_rect(1, 2)
@ -2573,48 +2490,32 @@ class TestPlane(DirectApiTestCase):
self.assertVectorAlmostEquals(p2.origin, p.origin, 6)
self.assertVectorAlmostEquals(p2.x_dir, p.x_dir, 6)
self.assertVectorAlmostEquals(p2.z_dir, -p.z_dir, 6)
self.assertVectorAlmostEquals(
p2.y_dir, (-p.z_dir).cross(p.x_dir).normalized(), 6
)
self.assertVectorAlmostEquals(p2.y_dir, (-p.z_dir).cross(p.x_dir).normalized(), 6)
p3 = p.reverse()
self.assertVectorAlmostEquals(p3.origin, p.origin, 6)
self.assertVectorAlmostEquals(p3.x_dir, p.x_dir, 6)
self.assertVectorAlmostEquals(p3.z_dir, -p.z_dir, 6)
self.assertVectorAlmostEquals(
p3.y_dir, (-p.z_dir).cross(p.x_dir).normalized(), 6
)
self.assertVectorAlmostEquals(p3.y_dir, (-p.z_dir).cross(p.x_dir).normalized(), 6)
def test_plane_mul(self):
p = Plane(origin=(1, 2, 3), x_dir=(1, 0, 0), z_dir=(0, 0, 1))
p2 = p * Location((1, 2, -1), (0, 0, 45))
self.assertVectorAlmostEquals(p2.origin, (2, 4, 2), 6)
self.assertVectorAlmostEquals(
p2.x_dir, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6
)
self.assertVectorAlmostEquals(
p2.y_dir, (-math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6
)
self.assertVectorAlmostEquals(p2.x_dir, (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6)
self.assertVectorAlmostEquals(p2.y_dir, (-math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 6)
self.assertVectorAlmostEquals(p2.z_dir, (0, 0, 1), 6)
p2 = p * Location((1, 2, -1), (0, 45, 0))
self.assertVectorAlmostEquals(p2.origin, (2, 4, 2), 6)
self.assertVectorAlmostEquals(
p2.x_dir, (math.sqrt(2) / 2, 0, -math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(p2.x_dir, (math.sqrt(2) / 2, 0, -math.sqrt(2) / 2), 6)
self.assertVectorAlmostEquals(p2.y_dir, (0, 1, 0), 6)
self.assertVectorAlmostEquals(
p2.z_dir, (math.sqrt(2) / 2, 0, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(p2.z_dir, (math.sqrt(2) / 2, 0, math.sqrt(2) / 2), 6)
p2 = p * Location((1, 2, -1), (45, 0, 0))
self.assertVectorAlmostEquals(p2.origin, (2, 4, 2), 6)
self.assertVectorAlmostEquals(p2.x_dir, (1, 0, 0), 6)
self.assertVectorAlmostEquals(
p2.y_dir, (0, math.sqrt(2) / 2, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(
p2.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6
)
self.assertVectorAlmostEquals(p2.y_dir, (0, math.sqrt(2) / 2, math.sqrt(2) / 2), 6)
self.assertVectorAlmostEquals(p2.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6)
with self.assertRaises(TypeError):
p2 * Vector(1, 1, 1)
@ -2669,9 +2570,7 @@ class TestPlane(DirectApiTestCase):
def test_shift_origin_vertex(self):
box = Box(1, 1, 1, align=Align.MIN)
front = box.faces().sort_by(Axis.X)[-1]
pln = Plane(front).shift_origin(
front.vertices().group_by(Axis.Z)[-1].sort_by(Axis.Y)[-1]
)
pln = Plane(front).shift_origin(front.vertices().group_by(Axis.Z)[-1].sort_by(Axis.Y)[-1])
with BuildPart() as p:
add(box)
with BuildSketch(pln):
@ -2757,9 +2656,7 @@ class TestPlane(DirectApiTestCase):
def test_plane_not_equal(self):
# type difference
for value in [None, 0, 1, "abc"]:
self.assertNotEqual(
Plane(origin=(0, 0, 0), x_dir=(1, 0, 0), z_dir=(0, 0, 1)), value
)
self.assertNotEqual(Plane(origin=(0, 0, 0), x_dir=(1, 0, 0), z_dir=(0, 0, 1)), value)
# origin difference
self.assertNotEqual(
Plane(origin=(0, 0, 0), x_dir=(1, 0, 0), z_dir=(0, 0, 1)),
@ -2782,9 +2679,7 @@ class TestPlane(DirectApiTestCase):
self.assertVectorAlmostEquals(loc.orientation, (0, 0, 90), 5)
def test_intersect(self):
self.assertVectorAlmostEquals(
Plane.XY.intersect(Axis((1, 2, 3), (0, 0, -1))), (1, 2, 0), 5
)
self.assertVectorAlmostEquals(Plane.XY.intersect(Axis((1, 2, 3), (0, 0, -1))), (1, 2, 0), 5)
self.assertIsNone(Plane.XY.intersect(Axis((1, 2, 3), (0, 1, 0))))
self.assertEqual(Plane.XY.intersect(Plane.XZ), Axis.X)
@ -2801,9 +2696,7 @@ class TestPlane(DirectApiTestCase):
flat = Face.make_rect(1, 1)
pln = Plane(flat)
self.assertTrue(isinstance(pln, Plane))
cyl = (
Solid.make_cylinder(1, 4).faces().filter_by(GeomType.PLANE, reverse=True)[0]
)
cyl = Solid.make_cylinder(1, 4).faces().filter_by(GeomType.PLANE, reverse=True)[0]
with self.assertRaises(ValueError):
pln = Plane(cyl)
@ -2854,8 +2747,7 @@ class TestProjection(DirectApiTestCase):
.faces()
)
projected_text_faces = [
f.project_to_shape(sphere, projection_direction)[0]
for f in planar_text_faces
f.project_to_shape(sphere, projection_direction)[0] for f in planar_text_faces
]
self.assertEqual(len(projected_text_faces), 4)
@ -2873,11 +2765,7 @@ class TestProjection(DirectApiTestCase):
def test_text_projection(self):
sphere = Solid.make_sphere(50)
arch_path = (
sphere.cut(
Solid.make_cylinder(
80, 100, Plane(origin=(-50, 0, -70), z_dir=(1, 0, 0))
)
)
sphere.cut(Solid.make_cylinder(80, 100, Plane(origin=(-50, 0, -70), z_dir=(1, 0, 0))))
.edges()
.sort_by(Axis.Z)[0]
)
@ -3038,9 +2926,7 @@ class TestShape(DirectApiTestCase):
# Test 3 - Invalid, wire on shape edge
target3 = Solid.make_cylinder(5, 10, Plane((0, 0, -5)))
square_projected = square.project_to_shape(target3, (-1, 0, 0))[0].unwrap(
fully=True
)
square_projected = square.project_to_shape(target3, (-1, 0, 0))[0].unwrap(fully=True)
project_perimeter = square_projected.outer_wire()
inside3 = target3.split_by_perimeter(project_perimeter, Keep.INSIDE)
self.assertIsNone(inside3)
@ -3075,9 +2961,7 @@ class TestShape(DirectApiTestCase):
max = test_object.max_fillet(test_object.edges())
self.assertAlmostEqual(max, max_values[i], 2)
with self.assertRaises(RuntimeError):
test_solids[0].max_fillet(
test_solids[0].edges(), tolerance=1e-6, max_iterations=1
)
test_solids[0].max_fillet(test_solids[0].edges(), tolerance=1e-6, max_iterations=1)
with self.assertRaises(ValueError):
box = Solid.make_box(1, 1, 1)
box.fillet(0.75, box.edges())
@ -3157,9 +3041,7 @@ class TestShape(DirectApiTestCase):
def test_intersection(self):
box = Solid.make_box(1, 1, 1)
intersections = (
box.intersect(Axis((0.5, 0.5, 4), (0, 0, -1))).vertices().sort_by(Axis.Z)
)
intersections = box.intersect(Axis((0.5, 0.5, 4), (0, 0, -1))).vertices().sort_by(Axis.Z)
self.assertVectorAlmostEquals(intersections[0], (0.5, 0.5, 0), 5)
self.assertVectorAlmostEquals(intersections[1], (0.5, 0.5, 1), 5)
@ -3259,15 +3141,10 @@ class TestShape(DirectApiTestCase):
self.assertTrue(Solid.make_box(1, 1, 1).is_manifold)
self.assertTrue(Solid.make_box(1, 1, 1).shell().is_manifold)
self.assertFalse(
Solid.make_box(1, 1, 1)
.shell()
.cut(Solid.make_box(0.5, 0.5, 0.5))
.is_manifold
Solid.make_box(1, 1, 1).shell().cut(Solid.make_box(0.5, 0.5, 0.5)).is_manifold
)
self.assertTrue(
Compound(
children=[Solid.make_box(1, 1, 1), Solid.make_cylinder(1, 1)]
).is_manifold
Compound(children=[Solid.make_box(1, 1, 1), Solid.make_cylinder(1, 1)]).is_manifold
)
def test_inherit_color(self):
@ -3333,9 +3210,7 @@ class TestShape(DirectApiTestCase):
box.topo_parent = box2
blank = Compound()
box.copy_attributes_to(
blank, ["color", "label", "joints", "children", "topo_parent"]
)
box.copy_attributes_to(blank, ["color", "label", "joints", "children", "topo_parent"])
self.assertEqual(blank.label, "box")
self.assertTrue(all(c1 == c2 for c1, c2 in zip(blank.color, Color("Red"))))
self.assertTrue(all(j1 == j2 for j1, j2 in zip(blank.joints, ["j1", "j2"])))
@ -3372,9 +3247,7 @@ class TestShapeList(DirectApiTestCase):
self.assertAlmostEqual(faces[-1].area, 2, 5)
def test_filter_by_geomtype(self):
non_planar_faces = (
Solid.make_cylinder(1, 1).faces().filter_by(GeomType.PLANE, reverse=True)
)
non_planar_faces = Solid.make_cylinder(1, 1).faces().filter_by(GeomType.PLANE, reverse=True)
self.assertEqual(len(non_planar_faces), 1)
self.assertAlmostEqual(non_planar_faces[0].area, 2 * math.pi, 5)
@ -3398,9 +3271,7 @@ class TestShapeList(DirectApiTestCase):
self.assertEqual(len(shapelist.filter_by(lambda s: s.label == "B")), 1)
def test_first_last(self):
vertices = (
Solid.make_box(1, 1, 1).vertices().sort_by(Axis((0, 0, 0), (1, 1, 1)))
)
vertices = Solid.make_box(1, 1, 1).vertices().sort_by(Axis((0, 0, 0), (1, 1, 1)))
self.assertVectorAlmostEquals(vertices.last, (1, 1, 1), 5)
self.assertVectorAlmostEquals(vertices.first, (0, 0, 0), 5)
@ -3411,12 +3282,7 @@ class TestShapeList(DirectApiTestCase):
edges = Solid.make_box(1, 1, 1).edges().group_by(SortBy.LENGTH)
self.assertEqual(len(edges[0]), 12)
edges = (
Solid.make_cone(2, 1, 2)
.edges()
.filter_by(GeomType.CIRCLE)
.group_by(SortBy.RADIUS)
)
edges = Solid.make_cone(2, 1, 2).edges().filter_by(GeomType.CIRCLE).group_by(SortBy.RADIUS)
self.assertEqual(len(edges[0]), 1)
edges = (Solid.make_cone(2, 1, 2).edges() | GeomType.CIRCLE) << SortBy.RADIUS
@ -3505,9 +3371,7 @@ class TestShapeList(DirectApiTestCase):
" [<build123d.topology.Edge object at 0x000001277FC86F90>,"
" <build123d.topology.Edge object at 0x000001277F6E1CD0>]]"
)
self.assertDunderReprEqual(
repr(nonagon.edges().group_by(Axis.X)), expected_repr
)
self.assertDunderReprEqual(repr(nonagon.edges().group_by(Axis.X)), expected_repr)
f = io.StringIO()
p = pretty.PrettyPrinter(f)
@ -3520,9 +3384,7 @@ class TestShapeList(DirectApiTestCase):
obj = (-0.2, 0.1, 0.5)
edges = box.edges().sort_by_distance(obj)
distances = [Vertex(*obj).distance_to(edge) for edge in edges]
self.assertTrue(
all([distances[i] >= distances[i - 1] for i in range(1, len(edges))])
)
self.assertTrue(all([distances[i] >= distances[i - 1] for i in range(1, len(edges))]))
def test_distance_reverse(self):
with BuildPart() as box:
@ -3530,9 +3392,7 @@ class TestShapeList(DirectApiTestCase):
obj = (-0.2, 0.1, 0.5)
edges = box.edges().sort_by_distance(obj, reverse=True)
distances = [Vertex(*obj).distance_to(edge) for edge in edges]
self.assertTrue(
all([distances[i] <= distances[i - 1] for i in range(1, len(edges))])
)
self.assertTrue(all([distances[i] <= distances[i - 1] for i in range(1, len(edges))]))
def test_distance_equal(self):
with BuildPart() as box:
@ -3577,9 +3437,7 @@ class TestShapeList(DirectApiTestCase):
self.assertEqual(len(sl.faces()), 9)
def test_face(self):
sl = ShapeList(
[Vertex(1, 1, 1), Edge.make_line((0, 0), (1, 1)), Face.make_rect(2, 1)]
)
sl = ShapeList([Vertex(1, 1, 1), Edge.make_line((0, 0), (1, 1)), Face.make_rect(2, 1)])
self.assertAlmostEqual(sl.face().area, 2 * 1, 5)
sl = ShapeList([Solid.make_box(1, 1, 1), Solid.make_cylinder(1, 1)])
with self.assertWarns(UserWarning):
@ -3686,6 +3544,14 @@ class TestShells(DirectApiTestCase):
self.assertEqual(len(sweep_c2_c1.faces()), 2)
self.assertEqual(len(sweep_w_w.faces()), 4)
self.assertEqual(len(sweep_c2_c2.faces()), 4)
def test_loft(self):
r = 3
h = 2
loft = Shell.make_loft([Wire.make_circle(r,Plane((0,0,h))), Wire.make_circle(r) ])
self.assertEqual(loft.volume, 0, "A shell has no volume")
cylinder_area = 2*math.pi*r*h
self.assertAlmostEqual(loft.area, cylinder_area)
class TestSolid(DirectApiTestCase):
@ -3735,9 +3601,7 @@ class TestSolid(DirectApiTestCase):
for taper in [10, -10]:
offset_amt = -direction.length * math.tan(math.radians(taper))
for face in [rect, flipped]:
with self.subTest(
f"{direction=}, {taper=}, flipped={face==flipped}"
):
with self.subTest(f"{direction=}, {taper=}, flipped={face==flipped}"):
taper_solid = Solid.extrude_taper(face, direction, taper)
# V = 1/3 × h × (a² + b² + ab)
h = Vector(direction).length
@ -3747,14 +3611,10 @@ class TestSolid(DirectApiTestCase):
bbox = taper_solid.bounding_box()
size = max(1, b) / 2
if direction.Z > 0:
self.assertVectorAlmostEquals(
bbox.min, (-size, -size, 0), 1
)
self.assertVectorAlmostEquals(bbox.min, (-size, -size, 0), 1)
self.assertVectorAlmostEquals(bbox.max, (size, size, h), 1)
else:
self.assertVectorAlmostEquals(
bbox.min, (-size, -size, -h), 1
)
self.assertVectorAlmostEquals(bbox.min, (-size, -size, -h), 1)
self.assertVectorAlmostEquals(bbox.max, (size, size, 0), 1)
def test_extrude_taper_with_hole(self):
@ -3805,14 +3665,22 @@ class TestSolid(DirectApiTestCase):
self.assertAlmostEqual(top.translate((0, 0, -1)).intersect(bottom).area, 1, 5)
def test_make_loft(self):
loft = Solid.make_loft(
[Wire.make_rect(2, 2), Wire.make_circle(1, Plane((0, 0, 1)))]
)
loft = Solid.make_loft([Wire.make_rect(2, 2), Wire.make_circle(1, Plane((0, 0, 1)))])
self.assertAlmostEqual(loft.volume, (4 + math.pi) / 2, 1)
with self.assertRaises(ValueError):
Solid.make_loft([Wire.make_rect(1, 1)])
def test_make_loft_with_vertices(self):
loft = Solid.make_loft([Vertex(0, 0, -1), Wire.make_rect(1, 1.5), Vertex(0, 0, 1)], True)
self.assertAlmostEqual(loft.volume, 1, 5)
with self.assertRaises(ValueError):
Solid.make_loft([Wire.make_rect(1, 1), Vertex(0, 0, 1), Wire.make_rect(1, 1)])
with self.assertRaises(ValueError):
Solid.make_loft([Vertex(0, 0, 1), Vertex(0, 0, 2)])
def test_extrude_until(self):
square = Face.make_rect(1, 1)
box = Solid.make_box(4, 4, 1, Plane((-2, -2, 3)))
@ -3889,9 +3757,7 @@ class TestVector(DirectApiTestCase):
vector_x = Vector(1, 0, 1).rotate(Axis.X, 45)
vector_y = Vector(1, 2, 1).rotate(Axis.Y, 45)
vector_z = Vector(-1, -1, 3).rotate(Axis.Z, 45)
self.assertVectorAlmostEquals(
vector_x, (1, -math.sqrt(2) / 2, math.sqrt(2) / 2), 7
)
self.assertVectorAlmostEquals(vector_x, (1, -math.sqrt(2) / 2, math.sqrt(2) / 2), 7)
self.assertVectorAlmostEquals(vector_y, (math.sqrt(2), 2, 0), 7)
self.assertVectorAlmostEquals(vector_z, (0, -math.sqrt(2), 3), 7)
@ -4043,21 +3909,11 @@ class TestVector(DirectApiTestCase):
pxy = Plane.XY
pxy_o1 = Plane.XY.offset(1)
self.assertEqual(a.transform(pxy.forward_transform, is_direction=False), a)
self.assertEqual(
a.transform(pxy.forward_transform, is_direction=True), a.normalized()
)
self.assertEqual(
a.transform(pxy_o1.forward_transform, is_direction=False), Vector(1, 2, 2)
)
self.assertEqual(
a.transform(pxy_o1.forward_transform, is_direction=True), a.normalized()
)
self.assertEqual(
a.transform(pxy_o1.reverse_transform, is_direction=False), Vector(1, 2, 4)
)
self.assertEqual(
a.transform(pxy_o1.reverse_transform, is_direction=True), a.normalized()
)
self.assertEqual(a.transform(pxy.forward_transform, is_direction=True), a.normalized())
self.assertEqual(a.transform(pxy_o1.forward_transform, is_direction=False), Vector(1, 2, 2))
self.assertEqual(a.transform(pxy_o1.forward_transform, is_direction=True), a.normalized())
self.assertEqual(a.transform(pxy_o1.reverse_transform, is_direction=False), Vector(1, 2, 4))
self.assertEqual(a.transform(pxy_o1.reverse_transform, is_direction=True), a.normalized())
def test_intersect(self):
v1 = Vector(1, 2, 3)
@ -4073,12 +3929,8 @@ class TestVector(DirectApiTestCase):
self.assertVectorAlmostEquals(v1 & Plane((1, 2, 3)), (1, 2, 3), 5)
self.assertIsNone(v1 & Plane.XY)
self.assertVectorAlmostEquals(
(v1 & Solid.make_box(2, 4, 5)).vertex(), (1, 2, 3), 5
)
self.assertTrue(
len(v1.intersect(Solid.make_box(0.5, 0.5, 0.5)).vertices()) == 0
)
self.assertVectorAlmostEquals((v1 & Solid.make_box(2, 4, 5)).vertex(), (1, 2, 3), 5)
self.assertTrue(len(v1.intersect(Solid.make_box(0.5, 0.5, 0.5)).vertices()) == 0)
class TestVectorLike(DirectApiTestCase):
@ -4122,12 +3974,8 @@ class TestVertex(DirectApiTestCase):
def test_vertex_add(self):
test_vertex = Vertex(0, 0, 0)
self.assertVectorAlmostEquals(
Vector(test_vertex + (100, -40, 10)), (100, -40, 10), 7
)
self.assertVectorAlmostEquals(
Vector(test_vertex + Vector(100, -40, 10)), (100, -40, 10), 7
)
self.assertVectorAlmostEquals(Vector(test_vertex + (100, -40, 10)), (100, -40, 10), 7)
self.assertVectorAlmostEquals(Vector(test_vertex + Vector(100, -40, 10)), (100, -40, 10), 7)
self.assertVectorAlmostEquals(
Vector(test_vertex + Vertex(100, -40, 10)),
(100, -40, 10),
@ -4138,9 +3986,7 @@ class TestVertex(DirectApiTestCase):
def test_vertex_sub(self):
test_vertex = Vertex(0, 0, 0)
self.assertVectorAlmostEquals(
Vector(test_vertex - (100, -40, 10)), (-100, 40, -10), 7
)
self.assertVectorAlmostEquals(Vector(test_vertex - (100, -40, 10)), (-100, 40, -10), 7)
self.assertVectorAlmostEquals(
Vector(test_vertex - Vector(100, -40, 10)), (-100, 40, -10), 7
)
@ -4175,42 +4021,30 @@ class TestVertex(DirectApiTestCase):
class TestWire(DirectApiTestCase):
def test_ellipse_arc(self):
full_ellipse = Wire.make_ellipse(2, 1)
half_ellipse = Wire.make_ellipse(
2, 1, start_angle=0, end_angle=180, closed=True
)
half_ellipse = Wire.make_ellipse(2, 1, start_angle=0, end_angle=180, closed=True)
self.assertAlmostEqual(full_ellipse.area / 2, half_ellipse.area, 5)
def test_stitch(self):
half_ellipse1 = Wire.make_ellipse(
2, 1, start_angle=0, end_angle=180, closed=False
)
half_ellipse2 = Wire.make_ellipse(
2, 1, start_angle=180, end_angle=360, closed=False
)
half_ellipse1 = Wire.make_ellipse(2, 1, start_angle=0, end_angle=180, closed=False)
half_ellipse2 = Wire.make_ellipse(2, 1, start_angle=180, end_angle=360, closed=False)
ellipse = half_ellipse1.stitch(half_ellipse2)
self.assertEqual(len(ellipse.wires()), 1)
def test_fillet_2d(self):
square = Wire.make_rect(1, 1)
squaroid = square.fillet_2d(0.1, square.vertices())
self.assertAlmostEqual(
squaroid.length, 4 * (1 - 2 * 0.1) + 2 * math.pi * 0.1, 5
)
self.assertAlmostEqual(squaroid.length, 4 * (1 - 2 * 0.1) + 2 * math.pi * 0.1, 5)
def test_chamfer_2d(self):
square = Wire.make_rect(1, 1)
squaroid = square.chamfer_2d(0.1, 0.1, square.vertices())
self.assertAlmostEqual(
squaroid.length, 4 * (1 - 2 * 0.1 + 0.1 * math.sqrt(2)), 5
)
self.assertAlmostEqual(squaroid.length, 4 * (1 - 2 * 0.1 + 0.1 * math.sqrt(2)), 5)
def test_chamfer_2d_edge(self):
square = Wire.make_rect(1, 1)
edge = square.edges().sort_by(Axis.Y)[0]
vertex = edge.vertices().sort_by(Axis.X)[0]
square = square.chamfer_2d(
distance=0.1, distance2=0.2, vertices=[vertex], edge=edge
)
square = square.chamfer_2d(distance=0.1, distance2=0.2, vertices=[vertex], edge=edge)
self.assertAlmostEqual(square.edges().sort_by(Axis.Y)[0].length, 0.9)
def test_make_convex_hull(self):