Пример #1
0
    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[:3, :3] = rotation_matrix

        point_vector = self.origin.pos_from(point)
        try:
            self._transform[3, :3] = point_vector.to_matrix(reference_frame).T
        except AttributeError:
            # In earlier versions of sympy, 'Vector' object has no attribute
            # 'to_matrix'.
            self._transform[3, 0] = point_vector.dot(reference_frame.x)
            self._transform[3, 1] = point_vector.dot(reference_frame.y)
            self._transform[3, 2] = point_vector.dot(reference_frame.z)
        return self._transform
Пример #2
0
    def generate_transformation_matrix(self, reference_frame, point):
        """
        Generates the symbolic Transformation matrix,
        with respect to the reference_frame, point in the argument.

        Parameters
        ==========

        reference_frame : ReferenceFrame
        A reference_frame with respect to which transformation matrix
        is generated.
        point : Point
        A point with respect to which transformation matrix
        is generated.

        Returns
        =======
        A SymPy 4by4 matrix, containing symbolic variables for
        transformation.

        """
        _rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[0:3, 0:3] = _rotation_matrix[0:3, 0:3]

        _point_vector = self.origin.pos_from(point).express(reference_frame)

        self._transform[3, 0] = _point_vector.dot(reference_frame.x)
        self._transform[3, 1] = _point_vector.dot(reference_frame.y)
        self._transform[3, 2] = _point_vector.dot(reference_frame.z)
        return self._transform
Пример #3
0
    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[0:3, 0:3] = rotation_matrix[0:3, 0:3]

        _point_vector = self.origin.pos_from(point).express(reference_frame)

        self._transform[3, 0] = _point_vector.dot(reference_frame.x)
        self._transform[3, 1] = _point_vector.dot(reference_frame.y)
        self._transform[3, 2] = _point_vector.dot(reference_frame.z)
        return self._transform
Пример #4
0
def test_invertible():
    assert ask(Q.invertible(X), Q.invertible(X))
    assert ask(Q.invertible(Y)) is False
    assert ask(Q.invertible(X * Y), Q.invertible(X)) is False
    assert ask(Q.invertible(X * Z), Q.invertible(X)) is None
    assert ask(Q.invertible(X * Z), Q.invertible(X) & Q.invertible(Z)) is True
    assert ask(Q.invertible(X.T)) is None
    assert ask(Q.invertible(X.T), Q.invertible(X)) is True
    assert ask(Q.invertible(X.I)) is True
    assert ask(Q.invertible(Identity(3))) is True
    assert ask(Q.invertible(ZeroMatrix(3, 3))) is False
    assert ask(Q.invertible(X), Q.fullrank(X) & Q.square(X))
Пример #5
0
def test_fullrank():
    assert ask(Q.fullrank(X), Q.fullrank(X))
    assert ask(Q.fullrank(X**2), Q.fullrank(X))
    assert ask(Q.fullrank(X.T), Q.fullrank(X)) is True
    assert ask(Q.fullrank(X)) is None
    assert ask(Q.fullrank(Y)) is None
    assert ask(Q.fullrank(X * Z), Q.fullrank(X) & Q.fullrank(Z)) is True
    assert ask(Q.fullrank(Identity(3))) is True
    assert ask(Q.fullrank(ZeroMatrix(3, 3))) is False
    assert ask(Q.fullrank(OneMatrix(1, 1))) is True
    assert ask(Q.fullrank(OneMatrix(3, 3))) is False
    assert ask(Q.invertible(X), ~Q.fullrank(X)) == False
Пример #6
0
def test_diagonal():
    assert ask(Q.diagonal(X + Z.T + Identity(2)),
               Q.diagonal(X) & Q.diagonal(Z)) is True
    assert ask(Q.diagonal(ZeroMatrix(3, 3)))
    assert ask(Q.diagonal(OneMatrix(1, 1))) is True
    assert ask(Q.diagonal(OneMatrix(3, 3))) is False
    assert ask(Q.lower_triangular(X) & Q.upper_triangular(X), Q.diagonal(X))
    assert ask(Q.diagonal(X), Q.lower_triangular(X) & Q.upper_triangular(X))
    assert ask(Q.symmetric(X), Q.diagonal(X))
    assert ask(Q.triangular(X), Q.diagonal(X))
    assert ask(Q.diagonal(C0x0))
    assert ask(Q.diagonal(A1x1))
    assert ask(Q.diagonal(A1x1 + B1x1))
    assert ask(Q.diagonal(A1x1 * B1x1))
    assert ask(Q.diagonal(V1.T * V2))
    assert ask(Q.diagonal(V1.T * (X + Z) * V1))
    assert ask(Q.diagonal(MatrixSlice(Y, (0, 1), (1, 2)))) is True
    assert ask(Q.diagonal(V1.T * (V1 + V2))) is True
    assert ask(Q.diagonal(X**3), Q.diagonal(X))
    assert ask(Q.diagonal(Identity(3)))
    assert ask(Q.diagonal(DiagMatrix(V1)))
    assert ask(Q.diagonal(DiagonalMatrix(X)))
Пример #7
0
def _test_orthogonal_unitary(predicate):
    assert ask(predicate(X), predicate(X))
    assert ask(predicate(X.T), predicate(X)) is True
    assert ask(predicate(X.I), predicate(X)) is True
    assert ask(predicate(X**2), predicate(X))
    assert ask(predicate(Y)) is False
    assert ask(predicate(X)) is None
    assert ask(predicate(X), ~Q.invertible(X)) is False
    assert ask(predicate(X*Z*X), predicate(X) & predicate(Z)) is True
    assert ask(predicate(Identity(3))) is True
    assert ask(predicate(ZeroMatrix(3, 3))) is False
    assert ask(Q.invertible(X), predicate(X))
    assert not ask(predicate(X + Z), predicate(X) & predicate(Z))
Пример #8
0
def test_issue_9817_simplify():
    # simplify on trace of substituted explicit quadratic form of matrix
    # expressions (a scalar) should return without errors (AttributeError)
    # See issue #9817 and #9190 for the original bug more discussion on this
    from sympy.matrices.expressions import Identity, trace
    v = MatrixSymbol('v', 3, 1)
    A = MatrixSymbol('A', 3, 3)
    x = Matrix([i + 1 for i in range(3)])
    X = Identity(3)
    quadratic = v.T * A * v
    assert simplify((trace(quadratic.as_explicit())).xreplace({
        v: x,
        A: X
    })) == 14
Пример #9
0
def test_issue_21866():
    n  = 10
    I  = Identity(n)
    O  = ZeroMatrix(n, n)
    A  = BlockMatrix([[  I,  O,  O,  O ],
                      [  O,  I,  O,  O ],
                      [  O,  O,  I,  O ],
                      [  I,  O,  O,  I ]])
    Ainv = block_collapse(A.inv())
    AinvT = BlockMatrix([[  I,  O,  O,  O ],
                      [  O,  I,  O,  O ],
                      [  O,  O,  I,  O ],
                      [  -I,  O,  O,  I ]])
    assert Ainv == AinvT
Пример #10
0
def test_positive_definite():
    assert ask(Q.positive_definite(X), Q.positive_definite(X))
    assert ask(Q.positive_definite(X.T), Q.positive_definite(X)) is True
    assert ask(Q.positive_definite(X.I), Q.positive_definite(X)) is True
    assert ask(Q.positive_definite(Y)) is False
    assert ask(Q.positive_definite(X)) is None
    assert ask(Q.positive_definite(X*Z*X),
            Q.positive_definite(X) & Q.positive_definite(Z)) is True
    assert ask(Q.positive_definite(X), Q.orthogonal(X))
    assert ask(Q.positive_definite(Y.T*X*Y),
            Q.positive_definite(X) & Q.fullrank(Y)) is True
    assert not ask(Q.positive_definite(Y.T*X*Y), Q.positive_definite(X))
    assert ask(Q.positive_definite(Identity(3))) is True
    assert ask(Q.positive_definite(ZeroMatrix(3, 3))) is False
    assert ask(Q.positive_definite(X + Z), Q.positive_definite(X) &
            Q.positive_definite(Z)) is True
    assert not ask(Q.positive_definite(-X), Q.positive_definite(X))
    assert ask(Q.positive(X[1, 1]), Q.positive_definite(X))
Пример #11
0
def test_symmetric():
    assert ask(Q.symmetric(X), Q.symmetric(X))
    assert ask(Q.symmetric(X * Z), Q.symmetric(X)) is None
    assert ask(Q.symmetric(X * Z), Q.symmetric(X) & Q.symmetric(Z)) is True
    assert ask(Q.symmetric(X + Z), Q.symmetric(X) & Q.symmetric(Z)) is True
    assert ask(Q.symmetric(Y)) is False
    assert ask(Q.symmetric(Y * Y.T)) is True
    assert ask(Q.symmetric(Y.T * X * Y)) is None
    assert ask(Q.symmetric(Y.T * X * Y), Q.symmetric(X)) is True
    assert ask(Q.symmetric(X**10), Q.symmetric(X)) is True
    assert ask(Q.symmetric(A1x1)) is True
    assert ask(Q.symmetric(A1x1 + B1x1)) is True
    assert ask(Q.symmetric(A1x1 * B1x1)) is True
    assert ask(Q.symmetric(V1.T * V1)) is True
    assert ask(Q.symmetric(V1.T * (V1 + V2))) is True
    assert ask(Q.symmetric(V1.T * (V1 + V2) + A1x1)) is True
    assert ask(Q.symmetric(MatrixSlice(Y, (0, 1), (1, 2)))) is True
    assert ask(Q.symmetric(Identity(3))) is True
    assert ask(Q.symmetric(ZeroMatrix(3, 3))) is True
    assert ask(Q.symmetric(OneMatrix(3, 3))) is True
Пример #12
0
class VisualizationFrame(object):
    """
    A VisualizationFrame represents an object that you want to visualize.
    It allows you to easily associate a reference frame and a point
    with a shape.

    A VisualizationFrame can be attached to only one Shape Object.
    It can be nested, i.e we can add/remove multiple visualization frames to
    one visualization frame. On adding the parent frame to the
    Scene object, all the children of the parent visualization frame
    are also added, and hence can be visualized and animated.

    A VisualizationFrame needs to have a ReferenceFrame, and a Point
    for it to form transformation matrices for visualization and
    animations.

    The ReferenceFrame and Point are required to be provided during
    initialization. They can be supplied in the form of any one of these:

    1)reference_frame, point argument.
    2)a RigidBody argument
    3)reference_frame, particle argument.

    In addition to these arguments, A shape argument is also required.
    """
    def __init__(self, *args):
        """
        To initialize a visualization frame a ReferenceFrame,
        Point, and Shape are required. These ReferenceFrame
        and Point can be passed provided in three ways:

        1) RigidBody: the RigidBody's frame and mass center are used.
        2) ReferenceFrame and a Particle: The Particle's Point is used.
        3) ReferenceFrame and a Point

        Parameters
        ==========
        name : str, optional
            Name assigned to VisualizationFrame, default is unnamed
        reference_frame : ReferenceFrame
            A reference_frame with respect to which all orientations of the
            shape takes place, during visualizations/animations.
        origin : Point
            A point with respect to which all the translations of the shape
            takes place, during visualizations/animations.
        rigidbody : RigidBody
            A rigidbody whose reference frame and mass center are to be
            assigned as reference_frame and origin of the
            VisualizationFrame.
        particle : Particle
            A particle whose point is assigned as origin of the
            VisualizationFrame.
        shape : Shape
            A shape to be attached to the VisualizationFrame

        Examples
        ========
        >>> from pydy.viz import VisualizationFrame, Sphere
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Sphere(5)
        >>> #initializing with reference frame, point
        >>> frame1 = VisualizationFrame('frame1', I, O, shape)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> frame2 = VisualizationFrame('frame2', rbody, shape)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> frame3 = VisualizationFrame('frame3', I, Pa, shape)
        """
        #Last arg should be a Shape ..
        if isinstance(args[-1], Shape):
            self._shape = args[-1]
        else:
            raise TypeError('''Please provide a valid shape object''')
        i = 0
        #If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self._reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()

            except AttributeError:
                self._origin = args[i]

    #setting attributes ..
    def __str__(self):
        return 'VisualizationFrame ' + self._name

    def __repr__(self):
        return 'VisualizationFrame'

    @property
    def name(self):
        """
        Name of the VisualizationFrame.
        """
        return self._name

    @name.setter
    def name(self, new_name):
        """
        Sets the name of the VisualizationFrame.

        """
        if not isinstance(new_name, str):
            raise TypeError('''Name should be a str object''')
        else:
            self._name = new_name

    @property
    def origin(self):
        """
        Origin of the VisualizationFrame,
        with respect to which all translational transformations
        take place.
        """
        return self._origin

    @origin.setter
    def origin(self, new_origin):
        """
        Sets the origin of the VisualizationFrame.
        """
        if not isinstance(new_origin, Point):
            raise TypeError('''origin should be a valid Point Object''')
        else:
            self._origin = new_origin

    @property
    def reference_frame(self):
        """
        reference_frame of the VisualizationFrame,
        with respect to which all rotational/orientational
        transformations take place.
        """
        return self._reference_frame

    @reference_frame.setter
    def reference_frame(self, new_reference_frame):
        if not isinstance(new_reference_frame, ReferenceFrame):
            raise TypeError('''reference_frame should be a valid
                                ReferenceFrame object.''')
        else:
            self._reference_frame = new_reference_frame

    @property
    def shape(self):
        """
        shape in the VisualizationFrame.
        A shape attached to the visualization frame.
        NOTE: Only one shape can be attached to a visualization frame.
        """
        return self._shape

    @shape.setter
    def shape(self, new_shape):
        """
        Sets the shape for VisualizationFrame.
        """
        if not isinstance(new_shape, Shape):
            raise TypeError('''shape should be a valid Shape object.''')
        else:
            self._shape = new_shape

    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[0:3, 0:3] = rotation_matrix[0:3, 0:3]

        _point_vector = self.origin.pos_from(point).express(reference_frame)

        self._transform[3, 0] = _point_vector.dot(reference_frame.x)
        self._transform[3, 1] = _point_vector.dot(reference_frame.y)
        self._transform[3, 2] = _point_vector.dot(reference_frame.z)
        return self._transform

    def generate_numeric_transform_function(self, dynamic_variables,
                                            constant_variables):
        """Returns a function which can compute the numerical values of the
        transformation matrix given the numerical dynamic variables (i.e.
        functions of time or states) and the numerical system constants.


        Parameters
        ==========
        dynamic_variables : list of sympy.Functions(time)
            All of the dynamic symbols used in defining the orientation and
            position of this visualization frame.
        constant_variables : list of sympy.Symbols
            All of the constants used in defining the orientation and
            position of this visualization frame.

        Returns
        =======
        numeric_transform : function
            A function which returns the numerical transformation matrix.

        """

        dummy_symbols = [Dummy() for i in dynamic_variables]
        dummy_dict = dict(zip(dynamic_variables, dummy_symbols))
        transform = self._transform.subs(dummy_dict)

        self._numeric_transform = lambdify(dummy_symbols +
                                           constant_variables, transform,
                                           modules="numpy")

        return self._numeric_transform

    def evaluate_transformation_matrix(self, dynamic_values, constant_values):
        """Returns the numerical transformation matrices for each time step.

        Parameters
        ----------
        dynamic_values : array_like, shape(m,) or shape(n, m)
            The m state values for each n time step.
        constant_values : array_like, shape(p,)
            The p constant parameter values of the system.

        Returns
        -------
        transform_matrix : numpy.array, shape(n, 4, 4)
            A 4 x 4 transformation matrix for each time step.

        """
        #If states is instance of numpy array, well and good.
        #else convert it to one:

        states = np.array(dynamic_values)
        if len(states.shape) > 1:
            n = states.shape[0]
            new = np.zeros((n, 4, 4))
            for i, time_instance in enumerate(states):
                args = np.hstack((time_instance, constant_values))
                new[i, :, :] = self._numeric_transform(*args)
        else:
            n = 1
            args = np.hstack((states, constant_values))
            new = self._numeric_transform(*args)

        self._visualization_matrix = new.reshape(n, 16)
        return self._visualization_matrix

    def generate_visualization_dict(self, constant_map={}):
        """Returns a dictionary of all the info required for the
        visualization of this frame.

        Before calling this method, all the transformation matrix generation
        methods should be called, or it will give an error.

        Parameters
        ==========
        constant_map : dictionary
            If the shape associated with this visualization frame has
            symbolic values for its geometric parameters, then you must
            supply a dictionary mapping the necessary SymPy symbols to
            Python floats.

        Returns
        =======
        data : dictionary
            The dictionary contains the following keys:
            name : string
                The name of the VisualizationFrame.
            shape : dictionary
                A dictionary generated from the associated Shape.
            simulation_matrix : list
                The N x 4 x 4 array provided as a list of lists of lists. N
                is the number of time steps and the 4 x 4 matrix at each
                time step represents the transformation matrix for animation
                purposes.

        """
        data = {}
        data['name'] = self.name
        data['shape'] = self.shape.generate_dict(constant_map=constant_map)
        try:
            data['simulation_matrix'] = self._visualization_matrix.tolist()
        except:
            raise RuntimeError("Please call the numerical ",
                               "transformation methods, ",
                               "before generating visualization dict")

        return data
Пример #13
0
    def __init__(self, *args, **kwargs):
        """
        Initialises a PointLight object.
        To initialize a point light, we need to supply
        a name(optional), a reference frame, and a point.


        Parameters
        ==========
        name : str, optional
            Name assigned to VisualizationFrame, default is unnamed
        reference_frame : ReferenceFrame
            A reference_frame with respect to which all orientations of the
            shape takes place, during visualizations/animations.
        origin : Point
            A point with respect to which all the translations of the shape
            takes place, during visualizations/animations.
        rigidbody : RigidBody
            A rigidbody whose reference frame and mass center are to be
            assigned as reference_frame and origin of the
            VisualizationFrame.
        particle : Particle
            A particle whose point is assigned as origin of the
            VisualizationFrame.

        Examples
        ========
        >>> from pydy.viz import PointLight
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> #initializing with reference frame, point
        >>> light = PointLight('light', I, O)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> light = PointLight('frame2', rbody)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> light = PointLight('frame3', I, Pa)
        """

        try:
            self._color = kwargs['color']
        except KeyError:
            self._color = 'white'

        #Now we use same approach as in VisualizationFrame
        #for setting reference_frame and origin
        i = 0
        #If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self._reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()
            except AttributeError:
                self._origin = args[i]

        #basic thing required, transform matrix
        self._transform = Identity(4).as_mutable()
Пример #14
0
def test_eval_determinant():
    assert det(Identity(n)) == 1
    assert det(ZeroMatrix(n, n)) == 0
    assert det(Transpose(A)) == det(A)
Пример #15
0
class VisualizationFrame(object):
    """
    A VisualizationFrame represents an object that you want to visualize.
    It allows you to easily associate a reference frame and a point
    with a shape.

    A VisualizationFrame can be attached to only one Shape Object.
    It can be nested, i.e we can add/remove multiple visualization frames to
    one visualization frame. On adding the parent frame to the
    Scene object, all the children of the parent visualization frame
    are also added, and hence can be visualized and animated.

    A VisualizationFrame needs to have a ReferenceFrame, and a Point
    for it to form transformation matrices for visualization and
    animations.

    The ReferenceFrame and Point are required to be provided during
    initialization. They can be supplied in the form of any one of these:

    1)reference_frame, point argument.
    2)a RigidBody argument
    3)reference_frame, particle argument.

    In addition to these arguments, A shape argument is also required.
    """
    def __init__(self, *args):
        """
        To initialize a visualization frame a ReferenceFrame,
        Point, and Shape are required. These ReferenceFrame
        and Point can be passed provided in three ways:

        1) RigidBody: the RigidBody's frame and mass center are used.
        2) ReferenceFrame and a Particle: The Particle's Point is used.
        3) ReferenceFrame and a Point

        Parameters
        ==========
        name : str, optional
            Name assigned to VisualizationFrame, default is unnamed
        reference_frame : ReferenceFrame
            A reference_frame with respect to which all orientations of the
            shape takes place, during visualizations/animations.
        origin : Point
            A point with respect to which all the translations of the shape
            takes place, during visualizations/animations.
        rigidbody : RigidBody
            A rigidbody whose reference frame and mass center are to be
            assigned as reference_frame and origin of the
            VisualizationFrame.
        particle : Particle
            A particle whose point is assigned as origin of the
            VisualizationFrame.
        shape : Shape
            A shape to be attached to the VisualizationFrame

        Examples
        ========
        >>> from pydy.viz import VisualizationFrame, Sphere
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Sphere(5)
        >>> #initializing with reference frame, point
        >>> frame1 = VisualizationFrame('frame1', I, O, shape)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> frame2 = VisualizationFrame('frame2', rbody, shape)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> frame3 = VisualizationFrame('frame3', I, Pa, shape)
        """
        #Last arg should be a Shape ..
        if isinstance(args[-1], Shape):
            self._shape = args[-1]
        else:
            raise TypeError('''Please provide a valid shape object''')
        i = 0
        #If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self._reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()

            except AttributeError:
                self._origin = args[i]

    #setting attributes ..
    def __str__(self):
        return 'VisualizationFrame ' + self._name

    def __repr__(self):
        return 'VisualizationFrame'

    @property
    def name(self):
        """
        Name of the VisualizationFrame.
        """
        return self._name

    @name.setter
    def name(self, new_name):
        """
        Sets the name of the VisualizationFrame.

        """
        if not isinstance(new_name, str):
            raise TypeError('''Name should be a str object''')
        else:
            self._name = new_name

    @property
    def origin(self):
        """
        Origin of the VisualizationFrame,
        with respect to which all translational transformations
        take place.
        """
        return self._origin

    @origin.setter
    def origin(self, new_origin):
        """
        Sets the origin of the VisualizationFrame.
        """
        if not isinstance(new_origin, Point):
            raise TypeError('''origin should be a valid Point Object''')
        else:
            self._origin = new_origin

    @property
    def reference_frame(self):
        """
        reference_frame of the VisualizationFrame,
        with respect to which all rotational/orientational
        transformations take place.
        """
        return self._reference_frame

    @reference_frame.setter
    def reference_frame(self, new_reference_frame):
        if not isinstance(new_reference_frame, ReferenceFrame):
            raise TypeError('''reference_frame should be a valid
                                ReferenceFrame object.''')
        else:
            self._reference_frame = new_reference_frame

    @property
    def shape(self):
        """
        shape in the VisualizationFrame.
        A shape attached to the visualization frame.
        NOTE: Only one shape can be attached to a visualization frame.
        """
        return self._shape

    @shape.setter
    def shape(self, new_shape):
        """
        Sets the shape for VisualizationFrame.
        """
        if not isinstance(new_shape, Shape):
            raise TypeError('''shape should be a valid Shape object.''')
        else:
            self._shape = new_shape

    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[0:3, 0:3] = rotation_matrix[0:3, 0:3]

        _point_vector = self.origin.pos_from(point).express(reference_frame)

        self._transform[3, 0] = _point_vector.dot(reference_frame.x)
        self._transform[3, 1] = _point_vector.dot(reference_frame.y)
        self._transform[3, 2] = _point_vector.dot(reference_frame.z)
        return self._transform

    def generate_numeric_transform_function(self, dynamic_variables,
                                            constant_variables):
        """Returns a function which can compute the numerical values of the
        transformation matrix given the numerical dynamic variables (i.e.
        functions of time or states) and the numerical system constants.


        Parameters
        ==========
        dynamic_variables : list of sympy.Functions(time)
            All of the dynamic symbols used in defining the orientation and
            position of this visualization frame.
        constant_variables : list of sympy.Symbols
            All of the constants used in defining the orientation and
            position of this visualization frame.

        Returns
        =======
        numeric_transform : function
            A function which returns the numerical transformation matrix.

        """

        dummy_symbols = [Dummy() for i in dynamic_variables]
        dummy_dict = dict(zip(dynamic_variables, dummy_symbols))
        transform = self._transform.subs(dummy_dict)

        self._numeric_transform = lambdify(dummy_symbols + constant_variables,
                                           transform,
                                           modules="numpy")

        return self._numeric_transform

    def evaluate_transformation_matrix(self, dynamic_values, constant_values):
        """Returns the numerical transformation matrices for each time step.

        Parameters
        ----------
        dynamic_values : array_like, shape(m,) or shape(n, m)
            The m state values for each n time step.
        constant_values : array_like, shape(p,)
            The p constant parameter values of the system.

        Returns
        -------
        transform_matrix : numpy.array, shape(n, 4, 4)
            A 4 x 4 transformation matrix for each time step.

        """
        #If states is instance of numpy array, well and good.
        #else convert it to one:

        states = np.array(dynamic_values)
        if len(states.shape) > 1:
            n = states.shape[0]
            new = np.zeros((n, 4, 4))
            for i, time_instance in enumerate(states):
                args = np.hstack((time_instance, constant_values))
                new[i, :, :] = self._numeric_transform(*args)
        else:
            n = 1
            args = np.hstack((states, constant_values))
            new = self._numeric_transform(*args)

        self._visualization_matrix = new.reshape(n, 16)
        return self._visualization_matrix

    def generate_visualization_dict(self, constant_map={}):
        """Returns a dictionary of all the info required for the
        visualization of this frame.

        Before calling this method, all the transformation matrix generation
        methods should be called, or it will give an error.

        Parameters
        ==========
        constant_map : dictionary
            If the shape associated with this visualization frame has
            symbolic values for its geometric parameters, then you must
            supply a dictionary mapping the necessary SymPy symbols to
            Python floats.

        Returns
        =======
        data : dictionary
            The dictionary contains the following keys:
            name : string
                The name of the VisualizationFrame.
            shape : dictionary
                A dictionary generated from the associated Shape.
            simulation_matrix : list
                The N x 4 x 4 array provided as a list of lists of lists. N
                is the number of time steps and the 4 x 4 matrix at each
                time step represents the transformation matrix for animation
                purposes.

        """
        data = {}
        data['name'] = self.name
        data['shape'] = self.shape.generate_dict(constant_map=constant_map)
        try:
            data['simulation_matrix'] = self._visualization_matrix.tolist()
        except:
            raise RuntimeError("Please call the numerical ",
                               "transformation methods, ",
                               "before generating visualization dict")

        return data
Пример #16
0
def test_invertible_BlockDiagMatrix():
    assert ask(Q.invertible(BlockDiagMatrix(Identity(3), Identity(5)))) == True
    assert ask(Q.invertible(BlockDiagMatrix(ZeroMatrix(3, 3),
                                            Identity(5)))) == False
    assert ask(Q.invertible(BlockDiagMatrix(Identity(3),
                                            OneMatrix(5, 5)))) == False
Пример #17
0
class VisualizationFrame(object):
    """
    A VisualizationFrame represents an object that you want to visualize.
    It allows you to easily associate a reference frame and a point
    with a shape.

    A VisualizationFrame can be attached to only one Shape Object.
    It can be nested, i.e we can add/remove multiple visualization frames to
    one visualization frame. On adding the parent frame to the
    Scene object, all the children of the parent visualization frame
    are also added, and hence can be visualized and animated.

    A VisualizationFrame needs to have a ReferenceFrame, and a Point
    for it to form transformation matrices for visualization and
    animations.

    The ReferenceFrame and Point are required to be provided during
    initialization. They can be supplied in the form of any one of these:

    1)reference_frame, point argument.
    2)a RigidBody argument
    3)reference_frame, particle argument.

    In addition to these arguments, A shape argument is also required.
    """
    def __init__(self, *args):
        """
        To initialize a visualization frame a ReferenceFrame,
        Point, and Shape are required. These ReferenceFrame
        and Point can be passed provided in three ways:

        1) RigidBody: the RigidBody's frame and mass center are used.
        2) ReferenceFrame and a Particle: The Particle's Point is used.
        3) ReferenceFrame and a Point

        Parameters
        ==========
        name : str, optional
            Name assigned to VisualizationFrame, default is unnamed
        reference_frame : ReferenceFrame
            A reference_frame with respect to which all orientations of the
            shape takes place, during visualizations/animations.
        origin : Point
            A point with respect to which all the translations of the shape
            takes place, during visualizations/animations.
        rigidbody : RigidBody
            A rigidbody whose reference frame and mass center are to be
            assigned as reference_frame and origin of the
            VisualizationFrame.
        particle : Particle
            A particle whose point is assigned as origin of the
            VisualizationFrame.
        shape : Shape
            A shape to be attached to the VisualizationFrame

        Examples
        ========
        >>> from pydy.viz import VisualizationFrame, Sphere
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Sphere(5)
        >>> #initializing with reference frame, point
        >>> frame1 = VisualizationFrame('frame1', I, O, shape)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> frame2 = VisualizationFrame('frame2', rbody, shape)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> frame3 = VisualizationFrame('frame3', I, Pa, shape)
        """
        #Last arg should be a Shape ..
        if isinstance(args[-1], Shape):
            self._shape = args[-1]
        else:
            raise TypeError("Please provide a valid shape object as the last "
                            " positional argument.")
        i = 0
        # If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self.name = args[i]
            i += 1
        else:
            self.name = 'unnamed'

        try:
            self.reference_frame = args[i].get_frame()
            self.origin = args[i].masscenter

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self.reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self.origin = args[i].point

            except AttributeError:
                self.origin = args[i]

    #setting attributes ..
    def __str__(self):
        return 'VisualizationFrame ' + self.name

    def __repr__(self):
        return 'VisualizationFrame'

    @property
    def name(self):
        """
        Name of the VisualizationFrame.
        """
        return self._name

    @name.setter
    def name(self, new_name):
        """
        Sets the name of the VisualizationFrame.

        """
        if not isinstance(new_name, str):
            raise TypeError('''Name should be a str object''')
        else:
            self._name = new_name

    @property
    def origin(self):
        """
        Origin of the VisualizationFrame,
        with respect to which all translational transformations
        take place.
        """
        return self._origin

    @origin.setter
    def origin(self, new_origin):
        """
        Sets the origin of the VisualizationFrame.
        """
        if not isinstance(new_origin, Point):
            raise TypeError('''origin should be a valid Point Object''')
        else:
            self._origin = new_origin

    @property
    def reference_frame(self):
        """
        reference_frame of the VisualizationFrame,
        with respect to which all rotational/orientational
        transformations take place.
        """
        return self._reference_frame

    @reference_frame.setter
    def reference_frame(self, new_reference_frame):
        if not isinstance(new_reference_frame, ReferenceFrame):
            raise TypeError('''reference_frame should be a valid
                                ReferenceFrame object.''')
        else:
            self._reference_frame = new_reference_frame

    @property
    def shape(self):
        """
        shape in the VisualizationFrame.
        A shape attached to the visualization frame.
        NOTE: Only one shape can be attached to a visualization frame.
        """
        return self._shape

    @shape.setter
    def shape(self, new_shape):
        """
        Sets the shape for VisualizationFrame.
        """
        if not isinstance(new_shape, Shape):
            raise TypeError('''shape should be a valid Shape object.''')
        else:
            self._shape = new_shape

    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[:3, :3] = rotation_matrix

        point_vector = self.origin.pos_from(point)
        try:
            self._transform[3, :3] = point_vector.to_matrix(reference_frame).T
        except AttributeError:
            # In earlier versions of sympy, 'Vector' object has no attribute
            # 'to_matrix'.
            self._transform[3, 0] = point_vector.dot(reference_frame.x)
            self._transform[3, 1] = point_vector.dot(reference_frame.y)
            self._transform[3, 2] = point_vector.dot(reference_frame.z)
        return self._transform

    def generate_numeric_transform_function(self, dynamic_variables,
                                            constant_variables):
        """Returns a function which can compute the numerical values of the
        transformation matrix given the numerical dynamic variables (i.e.
        functions of time or states) and the numerical system constants.


        Parameters
        ==========
        dynamic_variables : list of sympy.Functions(time)
            All of the dynamic symbols used in defining the orientation and
            position of this visualization frame.
        constant_variables : list of sympy.Symbols
            All of the constants used in defining the orientation and
            position of this visualization frame.

        Returns
        =======
        numeric_transform : list of functions
            A list of functions which return the numerical transformation
            for each element in the transformation matrix.

        """

        dummy_symbols = [Dummy() for i in dynamic_variables]
        dummy_dict = dict(zip(dynamic_variables, dummy_symbols))
        transform = self._transform.subs(dummy_dict).reshape(16, 1)
        dummy_symbols.extend(constant_variables)

        # Create a numeric transformation for each element in the transformation
        # matrix. We cannot lambdify the transformation matrix as calling
        # lambdify of a constant expression returns a scalar, even if the
        # lambdify function arguments are sequences:
        # https://github.com/sympy/sympy/issues/5642
        self._numeric_transform = []
        for i in range(16):
            t = transform[i]
            if t.has(Dummy):
                f = lambdify(dummy_symbols, t, modules='numpy')
            else:
                f = lambdify(constant_variables, t, modules='numpy')
            self._numeric_transform.append(f)
        return self._numeric_transform

    def evaluate_transformation_matrix(self, dynamic_values, constant_values):
        """Returns the numerical transformation matrices for each time step.

        Parameters
        ----------
        dynamic_values : array_like, shape(m,) or shape(n, m)
            The m state values for each n time step.
        constant_values : array_like, shape(p,)
            The p constant parameter values of the system.

        Returns
        -------
        transform_matrix : numpy.array, shape(n, 16)
            A 4 x 4 transformation matrix for each time step.

        """
        #If states is instance of numpy array, well and good.
        #else convert it to one:

        states = np.squeeze(np.array(dynamic_values))
        if not isinstance(constant_values, Iterator):
            constant_values = list(constant_values)
        if len(states.shape) > 1:
            n = states.shape[0]
            args = []
            for a in np.split(states, states.shape[1], 1):
                args.append(np.squeeze(a))
            for a in constant_values:
                args.append(np.repeat(a, n))
        else:
            n = 1
            args = np.hstack((states, constant_values))

        new = np.zeros((n, 16))
        for i, t in enumerate(self._numeric_transform):
            if callable(t):
                try:
                    new[:, i] = t(*args)
                except TypeError:
                    # dynamic values are not necessary so pass only constant
                    # values into transform function
                    new[:, i] = np.repeat(t(*constant_values), n)
            else:
                new[:, i] = np.repeat(t, n)
        self._visualization_matrix = new.tolist()
        return self._visualization_matrix

    def generate_scene_dict(self, constant_map={}):
        """
        This method generates information for a static
        visualization in the initial conditions, in the form
        of dictionary. This contains shape information
        from `Shape.generate_dict()` followed by an
        init_orientation Key.

        Before calling this method, all the transformation matrix
        generation methods should be called, or it will give an error.

        Parameters
        ==========
        constant_map : dictionary
            Constant map is required when Shape contains sympy expressions.This
            dictionary maps sympy expressions/symbols to numerical values(floats)

        Returns
        =======
        A dictionary built with a call to `Shape.generate_dict`.
        Additional keys included in the dict are following:

        1. init_orientation: Specifies the initial orientation
           of the `VisualizationFrame`.
        2. reference_frame_name: Name(str) of the reference_frame
           attached to this VisualizationFrame.
        3. simulation_id: an arbitrary integer to map scene description
           with the simulation data.


        """
        scene_dict = {id(self): {}}
        scene_dict[id(self)] = self.shape.generate_dict(
            constant_map=constant_map)
        scene_dict[id(self)]['name'] = self.name
        scene_dict[id(self)]["reference_frame_name"] = str(
            self.reference_frame)
        scene_dict[id(self)]["simulation_id"] = id(self)

        try:
            scene_dict[id(
                self)]["init_orientation"] = self._visualization_matrix[0]
        except:
            raise RuntimeError("Cannot generate visualization data " + \
                                "because numerical transformation " + \
                               "has not been performed, " + \
                                "Please call the numerical " + \
                               "transformation methods, " + \
                               "before generating visualization dict")

        return scene_dict

    def generate_simulation_dict(self):
        """
        Generates the simulation information for this visualization
        frame. It maps the simulation data information to the
        scene information via a unique id.

        Before calling this method, all the transformation matrix
        generation methods should be called, or it will give an error.

        Returns
        =======

        A dictionary containing list of 4x4 matrices mapped to
        the unique id as the key.

        """
        simulation_dict = {}
        try:
            simulation_dict[id(self)] = self._visualization_matrix

        except:
            raise RuntimeError("Cannot generate visualization data " + \
                                "because numerical transformation " + \
                               "has not been performed, " + \
                                "Please call the numerical " + \
                               "transformation methods, " + \
                               "before generating visualization dict")

        return simulation_dict
Пример #18
0
    def __init__(self, *args, **kwargs):
        """
        Initialises a PerspectiveCamera object.
        To initialize a visualization frame, we need to supply
        a name(optional), a reference frame, a point,
        field of view(fov) (optional), near plane distance(optional)
        and far plane distance(optional).

        Examples
        ========
        >>> from pydy.viz import VisualizationFrame, Shape
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Shape()
        >>> #initializing with reference frame, point
        >>> camera1 = PerspectiveCamera('frame1', I, O)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> camera2 = PerspectiveCamera('frame2', rbody)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> camera3 = PerspectiveCamera('frame3', I, Pa)
        """
        try:
            self._fov = kwargs['fov']
        except KeyError:
            self._fov = 45

        try:
            self._near = kwargs['near']
        except KeyError:
            self._near = 1

        try:
            self._far = kwargs['far']
        except KeyError:
            self._far = 1000


        #Now we use same approach as in VisualizationFrame
        #for setting reference_frame and origin
        i = 0
        #If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self._reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()
            except AttributeError:
                self._origin = args[i]

        #basic thing required, transform matrix
        self._transform = Identity(4).as_mutable()
Пример #19
0
    def __init__(self, *args, **kwargs):
        """
        Initialises an OrthoGraphicCamera object. To initialize a
        visualization frame, we need to supply a name (optional), a
        reference frame, a point, near plane distance (optional) and far
        plane distance (optional).

        Examples
        ========
        >>> from pydy.viz import OrthoGraphicCamera
        >>> from sympy.physics.mechanics import (ReferenceFrame, Point,
        ...                                      RigidBody, Particle,
        ...                                      inertia)
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Shape()
        >>> # Initializing with ReferenceFrame, Point
        >>> camera1 = OrthoGraphicCamera('frame1', I, O)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a Rigidbody
        >>> camera2 = OrthoGraphicCamera('frame2', rbody)
        >>> Pa = Particle('Pa', O, mass)
        >>> # Initializing with Particle, ReferenceFrame
        >>> camera3 = OrthoGraphicCamera('frame3', I, Pa)
        """
        try:
            self._near = kwargs['near']
        except KeyError:
            self._near = 1

        try:
            self._far = kwargs['far']
        except KeyError:
            self._far = 1000

        # Now we use same approach as in VisualizationFrame for setting
        # reference_frame and origin
        i = 0
        # If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            # It is not a rigidbody, hence this arg should be a reference
            # frame.
            self._reference_frame = args[i]
            i += 1

            # Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()
            except AttributeError:
                self._origin = args[i]

        # basic thing required, transform matrix
        self._transform = Identity(4).as_mutable()
Пример #20
0
class VisualizationFrame(object):
    """
    A VisualizationFrame represents an object that you want to visualize.
    It allows you to easily associate a reference frame and a point
    with a shape.

    A VisualizationFrame can be attached to only one Shape Object.
    It can be nested, i.e we can add/remove multiple visualization frames to
    one visualization frame. On adding the parent frame to the
    Scene object, all the children of the parent visualization frame
    are also added, and hence can be visualized and animated.

    A VisualizationFrame needs to have a ReferenceFrame, and a Point
    for it to form transformation matrices for visualization and
    animations.

    The ReferenceFrame and Point are required to be provided during
    initialization. They can be supplied in the form of any one of these:

    1)reference_frame, point argument.
    2)a RigidBody argument
    3)reference_frame, particle argument.

    In addition to these arguments, A shape argument is also required.
    """
    def __init__(self, *args):
        """
        To initialize a visualization frame a ReferenceFrame,
        Point, and Shape are required. These ReferenceFrame
        and Point can be passed provided in three ways:

        1) RigidBody: the RigidBody's frame and mass center are used.
        2) ReferenceFrame and a Particle: The Particle's Point is used.
        3) ReferenceFrame and a Point

        Parameters
        ==========
        name : str, optional
            Name assigned to VisualizationFrame, default is unnamed
        reference_frame : ReferenceFrame
            A reference_frame with respect to which all orientations of the
            shape takes place, during visualizations/animations.
        origin : Point
            A point with respect to which all the translations of the shape
            takes place, during visualizations/animations.
        rigidbody : RigidBody
            A rigidbody whose reference frame and mass center are to be
            assigned as reference_frame and origin of the
            VisualizationFrame.
        particle : Particle
            A particle whose point is assigned as origin of the
            VisualizationFrame.
        shape : Shape
            A shape to be attached to the VisualizationFrame

        Examples
        ========
        >>> from pydy.viz import VisualizationFrame, Sphere
        >>> from sympy.physics.mechanics import \
                               ReferenceFrame, Point, RigidBody, \
                                Particle, inertia
        >>> from sympy import symbols
        >>> I = ReferenceFrame('I')
        >>> O = Point('O')
        >>> shape = Sphere(5)
        >>> #initializing with reference frame, point
        >>> frame1 = VisualizationFrame('frame1', I, O, shape)
        >>> Ixx, Iyy, Izz, mass = symbols('Ixx Iyy Izz mass')
        >>> i = inertia(I, Ixx, Iyy, Izz)
        >>> rbody = RigidBody('rbody', O, I, mass, (inertia, O))
        >>> # Initializing with a rigidbody ..
        >>> frame2 = VisualizationFrame('frame2', rbody, shape)
        >>> Pa = Particle('Pa', O, mass)
        >>> #initializing with Particle, reference_frame ...
        >>> frame3 = VisualizationFrame('frame3', I, Pa, shape)
        """
        #Last arg should be a Shape ..
        if isinstance(args[-1], Shape):
            self._shape = args[-1]
        else:
            raise TypeError('''Please provide a valid shape object''')
        i = 0
        #If first arg is not str, name the visualization frame 'unnamed'
        if isinstance(args[i], str):
            self._name = args[i]
            i += 1
        else:
            self._name = 'unnamed'

        try:
            self._reference_frame = args[i].get_frame()
            self._origin = args[i].get_masscenter()

        except AttributeError:
            #It is not a rigidbody, hence this arg should be a
            #reference frame
            try:
                dcm = args[i]._dcm_dict
                self._reference_frame = args[i]
                i += 1
            except AttributeError:
                raise TypeError(''' A ReferenceFrame is to be supplied
                                   before a Particle/Point. ''')

            #Now next arg can either be a Particle or point
            try:
                self._origin = args[i].get_point()

            except AttributeError:
                self._origin = args[i]

    #setting attributes ..
    def __str__(self):
        return 'VisualizationFrame ' + self._name

    def __repr__(self):
        return 'VisualizationFrame'

    @property
    def name(self):
        """
        Name of the VisualizationFrame.
        """
        return self._name

    @name.setter
    def name(self, new_name):
        """
        Sets the name of the VisualizationFrame.

        """
        if not isinstance(new_name, str):
            raise TypeError('''Name should be a str object''')
        else:
            self._name = new_name

    @property
    def origin(self):
        """
        Origin of the VisualizationFrame,
        with respect to which all translational transformations
        take place.
        """
        return self._origin

    @origin.setter
    def origin(self, new_origin):
        """
        Sets the origin of the VisualizationFrame.
        """
        if not isinstance(new_origin, Point):
            raise TypeError('''origin should be a valid Point Object''')
        else:
            self._origin = new_origin

    @property
    def reference_frame(self):
        """
        reference_frame of the VisualizationFrame,
        with respect to which all rotational/orientational
        transformations take place.
        """
        return self._reference_frame

    @reference_frame.setter
    def reference_frame(self, new_reference_frame):
        if not isinstance(new_reference_frame, ReferenceFrame):
            raise TypeError('''reference_frame should be a valid
                                ReferenceFrame object.''')
        else:
            self._reference_frame = new_reference_frame

    @property
    def shape(self):
        """
        shape in the VisualizationFrame.
        A shape attached to the visualization frame.
        NOTE: Only one shape can be attached to a visualization frame.
        """
        return self._shape

    @shape.setter
    def shape(self, new_shape):
        """
        Sets the shape for VisualizationFrame.
        """
        if not isinstance(new_shape, Shape):
            raise TypeError('''shape should be a valid Shape object.''')
        else:
            self._shape = new_shape

    def generate_transformation_matrix(self, reference_frame, point):
        """Generates a symbolic transformation matrix, with respect to the
        provided reference frame and point.

        Parameters
        ==========
        reference_frame : ReferenceFrame
            A reference_frame with respect to which transformation matrix is
            generated.
        point : Point
            A point with respect to which transformation matrix is
            generated.

        Returns
        =======
        A 4 x 4 SymPy matrix, containing symbolic expressions describing the
        transformation as a function of time.

        """
        rotation_matrix = self.reference_frame.dcm(reference_frame)
        self._transform = Identity(4).as_mutable()
        self._transform[:3, :3] = rotation_matrix

        point_vector = self.origin.pos_from(point)
        try:
            self._transform[3, :3] = point_vector.to_matrix(reference_frame).T
        except AttributeError:
            # In earlier versions of sympy, 'Vector' object has no attribute
            # 'to_matrix'.
            self._transform[3, 0] = point_vector.dot(reference_frame.x)
            self._transform[3, 1] = point_vector.dot(reference_frame.y)
            self._transform[3, 2] = point_vector.dot(reference_frame.z)
        return self._transform

    def generate_numeric_transform_function(self, dynamic_variables,
                                            constant_variables):
        """Returns a function which can compute the numerical values of the
        transformation matrix given the numerical dynamic variables (i.e.
        functions of time or states) and the numerical system constants.


        Parameters
        ==========
        dynamic_variables : list of sympy.Functions(time)
            All of the dynamic symbols used in defining the orientation and
            position of this visualization frame.
        constant_variables : list of sympy.Symbols
            All of the constants used in defining the orientation and
            position of this visualization frame.

        Returns
        =======
        numeric_transform : list of functions
            A list of functions which return the numerical transformation
            for each element in the transformation matrix.

        """

        dummy_symbols = [Dummy() for i in dynamic_variables]
        dummy_dict = dict(zip(dynamic_variables, dummy_symbols))
        transform = self._transform.subs(dummy_dict).reshape(16, 1)
        dummy_symbols.extend(constant_variables)

        # Create a numeric transformation for each element in the transformation
        # matrix. We cannot lambdify the transformation matrix as calling
        # lambdify of a constant expression returns a scalar, even if the
        # lambdify function arguments are sequences:
        # https://github.com/sympy/sympy/issues/5642
        self._numeric_transform = []
        for i in range(16):
            t = transform[i]
            if t.has(Dummy):
                f = lambdify(dummy_symbols, t, modules='numpy')
            else:
                f = lambdify(constant_variables, t, modules='numpy')
            self._numeric_transform.append(f)
        return self._numeric_transform

    def evaluate_transformation_matrix(self, dynamic_values, constant_values):
        """Returns the numerical transformation matrices for each time step.

        Parameters
        ----------
        dynamic_values : array_like, shape(m,) or shape(n, m)
            The m state values for each n time step.
        constant_values : array_like, shape(p,)
            The p constant parameter values of the system.

        Returns
        -------
        transform_matrix : numpy.array, shape(n, 16)
            A 4 x 4 transformation matrix for each time step.

        """
        #If states is instance of numpy array, well and good.
        #else convert it to one:

        states = np.squeeze(np.array(dynamic_values))
        if not isinstance(constant_values, Iterator):
            constant_values = list(constant_values)
        if len(states.shape) > 1:
            n = states.shape[0]
            args = []
            for a in np.split(states, states.shape[1], 1):
                args.append(np.squeeze(a))
            for a in constant_values:
                args.append(np.repeat(a, n))
        else:
            n = 1
            args = np.hstack((states, constant_values))

        new = np.zeros((n, 16))
        for i, t in enumerate(self._numeric_transform):
            if callable(t):
                try:
                    new[:, i] = t(*args)
                except TypeError:
                    # dynamic values are not necessary so pass only constant
                    # values into transform function
                    new[:, i] = np.repeat(t(*constant_values), n)
            else:
                new[:, i] = np.repeat(t, n)
        self._visualization_matrix = new.tolist()
        return self._visualization_matrix


    def generate_scene_dict(self, constant_map={}):
        """
        This method generates information for a static
        visualization in the initial conditions, in the form
        of dictionary. This contains shape information
        from `Shape.generate_dict()` followed by an
        init_orientation Key.

        Before calling this method, all the transformation matrix
        generation methods should be called, or it will give an error.

        Parameters
        ==========
        constant_map : dictionary
            Constant map is required when Shape contains sympy expressions.This
            dictionary maps sympy expressions/symbols to numerical values(floats)

        Returns
        =======
        A dictionary built with a call to `Shape.generate_dict`.
        Additional keys included in the dict are following:

        1. init_orientation: Specifies the initial orientation
           of the `VisualizationFrame`.
        2. reference_frame_name: Name(str) of the reference_frame
           attached to this VisualizationFrame.
        3. simulation_id: an arbitrary integer to map scene description
           with the simulation data.


        """
        scene_dict = { id(self): {} }
        scene_dict[id(self)] = self.shape.generate_dict(constant_map=constant_map)
        scene_dict[id(self)]["init_orientation"] = self._visualization_matrix[0]
        scene_dict[id(self)]["reference_frame_name"] = str(self._reference_frame)
        scene_dict[id(self)]["simulation_id"] = id(self)

        return scene_dict

    def generate_simulation_dict(self):
        """
        Generates the simulation information for this visualization
        frame. It maps the simulation data information to the
        scene information via a unique id.

        Before calling this method, all the transformation matrix
        generation methods should be called, or it will give an error.

        Returns
        =======

        A dictionary containing list of 4x4 matrices mapped to
        the unique id as the key.

        """
        simulation_dict = {}
        try:
            simulation_dict[id(self)] = self._visualization_matrix

        except:
            raise RuntimeError("Cannot generate visualization data " + \
                                "because numerical transformation " + \
                               "has not been performed, " + \
                                "Please call the numerical " + \
                               "transformation methods, " + \
                               "before generating visualization dict")


        return simulation_dict