Esempio n. 1
0
    def direction_length(cls, direction, length, base_radius, cap_radius):
        direction = Vector.convert(direction, "direction", required_length=3)
        length = number.convert(length, "length")

        if direction.is_zero:
            raise ValueError("direction must be non-zero")

        base = Vector(0, 0, 0)
        cap = direction.normalized() * length
        return cls(base, cap, base_radius, cap_radius)
Esempio n. 2
0
    def __init__(self, base, cap, base_radius, cap_radius):
        super().__init__()

        self._base = Vector.convert(base, "base", required_length=3)
        self._cap = Vector.convert(cap, "cap", required_length=3)
        self._base_radius = number.convert(base_radius, "base_radius")
        self._cap_radius = number.convert(cap_radius, "cap_radius")

        # Warn if the cap is equal to the base (zero length) or both radii are
        # zero (zero thickness; note that it is allowed for *one* of the radii
        # to be zero).
        if self._base == self._cap: warn("length is 0")
        if self._base_radius == self._cap_radius == 0: warn("radius is 0")
Esempio n. 3
0
    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))
Esempio n. 4
0
    def test_equality(self):
        # Same object
        self.assertEqualToItself(RotateFromTo(Vector(1, 2, 3), Vector(4, 5, 6)))

        # Equal objects
        self.assertEqual(RotateFromTo([1, 2, 3], [4, 5, 6]), RotateFromTo([1, 2, 3], [4, 5, 6])) # Equal

        # Different objects
        self.assertNotEqual(RotateFromTo([1, 2, 3], [4, 5, 6]), RotateFromTo([1, 2, 3], [4, 5, 7])) # Different from
        self.assertNotEqual(RotateFromTo([1, 2, 3], [4, 5, 6]), RotateFromTo([1, 2, 7], [4, 5, 6])) # Different to

        # Equal objects from different specifications
        self.assertEqual(RotateFromTo(Vector(1, 2, 3), [4, 5, 6]), RotateFromTo([1, 2, 3], [4, 5, 6]))
        self.assertEqual(RotateFromTo([1, 2, 3], Vector(4, 5, 6)), RotateFromTo([1, 2, 3], [4, 5, 6]))
Esempio n. 5
0
    def __init__(self, frm, to, ignore_ambiguity = False):
        frm = Vector.convert(frm, "frm", required_length=3)
        to  = Vector.convert(to , "to" , required_length=3)

        if frm.is_zero:
            raise ValueError("frm may not be zero-length")
        if to.is_zero:
            raise ValueError("frm may not be zero-length")

        if not ignore_ambiguity:
            if frm.collinear(to) and frm.dot(to) < 0:
                warn("Rotation from {} to {} is ambiguous because the vectors are colinear and opposite"
                     .format(frm, to), UserWarning, 2)

        self._frm = frm
        self._to  = to
Esempio n. 6
0
def cylinder(direction_or_base, length_or_cap, r=None, d=None):
    """Generate a cylinder.

    Signatures (convenience forms only):
      * cylinder(direction, length, radius)
      * cylinder(direction, length, d = diameter)
      * cylinder(base, cap, radius)
      * cylinder(base, cap, d = diameter)
    """

    # Radius or diameter
    radius = _get_radius(r, d)

    # length_or_base must be a vector or a number
    if number.valid(length_or_cap):
        # Number - direction/length
        return Frustum.direction_length(direction_or_base, length_or_cap,
                                        radius, radius)

    elif Vector.valid_type(length_or_cap):
        # Vector type - base/cap
        return Frustum(direction_or_base, length_or_cap, radius, radius)

    else:
        raise TypeError(
            "Invalid call signature: length_or_cap must be a vector type or a number"
        )
Esempio n. 7
0
    def __init__(self, axis, angle=None):
        axis = Vector.convert(axis, "axis", required_length=3)
        if axis.is_zero:
            raise ValueError("axis may not be zero-length")

        self._axis = axis
        self._angle = number.convert(angle, "angle", default=self._axis.length)
Esempio n. 8
0
def frustum(direction_or_base, length_or_cap, r=None, d=None):
    """Generate a frustum.

    For the forms with a direction, the base will be at the origin.

    Signatures (convenience forms only):
      * frustum (direction, length, radii)
      * frustum (direction, length, d = diameters)
      * frustum (base, cap, radii)
      * frustum (base, cap, d = diameters)
    """

    # Radius or diameter
    radii = _get_radii(r, d)

    # length_or_base must be a vector or a number
    if number.valid(length_or_cap):
        # Number - direction/length
        return Frustum.direction_length(direction_or_base, length_or_cap,
                                        *radii)

    elif Vector.valid_type(length_or_cap):
        # Vector type - base/cap
        return Frustum(direction_or_base, length_or_cap, *radii)

    else:
        raise TypeError(
            "Invalid call signature: length_or_cap must be a vector type or a number"
        )
Esempio n. 9
0
def cone(direction_or_base, length_or_tip, r=None, d=None):
    """Generate a cone.

    For the forms with a direction, the base will be at the origin.

    Signatures (convenience forms only):
      * cone(direction, length, radius)
      * cone(direction, length, d = diameter)
      * cone(base, cap, radius)
      * cone(base, cap, d = diameter)
    """
    # TODO should use r = None, *, d = None? (not just here)

    # Radius or diameter
    radius = _get_radius(r, d)

    # length_or_base must be a vector or a number
    if number.valid(length_or_tip):
        # Number - direction/length
        return Frustum.direction_length(direction_or_base, length_or_tip,
                                        radius, 0)

    elif Vector.valid_type(length_or_tip):
        # Vector type - base/cap
        return Frustum(direction_or_base, length_or_tip, radius, 0)

    else:
        raise TypeError(
            "Invalid call signature: length_or_cap must be a vector type or a number"
        )
Esempio n. 10
0
    def __init__(self, axis, factor):
        axis = Vector.convert(axis, "axis", required_length=3)
        if axis.is_zero:
            raise ValueError("axis may not be zero-length")

        self._axis   = axis
        self._factor = number.convert(factor, "factor", default=self._axis.length)

        if self._factor == 0: warn("factor is 0")
Esempio n. 11
0
    def test_construction(self):
        # Valid
        RotateFromTo(      [1, 2, 3],       [4, 5, 6])
        RotateFromTo(Vector(1, 2, 3), Vector(4, 5, 6))

        # Invalid
        with self.assertRaises(TypeError ): RotateFromTo([1, 2, 3], 45 )
        with self.assertRaises(TypeError ): RotateFromTo(45, [1, 2, 3])
        with self.assertRaises(TypeError ): RotateFromTo([1, 2, "3"], [4, 5,  6])
        with self.assertRaises(TypeError ): RotateFromTo([1, 2,  3 ], [4, 5, "6"])

        # Zero vectors
        o = Vector.zero(3)
        with self.assertRaises(ValueError): RotateFromTo(X, o)
        with self.assertRaises(ValueError): RotateFromTo(X, o)
        with self.assertRaises(ValueError): RotateFromTo(o, o)

        # Opposite direction (ambiguous rotation)
        with self.assertWarns(UserWarning): RotateFromTo([1, 0, 0], [-1,  0,  0])
        with self.assertWarns(UserWarning): RotateFromTo([1, 2, 3], [-2, -4, -6])
Esempio n. 12
0
    def test_construction(self):
        # Valid
        RotateAxisAngle([1, 2, 3], 45)
        RotateAxisAngle(Vector(1, 2, 3), 45)

        # Invalid
        with self.assertRaises(ValueError):
            RotateAxisAngle([0, 0, 0], 45)
        with self.assertRaises(TypeError):
            RotateAxisAngle([1, 2, "3"], 4)
        with self.assertRaises(TypeError):
            RotateAxisAngle([1, 2, 3], "4")
        with self.assertRaises(TypeError):
            RotateAxisAngle(1, "4")
Esempio n. 13
0
    def test_equality(self):
        # Same object
        self.assertEqualToItself(ScaleAxisFactor([1, 2, 3], 4))

        # Equal objects
        self.assertEqual(ScaleAxisFactor([1, 2, 3], 4),
                         ScaleAxisFactor([1, 2, 3], 4))

        # Different objects
        self.assertNotEqual(ScaleAxisFactor([1, 2, 3], 4),
                            ScaleAxisFactor([1, 2, 4], 5))
        self.assertNotEqual(ScaleAxisFactor([1, 2, 3], 4),
                            ScaleAxisFactor([1, 2, 3], 5))

        # Equal objects from different specifications
        self.assertEqual(ScaleAxisFactor(Vector(1, 2, 3), 4),
                         ScaleAxisFactor([1, 2, 3], 4))
Esempio n. 14
0
    def test_construction(self):
        # Valid
        ScaleAxisFactor([1, 2, 3], 4)
        ScaleAxisFactor(Vector(1, 2, 3), 4)

        # Valid, but with warning
        with self.assertWarns(UserWarning):
            ScaleAxisFactor([1, 2, 3], 0)  # Valid, though useless

        # Invalid
        with self.assertRaises(ValueError):
            ScaleAxisFactor([0, 0, 0], 4)
        with self.assertRaises(TypeError):
            ScaleAxisFactor([1, 2, "3"], 4)
        with self.assertRaises(TypeError):
            ScaleAxisFactor([1, 2, 3], "4")
        with self.assertRaises(TypeError):
            ScaleAxisFactor(1, "4")
Esempio n. 15
0
    def test_equality(self):
        # Same object
        self.assertEqualToItself(RotateAxisAngle(Vector(1, 2, 3), 45))

        # Equal objects
        self.assertEqual(RotateAxisAngle(Vector(1, 2, 3), 45),
                         RotateAxisAngle(Vector(1, 2, 3), 45))  # Equal

        # Different objects
        self.assertNotEqual(RotateAxisAngle(Vector(1, 2, 3), 45),
                            RotateAxisAngle(Vector(1, 2, 4),
                                            45))  # Different axis
        self.assertNotEqual(RotateAxisAngle(Vector(1, 2, 3), 45),
                            RotateAxisAngle(Vector(1, 2, 3),
                                            46))  # Different angle

        # Equal objects from different specifications
        self.assertEqual(RotateAxisAngle([1, 2, 3], 45),
                         RotateAxisAngle(Vector(1, 2, 3),
                                         45))  # list vs. Vector
Esempio n. 16
0
    def test_to_scad(self):
        self.ignore_scad_comments = True

        # Special case: axis aligned with one of the coordinate axes
        # X
        self.assertEqual(
            ScaleAxisFactor(X, 2).to_scad(None),
            ScadObject("scale", [[2, 1, 1]], None, None))
        # Y
        self.assertEqual(
            ScaleAxisFactor(Y, 3).to_scad(None),
            ScadObject("scale", [[1, 3, 1]], None, None))
        # Z
        self.assertEqual(
            ScaleAxisFactor(Z, 4).to_scad(None),
            ScadObject("scale", [[1, 1, 4]], None, None))
        # -Z
        self.assertEqual(
            ScaleAxisFactor(-Z, 4).to_scad(None),
            ScadObject("scale", [[1, 1, 4]], None, None))

        # Special case: factor 1 (aligned or non-aligned)
        self.assertEqual(
            ScaleAxisFactor(X, 1).to_scad(None),
            ScadObject("scale", [[1, 1, 1]], None, None))
        self.assertEqual(
            ScaleAxisFactor(Vector(1, 1, 0), 1).to_scad(None),
            ScadObject("scale", [[1, 1, 1]], None, None))

        # General case, with the scale axis in the Y/Z plane. The scale axis is
        # rotated to the X axis.
        # Note that we're comparing floating point numbers here - rounding
        # errors might become an issue.
        angle = math.atan(1 / 2) / degree
        self.assertEqual(
            ScaleAxisFactor([2, 1, 0], 3).to_scad(None),
            ScadObject("rotate", None, [
                ('a', angle), ('v', [0.0, 0.0, 1.0])
            ], [
                ScadObject("scale", [[3, 1, 1]], None, [
                    ScadObject("rotate", None, [('a', angle),
                                                ('v', [0.0, 0.0, -1.0])], None)
                ])
            ]))
Esempio n. 17
0
    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")
Esempio n. 18
0
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")
Esempio n. 19
0
    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
Esempio n. 20
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)
Esempio n. 21
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")
Esempio n. 22
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")
Esempio n. 23
0
    def test_frustum_generators(self):
        v0 = Vector(0, 0, 0)

        # Cylinder - base/cap
        self.assertEqual(cylinder(X, Y, 2), Frustum(X, Y, 2, 2))
        self.assertEqual(cylinder(X, Y, r=2), Frustum(X, Y, 2, 2))
        self.assertEqual(cylinder(X, Y, d=4), Frustum(X, Y, 2, 2))
        self.assertEqual(cylinder(origin, Y, 2),
                         Frustum(origin, Y, 2, 2))  # 0 is allowed as base
        # 0 is not allowed as cap because it would be interpreted as
        # direction/length

        # Cylinder - direction/length
        self.assertEqual(cylinder(X, 1, 2), Frustum(origin, X, 2, 2))
        self.assertEqual(cylinder(X, 1, r=2), Frustum(origin, X, 2, 2))
        self.assertEqual(cylinder(X, 1, d=4), Frustum(origin, X, 2, 2))

        # Cylinder - invalid
        with self.assertNothingRaised():
            cylinder(X, 5, 1)  # Reference
        with self.assertRaises(TypeError):
            cylinder(5, 5, 1)  # First is not a vector
        with self.assertRaises(TypeError):
            cylinder(X, "", 1)  # Second is not a vector or number
        with self.assertRaises(TypeError):
            cylinder(X, 5)  # Neither radius nor diameter
        with self.assertRaises(TypeError):
            cylinder(X, 5, 1, 1)  # Both radius and diameter
        with self.assertRaises(ValueError):
            cylinder(v0, 5, 1)  # Zero vector is not allowed as direction
        with self.assertRaises(TypeError):
            cylinder(X, 5, r=(1, 2))  # Radii
        with self.assertRaises(TypeError):
            cylinder(X, 5, d=(1, 2))  # Diameters

        # Cone - base/tip
        self.assertEqual(cone(X, Y, 2), Frustum(X, Y, 2, 0))
        self.assertEqual(cone(X, Y, r=2), Frustum(X, Y, 2, 0))
        self.assertEqual(cone(X, Y, d=4), Frustum(X, Y, 2, 0))

        # Cone - direction/length
        self.assertEqual(cone(X, 1, 2), Frustum(origin, X, 2, 0))
        self.assertEqual(cone(X, 1, r=2), Frustum(origin, X, 2, 0))
        self.assertEqual(cone(X, 1, d=4), Frustum(origin, X, 2, 0))

        # Cone - invalid
        with self.assertNothingRaised():
            cone(X, 5, 1)  # Reference
        with self.assertRaises(TypeError):
            cone(5, 5, 1)  # First is not a vector
        with self.assertRaises(TypeError):
            cone(X, "", 1)  # Second is not a vector or number
        with self.assertRaises(TypeError):
            cone(X, 5)  # Neither radius nor diameter
        with self.assertRaises(TypeError):
            cone(X, 5, 1, 1)  # Both radius and diameter
        with self.assertRaises(ValueError):
            cone(v0, 5, 1)  # Zero vector is not allowed as direction
        with self.assertRaises(TypeError):
            cone(v0, 5, r=(1, 2))  # Radii
        with self.assertRaises(TypeError):
            cone(v0, 5, d=(1, 2))  # Diameters

        # Frustum - base/cap
        self.assertEqual(frustum(X, Y, (2, 3)), Frustum(X, Y, 2, 3))
        self.assertEqual(frustum(X, Y, r=(2, 3)), Frustum(X, Y, 2, 3))
        self.assertEqual(frustum(X, Y, d=(4, 6)), Frustum(X, Y, 2, 3))

        # Frustum - direction/length
        self.assertEqual(frustum(X, 1, (2, 3)), Frustum(origin, X, 2, 3))
        self.assertEqual(frustum(X, 1, r=(2, 3)), Frustum(origin, X, 2, 3))
        self.assertEqual(frustum(X, 1, d=(4, 6)), Frustum(origin, X, 2, 3))

        # Frustum - invalid
        with self.assertNothingRaised():
            frustum(X, 5, (1, 2))  # Reference
        with self.assertRaises(TypeError):
            frustum(5, 5, (1, 2))  # First is not a vector
        with self.assertRaises(TypeError):
            frustum(X, "", (1, 2))  # Second is not a vector or number
        with self.assertRaises(TypeError):
            frustum(X, 5)  # Neither radii nor diameters
        with self.assertRaises(TypeError):
            frustum(X, 5, (1, 2), (1, 2))  # Both radii and diameters
        with self.assertRaises(ValueError):
            frustum(v0, 5, (2, 3))  # Zero vector is not allowed as direction
        with self.assertRaises(TypeError):
            frustum(X, 1, r=2)  # Single radius
        with self.assertRaises(TypeError):
            frustum(X, 1, d=4)  # Single diameter

        # Errors caught by _get_radii
        with self.assertRaises(TypeError):
            frustum(X, 1, r=(2, 3), d=(4, 6))  # Both radius and diameter
        with self.assertRaises(TypeError):
            frustum(X, 1)  # Neither radius nor diameter
        with self.assertRaises(ValueError):
            frustum(X, 1, r=(2, 3, 4))  # Too many radii
        with self.assertRaises(ValueError):
            frustum(X, 1, d=(4, 6, 8))  # Too many diameters
        with self.assertRaises(ValueError):
            frustum(X, 1, r=(2, ))  # Too few radii
        with self.assertRaises(ValueError):
            frustum(X, 1, d=(4, ))  # Too few diameters
Esempio n. 24
0
 def __init__(self, vector):
     self._vector = Vector.convert(vector, "vector", required_length=3)
Esempio n. 25
0
 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)))
Esempio n. 26
0
 def __init__(self, object, name, position):  # TODO remove name?
     self._object = object
     self._name = name
     self._position = Vector.convert(position,
                                     "position",
                                     required_length=3)
Esempio n. 27
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