import math import unittest from build123d import * from build123d.topology import Shape def _assertTupleAlmostEquals(self, expected, actual, places, msg=None): """Check Tuples""" for i, j in zip(actual, expected): self.assertAlmostEqual(i, j, places, msg=msg) unittest.TestCase.assertTupleAlmostEquals = _assertTupleAlmostEquals class ObjectTests(unittest.TestCase): """Test adding objects""" # Solid def test_box(self): b = Solid.make_box(1, 2, 3) self.assertTrue(isinstance(b, Shape)) self.assertTupleAlmostEquals(b.center(), (0.5, 1.0, 1.5), 6) self.assertTupleAlmostEquals(b.bounding_box().min, (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(b.bounding_box().max, (1.0, 2.0, 3.0), 6) # Parts def test_box_min(self): b = Box(1, 2, 3, align=(Align.MIN, Align.MIN, Align.MIN)) self.assertTrue(isinstance(b, Part)) self.assertTrue(isinstance(b, Shape)) self.assertTupleAlmostEquals(b.center(), (0.5, 1.0, 1.5), 6) self.assertTupleAlmostEquals(b.bounding_box().min, (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(b.bounding_box().max, (1.0, 2.0, 3.0), 6) def test_box_center(self): b = Box(1, 2, 3, align=(Align.CENTER, Align.CENTER, Align.CENTER)) self.assertTrue(isinstance(b, Part)) self.assertTrue(isinstance(b, Shape)) self.assertTupleAlmostEquals(b.center(), (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(b.bounding_box().min, (-0.5, -1.0, -1.5), 6) self.assertTupleAlmostEquals(b.bounding_box().max, (0.5, 1.0, 1.5), 6) def test_box_max(self): b = Box(1, 2, 3, align=(Align.MAX, Align.MAX, Align.MAX)) self.assertTrue(isinstance(b, Part)) self.assertTrue(isinstance(b, Shape)) self.assertTupleAlmostEquals(b.center(), (-0.5, -1.0, -1.5), 6) self.assertTupleAlmostEquals(b.bounding_box().min, (-1.0, -2.0, -3.0), 6) self.assertTupleAlmostEquals(b.bounding_box().max, (0.0, 0.0, 0.0), 6) def test_cylinder_(self): s = Cylinder(1, 2) self.assertTupleAlmostEquals(s.center(), (0, 0, 0), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -1.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 1.0), 3) def test_cylinder_min(self): s = Cylinder(1, 2, align=(Align.MIN, Align.MIN, Align.MIN)) self.assertTupleAlmostEquals(s.center(), (1, 1, 1), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (2.0, 2.0, 2.0), 3) def test_cylinder_max(self): s = Cylinder(1, 2, align=(Align.MAX, Align.MAX, Align.MAX)) self.assertTupleAlmostEquals(s.center(), (-1, -1, -1), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-2.0, -2.0, -2.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.0, 0.0, 0.0), 3) def test_cylinder_90(self): s = Cylinder(1, 2, 90) self.assertTupleAlmostEquals(s.center(), (-0.07558681, -0.07558681, 0), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-0.5, -0.5, -1.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.5, 0.5, 1.0), 3) def test_cylinder_90_min(self): s = Cylinder(1, 2, 90, align=Align.MIN) self.assertTupleAlmostEquals(s.center(), (0.424413, 0.424413, 1.0), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 2.0), 3) def test_cylinder_90_max(self): s = Cylinder(1, 2, 90, align=Align.MAX) self.assertTupleAlmostEquals(s.center(), (-0.575586, -0.575586, -1), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -2.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.0, 0.0, 0.0), 3) def test_cone(self): s = Cone(1, 0.1, 1) self.assertTupleAlmostEquals(s.center(), (0.0, 0.0, -0.2229729), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -0.5), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 0.5), 3) def test_cone_90(self): s = Cone(1, 0.1, 1, 90) self.assertTupleAlmostEquals(s.center(), (-0.1814033, -0.1814033, -0.22297), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-0.5, -0.5, -0.5), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.5, 0.5, 0.5), 3) def test_cone0(self): s = Cone(1, 0, 2) self.assertTupleAlmostEquals(s.center(), (0.0, 0.0, -0.5), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -1.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 1.0), 3) def test_cone0_min(self): s = Cone(1, 0, 2, align=(Align.MIN, Align.MIN, Align.MIN)) self.assertTupleAlmostEquals(s.center(), (1.0, 1.0, 0.5), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (2.0, 2.0, 2.0), 3) def test_cone0_max(self): s = Cone(1, 0, 2, align=Align.MAX) self.assertTupleAlmostEquals(s.center(), (-1.0, -1.0, -1.5), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-2.0, -2.0, -2.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.0, 0.0, 0.0), 3) def test_sphere(self): s = Sphere(1) self.assertTupleAlmostEquals(s.center(), (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -1.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 1.0), 3) def test_sphere_45_90(self): s = Sphere(1, arc_size2=45, arc_size3=90) self.assertTupleAlmostEquals(s.center(), (-0.1169322, -0.1169322, 0.0966823), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-0.5, -0.5, -0.853553), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.5, 0.5, 0.853553), 3) def test_sphere_45_90_min(self): s = Sphere(1, arc_size2=45, arc_size3=90, align=Align.MIN) self.assertTupleAlmostEquals(s.center(), (0.383068, 0.383068, 0.950235), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.0, 1.0, 1.707106), 3) def test_sphere_45_90_max(self): s = Sphere( 1, arc_size2=45, arc_size3=90, align=(Align.MAX, Align.MAX, Align.MAX) ) self.assertTupleAlmostEquals(s.center(), (-0.616932, -0.616932, -0.756871), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.0, -1.0, -1.707106), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.0, 0.0, 0.0), 3) def test_torus_(self): s = Torus(1, 0.2) self.assertTupleAlmostEquals(s.center(), (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-1.2, -1.2, -0.2), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (1.2, 1.2, 0.2), 3) def test_torus_min(self): s = Torus(1, 0.2, align=(Align.MIN, Align.MIN, Align.MIN)) self.assertTupleAlmostEquals(s.center(), (1.2, 1.2, 0.2), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (0.0, 0.0, 0.0), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (2.4, 2.4, 0.4), 3) def test_torus_max(self): s = Torus(1, 0.2, align=Align.MAX) self.assertTupleAlmostEquals(s.center(), (-1.2, -1.2, -0.2), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-2.4, -2.4, -0.4), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.0, 0.0, 0.0), 3) def test_wedge(self): s = Wedge(1, 1, 1, 0.1, 0.1, 0.5, 0.5) self.assertTupleAlmostEquals(s.center(), (-0.073076, -0.134615, -0.073077), 3) self.assertTupleAlmostEquals(s.bounding_box().min, (-0.5, -0.5, -0.5), 3) self.assertTupleAlmostEquals(s.bounding_box().max, (0.5, 0.5, 0.5), 3) def test_hole(self): obj = Box(10, 10, 10) obj -= Hole(3, 10) self.assertAlmostEqual(obj.volume, 10**3 - 10 * math.pi * 3**2, 4) def test_hole_error(self): obj = Box(10, 10, 10) with self.assertRaises(ValueError): obj -= Hole(3) def test_counter_bore_hole(self): obj = Box(10, 10, 10, align=(Align.CENTER, Align.CENTER, Align.MAX)) obj -= CounterBoreHole( radius=3, depth=3, counter_bore_radius=4, counter_bore_depth=2 ) self.assertAlmostEqual( obj.volume, 10**3 - 2 * math.pi * 4**2 - 1 * math.pi * 3**2, 4 ) def test_counter_bore_hole_error(self): obj = Box(10, 10, 10) with self.assertRaises(ValueError): obj -= CounterBoreHole(3, 6, 2) def test_counter_sink_hole(self): obj = Box(10, 10, 10, align=(Align.CENTER, Align.CENTER, Align.MAX)) obj -= CounterSinkHole(radius=3, counter_sink_radius=6, depth=3) self.assertAlmostEqual(obj.volume, 747.9311207661184, 4) def test_counter_sink_hole_error(self): obj = Box(10, 10, 10) with self.assertRaises(ValueError): obj -= CounterSinkHole(3, 6) # Face def test_rect(self): r = Face.make_rect(2, 1) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-1.0, -0.5, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (1.0, 0.5, 0.0), 6) # Sketch def test_rect_min(self): r = Rectangle(1, 2, align=(Align.MIN, Align.MIN)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (0.5, 1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (1.0, 2.0, 0.0), 6) def test_rect_center(self): r = Rectangle(1, 2, align=(Align.CENTER, Align.CENTER)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-0.5, -1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (0.5, 1.0, 0.0), 6) def test_rect_max(self): r = Rectangle(1, 2, align=Align.MAX) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (-0.5, -1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-1.0, -2.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (0.0, 0.0, 0.0), 6) def test_circle_min(self): r = Circle(1, align=(Align.MIN, Align.MIN)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (1.0, 1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (2.0, 2.0, 0.0), 6) def test_circle_center(self): r = Circle(1, align=(Align.CENTER, Align.CENTER)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-1.0, -1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (1.0, 1.0, 0.0), 6) def test_circle_max(self): r = Circle(1, align=Align.MAX) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (-1.0, -1.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-2.0, -2.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (0.0, 0.0, 0.0), 6) def test_ellipse_min(self): r = Ellipse(1, 2, align=(Align.MIN, Align.MIN)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (1.0, 2.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (2.0, 4.0, 0.0), 6) def test_ellipse_center(self): r = Ellipse(1, 2, align=(Align.CENTER, Align.CENTER)) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-1.0, -2.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (1.0, 2.0, 0.0), 6) def test_ellipse_max(self): r = Ellipse(1, 2, align=Align.MAX) self.assertTrue(isinstance(r, Sketch)) self.assertTrue(isinstance(r, Shape)) self.assertTupleAlmostEquals(r.center(), (-1.0, -2.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().min, (-2.0, -4.0, 0.0), 6) self.assertTupleAlmostEquals(r.bounding_box().max, (0.0, 0.0, 0.0), 6) # further sketches are tested via context mode # Curve def test_edge(self): e = Edge.make_line((1, 2), (4, 4)) self.assertTrue(isinstance(e, Shape)) self.assertTupleAlmostEquals(e.center(), (2.5, 3.0, 0.0), 6) self.assertTupleAlmostEquals(e @ 0, (1.0, 2.0, 0.0), 6) self.assertTupleAlmostEquals(e @ 1, (4.0, 4.0, 0.0), 6) def test_line(self): e = Line((1, 2), (4, 4)) self.assertTrue(isinstance(e, Shape)) self.assertTupleAlmostEquals(e.center(), (2.5, 3.0, 0.0), 6) self.assertTupleAlmostEquals(e @ 0, (1.0, 2.0, 0.0), 6) self.assertTupleAlmostEquals(e @ 1, (4.0, 4.0, 0.0), 6) # further curves are tested via context mode class AlgebraTests(unittest.TestCase): # Shapes def test_shape_plus(self): f1 = Face.make_rect(1, 3) f2 = Face.make_rect(3, 1) f3 = f1 + f2 self.assertTupleAlmostEquals(f3.bounding_box().size, (3, 3, 0), 6) f4 = f1 + [] self.assertTupleAlmostEquals(f4.bounding_box().size, (1, 3, 0), 6) e1 = Edge.make_line((0, 0), (1, 1)) with self.assertRaises(ValueError): _ = f1 + e1 with self.assertRaises(ValueError): _ = Shape() + f2 f5 = Face() + f1 self.assertTupleAlmostEquals(f5.bounding_box().size, (1, 3, 0), 6) f6 = Face() + [f1, f2] self.assertTupleAlmostEquals(f6.bounding_box().size, (3, 3, 0), 6) # Part def test_part_plus(self): box = Box(1, 2, 3) cylinder = Cylinder(0.2, 5) last_edges = box.edges() result = box + cylinder self.assertEqual(len(result.edges() - last_edges), 6) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.MASS), (0, 0, -2.5), 6, ) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .last.center(CenterOf.MASS), (0, 0, 2.5), 6, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.5, -1.0, -2.5), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.5, 1.0, 2.5), 6) def test_part_minus(self): box = Box(1, 2, 3) cylinder = Cylinder(0.2, 5) last_edges = box.edges() result = box - cylinder self.assertEqual(len(result.edges() - last_edges), 3) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.MASS), (0, 0, -1.5), 6, ) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .last.center(CenterOf.MASS), (0, 0, 1.5), 6, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.5, -1.0, -1.5), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.5, 1.0, 1.5), 6) def test_part_and(self): box = Box(1, 2, 3) cylinder = Cylinder(0.2, 5) last_edges = box.edges() result = box & cylinder self.assertEqual(len(result.edges() - last_edges), 3) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.MASS), (0, 0, -1.5), 6, ) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .last.center(CenterOf.MASS), (0, 0, 1.5), 6, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.2, -0.2, -1.5), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.2, 0.2, 1.5), 6) # Sketch def test_sketch_plus(self): rect = Rectangle(1, 2) circle = Circle(0.6) last_edges = rect.edges() result = rect + circle self.assertEqual(len(result.edges()), 8) self.assertEqual(len(result.edges() - last_edges), 6) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.BOUNDING_BOX), (0.55, 0, 0), 6, ) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .last.center(CenterOf.BOUNDING_BOX), (-0.55, 0, 0), 6, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.6, -1.0, 0.0), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.6, 1.0, 0.0), 6) def test_sketch_minus(self): rect = Rectangle(1, 2) circle = Circle(0.2) last_edges = rect.edges() result = rect - circle self.assertEqual(len(result.edges()), 5) self.assertEqual(len(result.edges() - last_edges), 1) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.BOUNDING_BOX), (0, 0, 0), 3, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.5, -1.0, 0.0), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.5, 1.0, 0.0), 6) def test_sketch_minus_2(self): rect = Rectangle(1, 2) circle = Circle(0.7) last_edges = rect.edges() result = rect - circle self.assertEqual(len(result.edges()), 8) self.assertEqual(len(result.edges() - last_edges), 6) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.BOUNDING_BOX), (0.0, -0.5949, 0.0), 3, ) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .last.center(CenterOf.BOUNDING_BOX), (0.0, 0.5949, 0.0), 3, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.5, -1.0, 0.0), 6) self.assertTupleAlmostEquals(result.bounding_box().max, (0.5, 1.0, 0.0), 6) def test_sketch_and(self): rect = Rectangle(1, 2) circle = Circle(0.4) last_edges = rect.edges() result = rect & circle self.assertEqual(len(result.edges()), 1) self.assertEqual(len(result.edges() - last_edges), 1) self.assertTupleAlmostEquals( result.edges() .sort_by() .filter_by(GeomType.CIRCLE) .first.center(CenterOf.BOUNDING_BOX), (0, 0, 0), 3, ) self.assertTupleAlmostEquals(result.bounding_box().min, (-0.4, -0.4, 0.0), 3) self.assertTupleAlmostEquals(result.bounding_box().max, (0.4, 0.4, 0.0), 3) # Curve def test_curve_plus_continuous(self): l1 = Polyline((0, 0), (1, 0), (1, 1)) l2 = Line((1, 1), (0, 0)) l = l1 + l2 self.assertTrue(isinstance(l, Wire)) self.assertTrue(l.is_closed) self.assertTupleAlmostEquals( l.center(CenterOf.MASS), (0.6464466094067263, 0.35355339059327373, 0.0), 6 ) def test_curve_plus_noncontinuous(self): e1 = Edge.make_line((0, 1), (1, 1)) e2 = Edge.make_line((1, 1), (2, 1)) e3 = Edge.make_line((2, 1), (3, 1)) l = Curve() + [e1, e3] self.assertTrue(isinstance(l, Compound)) l += e2 # fills the hole and makes a single edge self.assertTrue(isinstance(l, Edge)) self.assertAlmostEqual(l.length, 3, 5) l2 = e1 + e3 self.assertTrue(isinstance(l2, list)) def test_curve_plus_nothing(self): e1 = Edge.make_line((0, 1), (1, 1)) l = e1 + Curve() self.assertTrue(isinstance(l, Edge)) self.assertAlmostEqual(l.length, 1, 5) def test_nothing_plus_curve(self): e1 = Edge.make_line((0, 1), (1, 1)) l = Curve() + e1 self.assertTrue(isinstance(l, Edge)) self.assertAlmostEqual(l.length, 1, 5) def test_bad_dims(self): e1 = Edge.make_line((0, 1), (1, 1)) f1 = Face.make_rect(1, 1) with self.assertRaises(ValueError): _ = e1 + f1 def test_curve_minus(self): l1 = Line((0, 0), (1, 1)) l2 = Line((0.25, 0.25), (0.75, 0.75)) l = l1 - l2 vertices = l.vertices().sort_by(Axis.X) self.assertEqual(len(vertices), 4) self.assertTupleAlmostEquals(vertices[0], (0.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals(vertices[1], (0.25, 0.25, 0.0), 6) self.assertTupleAlmostEquals(vertices[2], (0.75, 0.75, 0.0), 6) self.assertTupleAlmostEquals(vertices[3], (1.0, 1.0, 0.0), 6) def test_curve_and(self): l1 = Line((0, 0), (1, 1)) l2 = Line((0.25, 0.25), (0.75, 0.75)) l = l1 & l2 vertices = l.vertices().sort_by(Axis.X) self.assertEqual(len(vertices), 2) self.assertTupleAlmostEquals(vertices[0], l2 @ 0, 6) self.assertTupleAlmostEquals(vertices[1], l2 @ 1, 6) def test_curve_operators(self): l1 = CenterArc((0, 0), 1, 0, 180) l2 = CenterArc((2, 0), 1, 0, -180) l = Curve() + [l1, l2] self.assertTupleAlmostEquals(l @ 0.25, Vector(2.0, -1.0, 0.0), 6) self.assertTupleAlmostEquals(l % 0.25, Vector(-1.0, 0.0, 0.0), 6) self.assertTupleAlmostEquals((l ^ 0.25).position, l @ 0.25, 6) self.assertTupleAlmostEquals( (l ^ 0.25).orientation, Vector(0.0, -90.0, 90.0), 6 ) # Part + - & Empty def test_empty_plus_part(self): b = Box(1, 2, 3) r = Part() + b self.assertAlmostEqual(b.volume, r.volume, 5) self.assertEqual(r._dim, 3) def test_part_plus_empty(self): b = Box(1, 2, 3) r = b + Part() self.assertAlmostEqual(b.volume, r.volume, 5) self.assertEqual(r._dim, 3) def test_empty_minus_part(self): b = Box(1, 2, 3) with self.assertRaises(ValueError): r = Part() - b def test_part_minus_empty(self): b = Box(1, 2, 3) r = b - Part() self.assertAlmostEqual(b.volume, r.volume, 5) self.assertEqual(r._dim, 3) def test_empty_and_part(self): b = Box(1, 2, 3) with self.assertRaises(ValueError): r = Part() & b def test_part_and_empty(self): b = Box(1, 2, 3) with self.assertRaises(ValueError): r = b & Part() # Sketch + - & Empty def test_empty_plus_sketch(self): b = Rectangle(1, 2) r = Sketch() + b self.assertAlmostEqual(b.area, r.area, 5) self.assertEqual(r._dim, 2) def test_sketch_plus_empty(self): b = Rectangle(1, 2) r = b + Sketch() self.assertAlmostEqual(b.area, r.area, 5) self.assertEqual(r._dim, 2) def test_empty_minus_sketch(self): b = Rectangle(1, 2) with self.assertRaises(ValueError): r = Sketch() - b def test_sketch_minus_empty(self): b = Rectangle(1, 2) r = b - Sketch() self.assertAlmostEqual(b.area, r.area, 5) self.assertEqual(r._dim, 2) def test_empty_and_sketch(self): b = Rectangle(1, 3) with self.assertRaises(ValueError): r = Sketch() & b def test_sketch_and_empty(self): b = Rectangle(1, 2) with self.assertRaises(ValueError): r = b & Sketch() def test_1d_2d_minus(self): line = Line((0, 0), (1, 1)) rectangle = Rectangle(1, 1) r = line - rectangle vertices = r.vertices() self.assertEqual(len(vertices), 2) self.assertTupleAlmostEquals(vertices[0], (0.5, 0.5, 0.0), 6) self.assertTupleAlmostEquals(vertices[1], (1.0, 1.0, 0.0), 6) def test_1d_3d_minus(self): line = Line((0, 0), (1, 1)) box = Box(1, 1, 1) r = line - box vertices = r.vertices() self.assertEqual(len(vertices), 2) self.assertTupleAlmostEquals(vertices[0], (0.5, 0.5, 0.0), 6) self.assertTupleAlmostEquals(vertices[1], (1.0, 1.0, 0.0), 6) def test_2d_3d_minus(self): rectangle = Pos(0.5, 0, 0) * Rectangle(1, 1) box = Box(1, 1, 1) r = rectangle - box vertices = r.vertices() self.assertEqual(len(vertices), 4) self.assertTupleAlmostEquals(vertices[0], (0.5, -0.5, 0.0), 6) self.assertTupleAlmostEquals(vertices[1], (0.5, 0.5, 0.0), 6) self.assertTupleAlmostEquals(vertices[2], (1.0, 0.5, 0.0), 6) self.assertTupleAlmostEquals(vertices[3], (1.0, -0.5, 0.0), 6) def test_3d_2d_minus(self): box = Box(1, 1, 1) rectangle = Rectangle(1, 1) with self.assertRaises(ValueError): _ = box - rectangle def test_3d_1d_minus(self): box = Box(1, 1, 1) line = Line((0, 0), (1, 1)) with self.assertRaises(ValueError): _ = box - line def test_2d_1d_minus(self): rectangle = Rectangle(1, 1) line = Line((0, 0), (1, 1)) with self.assertRaises(ValueError): _ = rectangle - line def test_compound_plus(self): addend0 = Box(4, 4, 4) addend1 = Box(1, 1, 8) base_shapes = PolarLocations(15, 20) * addend0 addons = PolarLocations(16, 20) * addend1 p1 = Compound(base_shapes) + Compound(addons) self.assertEqual(p1._dim, 3) self.assertAlmostEqual(p1.volume, 20 * (4**3 + 1 * 1 * 4), 5) with self.assertRaises(ValueError): Compound(children=[addend0] + addend1.faces()) + Box(1, 1, 1) with self.assertRaises(ValueError): Box(1, 1, 1) + Compound(children=addend1.faces()) def test_compound_minus(self): minuend = Box(4, 4, 4) subtrahend = Box(1, 1, 4) base_shapes = PolarLocations(15, 20) * minuend holes = PolarLocations(17, 20) * subtrahend s1 = Compound(base_shapes) - Compound(holes) self.assertAlmostEqual(s1.volume, 20 * (16 - 0.5) * 4, 5) s2 = minuend - subtrahend self.assertEqual(s2._dim, 3) self.assertAlmostEqual(s2.volume, (16 - 1) * 4, 5) s3 = minuend.solid() - subtrahend.solid() self.assertEqual(s3._dim, 3) self.assertAlmostEqual(s3.volume, (16 - 1) * 4, 5) s4 = Compound(minuend.faces()) - subtrahend self.assertEqual(s4._dim, 2) self.assertAlmostEqual(s4.area, 4 * 16 + 2 * (16 - 1), 5) s5 = minuend.shell() - subtrahend self.assertEqual(s5._dim, 2) self.assertAlmostEqual(s5.area, 4 * 16 + 2 * (16 - 1), 5) s6 = minuend - None self.assertEqual(s6._dim, 3) self.assertAlmostEqual(s6.volume, 4**3, 5) s7 = minuend - Pos((8, 0, 0)) * subtrahend self.assertEqual(s7._dim, 3) self.assertAlmostEqual(s7.volume, 4**3, 5) s8 = minuend - [subtrahend, Box(4, 1, 1)] self.assertEqual(s8._dim, 3) self.assertAlmostEqual(s8.volume, (16 - 1) * 4 - 1 * 3, 5) s9 = Compound(children=[minuend]) - Compound(children=[subtrahend]) self.assertEqual(s9._dim, 3) self.assertAlmostEqual(s9.volume, (16 - 1) * 4, 5) s10 = Compound(minuend) - Compound(subtrahend) self.assertEqual(s10._dim, 3) self.assertAlmostEqual(s10.volume, (16 - 1) * 4, 5) s11 = minuend.shell() - Compound(children=[subtrahend, Box(4, 1, 1)]) self.assertEqual(s11._dim, 2) self.assertAlmostEqual(s11.area, 2 * 16 + 4 * (16 - 1), 5) b1 = minuend.shell() s12 = b1 - Compound(children=[subtrahend, b1.faces()[0]]) self.assertEqual(s12._dim, 2) self.assertAlmostEqual(s12.area, 3 * 16 + 2 * (16 - 1), 5) self.assertEqual(len(s12.faces()), 5) with self.assertRaises(ValueError): Compound(children=[minuend] + subtrahend.faces()) - Box(1, 1, 1) class LocationTests(unittest.TestCase): def test_wheel(self): plane = Plane.ZX cyl = Cylinder(1, 0.5) box = Box(0.3, 0.3, 0.5) p = plane * cyl for loc in PolarLocations(0.7, 10): p -= plane * loc * box front_faces = p.faces().sort_by(Axis.Y).first wires = front_faces.wires().sort_by(SortBy.DISTANCE, 1)[1:] centers = [wire.center(CenterOf.MASS) for wire in wires] self.assertEqual(len(p.edges()), 123) self.assertTupleAlmostEquals([c.length for c in centers], [0.7433034] * 10, 6) self.assertTupleAlmostEquals(p.bounding_box().min, (-1.0, -0.25, -1.0), 6) self.assertTupleAlmostEquals(p.bounding_box().max, (1.0, 0.25, 1.0), 6) def test_wheels(self): plane = Plane.ZX rotations = [Rot(Y=a) for a in (0, 45, 90, 135)] s = Sketch() for i, outer_loc in enumerate(GridLocations(3, 3, 2, 2)): # on plane, located to grid position, and finally rotated c_plane = plane * outer_loc * rotations[i] s += c_plane * Circle(1) s = Sketch(s.faces()) for loc in PolarLocations(0.8, (i + 3) * 2): # Use polar locations on c_plane s -= c_plane * loc * Rectangle(0.1, 0.3) nested = [g.sort_by(Axis.X) for g in s.faces().group_by()] faces = [item for s in nested for item in s] # flatten self.assertEqual(len(s.faces()), 4) self.assertListEqual([len(f.edges()) for f in s.faces()], [25, 33, 41, 49]) self.assertTupleAlmostEquals( faces[0].normal_at(faces[0].center()), (0.0, 1.0, 0.0), 6 ) self.assertTupleAlmostEquals( faces[1].normal_at(faces[1].center()), (0.0, math.sqrt(2) / 2, math.sqrt(2) / 2), 6, ) self.assertTupleAlmostEquals( faces[2].normal_at(faces[2].center()), (0.0, 0.0, 1.0), 6 ) self.assertTupleAlmostEquals( faces[3].normal_at(faces[3].center()), (0.0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6, ) class OperationsTests(unittest.TestCase): def test_fillet_3d(self): b = Box(1, 2, 3) c = fillet(b.edges(), radius=0.2) self.assertAlmostEqual(b.volume, 6.0, 6) self.assertAlmostEqual(c.volume, 5.804696, 4) def test_fillet_2d(self): r = Rectangle(1, 2) c = fillet(r.vertices(), radius=0.2) self.assertAlmostEqual(r.area, 2.0, 6) self.assertAlmostEqual(c.area, 1.965663, 4) def test_chamfer_3d(self): b = Box(1, 2, 3) c = chamfer(b.edges(), length=0.2) self.assertAlmostEqual(b.volume, 6.0, 6) self.assertAlmostEqual(c.volume, 5.56266, 4) def test_chamfer_2d(self): r = Rectangle(1, 2) c = chamfer(r.vertices(), length=0.2) self.assertAlmostEqual(r.area, 2.0, 6) self.assertAlmostEqual(c.area, 1.92, 4) def test_extrude(self): s = Circle(1) p = extrude(s, amount=1) p = chamfer(p.edges().filter_by(GeomType.CIRCLE), 0.3) self.assertEqual(len(p.edges().filter_by(GeomType.CIRCLE)), 4) def test_extrude_both(self): s = Circle(1) p = extrude(s, amount=1, both=True) self.assertAlmostEqual(p.bounding_box().size.Z, 2, 4) class RightMultipleTests(unittest.TestCase): def test_rmul(self): c = [Location((1, 2, 3))] * Circle(1) self.assertTupleAlmostEquals(c[0].position, (1, 2, 3), 6) def test_rmul_error(self): with self.assertRaises(ValueError): [Vector(1, 2, 3)] * Circle(1) if __name__ == "__main__": unittest.main(failfast=True)