Exemplo n.º 1
0
    def _equivalent(self):
        yaw, pitch, roll = self._ypr

        # yaw-pitch-roll in local coordinates corresponds to roll-pitch-yaw in
        # global coordinates.
        transforms = []
        if yaw != 0 or pitch != 0: transforms.append(RotateXyz(pitch, 0   , yaw))
        if roll != 0             : transforms.append(RotateXyz(0    , roll, 0  ))
        return Chained(transforms)
Exemplo n.º 2
0
    def to_scad(self, target):
        equivalent = self._equivalent()

        if equivalent == Chained([]):
            # The equivalent transform is empty (this can happen if yaw, pitch,
            # and roll are all 0). This would result in an empty ScadObject
            # (with children if target is not None). Instead, generate an empty
            # rotation for improved clarity of the generated code.
            equivalent = RotateXyz(0, 0, 0)

        return equivalent.to_scad(target).comment(str(self))
Exemplo n.º 3
0
    def test_equality(self):
        # Same object
        self.assertEqualToItself(RotateXyz( 60, 30, 15 ))

        # Equal objects
        self.assertEqual(RotateXyz( 60, 30, 15 ), RotateXyz( 60, 30, 15 ))

        # Different objects
        self.assertNotEqual(RotateXyz( 60, 30, 15 ), RotateXyz( 60, 30, 16 ))

        # Equal objects from different specifications

        pass
Exemplo n.º 4
0
    def test_rotate(self):
        # Canonical axis/angle
        self.assertEqual(rotate(axis = Vector(1, 2, 3), angle = 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # Vector
        self.assertEqual(rotate(axis =       [1, 2, 3], angle = 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # List

        # Canonical from/to
        self.assertEqual(rotate(frm = Vector(1, 2, 3), to = Vector(4, 5, 6)), RotateFromTo(Vector(1, 2, 3), Vector(4, 5, 6))) # Vectors
        self.assertEqual(rotate(frm =       [1, 2, 3], to =       [4, 5, 6]), RotateFromTo(Vector(1, 2, 3), Vector(4, 5, 6))) # Lists

        # Canonical XYZ
        self.assertEqual(rotate(xyz = [45, 0, 30]), RotateXyz(45, 0, 30))

        # Canonical yaw/pitch/roll
        self.assertEqual(rotate(ypr = [90, -20, 5]), RotateYpr(90, -20, 5))

        # Convenience axis/angle (implicit)
        self.assertEqual(rotate(Vector(1, 2, 3), 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # Vector
        self.assertEqual(rotate(      [1, 2, 3], 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # List

        # Convenience axis/angle (explicit)
        self.assertEqual(rotate(Vector(1, 2, 3), angle = 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # Vector
        self.assertEqual(rotate(      [1, 2, 3], angle = 45), RotateAxisAngle(Vector(1, 2, 3), 45)) # List

        # Convenience from/to (implicit)
        self.assertEqual(rotate(X        , Y        ), RotateFromTo(X, Y)) # Vectors
        self.assertEqual(rotate([1, 0, 0], [0, 1, 0]), RotateFromTo(X, Y)) # Lists

        # Convenience from/to (explicit)
        self.assertEqual(rotate(X        , to = Y        ), RotateFromTo(X, Y)) # Vectors
        self.assertEqual(rotate([1, 0, 0], to = [0, 1, 0]), RotateFromTo(X, Y)) # Lists
Exemplo n.º 5
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]))
Exemplo n.º 6
0
 def test_str(self):
     self.assertStr(RotateXyz(0, 0, 0), "Rotate by 0° around X, Y, and Z")
     self.assertStr(RotateXyz(1, 0, 0), "Rotate by 1° around X")
     self.assertStr(RotateXyz(0, 2, 0), "Rotate by 2° around Y")
     self.assertStr(RotateXyz(0, 0, 3), "Rotate by 3° around Z")
     self.assertStr(RotateXyz(1, 2, 0), "Rotate by 1° around X and 2° around Y")
     self.assertStr(RotateXyz(1, 0, 3), "Rotate by 1° around X and 3° around Z")
     self.assertStr(RotateXyz(0, 2, 3), "Rotate by 2° around Y and 3° around Z")
     self.assertStr(RotateXyz(1, 2, 3), "Rotate by 1° around X, 2° around Y, and 3° around Z")
Exemplo n.º 7
0
    def to_scad(self, target):
        axis, angle = self._to_axis_angle()

        if axis is None:
            # No rotation. Generate a zero XYZ transform instead of simply
            # returning the target. This improves code clarity and also ensures
            # that a valid ScadObject is returned even if target is None.
            return RotateXyz(0, 0, 0).to_scad(target).comment(str(self))
        else:
            # Yes rotation
            return RotateAxisAngle(axis.normalized(), angle).to_scad(target).comment(str(self))
Exemplo n.º 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)
Exemplo n.º 9
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),
                ])
            ]))
Exemplo n.º 10
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
Exemplo n.º 11
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))
Exemplo n.º 12
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)
Exemplo n.º 13
0
    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())
Exemplo n.º 14
0
def rotate(axis_or_frm = None, angle_or_to = None, axis = None, angle = None, frm = None, to = None, xyz = None, ypr = None, ignore_ambiguity = False):
    """Generate a rotation around an axis through the origin.

    Signatures (canonical forms):
      * rotate(axis = x, angle = 45)
      * rotate(frm = x, to = y)
      * rotate(xyz = [45, 0, 30])
      * rotate(ypr = [45, -30, 10])
    Signatures (convenience forms):
      * rotate(x, 45)
      * rotate(x, angle = 45)
      * rotate(x, y)
      * rotate(x, to = y)
    """

    # Canonical forms:
    #     axis_or_axmag_or_frm  angle_or_to  axis  angle  frm   to   xyz   ypr
    #                        -            -   vec    num    -    -     -     -  # Axis/angle
    #                        -            -     -      -  vec  vec     -     -  # From/to
    #                        -            -     -      -    -    -  list     -  # XYZ
    #                        -            -     -      -    -    -     -  list  # Yaw/pitch/roll
    # Convenience forms (-: must be None, *: overwritten)
    #                      vec          num     *      *    -    -     -     -  # Axis/angle (implicit)
    #                      vec            -     *    num    -    -     -     -  # Axis/angle (explicit)
    #                      vec          vec     -      -    *    *     -     -  # From/to (implicit)
    #                      vec            -     -      -    *  vec     -     -  # From/to (explicit)
    #
    # "Vector type" is Vector, list, or tuple

    # Make sure that there are no conflicts between convenience parameters and canonical parameters
    if both(axis_or_frm, axis ): raise TypeError("axis"  " cannot be specified together with axis_or_frm")
    if both(axis_or_frm, frm  ): raise TypeError("frm"   " cannot be specified together with axis_or_frm")
    if both(angle_or_to, angle): raise TypeError("angle" " cannot be specified together with angle_or_to")
    if both(angle_or_to, to   ): raise TypeError("to"    " cannot be specified together with angle_or_to")

    # Transform the convenience forms to canonical form
    if axis_or_frm is not None:
        if not Vector.valid_type(axis_or_frm):
            raise TypeError("axis must be a vector type")

        if angle_or_to is not None:
            if number.valid(angle_or_to):
                # Axis/angle (implicit)
                axis = axis_or_frm
                angle = angle_or_to
            elif Vector.valid_type(angle_or_to):
                # From/to (implicit)
                frm = axis_or_frm
                to = angle_or_to
            else:
                raise TypeError("angle_or_to must be a number or a vector type")
        elif angle is not None:
            # Axis/angle (explicit)
            axis = axis_or_frm
        elif to is not None:
            # From/to (explicit)
            frm = axis_or_frm

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

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

        return RotateAxisAngle(axis, angle)

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

        return RotateFromTo(frm, to, ignore_ambiguity)

    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 frm")
        if frm  is not None: raise TypeError("frm"  " cannot be specified together with axis")
        if ypr  is not None: raise TypeError("ypr"  " cannot be specified together with axis")

        return RotateXyz(*xyz)

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

        return RotateYpr(*ypr)

    else:
        raise TypeError("Invalid call signature")
Exemplo n.º 15
0
    def test_construction(self):
        # Valid
        r = RotateXyz( 60, 30, 15 )

        # Invalid
        with self.assertRaises(TypeError): RotateXyz(1, 2, "3")
Exemplo n.º 16
0
    def test_to_matrix(self):
        # No rotation
        self.assertAlmostEqual(RotateXyz( 0 , 0,  0).to_matrix(), affine_matrix(X, Y, Z))

        # 90 degrees around a single axis
        self.assertAlmostEqual(RotateXyz(90,  0,  0).to_matrix(), affine_matrix( X,  Z, -Y))
        self.assertAlmostEqual(RotateXyz( 0, 90,  0).to_matrix(), affine_matrix(-Z,  Y,  X))
        self.assertAlmostEqual(RotateXyz( 0,  0, 90).to_matrix(), affine_matrix( Y, -X,  Z))

        # 180 degrees around a single axis
        self.assertAlmostEqual(RotateXyz(180,   0,   0).to_matrix(), affine_matrix( X, -Y, -Z))
        self.assertAlmostEqual(RotateXyz(  0, 180,   0).to_matrix(), affine_matrix(-X,  Y, -Z))
        self.assertAlmostEqual(RotateXyz(  0,   0, 180).to_matrix(), affine_matrix(-X, -Y,  Z))

        # 90 degrees each around two axes
        self.assertAlmostEqual(RotateXyz(90, 90,  0).to_matrix(), affine_matrix(-Z,  X, -Y))
        self.assertAlmostEqual(RotateXyz(90,  0, 90).to_matrix(), affine_matrix( Y,  Z,  X))
        self.assertAlmostEqual(RotateXyz( 0, 90, 90).to_matrix(), affine_matrix(-Z, -X,  Y))

        # 90 degrees each around all three axes
        self.assertAlmostEqual(RotateXyz(90, 90, 90).to_matrix(), affine_matrix(-Z, Y, X))
Exemplo n.º 17
0
 def test_repr(self):
     self.assertRepr(RotateXyz(1, 2, 3), "RotateXyz(1, 2, 3)")
Exemplo n.º 18
0
 def test_inverse(self):
     self.assertInverse(RotateXyz(30, 60, 90),
         RotateXyz(-30, 0, 0) * RotateXyz(0, -60, 0) * RotateXyz(0, 0, -90),
         symmetric=False)
Exemplo n.º 19
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))
Exemplo n.º 20
0
    def test_to_scad(self):
        r = RotateXyz(0, 0, 0)
        self.assertScadObjectTarget(r, None, "rotate", [[0, 0, 0]], None, None)

        r = RotateXyz(60, 30, 15)
        self.assertScadObjectTarget(r, None, "rotate", [[60, 30, 15]], None, None)
Exemplo n.º 21
0
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")