def test_construction(self): # Valid Translate([1, 2, 3]) # Invalid with self.assertRaises(TypeError): Translate([1, 2, "3"])
def test_equality(self): # Same object self.assertEqualToItself(Translate([60, 30, 15])) # Equal objects self.assertEqual(Translate([60, 30, 15]), Translate([60, 30, 15])) # Different objects self.assertNotEqual(Translate([60, 30, 15]), Translate([60, 30, 16]))
def translate(vector): """Generate a translation. Signatures (canonical forms): * scale(xyz = [2, 1, 1]) """ return Translate(vector)
def test_multiplication(self): # Create some transform r = RotateXyz(60, 30, 15) s = ScaleAxes (1, 2, -1) t = Translate([30, 20, 10]) # 2-chained self.assertEqual(r * s, Chained([r, s])) # 3-chained self.assertEqual( r * s * t , Chained([r, s, t])) self.assertEqual( (r * s) * t , Chained([r, s, t])) self.assertEqual( r * (s * t) , Chained([r, s, t])) # Multiplication is associative, but not commutative (kind-of already follows from the other tests) self.assertEqual ( (r * s) * t, r * (s * t) ) self.assertNotEqual( r * s , s * r ) # 4-chained self.assertEqual( r * s * r * s , Chained([r, s, r, s])) self.assertEqual( (r * s) * (r * s) , Chained([r, s, r, s])) self.assertEqual( ((r * s) * r) * s , Chained([r, s, r, s])) self.assertEqual( r * (s * (r * s)) , Chained([r, s, r, s])) rs = r * s self.assertEqual( rs * rs , Chained([r, s, r, s])) # Empty chained self.assertEqual(Chained([]) * Chained([]), Chained([])) self.assertEqual(Chained([]) * r, Chained([r])) self.assertEqual(r * Chained([]), Chained([r]))
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_invalid_operators(self): t = Translate([30, 20, 10]) for other in [t, None, 0, 0.0, ""]: with self.assertRaises(TypeError): t + other with self.assertRaises(TypeError): t - other with self.assertRaises(TypeError): other + t with self.assertRaises(TypeError): other - t
def test_equality(self): r = RotateXyz(60, 30, 15) s = ScaleAxes(1, 2, -1) t = Translate([60, 30, 15]) c = Chained([r, s, t]) # Same object self.assertEqualToItself(c) self.assertEqualToItself(Chained([])) # Equal objects self.assertEqual(Chained([]), Chained([])) self.assertEqual(Chained([r, s, t]), Chained([r, s, t])) # Identical children self.assertEqual( Chained([ RotateXyz(60, 30, 15), ScaleAxes(60, 30, 15), Translate([60, 30, 15]) ]), Chained([ RotateXyz(60, 30, 15), ScaleAxes(60, 30, 15), Translate([60, 30, 15]) ])) # Equal children # Different objects self.assertNotEqual(Chained([r, s, t]), Chained([r, s])) # Different number of children self.assertNotEqual(Chained([r, s, t]), Chained([r, t, s])) # Different order of children self.assertNotEqual( Chained([ RotateXyz(60, 30, 15), RotateXyz(60, 30, 15), Translate([60, 30, 15]) ]), Chained([ RotateXyz(60, 30, 15), RotateXyz(60, 30, 15), Translate([60, 30, 16]) ])) # Unequal children
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 test_operators_with_transforms(self): a = Sphere(2) t = Translate([0, 0, 0]) # The only operation we can do with a Transform is Transform*Object, which results in a Transformed Object. with self.assertRaises(TypeError): a + t with self.assertRaises(TypeError): a - t with self.assertRaises(TypeError): a * t with self.assertRaises(TypeError): t + a with self.assertRaises(TypeError): t - a self.assertEqual(t * a, Transformed(t, a))
def test_translate_shortcuts(self): self.assertEqual(shortcuts.up(1), Translate([0, 0, 1])) self.assertEqual(shortcuts.down(2), Translate([0, 0, -2])) self.assertEqual(shortcuts.left(3), Translate([-3, 0, 0])) self.assertEqual(shortcuts.right(4), Translate([4, 0, 0])) self.assertEqual(shortcuts.forward(5), Translate([0, 5, 0])) self.assertEqual(shortcuts.back(6), Translate([0, -6, 0]))
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 test_inequality(self): # Different-type transformations are not equal (even if the values are identical) transforms = [ RotateAxisAngle(X, 0), RotateFromTo(X, X), RotateXyz(0, 0, 0), RotateYpr(0, 0, 0), ScaleAxisFactor(X, 1), ScaleUniform(1), ScaleAxes (1, 1, 1), Translate([0, 0, 0]), ] for t1 in transforms: for t2 in transforms: if t1 is not t2: self.assertNotEqual(t1, t2)
def test_multiplication_with_vector(self): r = RotateAxisAngle(X, 90) s = ScaleAxes (1, 2, -1) t = Translate([1, 2, 3]) self.assertAlmostEqual(r * Vector( 0, 0, 0), Vector( 0, 0, 0)) self.assertAlmostEqual(r * Vector(10, 20, 30), Vector(10, -30, 20)) self.assertAlmostEqual(s * Vector( 0, 0, 0), Vector( 0, 0, 0)) self.assertAlmostEqual(s * Vector(10, 20, 30), Vector(10, 40, -30)) self.assertAlmostEqual(t * Vector( 0, 0, 0), Vector( 1, 2, 3)) self.assertAlmostEqual(t * Vector(10, 20, 30), Vector(11, 22, 33)) self.assertAlmostEqual((t * s) * Vector(10, 20, 30) , Vector(11, 42, -27)) self.assertAlmostEqual( t * (s * Vector(10, 20, 30)), Vector(11, 42, -27)) with self.assertRaises(ValueError): r * Vector(0, 0)
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_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_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_to_scad(self): # Create some transforms r = RotateXyz(60, 30, 15) s = ScaleAxes(1, 2, -1) t = Translate([30, 20, 10]) # Non-empty chained with None target self.assertEqual( Chained([r, s, t]).to_scad(None), ScadObject("rotate", [[60, 30, 15]], None, [ ScadObject( "scale", [[1, 2, -1]], None, [ScadObject("translate", [[30, 20, 10]], None, None)]) ])) # Empty chained with valid target dummy = ScadObject("dummy", None, None, None) self.assertEqual(Chained([]).to_scad(dummy), dummy) # Empty chained with None target self.assertEqual( Chained([]).to_scad(None), ScadObject(None, None, None, None))
def to_scad(self): length = (self._cap - self._base).length direction = (self._cap - self._base).normalized() # Create the cylinder if self._base_radius == self._cap_radius: cylinder = ScadObject("cylinder", [length], [('r', self._base_radius)], None) else: cylinder = ScadObject("cylinder", [length], [('r1', self._base_radius), ('r2', self._cap_radius)], None) # Rotate to the correct orientation (skip if it is along the Z axis) if direction != Z: cylinder = RotateFromTo(frm=Z, to=direction, ignore_ambiguity=True).to_scad(cylinder) # Move to the correct position (skip if the base is at the origin) if self._base != Vector(0, 0, 0): cylinder = Translate(self._base).to_scad(cylinder) return cylinder
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)
def test_to_matrix(self): t = Translate([1, 2, 3]) r = RotateXyz(0, 0, 90) # First rotate, then translate self.assertAlmostEqual( Chained([t, r]).to_matrix().row_values, [ [0, -1, 0, 1], [1, 0, 0, 2], [0, 0, 1, 3], [0, 0, 0, 1], ]) # First translate, then rotate self.assertAlmostEqual( Chained([r, t]).to_matrix().row_values, [ [0, -1, 0, -2], [1, 0, 0, 1], [0, 0, 1, 3], [0, 0, 0, 1], ]) # Empty self.assertIdentity(Chained([]).to_matrix())
def down(z): return Translate([0, 0, -z])
def up(z): return Translate([0, 0, z])
def back(y): return Translate([0, -y, 0])
def forward(y): return Translate([0, y, 0])
def left(x): return Translate([-x, 0, 0])
def right(x): return Translate([x, 0, 0])
def test_multiplication_with_invalid(self): t = Translate([30, 20, 10]) for invalid in [None, 0, 0.0, ""]: with self.assertRaises(TypeError): t * invalid with self.assertRaises(TypeError): invalid * t
def test_inverse(self): tf1 = ScaleAxes(1, 2, -1) * Translate([1, 2, 3]) tf2 = Translate([-1, -2, -3]) * ScaleAxes(1, 0.5, -1) self.assertInverse(tf1, tf2) self.assertInverse(Chained([]), Chained([]))
def test_translate(self): self.assertEqual(translate(Vector(1, 2, 3)), Translate(Vector(1, 2, 3))) self.assertEqual(translate( [1, 2, 3]), Translate(Vector(1, 2, 3)))
from cadlib.object.primitives import Cuboid, Cylinder, Sphere from cadlib.transform.primitives import RotateXyz, ScaleAxes, Translate 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=" ")) print(" OpenSCAD source:") print(object.to_scad().to_code(top_indent=" ")) o1 = Cuboid(10, 10, 10) o2 = Cylinder(Z, 5, 5) o3 = Sphere(2) rotate = RotateXyz(90, 0, 45) scale = ScaleAxes(1, 1, 2) translate = Translate([10, 10, 2]) test_case("Translated object", translate * o1) test_case("Intersection", o1 * o2 * o3) test_case("Complex", rotate * (translate * scale * (o2 + o3) + o1))