I want to get the same results mirroring two objects with either matching or different axes. For my examples, I have two objects,

In the first example, I have two objects whose axes are oriented differently (e.g. **+X** & **-X** on Z). I'm going to manipulate the transformations of *R_Cone*. I want to transfer this rotation to *L_Cone* so that it's mirrored on the ZY plane.

How can I code this transfer so that it gets the same result, regardless of the axis offset? In the first example with the different orientations, the position values are negated and the scale/rotation values are the same so it'd be as simple as:

```
lCone[c4d.ID_BASEOBJECT_ABS_POSITION] = -rCone[c4d.ID_BASEOBJECT_ABS_POSITION]
lCone[c4d.ID_BASEOBJECT_ABS_SCALE] = rCone[c4d.ID_BASEOBJECT_ABS_SCALE]
lcone[c4d.ID_BASEOBJECT_ABS_ROTATION] = rCone[c4d.ID_BASEOBJECT_ABS_ROTATION]
```

In the second example, however, that code wouldn't work because pos.X, rot.H, & rot.B are negated.

I thought I could get the difference between the Cones' Parents' Matrices and add that to the new values but it didn't work.

```
lMat = coneL.GetUpMg() # L_Cone's Parent Matrix
rMat = coneR.GetUpMg() # R_Cone's Parent Matrix
offsetMat = lMat-rMat # the offset between the two parents
coneL.SetMg(coneR.GetMg()+offsetMat) # the offset Matrix added to the R_Cone's Matrix
```

Could anyone please shed light on why this isn't working or how to do this another way?

Thank you!

]]>I am happy that I could help. It is very kind of you that you want to express your gratitude, but not necessary.

Happy coding and rendering,

zipit

I think I'm going to have to go the options way. The idea I had was the one I explained above: getting the axes' differences and applying them to the `target_adjustment_rotation`

. I might be able to make it work...let's see.

The Quaternion method you described sounded promising but I wouldn't know how to do it based on your explanation.

Can I connect with you somehow off of the forum? I'd like to send you something as a token of my gratitude for your help.

]]>`inverted_result_axis`

is similar to the xy, xz, and yz option in Cinema's tool and related to what we disused regarding left-handed and right-handed matrices. Reflecting a left-handed matrix (i.e. a Cinema matrix) will always give you a right-handed matrix. To make that result conform with Cinema's matrix orientation again, you will have to flip one axis of the result.

Actually you do not Because like I have shown in my first script and mentioned in the post before, you can feed a right-handed frame into a `c4d.Matrix`

constructor (or modify an existing matrix in such way). Cinema will then just silently flip some random axis in your matrix to make it conformant again (which is both a terrible workflow and API design IMHO).

And I agree, the whole process is rather bloated regarding its options. You could technically remove the flipping option and either flip a fixed axis or leave the choice to Cinema, if you do not care about the orientation of the object. The *from-to* approach would not reduce the number of options, but replace the degree field by another drop-down selection menu. The idea behind this approach is to let the user select an axis in the source and then select an axis in the target, to which the axis in source should be rotated. This would imply the transform between both frames.

The only way to cut down on options is , like it is almost always the case, to make your code smarter, i.e. go the route of what I did refer to as the hard way. One way to do this could be to try to choose or compute a characteristic vector for your internal point data (i.e. the vertices attached to it) for both objects and then construct a quaternion for each object with these vectors and common arbitrary vector (does not really matter what vector). With that you could rotate the objects in and out an identical neutral orientation (for the lack of a better description). But that is only a rough outline, I would have actually try this myself and I am not even sure if this will work.

Cheers,

zipit

`inverted_result_axis`

,`target_adjustment_axis`

,`target_adjustment_rotation`

and it's working. My concern now is that it's confusing to the user on how to get the desired output. I don't understand it all myself.
Could you please help me to understand what *Inverted Result Axis* means? In your code it says:

```
#inverted_result_axis (int): The axis to adjust in a reflected frame to make it conform with Cinema's left-handed matrices.
```

What would cause the need to change the *Inverted Result Axis* value? If there's a way to determine this value based on reflection axis, I'd rather not surface this as an option to the user. I noticed when the axes were the same, on opposite sides of the ZY plane, *Inverted Result Axis* was X, and in my examples where *Target Adjustment Axis* was Y, the *Inverted Result Axis* was Y.

Finally, if I'm able to reduce these options with the *from-to*-axis pair option, I'd be very interested if you would explain. As mentioned above, the pseudo code did not make sense to me enough to where I could build out the other `user_choice`

options. Could you please explain the *from-to*-axis pair options and how this would work with your testing-the-script example?

You're right: the objects wouldn't be topologically aligned.

I will have a look into the math you provided here, but I don't understand what the other user choice options would be or what determines an object as being "x_source" or "y_target." I couldn't get it working.

In the meantime, I'm going to look into the solutions you proposed in your testing-the-script.c4d file again. Thank you

]]>there are some fundamental problems with your code, but when I am trying to understand the intention of that code, you also seem to have overlooked the major prerequisite of your approach - It would require both objects to be topologically aligned. That was not the case in any of your example files.

Imagine an object *"source"* that you have just duplicated (*"target"*), so that *target* "sits in the same place" as *source*. If you now would rotate the frame of *target* with Cinema's axis-mode thingy, then its points would occupy the same coordinates in world space as before, but their local coordinates would be different. You could simply compute the transform between the two frames then by `correction = ~source.GetMg() * target.GetMg() `

. If however also *target* itself had been rotated (like it was the case in your files), then you cannot do this anymore, because there are now two sources of information that have been mixed: The orientation of the frame in respect to its vertices and the rotation of the frame in respect to the *source*. We do not have any clue how to untangle that (or more precisely - it is not so easy).

If you do not want to to dial in an angle, but also cannot guarantee that the objects are topologically aligned, you could also compute the correction transform by letting the user choose a *from-to*-axis pair. In pseudo code (i.e. I have written this on my iPad):

```
frm_source, frm_target = source.GetMg(), target.GetMg()
# Rotate the source x-axis to the y-target axis.
if user_choice is "x_source to y_target":
# The two axis in question
a, b = frm_source.v1, frm_target.v2
# The normal to both axis, which is the axis of rotation. We take
# the cross product and normalize it (normalization is technically
# not necessary, but better safe than sorry ;)
nrm = ~(a % b)
# The angle between both axis.
theta = math.acos(~a * ~b)
# We could construct the transform/matrix with that ourselves, but
# why should we when Cinema has Quaternions for us.
quat = c4d.Quaternion()
quat.SetAxis(nrm, theta)
# Get the rotation matrix for that Quaternion.
transform = quat.GetMatrix()
```

Cheers,

zipit

I have considered allowing the user to choose how the axes are different themselves as you have in your example, but I really want to see if calculating the delta is possible first. The reason is that there could be *many* left & right object pairs to be mirrored that have different axis orientations. Can you think of any way to do this?

For example, this is what I have tried:

*L_Cube*'s, local rotation is (45°, 0°, 0°). Save this matrix, reset the rotation to (0°,0°,0°) locally, and get the global rotation (-90°, 180°, 0°). We save this matrix and reset back to (45°, 0°, 0°).*R_Cube*'s, local rotation is (0°, 45°, 0°). Save this matrix, reset the rotation to (0°,0°,0°) locally, and get the global rotation (90°, 0°, 0°). We save this matrix and reset back to (0°, 45°, 0°).- Mirror the values using your code
- How can we then apply our global rotations that we got when the objects were reset: (-90°, 180°, 0°) and (90°, 0°, 0°) as corrections respectively?
- In other words, how do we determine the
*inverted_result_axis*and*target_adjustment_rotation*from your code based on these values?

Here is what I have tried:

```
l_diff_x = utils.Rad((-90+90)) #difference between x-axes
l_diff_y = utils.Rad((180-0)) #difference between y-axes
l_diff_z = utils.Rad((0-0)) #difference between z-axes
r_diff_x = utils.Rad((90-90)) #difference between x-axes
r_diff_y = utils.Rad((0-180)) #difference between y-axes
r_diff_z = utils.Rad((0-0)) #difference between z-axes
l_correction = utils.MatrixRotX(l_diff_x)
l_correction = utils.MatrixRotY(l_diff_y)
l_correction = utils.MatrixRotZ(l_diff_z)
r_correction = utils.MatrixRotX(r_diff_x)
r_correction = utils.MatrixRotY(r_diff_y)
r_correction = utils.MatrixRotZ(r_diff_z)
l_cube.SetMg(l_reflection * l_correction)
r_cube.SetMg(r_reflection * r_correction)
```

This makes sense to me but it doesn't work. There is something missing...putting the corrections into the objects' frames, or inverting them. I don't know; I have tried them to no avail.

Thank you!

]]>I have slightly updated my previous example for the case when the two frames are in a simple rotational relation around one of their standard basis vectors. As already stated, a general solution for this problem is much harder to accomplish and also not really a *'math'* problem, but more a conceptual and algorithmic problem.

There are also other ways to calculate the delta between your two frames if you are in the non-general case, which will have different advantages and disadvantages to the solution provided by me, but will also require certain guarantees regarding the source and target object and their frames to work properly (like for example having their vertices occupy the same points in world space).

Cheers,

zipit

When time has permitted we've always tried to be supportive even beyond the scope of the API providing snippets, scripts if not whole plugins. Unfortunately, this is not possible at this time but we'll try to keep this thread on our radars and hopefully to get back on it later.

Best, Riccardo

]]>I am still working on this problem every day and could really use some help before the weekend if it's possible.

I think I could just use help knowing how to get the difference between the axes in their default state. Could you please give me some advice on how to go about this? I am trying to apply the different axes' rotations to the mirrored matrices from @r_gigante 's code.

Can you help me to get the right axis rotation values to apply to the mirrored values? Any help would be huge so I don't spin my wheels on this problem for another weekend. **Thank you!**

I think I have the problem narrowed down to one issue: can you offer any code help for determining & applying the axes' rotational differences to the mirrored values when they are at 0 values in position & rotation please?

]]>With regard on how to take in account the different axis, you can immediately see that for **scenario 1** asking for Ml and Mg for both source and target object will return

```
----------------------------------------------------------------------------------------------------
L_Leg_con+ | L_Leg_zero | Scenario 1 - HPB Z- (works) (global):
Matrix(v1: (1, 0, 0); v2: (0, 1, 0); v3: (0, 0, 1); off: (10, 15.171, 3.922))
R_leg_con+ | R_leg_zero | Scenario 1 - HPB Z- (works) (global):
Matrix(v1: (1, 0, 0); v2: (0, 1, 0); v3: (0, 0, 1); off: (-10, 15.171, 3.922))
----------------------------------------------------------------------------------------------------
```

Given this numbers you can immediately see that the frames are aligned and no changes in the mirroring code should be done

Repeating the same with **scenario 2** will return:

```
----------------------------------------------------------------------------------------------------
L_Leg_con+ | L_Leg_zero | Scenario 2 - XYZ X+ (global):
Matrix(v1: (0, 0, -1); v2: (0, -1, 0); v3: (-1, 0, 0); off: (10, 5.171, 3.922))
R_Leg_con+ | R_Leg_zero | Scenario 2 - XYZ X+ (global):
Matrix(v1: (0, 0, 1); v2: (0, 1, 0); v3: (-1, 0, 0); off: (-10, 5.171, 3.922))
----------------------------------------------------------------------------------------------------
```

and here you see, comparing the global matrices that **Z** and **Y** components are inverted. Thus inverting the **Z** and **Y** components of the mirroring matrices will provide you with the correct transformation.

Finally in **scenario 3** checking again the frames will result in:

```
----------------------------------------------------------------------------------------------------
L_Leg_con+ | L_Leg_zero | Scenario 3 - XYZ Z+/Z- (global):
Matrix(v1: (1, 0, 0); v2: (0, 1, 0); v3: (0, 0, 1); off: (10, -4.829, 3.922))
R_leg_con+ | R_leg_zero | Scenario 3 - XYZ Z+/Z- (global):
Matrix(v1: (-1, 0, 0); v2: (0, 1, 0); v3: (0, 0, -1); off: (-10, -4.829, 3.922))
----------------------------------------------------------------------------------------------------
```

where the **X** and **Z** component are inverted. Thus inverting the **X** and **Z** components of the mirroring matrices will provide you again with the correct transformation.

Again due to time constraint i can't come with full code, but once you got the logic won't be hard to implement it in the code.

Finally consider that in the test project you've shared in the previous post in the **scenario 3** , *L_Leg_con+* and *R_Leg_con+* both had rotation mode set to HPB instead of XYZ

Best, R

]]>There is a major part of my issue that I'm still unsure how to resolve, however: even though it is useful to switch the mirroring plane to ZY, XY, XZ, I'm not able to mirror objects whose axes are inverted. This is common with character rigs so I need to account for it. I mention the Mirror Tool because it has Axes controls. I don't know how to do the matrix transformations for the differing axes along with the Mirror Plane and could * really* use your help.

While the original code works when the axes are the same, using the mirroring technique with the provided code flips the objects in Y and continually adds to their P rotation values. The rig is using X+ controls with XYZ rotation order. I've included these controls as Scenario 2 in the attached scene file, along with other axis orientations & rotation orders one might see in a rig:

Mirroring Different Axes.c4d

How can I account for the rotated axes? Thank you.

]]>With regard to the Mirror Tool's implementation it's not rocket science because the foundation of the tool have been properly explained by @zipit with regard to reflecting a transformation matrix on main axes, in case of XY, XZ, YZ mirroring and with the vertexes being actually displaced in the "mirrored" position in case

That said a very simplified version in Python could be based on the following matrix changes:

```
...
...
if flip == "XY":
targetMG.v1 = c4d.Vector( sourceMG.v1.x, sourceMG.v1.y, -sourceMG.v1.z)
targetMG.v2 = c4d.Vector( sourceMG.v2.x, sourceMG.v2.y, -sourceMG.v2.z)
targetMG.v3 = c4d.Vector(-sourceMG.v3.x, -sourceMG.v3.y, sourceMG.v3.z)
targetMG.off = c4d.Vector(sourceMG.off.x, sourceMG.off.y, -sourceMG.off.z)
elif flip == "YZ":
targetMG.v1 = c4d.Vector( sourceMG.v1.x, -sourceMG.v1.y, -sourceMG.v1.z)
targetMG.v2 = c4d.Vector(-sourceMG.v2.x, sourceMG.v2.y, sourceMG.v2.z)
targetMG.v3 = c4d.Vector(-sourceMG.v3.x, sourceMG.v3.y, sourceMG.v3.z)
targetMG.off = c4d.Vector(-sourceMG.off.x, sourceMG.off.y, sourceMG.off.z)
elif flip == "ZX":
targetMG.v1 = c4d.Vector(-sourceMG.v1.x, sourceMG.v1.y, -sourceMG.v1.z)
targetMG.v2 = c4d.Vector( sourceMG.v2.x, -sourceMG.v2.y, sourceMG.v2.z)
targetMG.v3 = c4d.Vector( sourceMG.v3.x, -sourceMG.v3.y, sourceMG.v3.z)
targetMG.off = c4d.Vector(sourceMG.off.x, -sourceMG.off.y, sourceMG.off.z)
...
...
```

Cheers

]]>I just had a quick look at both the online documentation and your file. The online documentation for the mirror tool has this image (I hope it is okay that I hotlink it here) for the dropdown in question.

The option *None* would be what we discussed under *reflecting the vertices*, only that they here also shove the frame a bit around so that it sits where the reflected frame would sit, they do not touch the orientation at all and simply reflect the internal point data. The option *Rotate* is something that does not really matter here I think. And the options *XY, XZ* and *YZ* are exactly that what we did discuss regarding left-handed matrices. In the *XY* example you can see that for a truly mirrored frame, the z-axis should point downwards on the right side or upwards on the left side. But since Cinema only has left handed matrices, one axis has to be flipped and Cinema leaves the decision which axis to flip to the user via that option.

But in your file in the *not working* part of the hierarchy you have an target object with a different frame orientation than the source object. This is a different problem than addressed by the mirror tool, I think. We could differentiate two types here:

- The frame orientations are different, but each component appears in the other frame as another axis or the inverse of another axis (e.g. the x axis in one frame is the inverse of the y axis in the other) . The
*other*frame would be here the result of rotating the primary frame by 90° around one of its axis or multiple of these operations. This could be relatively easily solved by swizzling the components of the reflected frame before assigning the result to the target object. The most straight forward option would be to leave the decision which planes/axis to swizzle to the user via the interface, because trying to detect the*swizzle relation*automatically would let you face the same problem as described in the next point under*the hard way*. - The frame orientations are truly arbitrary and in no hidden predefined relation to each other. This seems to be the case in your file, at least the parent null-object of target object is in such orientation. This is either an annoying or a hard problem to solve and if I am not mistaken, Cinema offers no solution for the hard way in its toolset.

*The annoying way* would be reflecting the internal point data of the object. While this is trivial for a bunch of vertices, it can become tedious for more complex structures, because if you reflect vertices that have polygons, you will also have to flip the vertex index order in each polygon, i.e. flip the normals of the polygons, because reflecting the vertices flipped the point normals. And the list goes on: you might have to handle uvw and weight information, untangle hierarchical information like in your example, etc. pp.

*The hard way* would be analysing the topology of the object so that one can place the target object in a way that it looks like it is reflected, but its frame is no reflection relation to the source object and you also do not touch the internal (point) data. I will leave it at that for now.

So knowing the problem in all its nasty requirements: I would say there is no easy way, or at least I do not see one.

Cheers,

zipit

I am not at home anymore, so I cannot look at your file right now, but your explanation was indeed a bit enlightening especially regarding "*In some cases, their axes will match. In others they might differ, so I want to account for this.*" part. As I already stated in my first post, it confused the heck out of me that the axes were all over the place in your pictures in the first posting.

If you have two objects which are topologically identical, but have a different frame and you want to "reflect" one object in respect to the other and a reflection plane, you would first have to compute a transform which transforms the frame of the one object to the other in respect to their topology. Cannot say much more without having a better look at the problem and your file myself.

I will probably take a look at your file tomorrow and I will probably also have to take a look at the character mirror tool, because I am completely clueless when it comes to rigging. Maybe MAXON will chime in and tell you what the character tool does under the hood or someone else already knows this specific problem.

Cheers,

zipit

I am so grateful for you taking the time to put this together, thank you! It is really cool that you can rotate the mirror plane. I never expected that, but it's a cool feature. The script I wrote wasn't for fun, but in response to when you wrote 'evaluate the angle between each axis and a normal of the reflection plane to determine which axis would be the least painful to invert.' I didn't fully understand the math, I was just trying to follow my understanding of your advice.

Your example works great with the second scenario from my original post where the axes are the same, but when I rotate the axis of the "source (transform me)" cone (mimicking the first scenario from my original post), it doesn't work anymore because, I believe, it's rotating the axis of the target. I do not wish to affect the axes in any way, but rather position & rotate the objects into the mirrored state. Being able to do this with different axes orientations is essentially what I am seeking.

In response to the comment in your code "(I am still not quite sure what you exactly want to do)", I am sorry if I have not made my intentions clear enough. I will try to explain once more in different terms, and please let me know if anything is unclear. Thank you for your patience and perseverance:

- This script will mirror two objects, identical to behavior that can be found with Cinema 4D's Mirror Tool (Character > Mirror Tool). In some cases, their axes will match. In others they might differ, so I want to account for this. Cinema 4D's Mirror Tool has an Axes dropdown. I'd be okay with an option like this, but would prefer to guess based on the difference of the objects' parents' axes. This is where I could really use help with the 3D Math behind this.
- I am not looking for a Python Tag that evaluates every frame, but rather something that can be run once to mirror a character rig pose.
- Both objects will have parent objects that reset their local transform values to zero. I mention this in case local or parent values would work better in repositioning & reorienting the objects correctly.

Here is the demo file you sent me with the other scenario for which I need to account (the objects are also parented under nulls):

frame_reflection_2.c4d

I need to create a programmatic solution for this as I cannot use the Mirror Tool for multiple objects. It only accepts one Source and Target object at a time and I will eventually use this for multiple control pairs. Right now, in the case of this forum example, I'd just like to get this behavior working with one pair of objects at a time (once with matching axes and once with different axes).

Please let me know if you need me to explain anything any more.

I am truly grateful for your help!

]]>I probably should have not mentioned that stuff, because it obviously side tracked you, what was not my intention. I suppose you wrote this code just for fun, which is cool, Trigonometry and Linear Algebra can be fun, but it has little to do with your problem. So I wrote a little snippet which should bring you back on track. I also created a little file [1] which demonstrates said snippet, because the setup is a bit complicated. Below you will also find the code in case you run into problems with the file (which was created with an educational license).

And finally two cents about your code. You got the major gist of the dot product, but normally you simply compute the unit vectors before computing the dot product if you are interested in the angle these vectors span. Your solution of adjusting the dot product afterwards is not wrong, but a bit convoluted in my opinion. You usually also do not compute the inverse cosine of a dot product if you are only interested in their angular relation and not the specific angle they span (it just is an unnecessary and relatively expensive computation) . For unit vectors the dot product already has very nice properties. Parallel unit vectors will have a dot product of `1`

, anti-parallel (parallel, but pointing in different directions) unit vectors a dot product of `-1`

and orthogonal unit vectors a dot product of `0`

.

Cheers,

zipit

```
""" Example for reflecting a frame (matrix).
This example will roughly do what you want (I am still not quite sure what
you exactly want to do). For this example to work, you will need.
1. A reflection plane. Any object will do, we just need the matrix.
2. An object to reflect, referred to in this script as "source".
3. An "reflected object", i.e. something to write the result to, referred
to in this script as "target".
4. A Python tag to put this script into.
The Python tag will need four user data elements:
ID 1 - A link to the reflection plane
ID 2 - A link to the source object
ID 3 - A link to the target object
ID 4 - The reflection axis vector. This should be a vector clamped to
the interval [-1, 1] with a step of 2 (i.e. it can only take
on the values -1 and 1 on each component).
I did not address any shenanigans regarding how Cinema interprets the
computed frame when constructing a matrix. This would be another topic.
"""
import c4d
def reflect_frame(source, plane, axis):
""" Reflects a frame on a reflection plane.
Reflects the frame "source" on the reflection plane defined by the
frame "plane" on the axis defined by the vector "axis."
Args:
source (c4d.Matrix): The frame to reflect.
plane (c4d.Matrix): The frame of the reflection plane.
axis (c4d.Vector): The reflection axis. See the code for details.
Returns:
c4d.Matrix: The reflected frame.
"""
# We do not want the reflection plane to apply any translation
# or scaling, i.e. we are only interested in its normalized
# orientation. So we remove anything except the orientation information.
# I took care in the Cinema file that this can not happen in the first
# place, so this is kind of redundant here.
plane.off = c4d.Vector()
plane.v1 = plane.v1.GetNormalized()
plane.v2 = plane.v2.GetNormalized()
plane.v3 = plane.v3.GetNormalized()
# Now we express the offset in the frame of the reflection plane.
pl = source.off * ~plane
# Now we express the three axis of our source as global points (e.g.
# source.off + source.v1) and then express these global points in the
# frame of our reflection plane.
xl = (source.off + source.v1) * ~plane
yl = (source.off + source.v2) * ~plane
zl = (source.off + source.v3) * ~plane
# Now we invert the component(s) we want to reflect by multiplying our
# points expressed in the plane of reflection componentwise by the
# reflection axis - which I did express as a signed vector
# (e.g.: (10, 10, 10) ^ (-1, 1, 1) = (-10, 10, 10) - a reflection on
# the x-axis). After that we express these points in the world frame
# again. These four components are now almost our finished frame.
# The offset, this is the final value.
off = (pl ^ axis) * plane
# The three axis, which are still points in world space.
x = (xl ^ axis) * plane
y = (yl ^ axis) * plane
z = (zl ^ axis) * plane
# Finally we localize our axis components again in respect to their
# origin and normalize them and reinstate their original scaling.
x = (x - off).GetNormalized() * source.v1.GetLength()
y = (y - off).GetNormalized() * source.v2.GetLength()
z = (z - off).GetNormalized() * source.v3.GetLength()
# The final frame converted into a Cinema matrix.
mg = c4d.Matrix(off, x, y, z)
return mg
def main():
"""Entry point.
"""
# The input user data.
plane = op[c4d.ID_USERDATA, 1]
source = op[c4d.ID_USERDATA, 2]
target = op[c4d.ID_USERDATA, 3]
axis = op[c4d.ID_USERDATA, 4]
# The user data are not fully populated.
if None in (plane, source, target, axis):
raise ValueError("User data input values are not fully populated.")
# Compute the reflected frame.
mg = reflect_frame(source.GetMg(), plane.GetMg(), axis)
# Set the world matrix of the target object.
target.SetMg(mg)
```

[1] The file: frame_reflection.c4d

]]>I'm sure it's more of a case that I don't know my linear algebra well enough I've taken some online courses in it this weekend to get a better understanding of the dot & cross product. I do understand the difference between left & right-handed matrices after reading your description and looking more into them, thank you.

You were right about the result of my second example image being off: there was an error in my the animation I created. *L_Cone* was using the axis of the first example. I have fixed it and replaced it in the original post. Sorry about that and very perceptive of you to notice! I appreciate your attention to my issue.

Regarding your last message's #7, I do care about the axes because I am using this with animation controllers that drive joints.

I came up with some code based on what I think you're describing with 'evaluate the angle between each axis and a normal of the reflection plane'. Am I right in using the objects' position in relation to the mirror plane's normal value? I got the same result for both axis orientation scenarios, so I'm not sure this is correct. Perhaps I should be evaluating their Matrices' v1, v2, & v3 vectors this way as well?

```
import c4d, math
from c4d import utils
mirrorPlane = c4d.Vector(1,0,0) #ZY's normal
rObj = doc.SearchObject("R_Cone").GetMg().off
lObj = doc.SearchObject("L_Cone").GetMg().off
mirNormMag = mirrorPlane.GetLength() #magnitude of the mirror plane's vector
rDotProd = mirrorPlane.Dot(rObj) #dot product of the mirror plane & right object
rMag = rObj.GetLength() #magnitude of the right object's position vector
rTheta = math.acos( rDotProd / (mirNormMag * rMag) ) #angle between two the two vectors
lDotProd = mirrorPlane.Dot(lObj) #dot product of the mirror plane & left object
lMag = lObj.GetLength() #magnitude of the left object's position vector
lTheta = math.acos( lDotProd / (mirNormMag * lMag) ) #angle between two the two vectors
# This is the result for both examples.
print "rTheta, rad: %s, deg: %s"%(rTheta,utils.RadToDeg(rTheta))
# rTheta, rad: 3.14159265359, deg: 180.0
print "lTheta, rad: %s, deg: %s"%(lTheta,utils.RadToDeg(lTheta))
# lTheta, rad: 0.0, deg: 0.0
```

]]>sorry, I was probably a bit too abstract.

- I was only describing one method (two if you count my last sentence as a "method"). What you refer to as method two, is just a way to deal with problems that come with general approach. While you can certainly construct the reflected frame in different ways, the general logic to achieve that construction is always the same.
- I might be wrong, but I think I haven't gotten my main point across (third paragraph about the general insight). The concept of left and right handed matrices is closely related to right-hand-rule of the cross product. Take your left or right hand and hold it in front of your body, with the thumb pointing up, the index finger pointing forward and your ring finger pointing along the plane parallel to your chest, forming a little axis gizmo. Now imagine a plane going through your nose, dividing your body into a left and right part and reflect that frame you are holding with the same hand along that plane. You will probably realise that this is impossible without breaking a few bones in your hand and that the only way to do this is using your other hand. This is what left and right handed matrices describe and the fundamental problem here.
- Since we cannot just use "the other hand" in Cinema, we will invert one axis in the frame result of our general approach in order to make the frame conform with the rules that Cinema does enforce on its frames (being left-handed). What you refer to as the second approach, was just a very broad assumption on how to find the axis that is probably the least "wrong looking" if being inverted. If you want to do something like this, you also have normally additional information on what you want to preserve and what not. If you are mirroring bones for example, you certainly do not want to flip the bone along its length so that you only have to choose in a more or less smart way from the remaining two axis.
- I was referring to the y-axis of the frame of your cone. Your pictures are a bit unfortunate, because the orientation of the frame in relation to your vertices changes (before the reflection the z-axis is pointing along the height of the cone and after the mirroring it is pointing along the direction which previously was the x-axis). But nevertheless the y-axis is pointing upwards in the "before" and downwards in the "after", but for a human this is all the same, since a cone is symmetrical to the plane the y-axis is a normal of.
- The analysing is not set in stone, you would have to define what you want to do. I was implying to evaluate the angle between each axis and a normal of the reflection plane to determine which axis would be the least painful to invert. But again, this is neither set in stone nor really helpful.
- Mirroring the vertices would be pretty much the same. Create a frame for your reflection plane, transform your vertices into that frame, reflect the vertices and transform back.
- I cannot really choose a way, since I do not know what you need. If you do not care about the frame of the reflected object, I probably would go for reflecting the vertices (you could also use
`SendModellingCommand`

for that). If you do care about the frames, this would be pure speculation on my side. You would have to explain what you want to do practically.

Cheers,

zipit

Thank you very much for the reply. My apologies, but I could use some help with where to start on your advice. In the first approach you mentioned (the enumerated one), which picture did you mean when you said 'in the case of the y-axis in your picture'? You said the first approach is unreliable because of Cinema 4D's left-handed matrices. If this approach wouldn't work for both scenarios and will product incorrect results in some cases, I do not wish to pursue it.

For the second approach, you mentioned determining a most-parallel plane of reflection by the input matrix. Which matrix did you mean by the input matrix: the target's matrix (*R_Cone* in my example)? How would I go about analyzing the input matrix?

Finally, you mentioned the only faithful way to reflect a point object is to reflect each point individually. If that's the case, I'd be most interested in pursuing this approach. How would I go about this with the scenarios above (with *R_Cone* for *L_Cone*)? Not all of my objects will be point objects and, even if the points are flipped correctly, returning the offset to the axes would be necessary as well.

For a little more clarity, which of these approaches would you choose for this task?

Thanks!

]]>