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.
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 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.
Not sure what you mean by "complete" mirroring, but there are two things to keep in mind when mirroring:
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.
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