def test_equality(self): # Same object self.assertEqualToItself(Cuboid(11, 11, 11)) # Equal objects self.assertEqual (Cuboid(11, 11, 11), Cuboid(11, 11, 11)) # Different objects self.assertNotEqual(Cuboid(11, 11, 11), Cuboid(11, 11, 12))
def test_to_scad(self): sphere = Sphere(2) cube = Cuboid(10, 10, 10) cylinder = Frustum(origin, Z, 11, 11) self.assertEqual( Difference([sphere, cube, cylinder]).to_scad(), ScadObject("difference", None, None, [ sphere.to_scad(), cube.to_scad(), cylinder.to_scad(), ]))
def test_multiplication(self): a = Sphere(2) b = Cuboid(10, 10, 10) c = Frustum(origin, Z, 11, 11) d = Cuboid(20, 20, 20) self.assertEqual( a * b , Intersection([a, b ])) self.assertEqual( c * c , Intersection([c, c ])) self.assertEqual( (a * b) * c , Intersection([a, b, c ])) self.assertEqual( a * (b * c) , Intersection([a, b, c ])) self.assertEqual( a * b * c * d , Intersection([a, b, c, d])) self.assertEqual( ((a * b) * c) * d , Intersection([a, b, c, d])) self.assertEqual( a * (b * (c * d)), Intersection([a, b, c, d])) self.assertEqual( (a * b) * (c * d) , Intersection([a, b, c, d]))
def test_subtraction(self): a = Sphere(2) b = Cuboid(10, 10, 10) c = Frustum(origin, Z, 11, 11) d = Cuboid(20, 20, 20) # Difference is non-associative, so we get nested differences self.assertEqual( a - b , Difference([a, b ])) self.assertEqual( c - c , Difference([c, c ])) self.assertEqual( (a - b) - c , Difference([a, b, c ])) self.assertEqual( a - (b - c) , Difference([a, Difference([b, c])])) self.assertEqual( a - b - c - d , Difference([a, b, c, d])) self.assertEqual( ((a - b) - c) - d , Difference([a, b, c, d])) self.assertEqual( a - (b - (c - d)), Difference([a, Difference([b, Difference([c, d])])])) self.assertEqual( (a - b) - (c - d) , Difference([Difference([a, b]), Difference([c, d])]))
def test_sum(self): sphere = Sphere(2) cube = Cuboid(10, 10, 10) cylinder = Frustum(origin, Z, 11, 11) objects = [sphere, cube, cylinder] self.assertEqual(sum(objects, Union.empty()), Union(objects))
def test_to_scad(self): sphere = Sphere(2) cube = Cuboid(10, 10, 10) cylinder = Frustum(origin, Z, 11, 11) self.assertEqual( Union([sphere, cube, cylinder]).to_scad(), ScadObject("union", None, None, [ sphere.to_scad(), cube.to_scad(), cylinder.to_scad(), ])) # Empty self.assertEqual( Union([]).to_scad(), ScadObject("union", None, None, None))
def test_to_scad(self): self.ignore_scad_comments = True # Unit axis to unit axis self.assertScadObjectTarget(RotateFromTo(X, Y), None, "rotate", None, [("a", 90), ("v", [ 0, 0, 1])], None) self.assertScadObjectTarget(RotateFromTo(Y, Z), None, "rotate", None, [("a", 90), ("v", [ 1, 0, 0])], None) self.assertScadObjectTarget(RotateFromTo(Z, X), None, "rotate", None, [("a", 90), ("v", [ 0, 1, 0])], None) self.assertScadObjectTarget(RotateFromTo(Y, X), None, "rotate", None, [("a", 90), ("v", [ 0, 0, -1])], None) self.assertScadObjectTarget(RotateFromTo(Z, Y), None, "rotate", None, [("a", 90), ("v", [-1, 0, 0])], None) self.assertScadObjectTarget(RotateFromTo(X, Z), None, "rotate", None, [("a", 90), ("v", [ 0, -1, 0])], None) # Unit axis to unit axis with different length self.assertScadObjectTarget(RotateFromTo(X, 2 * Y), None, "rotate", None, [("a", 90), ("v", [0, 0, 1])], None) # Different signs for X/Y axes self.assertScadObjectTarget(RotateFromTo( X, Y), None, "rotate", None, [("a", 90), ("v", [ 0, 0, 1])], None) self.assertScadObjectTarget(RotateFromTo( X, -Y), None, "rotate", None, [("a", 90), ("v", [ 0, 0, -1])], None) self.assertScadObjectTarget(RotateFromTo(-X, Y), None, "rotate", None, [("a", 90), ("v", [ 0, 0, -1])], None) self.assertScadObjectTarget(RotateFromTo(-X, -Y), None, "rotate", None, [("a", 90), ("v", [ 0, 0, 1])], None) self.assertScadObjectTarget(RotateFromTo( Y, X), None, "rotate", None, [("a", 90), ("v", [ 0, 0, -1])], None) self.assertScadObjectTarget(RotateFromTo( Y, -X), None, "rotate", None, [("a", 90), ("v", [ 0, 0, 1])], None) self.assertScadObjectTarget(RotateFromTo(-Y, X), None, "rotate", None, [("a", 90), ("v", [ 0, 0, 1])], None) self.assertScadObjectTarget(RotateFromTo(-Y, -X), None, "rotate", None, [("a", 90), ("v", [ 0, 0, -1])], None) # Same direction (no effect). This generates a zero XYZ transform (not # an empty ScadObject, which would also be possible). cube = Cuboid(2, 2, 2).to_scad() self.assertScadObjectTarget(RotateFromTo(X , X ), None, "rotate", [[0, 0, 0]], None, None) self.assertScadObjectTarget(RotateFromTo(X , X ), cube, "rotate", [[0, 0, 0]], None, [cube]) self.assertScadObjectTarget(RotateFromTo([1, 2, 3], [2, 4, 6]), cube, "rotate", [[0, 0, 0]], None, [cube]) # Opposite direction (ambiguous rotation) self.assertIn(("a", 180), RotateFromTo(X , -X , ignore_ambiguity=True).to_scad(None)._kw_parameters) self.assertIn(("a", 180), RotateFromTo([1, 2, 3], [-2, -4, -6], ignore_ambiguity=True).to_scad(None)._kw_parameters)
def test_repr(self): t = Translate([10, 20, 30]) cube = Cuboid(1, 1, 1) self.assertRepr( Transformed(t, cube), "Transformed(Translate(Vector(10, 20, 30)), Cuboid(1, 1, 1))")
def test_construction(self): sphere = Sphere(11) cube = Cuboid(22, 22, 22) cylinder = Frustum(origin, Z, 11, 11) object_list = [sphere, cube, cylinder] # Empty Csg([]) # With objects self.assertEqual(Csg(object_list).children, object_list) # With generator self.assertEqual(Csg(o for o in object_list).children, object_list) # With invalid objects with self.assertRaises(TypeError): Csg(None) # None instead of empty list with self.assertRaises(TypeError): Csg([Sphere]) # Class instead of object with self.assertRaises(TypeError): Csg(sphere) # Object instead of list with self.assertRaises(TypeError): Csg([None]) # List with invalid value with self.assertRaises(TypeError): Csg([sphere, None]) # List with object and invalid value
def test_to_scad(self): sphere = Sphere(2) cube = Cuboid(10, 10, 10) cylinder = Frustum(origin, Z, 5, 5) mixed = Union( [Intersection([sphere, cylinder]), Difference([cube, sphere])]) self.assertEqual( mixed.to_scad(), ScadObject("union", None, None, [ ScadObject( "intersection", None, None, [sphere.to_scad(), cylinder.to_scad()]), ScadObject("difference", None, None, [cube.to_scad(), sphere.to_scad()]), ]))
def test_csg_generators(self): sphere = Sphere(11) cuboid = Cuboid(11, 22, 33) cylinder = Frustum(origin, Z, 11, 11) object_list = [sphere, cuboid, cylinder] self.assertEqual(union(object_list), Union(object_list)) self.assertEqual(difference(object_list), Difference(object_list)) self.assertEqual(intersection(object_list), Intersection(object_list))
def test_addition(self): a = Sphere(2) b = Cuboid(10, 10, 10) c = Frustum(origin, Z, 11, 11) d = Cuboid(20, 20, 20) self.assertEqual( a + b , Union([a, b ])) self.assertEqual( c + c , Union([c, c ])) self.assertEqual( (a + b) + c , Union([a, b, c ])) self.assertEqual( a + (b + c) , Union([a, b, c ])) self.assertEqual( a + b + c + d , Union([a, b, c, d])) self.assertEqual( ((a + b) + c) + d , Union([a, b, c, d])) self.assertEqual( a + (b + (c + d)), Union([a, b, c, d])) self.assertEqual( (a + b) + (c + d) , Union([a, b, c, d])) # Empty union self.assertEqual(Union([]) + Union([]), Union([])) self.assertEqual(Union([]) + a, Union([a])) self.assertEqual(a + Union([]), Union([a]))
def test_equality(self): sphere = Sphere(11) cube = Cuboid(22, 22, 22) cylinder = Frustum(origin, Z, 11, 11) objects = [sphere, cube, cylinder] # Different types of CSG are not equal, even if their children are identical self.assertNotEqual(Union(objects), Intersection(objects)) self.assertNotEqual(Intersection(objects), Difference(objects)) self.assertNotEqual(Difference(objects), Union(objects))
def test_equality(self): scale1a = ScaleUniform(1) scale1b = ScaleUniform(1) scale2 = ScaleUniform(2) cube1a = Cuboid(1, 1, 1) cube1b = Cuboid(1, 1, 1) cube2 = Cuboid(2, 2, 2) self.assertEqual(Transformed(scale1a, cube1a), Transformed(scale1a, cube1b)) # Equal objects self.assertEqual(Transformed(scale1a, cube1a), Transformed(scale1b, cube1a)) # Equal transform self.assertNotEqual(Transformed(scale1a, cube1a), cube1a) # Transformed / original self.assertNotEqual(Transformed(scale1a, cube1a), Transformed(scale2, cube1a)) # Different transform self.assertNotEqual(Transformed(scale1a, cube1a), Transformed(scale1a, cube2)) # Different objects
def test_multiplication_with_object_and_transform(self): # Multiplication is used for both intersection (Object * Object) and transform (Transform * Object) a = Sphere(2) b = Cuboid(3, 3, 3) s = ScaleAxes(1, 2, -1) t = Translate([0, 0, 0]) self.assertEqual( t * (a * b), Transformed(t, Intersection([a, b]))) self.assertEqual((t * a) * b , Intersection([Transformed(t, a), b])) self.assertEqual((s * t) * a , Transformed(Chained([s, t]), a)) self.assertEqual( s * (t * a), Transformed(Chained([s, t]), a))
def cube(size): """Generate a cube. The cube will have one corner at the origin, will be aligned with the axes, and extend in the positive axis directions. Signatures (convenience forms only): * cube(size) """ size = number.convert(size, "size") return Cuboid(size, size, size)
def test_invalid_operations(self): a = Sphere(2) b = Cuboid(10, 10, 10) s = ScaleUniform(1) t = Translate([0, 0, 0]) for target in [a, t, t*a, a+b, a-b, a*b, s*t]: for invalid in [None, 0, 0.0, ""]: with self.assertRaises(TypeError): target + invalid with self.assertRaises(TypeError): target - invalid with self.assertRaises(TypeError): target * invalid with self.assertRaises(TypeError): invalid + target with self.assertRaises(TypeError): invalid - target with self.assertRaises(TypeError): invalid * target
def cuboid(size_or_x_or_xyz, y=None, z=None): """Generate a cuboid. The cuboid will have one corner at the origin, will be aligned with the axes, and extend in the positive axis directions. Signatures (convenience forms only): * cuboid(size) * cuboid(x, y, z) * cuboid(xyz) xzy can be Vector, list, or tuple. Note that for convenience, a Vector can be used for xyz even though xyz is not strictly a vector. """ if both(y, z): return Cuboid(size_or_x_or_xyz, y, z) elif neither(y, z) and number.valid(size_or_x_or_xyz): return Cuboid(size_or_x_or_xyz, size_or_x_or_xyz, size_or_x_or_xyz) elif neither(y, z) and Vector.valid_type(size_or_x_or_xyz): return Cuboid(*size_or_x_or_xyz) else: raise TypeError("y and z can only be specified together")
def test_construction(self): # Transformed objects can also be created by multiplying a transform with an object. This is # tested in test_object.py, as it concerns the operators defined by Object. # Transformed object t = Translate([10, 20, 30]) cube = Cuboid(1, 1, 1) with self.assertNothingRaised(): Transformed(t, cube) with self.assertRaises(TypeError): Transformed(t, t) with self.assertRaises(TypeError): Transformed(cube, cube) with self.assertRaises(TypeError): Transformed(t, None) with self.assertRaises(TypeError): Transformed(None, cube)
def test_equality(self): sphere = Sphere(11) cuboid = Cuboid(11, 22, 33) cylinder = Frustum(origin, Z, 11, 11) objects = [sphere, cuboid, cylinder] # Same object self.assertEqualToItself(Union([])) self.assertEqualToItself(Union(objects)) # Equal objects self.assertEqual(Union([]), Union([])) self.assertEqual(Union(objects), Union(objects)) # Different objects self.assertNotEqual(Union(objects), Union([sphere, cuboid])) self.assertNotEqual(Union([cuboid, sphere]), Union([sphere, cuboid])) # Equal objects from different specifications self.assertEqual(Union.empty(), Union([]))
def test_to_scad(self): r = RotateXyz(60, 30, 15) s = ScaleAxes(1, 2, -1) cube = Cuboid(11, 11, 11) # Simple transform self.assertEqual( Transformed(r, cube).to_scad(), ScadObject("rotate", [[60, 30, 15]], None, [ ScadObject("cube", [[11, 11, 11]], None, None), ])) # Chained transform self.assertEqual( Transformed(Chained([r, s]), cube).to_scad(), ScadObject("rotate", [[60, 30, 15]], None, [ ScadObject("scale", [[1, 2, -1]], None, [ ScadObject("cube", [[11, 11, 11]], None, None), ]) ]))
def test_to_tree(self): a = Sphere(2) b = Cuboid(3, 3, 3) s = ScaleAxes(1, 2, -1) t = Translate([0, 0, 0]) part = a + s*t*b actual = part.to_tree() expected = Node(part, [ Node(a), Node(s*t*b, [ Node(s*t, [ Node(s), Node(t), ]), Node(b), ]), ]) self.assertEqual(actual, expected)
def test_render_to_file(self): part = Sphere(0.6) + Cuboid(1, 2, 3) - Frustum([0, 0, 0], [0, 0, 4], 0.5, 0.5) expected_file_name = os.path.join(os.path.dirname(__file__), "test_scad_object_render_to_file_expected.scad") actual_file_name = os.path.join(os.path.dirname(__file__), "test_scad_object_render_to_file_actual.scad") self.assertTrue(os.path.isfile(expected_file_name)) self.assertFalse(os.path.exists(actual_file_name)) try: render_to_file(part, actual_file_name, fn=60) with open(actual_file_name) as actual_file: actual = actual_file.read() with open(expected_file_name) as expected_file: expected = expected_file.read() self.assertNotEqual(expected, "") self.assertEqual(actual, expected) finally: if os.path.exists(actual_file_name): os.unlink(actual_file_name)
def test_cuboid_generators(self): self.assertEqual(cuboid(1), Cuboid(1, 1, 1)) self.assertEqual(cuboid(1, 2, 3), Cuboid(1, 2, 3)) self.assertEqual(cuboid([1, 2, 3]), Cuboid(1, 2, 3)) self.assertEqual(cuboid((1, 2, 3)), Cuboid(1, 2, 3)) self.assertEqual(cuboid(Vector(1, 2, 3)), Cuboid(1, 2, 3)) self.assertEqual(cube(1), Cuboid(1, 1, 1)) with self.assertRaises(TypeError): cuboid(1, 2) with self.assertRaises(TypeError): cuboid(None) with self.assertRaises(TypeError): cuboid("1") with self.assertRaises(TypeError): cuboid(1, 2, None) with self.assertRaises(TypeError): cuboid(1, None, 2) with self.assertRaises(TypeError): cuboid(1, 2, "3")
def to_scad(self): return (Cuboid(inf, inf, inf).translate([-inf / 2, -inf / 2, -inf]).up( self._offset).rotate(frm=Z, to=self._normal, ignore_ambiguity=True).to_scad().comment( str(self)))
from cadlib.object.primitives import Cuboid from cadlib.scad import render_to_file from cadlib.transform.primitives import RotateXyz object = Cuboid(2, 3, 4) rotate = RotateXyz([20, 0, 45]) assembly = rotate * object render_to_file(assembly, "adhoc_test.scad")
from cadlib.object.primitives import Sphere, Cuboid, Frustum # from cadlib.object.generators import * #from cadlib.transform.primitives import RotateXyz, ScaleAxes, Translate from cadlib.util.vector import X, Y, Z from cadlib.scad import render_to_file # def test_case(name, object): # print(name) # print(" Cadlib tree:") # print(object.to_tree().format(top_indent=" ")) # print(" OpenSCAD tree:") # print(object.to_scad().to_tree().format(top_indent=" ")) # print(" OpenSCAD source:") # print(object.to_scad().to_code(top_indent=" ")) c = Cuboid(50, 50, 50).rotate(Z, 30).right(25) #c = Frustum(0, 20*Y, 5, 5) # TODO base/cap/center print(c.to_tree().format()) s = Sphere(25) #c2 = Cuboid(10, 10, 10).top_face.at([0, 0, 0]) assembly = c + s.center.at(c.top_face) + c2 print(assembly.to_scad().to_tree().format()) print(assembly.to_scad().to_code()) render_to_file(assembly, "anchor_test.scad", fn=30)
from cadlib.object.primitives import Cuboid, Cylinder, Sphere from cadlib.util.vector import Z def test_case(name, object): print(name) print(" Cadlib tree:") print(object.to_tree().format(top_indent=" ")) print(" OpenSCAD tree:") print(object.to_scad().to_tree().format(top_indent=" ")) o1 = Cuboid(10, 10, 10) o2 = Cylinder(Z, 5, 5) o3 = Sphere(2) test_case("2-intersection", o1 * o2) test_case("3-intersection (1)", (o1 * o2) * o3) test_case("3-intersection (2)", o1 * (o2 * o3)) test_case("2-union", o1 + o2) test_case("2-difference", o1 - o2)
def test_transform_shortcuts(self): a = Cuboid(11, 11, 11) self.assertEqual(a.up (1), Transformed(Translate([ 0, 0, 1]), a)) self.assertEqual(a.down (2), Transformed(Translate([ 0, 0, -2]), a)) self.assertEqual(a.left (3), Transformed(Translate([-3, 0, 0]), a)) self.assertEqual(a.right (4), Transformed(Translate([ 4, 0, 0]), a)) self.assertEqual(a.forward(5), Transformed(Translate([ 0, 5, 0]), a)) self.assertEqual(a.back (6), Transformed(Translate([ 0, -6, 0]), a)) self.assertEqual(a.yaw_left (1), Transformed(RotateYpr( 1, 0, 0), a)) self.assertEqual(a.yaw_right (2), Transformed(RotateYpr(-2, 0, 0), a)) self.assertEqual(a.pitch_up (3), Transformed(RotateYpr( 0, 3, 0), a)) self.assertEqual(a.pitch_down(4), Transformed(RotateYpr( 0, -4, 0), a)) self.assertEqual(a.roll_right(5), Transformed(RotateYpr( 0, 0, 5), a)) self.assertEqual(a.roll_left (6), Transformed(RotateYpr( 0, 0, -6), a))
def test_postfix_transform(self): cube = Cuboid(11, 11, 11) # Vectors rv = [60, 34, 30] sv = [ 2, 1, 1] tv = [10, 20, 30] # Transforms r = RotateXyz(*rv) s = ScaleAxes (*sv) t = Translate(tv) # Long shortcuts self.assertEqual(cube.rotate (xyz = rv).scale(sv) .translate(tv), t * s * r * cube) self.assertEqual(cube.transform(r) .scale(sv) .transform(t) , t * s * r * cube) self.assertEqual(cube.rotate (xyz = rv).transform(s).translate(tv), t * s * r * cube) self.assertEqual(cube.transform(s * r) .transform(t) , t * s * r * cube) self.assertEqual(cube.scale([1, 2, 3]), ScaleAxes(1, 2, 3) * cube) self.assertEqual(cube.scale(2), ScaleUniform(2) * cube) self.assertEqual(cube.scale([1, 2, 3], 4), ScaleAxisFactor([1, 2, 3], 4) * cube) # Error with self.assertRaises(TypeError): cube.transform(cube)