Skip to main content

https://support.zemax.com/hc/en-us/articles/1500005576822-Rotation-Matrix-and-Tilt-About-X-Y-Z-in-OpticStudio is misleading.

OpticStudio uses degrees for Tilt About X/Y/Z, and this graphic suggests using theta_x = tilt about x. Tilt about X is in degrees, but theta_x should be in radians. If you do as the graphic suggests and plug in degrees, you'll get the wrong rotation matrix.

Conversion

Please let me know if I'm wrong here.

Hi Chris,



Thank you for your post!



The argument of a Cosine or Sine function is an angle that can be expressed either in degrees or radians. In the rotation matrix you quoted above from the Knowledgebase article, the Cosine and Sine functions use angle in degrees, which is why it says Theta_x = Tilt about x, which also uses angle in degrees.



One way to confirm this is to create a randomly tilted system, compute the rotation matrix using this quoted expression (angles in degrees), and then compare it with the Rotation Matrix listed in the Prescription data. I think they should match.





Please feel free to let us know if you have any questions.



Best regards,



Hui  


I implemented that transformation in Python. Here it is, with my correction commented out:





def zos_tilts_to_rotation_matrix(tilt_about_x: float, tilt_about_y: float, tilt_about_z: float) -> np.ndarray:

# from https://my.zemax.com/en-US/Knowledge-Base/kb-article/?ka=KA-01638#Tilt%20About%20X/Y/Z

# tilt_about_x *= np.pi / 180

# tilt_about_y *= np.pi / 180

# tilt_about_z *= np.pi / 180

cos_x, cos_y, cos_z = np.cos([tilt_about_x, tilt_about_y, tilt_about_z])

sin_x, sin_y, sin_z = np.sin([tilt_about_x, tilt_about_y, tilt_about_z])

return np.asarray([

[cos_y * cos_z, -cos_y * sin_z, sin_y],

[cos_x * sin_z + sin_x * sin_y * cos_z, cos_x * cos_z - sin_x * sin_y * sin_z, -sin_x * cos_y],

[sin_x * sin_z - cos_x * sin_y * cos_z, sin_x * cos_z + cos_x * sin_y * sin_z, cos_x * cos_y],

])


Running that with 





zos_tilts_to_rotation_matrix(90, 0, 0)


Results in 



[[ 1.         -0.          0.        ]


 [ 0.         -0.44807362 -0.89399666]


 [ 0.          0.89399666 -0.44807362]]



Which is incorrect. If I convert to radians first (uncommenting the conversion in the original function), I get:





[[ 1.000000e+00 -0.000000e+00  0.000000e+00]


 [ 0.000000e+00  6.123234e-17 -1.000000e+00]


 [ 0.000000e+00  1.000000e+00  6.123234e-17]]






Which is correct, confirming that the conversion requires radians, right? My implementation matches https://www.andre-gaschler.com/rotationconverter/ results, FWIW.


Hi Chris,



I'm not sure what's happened with your script, but we can verify in that example that the correct answers come from using degrees. The example 90,0,0 is pretty easy, because cos 0 = 1 and sin 0 = 0 in either measure. It's only the 90s that will change anything. In row 2 column 3, for example, the formula is -sin(thetaX)*cos(thetaY), or -sin(90)cos(0) = -1*1 = -1 if done in degrees. This is shown in the second matrix answer. If I use radian mode, cos(0) is still 1 but sin(90) is .893997, so -sin90cos0 is -.893997. Somehow your Python script has flipped them, but by inspection we can see that using degrees works. Other matrix entries will show the same.



The greater issue is that the article doesn't specify degree or radians, nor is one needed. As long as the angles are input in the same units that the trig functions are expecting , the matrix will come out the same. Even if the code converted angles to radians internally (which is a likely possibility), it then uses trig functions designed for radians. The math in the matrix formula for the rotation matrix can work either way.



Best,



Kevin


On the same page referenced above, 

https://support.zemax.com/hc/en-us/articles/1500005576822-Rotation-Matrix-and-Tilt-About-X-Y-Z-in-OpticStudio

in the section “From given vector (L,M,N) to Tilt About X/Y/Z”,

I believe the extrinsic calculations below:

  • Then we can get Tilt About Z/Y as below.
  • Tilt About Z= ATAN2(M,L)
  • Tilt About Y = ACOS(N)

have an error and the second line should instead read:

  • Tilt About Z= ATAN2(L,M)

I needed to implement both the extrinsic formulas and also the intrinsic formulas earlier in the same section. The intrinsic worked no problem (had actually derived this independently long ago with the same results), and I was only able to get the extrinsic formula shown to work after swapping L and M. A bit more derivation in that section would have been helpful for figuring this out. 

Great article otherwise!

 


From the knowledgebase article “However, right now, there is no single operand to give Tilt About X/Y/Z for arbitrary surfaces.”

Can we get an operand to provide the tilt values? I just wrote a ZPL macro to do the ATAN2 calculations from rotation matrix values, then discovered this article provided something similar. It seems like a lot of effort for the user, just to get a number that appears in the Prescription Report. 


Reply