/* matrix3d.java History: 29 May 2006 Remove set(). Add arot(). Four assigns to a line is clearer in a lot of places. Put "m_" prefix on all member variables. Use Math.PI instead of pi. Add more comments. Don't bother with intermediate "l" variables in transform(). 26 May 2006 Add set() and supporting constants. */ /** Adapted from the JDK 1.1.1 wireframe example where it is characterized as "a fairly conventional 3D matrix object that can transform sets of 3D points and perform a variety of manipulations on the transform." With the arrangement of the matrix shown at the beginning of the class, the transform can be thought of as a matrix multiplication being applied to a 3D column vector (augmented by 1.0 in the fourth position) on the right. All the manipulations below can be thought of as post-transformation where the resulting transform is as if the additional transformation is applied *after* the existing one is applied to the column vector. The original documentation referred to this as right-hand side, but I find it clearer to call it left-hand side, so I'm not going to mention sides at all. */ public class matrix3d { float m_xx, m_xy, m_xz, m_xo; float m_yx, m_yy, m_yz, m_yo; float m_zx, m_zy, m_zz, m_zo; /** Create a new unit matrix */ public matrix3d () { m_xx = 1.0f; m_yy = 1.0f; m_zz = 1.0f; } /** Scale by f in all dimensions */ public void scale(float f) { m_xx *= f; m_xy *= f; m_xz *= f; m_xo *= f; m_yx *= f; m_yy *= f; m_yz *= f; m_yo *= f; m_zx *= f; m_zy *= f; m_zz *= f; m_zo *= f; } /** Scale along each axis independently -- scaling done *after* the original transform is applied. */ public void scale(float xf, float yf, float zf) { m_xx *= xf; m_xy *= xf; m_xz *= xf; m_xo *= xf; m_yx *= yf; m_yy *= yf; m_yz *= yf; m_yo *= yf; m_zx *= zf; m_zy *= zf; m_zz *= zf; m_zo *= zf; } /** Translate the origin */ public void translate(float x, float y, float z) { m_xo += x; m_yo += y; m_zo += z; } /** rotate theta degrees about the x axis -- rotation done *after* the original transform is applied. */ public void xrot(double theta) { // convert angle to radians theta *= (Math.PI / 180); // precompute sin and cos double ct = Math.cos(theta); double st = Math.sin(theta); float Nyx = (float) (m_yx * ct + m_zx * st); float Nyy = (float) (m_yy * ct + m_zy * st); float Nyz = (float) (m_yz * ct + m_zz * st); float Nyo = (float) (m_yo * ct + m_zo * st); float Nzx = (float) (m_zx * ct - m_yx * st); float Nzy = (float) (m_zy * ct - m_yy * st); float Nzz = (float) (m_zz * ct - m_yz * st); float Nzo = (float) (m_zo * ct - m_yo * st); m_yx = Nyx; m_yy = Nyy; m_yz = Nyz; m_yo = Nyo; m_zx = Nzx; m_zy = Nzy; m_zz = Nzz; m_zo = Nzo; } /** rotate theta degrees about the y axis -- rotation done *after* the original transform is applied. */ public void yrot(double theta) { // convert angle to radians theta *= (Math.PI / 180); // precompute sin and cos double ct = Math.cos(theta); double st = Math.sin(theta); float Nxx = (float) (m_xx * ct + m_zx * st); float Nxy = (float) (m_xy * ct + m_zy * st); float Nxz = (float) (m_xz * ct + m_zz * st); float Nxo = (float) (m_xo * ct + m_zo * st); float Nzx = (float) (m_zx * ct - m_xx * st); float Nzy = (float) (m_zy * ct - m_xy * st); float Nzz = (float) (m_zz * ct - m_xz * st); float Nzo = (float) (m_zo * ct - m_xo * st); m_xx = Nxx; m_xy = Nxy; m_xz = Nxz; m_xo = Nxo; m_zx = Nzx; m_zy = Nzy; m_zz = Nzz; m_zo = Nzo; } /** rotate theta degrees about the z axis -- rotation done *after* the original transform is applied. */ public void zrot(double theta) { // convert angle to radians theta *= (Math.PI / 180); // precompute sin and cos double ct = Math.cos(theta); double st = Math.sin(theta); float Nxx = (float) (m_xx * ct - m_yx * st); float Nxy = (float) (m_xy * ct - m_yy * st); float Nxz = (float) (m_xz * ct - m_yz * st); float Nxo = (float) (m_xo * ct - m_yo * st); float Nyx = (float) (m_yx * ct + m_xx * st); float Nyy = (float) (m_yy * ct + m_xy * st); float Nyz = (float) (m_yz * ct + m_xz * st); float Nyo = (float) (m_yo * ct + m_xo * st); m_xx = Nxx; m_xy = Nxy; m_xz = Nxz; m_xo = Nxo; m_yx = Nyx; m_yy = Nyy; m_yz = Nyz; m_yo = Nyo; } /** rotate theta degrees about an arbitrary axis with coordinates (x, y, z) -- rotation done *after* the original transform is applied. */ public void arot(double theta, float x, float y, float z) { // convert angle to radians theta *= (Math.PI / 180); // normalize axis float length = (float)Math.sqrt(x*x + y*y + z*z); if (length == 0.0) return; x /= length; y /= length; z /= length; // precompute sin and cos, x*x, y*y, z*z, x*y, x*z, y*z double ct = Math.cos(theta); double st = Math.sin(theta); float x2 = x*x; float y2 = y*y; float z2 = z*z; float xtimesy = x*y; float xtimesz = x*z; float ytimesz = y*z; // compute transform (D.F. Rogers and J.A. Adams, Mathematical // Elements for Computer Graphics, 1976, Chapter 3) float Axx = (float)(x2 + (1.0f - x2)*ct); float Axy = (float)(xtimesy*(1.0f - ct) - z*st); float Axz = (float)(xtimesz*(1.0f - ct) + y*st); float Ayx = (float)(xtimesy*(1.0f - ct) + z*st); float Ayy = (float)(y2 + (1.0f - y2)*ct); float Ayz = (float)(ytimesz*(1.0f - ct) - x*st); float Azx = (float)(xtimesz*(1.0f - ct) - y*st); float Azy = (float)(ytimesz*(1.0f - ct) + x*st); float Azz = (float)(z2 + (1.0f - z2)*ct); // apply the transform (see mult below) float Nxx = Axx * m_xx + Axy * m_yx + Axz * m_zx; float Nxy = Axx * m_xy + Axy * m_yy + Axz * m_zy; float Nxz = Axx * m_xz + Axy * m_yz + Axz * m_zz; float Nxo = Axx * m_xo + Axy * m_yo + Axz * m_zo; float Nyx = Ayx * m_xx + Ayy * m_yx + Ayz * m_zx; float Nyy = Ayx * m_xy + Ayy * m_yy + Ayz * m_zy; float Nyz = Ayx * m_xz + Ayy * m_yz + Ayz * m_zz; float Nyo = Ayx * m_xo + Ayy * m_yo + Ayz * m_zo; float Nzx = Azx * m_xx + Azy * m_yx + Azz * m_zx; float Nzy = Azx * m_xy + Azy * m_yy + Azz * m_zy; float Nzz = Azx * m_xz + Azy * m_yz + Azz * m_zz; float Nzo = Azx * m_xo + Azy * m_yo + Azz * m_zo; m_xx = Nxx; m_xy = Nxy; m_xz = Nxz; m_xo = Nxo; m_yx = Nyx; m_yy = Nyy; m_yz = Nyz; m_yo = Nyo; m_zx = Nzx; m_zy = Nzy; m_zz = Nzz; m_zo = Nzo; } /** Apply a post transformation -- the resulting transform is as if the new one were applied *after* the existing one. */ public void mult(matrix3d post) { float Nxx = post.m_xx * m_xx + post.m_xy * m_yx + post.m_xz * m_zx; float Nxy = post.m_xx * m_xy + post.m_xy * m_yy + post.m_xz * m_zy; float Nxz = post.m_xx * m_xz + post.m_xy * m_yz + post.m_xz * m_zz; float Nxo = post.m_xx * m_xo + post.m_xy * m_yo + post.m_xz * m_zo + post.m_xo; float Nyx = post.m_yx * m_xx + post.m_yy * m_yx + post.m_yz * m_zx; float Nyy = post.m_yx * m_xy + post.m_yy * m_yy + post.m_yz * m_zy; float Nyz = post.m_yx * m_xz + post.m_yy * m_yz + post.m_yz * m_zz; float Nyo = post.m_yx * m_xo + post.m_yy * m_yo + post.m_yz * m_zo + post.m_yo; float Nzx = post.m_zx * m_xx + post.m_zy * m_yx + post.m_zz * m_zx; float Nzy = post.m_zx * m_xy + post.m_zy * m_yy + post.m_zz * m_zy; float Nzz = post.m_zx * m_xz + post.m_zy * m_yz + post.m_zz * m_zz; float Nzo = post.m_zx * m_xo + post.m_zy * m_yo + post.m_zz * m_zo + post.m_zo; m_xx = Nxx; m_xy = Nxy; m_xz = Nxz; m_xo = Nxo; m_yx = Nyx; m_yy = Nyy; m_yz = Nyz; m_yo = Nyo; m_zx = Nzx; m_zy = Nzy; m_zz = Nzz; m_zo = Nzo; } /** Reinitialize to the unit matrix */ public void unit() { m_xx = 1; m_xy = 0; m_xz = 0; m_xo = 0; m_yx = 0; m_yy = 1; m_yz = 0; m_yo = 0; m_zx = 0; m_zy = 0; m_zz = 1; m_zo = 0; } /** Transform nvert points from v into tv. v contains the input coordinates in floating point. Three successive entries in the array constitute a point. tv ends up holding the transformed points as integers; three successive entries per point. (1, 0, 0) gets mapped to (m_xx + m_xo, m_yx + m_yo, m_zx + m_zo), (0, 1, 0) to (m_xy + m_xo, m_yy + m_yo, m_zy + m_zo) and (0, 0, 1) to (m_xz + m_xo, m_yz + m_yo, m_zz + m_zo). */ public void transform(float v[], int tv[], int nvert) { for (int i = nvert * 3; (i -= 3) >= 0;) { float x = v[i]; float y = v[i + 1]; float z = v[i + 2]; tv[i ] = (int) (x * m_xx + y * m_xy + z * m_xz + m_xo); tv[i + 1] = (int) (x * m_yx + y * m_yy + z * m_yz + m_yo); tv[i + 2] = (int) (x * m_zx + y * m_zy + z * m_zz + m_zo); } } public String toString() { return ("[" + m_xx + "," + m_xy + "," + m_xz + "," + m_xo + ";" + m_yx + "," + m_yy + "," + m_yz + "," + m_yo + ";" + m_zx + "," + m_zy + "," + m_zz + "," + m_zo + "]"); } }