Most unittest complete

This commit is contained in:
Roger Maitland 2022-07-29 14:48:40 -04:00
parent c661df3472
commit dcdad16075
2 changed files with 353 additions and 74 deletions

View file

@ -85,12 +85,18 @@ class BuildPart(Builder):
@property
def pending_faces_count(self) -> int:
"""Number of pending faces"""
return len(self.pending_faces.values())
count = 0
for face_list in self.pending_faces.values():
count += len(face_list)
return count
@property
def pending_edges_count(self) -> int:
"""Number of pending edges"""
return len(self.pending_edges.values())
count = 0
for edge_list in self.pending_edges.values():
count += len(edge_list)
return count
@property
def pending_location_count(self) -> int:
@ -161,7 +167,7 @@ class BuildPart(Builder):
if select == Select.ALL:
face_list = self.part.Faces()
elif select == Select.LAST:
face_list = self.last_edges
face_list = self.last_faces
return ShapeList(face_list)
def solids(self, select: Select = Select.ALL) -> ShapeList[Solid]:
@ -280,16 +286,14 @@ class BuildPart(Builder):
self.part = self.part.fuse(*new_solids).clean()
elif mode == Mode.SUBTRACT:
if self.part is None:
raise ValueError("Nothing to subtract from")
raise RuntimeError("Nothing to subtract from")
self.part = self.part.cut(*new_solids).clean()
elif mode == Mode.INTERSECT:
if self.part is None:
raise ValueError("Nothing to intersect with")
raise RuntimeError("Nothing to intersect with")
self.part = self.part.intersect(*new_solids).clean()
elif mode == Mode.REPLACE:
self.part = Compound.makeCompound(new_solids).clean()
else:
raise ValueError(f"Invalid mode: {mode}")
post_vertices = set() if self.part is None else set(self.part.Vertices())
post_edges = set() if self.part is None else set(self.part.Edges())
@ -335,12 +339,12 @@ class CounterBoreHole(Compound):
depth: float = None,
mode: Mode = Mode.SUBTRACT,
):
context: BuildPart = BuildPart._get_context()
hole_depth = (
BuildPart._get_context().part.BoundingBox().DiagonalLength
if depth is None
else depth
context.part.BoundingBox().DiagonalLength if depth is None else depth
)
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
new_solids = [
Solid.makeCylinder(
radius, hole_depth, loc.position(), plane.zDir * -1.0
@ -354,7 +358,7 @@ class CounterBoreHole(Compound):
)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -379,13 +383,13 @@ class CounterSinkHole(Compound):
counter_sink_angle: float = 82, # Common tip angle
mode: Mode = Mode.SUBTRACT,
):
context: BuildPart = BuildPart._get_context()
hole_depth = (
BuildPart._get_context().part.BoundingBox().DiagonalLength
if depth is None
else depth
context.part.BoundingBox().DiagonalLength if depth is None else depth
)
cone_height = counter_sink_radius / tan(radians(counter_sink_angle / 2.0))
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
new_solids = [
Solid.makeCylinder(
radius, hole_depth, loc.position(), plane.zDir * -1.0
@ -394,13 +398,13 @@ class CounterSinkHole(Compound):
counter_sink_radius,
0.0,
cone_height,
loc,
loc.position(),
plane.zDir * -1.0,
)
)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -424,12 +428,14 @@ class Extrude(Compound):
mode: Mode = Mode.ADD,
):
new_solids: list[Solid] = []
for plane_index, faces in BuildPart._get_context().pending_faces.items():
context: BuildPart = BuildPart._get_context()
for plane_index, faces in context.pending_faces.items():
for face in faces:
new_solids.append(
Solid.extrudeLinear(
face,
BuildPart._get_context().workplanes[plane_index].zDir * until,
context.workplanes[plane_index].zDir * until,
0,
)
)
@ -437,15 +443,13 @@ class Extrude(Compound):
new_solids.append(
Solid.extrudeLinear(
face,
BuildPart._get_context().workplanes[plane_index].zDir
* until
* -1.0,
context.workplanes[plane_index].zDir * until * -1.0,
0,
)
)
BuildPart._get_context().pending_faces = {0: []}
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context.pending_faces = {0: []}
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -466,19 +470,19 @@ class Hole(Compound):
depth: float = None,
mode: Mode = Mode.SUBTRACT,
):
context: BuildPart = BuildPart._get_context()
hole_depth = (
BuildPart._get_context().part.BoundingBox().DiagonalLength
if depth is None
else depth
context.part.BoundingBox().DiagonalLength if depth is None else depth
)
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
new_solids = [
Solid.makeCylinder(
radius, hole_depth, loc.position(), plane.zDir * -1.0, 360
)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -494,14 +498,16 @@ class Loft(Solid):
def __init__(self, ruled: bool = False, mode: Mode = Mode.ADD):
context: BuildPart = BuildPart._get_context()
loft_wires = []
for i in range(len(BuildPart._get_context().workplanes)):
for face in BuildPart._get_context().pending_faces[i]:
for i in range(len(context.workplanes)):
for face in context.pending_faces[i]:
loft_wires.append(face.outerWire())
new_solid = Solid.makeLoft(loft_wires, ruled)
BuildPart._get_context().pending_faces = {0: []}
BuildPart._get_context()._add_to_context(new_solid, mode=mode)
context.pending_faces = {0: []}
context._add_to_context(new_solid, mode=mode)
super().__init__(new_solid.wrapped)
@ -524,13 +530,16 @@ class Revolve(Compound):
axis_end: VectorLike = None,
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
# Make sure we account for users specifying angles larger than 360 degrees, and
# for OCCT not assuming that a 0 degree revolve means a 360 degree revolve
angle = revolution_arc % 360.0
angle = 360.0 if angle == 0 else angle
new_solids = []
for i, workplane in enumerate(BuildPart._get_context().workplanes):
for i, workplane in enumerate(context.workplanes):
axis = []
if axis_start is None:
axis.append(workplane.fromLocalCoords(Vector(0, 0, 0)))
@ -542,31 +551,52 @@ class Revolve(Compound):
else:
axis.append(workplane.fromLocalCoords(Vector(axis_end)))
for face in BuildPart._get_context().pending_faces[i]:
for face in context.pending_faces[i]:
new_solids.append(Solid.revolve(face, angle, *axis))
BuildPart._get_context().pending_faces = {0: []}
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context.pending_faces = {0: []}
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
class Section(Compound):
"""Part Operation: Section
Slices current part at the given height from current workplane(s).
Slices current part at the given height by section_by or current workplane(s).
Args:
section_by (Plane, optional): sequence of planes to section object.
Defaults to None.
height (float, optional): workplane offset. Defaults to 0.0.
mode (Mode, optional): combination mode. Defaults to Mode.INTERSECT.
"""
def __init__(self, height: float = 0.0, mode: Mode = Mode.INTERSECT):
def __init__(
self,
*section_by: Plane,
height: float = 0.0,
mode: Mode = Mode.INTERSECT,
):
context: BuildPart = BuildPart._get_context()
max_size = context.part.BoundingBox().DiagonalLength
section_planes = section_by if section_by else context.workplanes
section_planes = (
section_planes if isinstance(section_planes, Iterable) else [section_planes]
)
planes = [
Face.makePlane(basePnt=plane.origin + plane.zDir * height, dir=plane.zDir)
for plane in BuildPart._get_context().workplanes
Face.makePlane(
2 * max_size,
2 * max_size,
basePnt=plane.origin + plane.zDir * height,
dir=plane.zDir,
)
for plane in section_planes
]
BuildPart._get_context()._add_to_context(planes, mode=mode)
context._add_to_context(*planes, mode=mode)
super().__init__(Compound.makeCompound(planes).wrapped)
@ -589,10 +619,10 @@ class Shell(Compound):
kind: Kind = Kind.ARC,
mode: Mode = Mode.REPLACE,
):
new_part = BuildPart._get_context().part.shell(
faces, thickness, kind=kind.name.lower()
)
BuildPart._get_context()._add_to_context(new_part, mode=mode)
context: BuildPart = BuildPart._get_context()
new_part = context.part.shell(faces, thickness, kind=kind.name.lower())
context._add_to_context(new_part, mode=mode)
super().__init__(new_part.wrapped)
@ -613,7 +643,9 @@ class Split(Compound):
keep: Keep = Keep.TOP,
mode: Mode = Mode.INTERSECT,
):
max_size = BuildPart._get_context().BoundingBox().DiagonalLength
context: BuildPart = BuildPart._get_context()
max_size = context.part.BoundingBox().DiagonalLength
def build_cutter(keep: Keep) -> Solid:
cutter_center = (
@ -634,8 +666,8 @@ class Split(Compound):
else:
cutters.append(build_cutter(keep))
BuildPart._get_context()._add_to_context(*cutters, mode=mode)
super().__init__(BuildPart._get_context().part.wrapped)
context._add_to_context(*cutters, mode=mode)
super().__init__(context.part.wrapped)
class Sweep(Compound):
@ -666,6 +698,8 @@ class Sweep(Compound):
binormal: Union[Edge, Wire] = None,
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
path_wire = Wire.assembleEdges([path]) if isinstance(path, Edge) else path
if binormal is None:
binormal_mode = Vector(normal)
@ -675,9 +709,9 @@ class Sweep(Compound):
binormal_mode = binormal
new_solids = []
for i in range(BuildPart._get_context().workplane_count):
for i in range(context.workplane_count):
if not multisection:
for face in BuildPart._get_context().pending_faces[i]:
for face in context.pending_faces[i]:
new_solids.append(
Solid.sweep(
face,
@ -689,18 +723,15 @@ class Sweep(Compound):
)
)
else:
sections = [
face.outerWire()
for face in BuildPart._get_context().pending_faces[i]
]
sections = [face.outerWire() for face in context.pending_faces[i]]
new_solids.append(
Solid.sweep_multi(
sections, path_wire, make_solid, is_frenet, binormal_mode
)
)
BuildPart._get_context().pending_faces = {0: []}
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context.pending_faces = {0: []}
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -767,8 +798,10 @@ class Box(Compound):
centered: tuple[bool, bool, bool] = (True, True, True),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
center_offset = Vector(
-length / 2 if centered[0] else 0,
-width / 2 if centered[1] else 0,
@ -784,7 +817,7 @@ class Box(Compound):
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -814,8 +847,10 @@ class Cone(Compound):
centered: tuple[bool, bool, bool] = (True, True, True),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
center_offset = Vector(
0 if centered[0] else max(bottom_radius, top_radius),
0 if centered[1] else max(bottom_radius, top_radius),
@ -832,7 +867,7 @@ class Cone(Compound):
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -860,8 +895,10 @@ class Cylinder(Compound):
centered: tuple[bool, bool, bool] = (True, True, True),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
center_offset = Vector(
0 if centered[0] else radius,
0 if centered[1] else radius,
@ -877,7 +914,7 @@ class Cylinder(Compound):
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -907,8 +944,10 @@ class Sphere(Compound):
centered: tuple[bool, bool, bool] = (True, True, True),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
center_offset = Vector(
0 if centered[0] else radius,
0 if centered[1] else radius,
@ -925,7 +964,7 @@ class Sphere(Compound):
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -956,8 +995,10 @@ class Torus(Compound):
centered: tuple[bool, bool, bool] = (True, True, True),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
center_offset = Vector(
0 if centered[0] else major_radius,
0 if centered[1] else major_radius,
@ -974,7 +1015,7 @@ class Torus(Compound):
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
@ -1007,13 +1048,15 @@ class Wedge(Compound):
rotation: RotationLike = (0, 0, 0),
mode: Mode = Mode.ADD,
):
context: BuildPart = BuildPart._get_context()
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
location_planes = BuildPart._get_context()._get_and_clear_locations()
location_planes = context._get_and_clear_locations()
new_solids = [
Solid.makeWedge(
dx, dy, dz, xmin, zmin, xmax, zmax, loc.position(), plane.zDir
).moved(rotate)
for loc, plane in location_planes
]
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)

View file

@ -26,7 +26,9 @@ license:
"""
import unittest
from math import pi, sin
from build123d import *
from cadquery import Compound, Plane, Vector
def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
@ -47,7 +49,7 @@ class BuildPartTests(unittest.TestCase):
Box(10, 10, 10)
self.assertEqual(len(test.vertices()), 8)
Box(5, 5, 20, centered=(True, True, False))
self.assertEqual(len(test.vertices(Select.LAST)), 8)
self.assertEqual(len(test.vertices(Select.LAST)), 8)
def test_select_edges(self):
"""Test edges()"""
@ -55,7 +57,7 @@ class BuildPartTests(unittest.TestCase):
Box(10, 10, 10)
self.assertEqual(len(test.edges()), 12)
Box(5, 5, 20, centered=(True, True, False))
self.assertEqual(len(test.edges(Select.LAST)), 12)
self.assertEqual(len(test.edges(Select.LAST)), 12)
def test_select_faces(self):
"""Test faces()"""
@ -66,8 +68,242 @@ class BuildPartTests(unittest.TestCase):
with BuildSketch():
Rectangle(5, 5)
Extrude(5)
self.assertEqual(len(test.faces()), 11)
self.assertEqual(len(test.faces(Select.LAST)), 6)
self.assertEqual(len(test.faces()), 11)
self.assertEqual(len(test.faces(Select.LAST)), 6)
def test_select_solids(self):
"""Test faces()"""
with BuildPart() as test:
for i in [5, 10]:
PushPoints((3 * i, 0, 0))
Box(10, 10, i)
Box(20, 5, 5)
self.assertEqual(len(test.solids()), 2)
self.assertEqual(len(test.solids(Select.LAST)), 1)
def test_mode_add_multiple(self):
with BuildPart() as test:
PolarArray(30, 0, 360, 5)
Box(20, 20, 20)
self.assertAlmostEqual(len(test.solids()), 5)
def test_mode_subtract(self):
with BuildPart() as test:
Box(20, 20, 20)
Sphere(10, mode=Mode.SUBTRACT)
self.assertTrue(isinstance(test._obj, Compound))
self.assertAlmostEqual(test.part.Volume(), 8000 - (4000 / 3) * pi, 5)
def test_mode_intersect(self):
"""Note that a negative volume is created"""
with BuildPart() as test:
Box(20, 20, 20)
Sphere(10, mode=Mode.INTERSECT)
self.assertAlmostEqual(abs(test.part.Volume()), (4000 / 3) * pi, 5)
def test_mode_replace(self):
with BuildPart() as test:
Box(10, 10, 10)
Sphere(10, mode=Mode.REPLACE)
self.assertAlmostEqual(test.part.Volume(), (4000 / 3) * pi, 5)
def test_add_pending_faces(self):
with BuildPart() as test:
Box(100, 100, 100)
WorkplanesFromFaces(*test.faces())
with BuildSketch():
PolarArray(10, 0, 360, 5)
Circle(2)
self.assertEqual(test.workplane_count, 6)
self.assertEqual(test.pending_faces_count, 30)
def test_add_pending_edges(self):
with BuildPart() as test:
Box(100, 100, 100)
WorkplanesFromFaces(*test.faces())
with BuildLine():
CenterArc((0, 0), 5, 0, 180)
self.assertEqual(test.pending_edges_count, 6)
def test_add_pending_location_count(self):
with BuildPart() as test:
PolarArray(30, 0, 360, 5)
self.assertEqual(test.pending_location_count, 5)
class BuildPartExceptions(unittest.TestCase):
"""Test exception handling"""
def test_invalid_subtract(self):
with self.assertRaises(RuntimeError):
with BuildPart():
Sphere(10, mode=Mode.SUBTRACT)
def test_invalid_intersect(self):
with self.assertRaises(RuntimeError):
with BuildPart():
Sphere(10, mode=Mode.INTERSECT)
class TestCounterBoreHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
CounterBoreHole(2, 3, 1, 5)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 4 * pi - 9 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
CounterBoreHole(2, 3, 1)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 9 * pi - 9 * pi, 5)
class TestCounterSinkHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
CounterSinkHole(2, 4, 5)
self.assertLess(test.part.Volume(), 1000, 5)
self.assertGreater(test.part.Volume(), 1000 - 16 * 5 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
CounterSinkHole(2, 4)
self.assertLess(test.part.Volume(), 1000, 5)
self.assertGreater(test.part.Volume(), 1000 - 16 * 10 * pi, 5)
class TestExtrude(unittest.TestCase):
def test_extrude_both(self):
with BuildPart() as test:
with BuildSketch():
Rectangle(5, 5)
Extrude(2.5, both=True)
self.assertAlmostEqual(test.part.Volume(), 125, 5)
class TestHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
Hole(2, 5)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 5 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
PushPoints(test.faces().filter_by_axis(Axis.Z)[-1].Center())
Hole(2)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 10 * pi, 5)
class TestLoft(unittest.TestCase):
def test_simple_loft(self):
with BuildPart() as test:
slice_count = 10
for i in range(slice_count + 1):
Workplanes(Plane(origin=(0, 0, i * 3), normal=(0, 0, 1)))
with BuildSketch() as slice:
Circle(10 * sin(i * pi / slice_count) + 5)
Loft()
self.assertLess(test.part.Volume(), 225 * pi * 30, 5)
self.assertGreater(test.part.Volume(), 25 * pi * 30, 5)
class TestRevolve(unittest.TestCase):
def test_simple_revolve(self):
with BuildPart() as test:
with BuildSketch():
with BuildLine():
l1 = Line((0, 0), (12, 0))
l2 = RadiusArc(l1 @ 1, (15, 20), 50)
l3 = Spline(
l2 @ 1, (22, 40), (20, 50), tangents=(l2 % 1, (-0.75, 1))
)
l4 = RadiusArc(l3 @ 1, l3 @ 1 + Vector(0, 5), 5)
l5 = Spline(
l4 @ 1,
l4 @ 1 + Vector(2.5, 2.5),
l4 @ 1 + Vector(0, 5),
tangents=(l4 % 1, (-1, 0)),
)
Polyline(
l5 @ 1,
l5 @ 1 + Vector(0, 1),
(0, (l5 @ 1).y + 1),
l1 @ 0,
)
BuildFace()
Revolve()
self.assertLess(test.part.Volume(), 22**2 * pi * 50, 5)
self.assertGreater(test.part.Volume(), 144 * pi * 50, 5)
def test_revolve_with_axis(self):
with BuildPart() as test:
with BuildSketch():
with BuildLine():
l1 = Line((0, 0), (0, 12))
l2 = RadiusArc(l1 @ 1, (20, 10), 50)
l3 = Line(l2 @ 1, (20, 0))
l4 = Line(l3 @ 1, l1 @ 0)
BuildFace()
Revolve(axis_start=(0, 0, 0), axis_end=(1, 0, 0))
self.assertLess(test.part.Volume(), 244 * pi * 20, 5)
self.assertGreater(test.part.Volume(), 100 * pi * 20, 5)
class TestSection(unittest.TestCase):
def test_circle(self):
with BuildPart() as test:
Sphere(10)
Section()
self.assertAlmostEqual(test.faces()[-1].Area(), 100 * pi, 5)
# def test_custom_plane(self):
# with BuildPart() as test:
# Sphere(10)
# Section(Plane.named("XZ"))
# self.assertAlmostEqual(
# test.faces().filter_by_axis(Axis.Y)[-1].Area(), 100 * pi, 5
# )
class TestShell(unittest.TestCase):
def test_box_shell(self):
with BuildPart() as test:
Cylinder(10, 10)
Shell(thickness=1, kind=Kind.INTERSECTION)
self.assertAlmostEqual(
test.part.Volume(), 11**2 * pi * 12 - 10**2 * pi * 10, 5
)
class TestSplit(unittest.TestCase):
def test_split(self):
with BuildPart() as test:
Sphere(10)
Split(keep=Keep.TOP)
self.assertAlmostEqual(test.part.Volume(), (2 / 3) * 1000 * pi, 5)
def test_split_both(self):
with BuildPart() as test:
Sphere(10)
Split(keep=Keep.BOTH)
self.assertEqual(len(test.solids()), 2)
class TestTorus(unittest.TestCase):
def test_simple_torus(self):
with BuildPart() as test:
Torus(100, 10)
self.assertAlmostEqual(test.part.Volume(), pi * 100 * 2 * pi * 100, 5)
if __name__ == "__main__":