Beispiel #1
0
    def test_construction(self):
        # Parameters
        t1 = ScaleAxes(1, 1, 1)
        t2 = ScaleAxes(2, 2, 2)
        t3 = ScaleAxes(3, 3, 3)

        # Valid
        self.assertEqual(Chained([t1, t2, t3]).transforms, [t1, t2, t3])
        with self.assertNothingRaised():
            chained0 = Chained([])  # No children
        with self.assertNothingRaised():
            chained1 = Chained([t1, t2, t3])  # Regular
        with self.assertNothingRaised():
            chained2 = Chained([t1, t1, t1])  # Repeated child
        with self.assertNothingRaised():
            chained3 = Chained([chained1, t1, chained2])  # Chained child

        # From generator
        self.assertEqual(Chained(tf for tf in [t1, t2, t3]),
                         Chained([t1, t2, t3]))

        # Invalid
        with self.assertRaises(TypeError):
            Chained(None)
        with self.assertRaises(TypeError):
            Chained([None])
        with self.assertRaises(TypeError):
            Chained([1])
    def test_scale(self):
        # Canonical axis/factor
        self.assertEqual(scale(axis = Vector(1, 2, 3), factor = 4), ScaleAxisFactor(Vector(1, 2, 3), 4))
        self.assertEqual(scale(axis =       [1, 2, 3], factor = 4), ScaleAxisFactor(      [1, 2, 3], 4))

        # Canonical XYZ
        self.assertEqual(scale(xyz = Vector(1, 2, 3)), ScaleAxes(1, 2, 3))
        self.assertEqual(scale(xyz =       [1, 2, 3]), ScaleAxes(1, 2, 3))

        # Canonical uniform
        self.assertEqual(scale(factor = 2), ScaleUniform(2))


        # Convenience axis/factor (implicit)
        self.assertEqual(scale(Vector(1, 2, 3), 4), ScaleAxisFactor(Vector(1, 2, 3), 4))
        self.assertEqual(scale(      [1, 2, 3], 4), ScaleAxisFactor(      [1, 2, 3], 4))

        # Convenience axis/factor (explicit)
        self.assertEqual(scale(Vector(1, 2, 3), factor = 4), ScaleAxisFactor(Vector(1, 2, 3), 4))
        self.assertEqual(scale(      [1, 2, 3], factor = 4), ScaleAxisFactor(      [1, 2, 3], 4))

        # Convenience XYZ
        self.assertEqual(scale(Vector(1, 2, 3)), ScaleAxes(1, 2, 3))
        self.assertEqual(scale(      [1, 2, 3]), ScaleAxes(1, 2, 3))

        # Convenience uniform
        self.assertEqual(scale(2), ScaleUniform(2))
Beispiel #3
0
    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]))
Beispiel #4
0
    def _equivalent(self):
        transform_axis = self._axis.closest_axis()

        forward_rotation = RotateFromTo(self._axis, transform_axis)
        scale = ScaleAxes(*(transform_axis * self._factor).replace(0, 1))
        back_rotation = RotateFromTo(transform_axis, self._axis)

        return back_rotation * scale * forward_rotation
Beispiel #5
0
    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
Beispiel #6
0
    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))
Beispiel #7
0
    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)
Beispiel #8
0
    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)
Beispiel #9
0
    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)
Beispiel #10
0
    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),
                ])
            ]))
Beispiel #11
0
    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)
Beispiel #12
0
    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))
Beispiel #13
0
def scale(xyz_or_axis_or_factor = None, factor = None, xyz = None, axis = None):
    """Generate a scaling transform around the origin.

    Signatures (canonical forms):
      * scale(xyz = [2, 1, 1])
      * scale(factor = 2)
      * scale(axis = X, factor = 2)
    Signatures (convenience forms):
      * scale([2, 1, 1])
      * scale(2)
      * scale(X, 2)

    Vectors can be specified as Vector, list, or tuple. Note that a Vector can
    be used for xyz even though xyz is not strictly a vector.
    """

    # Canonical forms:
    #     xyz_or_axis_or_factor  factor   xyz  axis
    #                         -       -  list     -  # XYZ
    #                         -     num     -   vec  # Axis/factor
    #                         -     num     -     -  # Uniform
    # Convenience forms (-: must be None, *: overwritten)
    #                       vec     num     -     *  # Axis/factor (implicit or explicit)
    #                       vec       -     *     -  # XYZ
    #                       num       *     -     -  # Isotropic XYZ
    #

    # Make sure that there are no conflicts between convenience parameters and canonical parameters
    if both(xyz_or_axis_or_factor, xyz ): raise TypeError("xyz"   " cannot be specified together with xyz_or_axis")
    if both(xyz_or_axis_or_factor, axis): raise TypeError("axis"  " cannot be specified together with xyz_or_axis")

    # Transform the convenience forms to canonical form
    if xyz_or_axis_or_factor is not None:
        if Vector.valid_type(xyz_or_axis_or_factor):
            if factor is None:
                # Xyz
                xyz = xyz_or_axis_or_factor
            else:
                # Axis part of axis/factor
                axis = xyz_or_axis_or_factor
        elif number.valid(xyz_or_axis_or_factor):
            if factor is None:
                # Factor
                factor = xyz_or_axis_or_factor
            else:
                raise TypeError("factor cannot be specified together with numeric xyz_or_axis")
        else:
            raise TypeError("xyz_or_axis_or_factor must be a vector type or a number")

    # Check the parameters that must appear in pairs
    if axis is not None and factor is None: raise TypeError("factor" " is required when " "axis" " is given")

    # Handle the different cases
    if axis is not None:
        # Check that no other specification is given
        if xyz is not None: raise TypeError("xyz" " cannot be specified together with axis")

        return ScaleAxisFactor(axis, factor)

    elif xyz is not None:
        # Check that no other specification is given
        if axis   is not None: raise TypeError("axis"   " cannot be specified together with xyz")
        if factor is not None: raise TypeError("factor" " cannot be specified together with xyz")

        return ScaleAxes(*xyz)

    elif factor is not None:
        return ScaleUniform(factor)

    else:
        raise TypeError("Invalid call signature")
Beispiel #14
0
    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([]))
Beispiel #15
0
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))