Mirror a matrix over a defined plane.



  • Hello,

    how would you mirror a matrix of an object over a plane which of course must be defined by an other object?

    Thanks in advance.



  • Hi,

    there are two ambiguities in your question. What do you mean by mirroring a matrix in respect to a plane? And also an object does not define unambiguously a plane. Its matrix defines three planes. Below is a script that will get you going. If you mean by mirroring a matrix constructing a matrix for a point in respect to a plane, please post a follow up question.

    import c4d
    
    """ This script expects you to have two objects in your document. One named 
     "obj", the object to be mirrored, and one named "mirror", the 'mirroring
     plane', which should be a plane object, so my comments below make more sense.
    """
    
    def main():
        # Get the two objects, BaseDocument.SearchObject() should not be used
        # in a production environment.
        obj_to_mirror = doc.SearchObject("obj")
        obj_mirror_plane = doc.SearchObject("mirror")
    
        # Get out if the objects cannot be found.
        if not obj_to_mirror or not obj_mirror_plane:
            return
    
        # Get the global matrices of both objects
        mg_obj = obj_to_mirror.GetMg()
        mg_pln = obj_mirror_plane.GetMg()
    
        # Get the position of "obj" in the frame of reference (transform matrix) of 
        # mirror.
        pl = ~mg_pln * mg_obj.off
        """Do the mirroring operation for the point in respect to a plane, I am 
         choosing the xz-plane here, as it lines up nicely with the plane object. 
         This is the first ambiguity of your question. An object (its matrix) does
         not define one plane, it defines three (for each axis one). There is no 
         definitive way to find the plane a human would consider the correct one,
         if none is given. 
         One way could be to calculate the dot product between the unit vector of
         pl and the unit vector for each plane ([1, 0, 0], [0, 1, 0], [0, 0, 1]) 
         and then choose the plane with dot product closest to zero, i.e. the plane
         the vector pl "is most orthogonal" to."""
        pl.y *= -1.
        # Convert our point back into the global frame
        pg = mg_pln * pl
        """Constructing the final global matrix for "obj". Here comes in the second
         ambiguity of your question. "Mirroring a matrix in respect to a plane"
         could mean many things. I will put in our mirrored point for now and
         leave the scale and orientation of the "obj" object untouched. Post a
         follow up question, explaining in more detail what you mean by mirroring
         a matrix if you need more help."""
        mg_new = c4d.Matrix(pg, mg_obj.v1, mg_obj.v2, mg_obj.v3)
        obj_to_mirror.SetMg(mg_new)
        c4d.EventAdd()
    
    
    if __name__=='__main__':
        main()
    

    Cheers
    zipit



  • @zipit Thanks, that's exactly what I was looking for.



  • @zipit Okay, I have a question after all. How would a complete inverse mirroring look like? Which also considers the rotation but scale is untouched. Thanks for yur help.
    mirror.jpg



  • Not sure what you mean by "complete" mirroring, but there are two things to keep in mind when mirroring:

    1. C4D has a left-handed coordinate system. A "complete" mirroring would change that into right-handed coordinate systems. That is not possible, so one axis coordinate will need to be inverted for the mirroring.

    2. Mirroring a polygon (with no other changes) will invert the point sequence and therefore the normal.



  • Hi,

    the image you are showing is not a reflection [1]. The line segment AA', where A is a point and A' is the reflected point will always be orthogonal to line/plane of reflection. Your image does not fulfill this criteria.

    Some own effort is usually also required to get any help. Below is a script which probably does what you want, I added some comments to make it somewhat educational.

    import c4d
    
    """ This script expects you to have two objects in your document. One named 
     "obj", the object to be mirrored, and one named "mirror", the 'mirroring
     plane', which should be a plane object, so my comments below make more sense.
    
     Note: Mirroring is a somewhat an ambiguous term, I will use reflect/reflection
      for the rest of the script.
    """
    
    # The row vectors of an identity matrix, where rows define the axis
    IDENTITY_VECTORS = [c4d.Vector(1., 0., 0.),
                        c4d.Vector(0., 1., 0.),
                        c4d.Vector(1., 0., 1.)]
    
    
    def filter_dot_products(filter, p, others, absolute=True):
        """Computes the dot product for a vector and a list of vectors. Returns the
         index of the first element in others, where the dot product satisfies the
         filter.
    
        Args:
            filter (callable): The filter to apply. The signature is expected to be
             filter(values: list[float]) -> index_in_values: int.
            p (c4d.Vector): The first operand for the dot products. 
            others (list[c4d.Vector]): The second operands for the dot products.
            absolute (bool, optional): If the absolute value of the dot products
             should be taken.
    
        Returns:
            int: The index of the first element in others, where the dot product
             satisfies the filter. 
        """
        dot_products = ([abs(p.Dot(q)) for q in others] if absolute else
                        [p.Dot(q) for q in others])
        filter_index = filter(dot_products)
        return dot_products.index(filter_index)
    
    
    def get_reflection_plane(p):
        """ Returns the plane of the frame for a given point, that is 'most
        orthogonal' to the given point. Will favor the planes in the order
        YZ, XZ, XY if the angles are equal.
    
        Args:
            p (c4d.Vector): The point to get the orthogonal plane for.
    
        Returns:
            int: The plane the point is most orthogonal to, defined as
            YZ == 0, XZ == 1, XY == 2.
        """
        p = p.GetNormalized()
        # We calculate the dot product for p and the normal of each plane and then
        # find the index of the maximum value of the absolute of these dot
        # products, i.e. the plane, where the normal of the plane is 'most
        # (anti-)parallel' to p.
        index = filter_dot_products(
            filter=max, p=p, others=IDENTITY_VECTORS, absolute=True)
        return index
    
    
    def get_reflected_matrix(m, n, plane, offset=None):
        """Returns a reflection of the matrix M on the plane P of the matrix N.
         Does not include the reflection of the translation component.
    
        Args:
            m (c4d.Matrix): The matrix M to reflect.
            n (c4d.Matrix): The matrix N of the reflection plane.
            plane (int): The plane P of reflection in N.
            offset (c4d.Vector, optional): The offset of the resulting matrix, if
             None, the offset of M will be used.
    
        Returns:
            c4d.Matrix: The reflected matrix.
        """
        # Overwrite the offset with the offset of M if offset is None.
        if offset is None:
            offset = m.off
        # A matrix with the orientation of M and at the position N
        hlp = c4d.Matrix(n.off, m.v1, m.v2, m.v3)
        # The vectors of the identity matrix in the frame of hlp, expressed
        # in the world frame.
        axis = [hlp * v for v in IDENTITY_VECTORS]
        # Get the axis of M, where the absolute value of the dot product of the
        # identity vector of the axis (p) and the normal of the reflection plane 
        # (axis) is maximized, i.e. the axis which is 'most (anti-)parallel' to 
        # the normal of the reflection plane.
        p = IDENTITY_VECTORS[plane]
        axis_index = filter_dot_products(
            filter=max, p=p, others=axis, absolute=True)
    
        # Invert the selected axis and construct the rest of the matrix for it.
        # We construct the matrix with the cross product of a component of the
        # original matrix to match its banking.
        if axis_index == 0:
            # The inverted axis
            x_comp = -m.v1
            # Take the cross product with one of the components of the original
            # matrix that are not (anti-)parallel to the first new component as
            # our second component.
            y_comp = x_comp.Cross(m.v3).GetNormalized()
            # Construct the remaining component as the cross product of the two
            # existing components of our new matrix.
            z_comp = x_comp.Cross(y_comp).GetNormalized()
        elif axis_index == 1:
            y_comp = -m.v2
            x_comp = y_comp.Cross(m.v3).GetNormalized()
            z_comp = y_comp.Cross(x_comp).GetNormalized()
        elif axis_index == 2:
            z_comp = -m.v3
            x_comp = z_comp.Cross(m.v2).GetNormalized()
            y_comp = z_comp.Cross(x_comp).GetNormalized()
    
        return c4d.Matrix(offset, x_comp, y_comp, z_comp)
    
    
    def main():
        # Get the two objects, BaseDocument.SearchObject() should not be used
        # in a production environment.
        obj_to_mirror = doc.SearchObject("obj")
        obj_mirror_plane = doc.SearchObject("mirror")
    
        # Get out if the objects cannot be found.
        if not obj_to_mirror or not obj_mirror_plane:
            return
    
        # Get the global matrices of both objects
        mg_obj = obj_to_mirror.GetMg()
        mg_pln = obj_mirror_plane.GetMg()
    
        # Get the position of "obj" in the frame of reference (transform matrix) of
        # the 'mirror' object.
        pl = ~mg_pln * mg_obj.off
        # Since it does not really make sense to work with a predefined plane of
        # reflection in this example, I implemented the suggested approach using
        # the dot product.
        # Get the best fitting reflection plane
        refl_plane = get_reflection_plane(p=pl)
        # Invert the axis
        if refl_plane == 0:
            pl.x *= -1.
        elif refl_plane == 1:
            pl.y *= -1.
        elif refl_plane == 2:
            pl.z *= -1.
        # Convert our point back into the global frame
        pg = mg_pln * pl
        # Reflect the matrix of our object.
        mg_new = get_reflected_matrix(
            m=mg_obj, n=mg_pln, plane=refl_plane, offset=pg)
        obj_to_mirror.SetMg(mg_new)
        c4d.EventAdd()
    
    
    if __name__ == '__main__':
        main()
    

    Cheers
    zipit

    [1] https://en.wikipedia.org/wiki/Reflection_(mathematics)



  • @zipit Thank you. That'll help me.