/* matrix3d.java History: 12 Jul 2006 Use tuple3d in representation. Define transform() for arrays of tuple3ds. 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 { tuple3d m_x, m_y, m_z; float m_xo, m_yo, m_zo; /** Create a new unit matrix */ public matrix3d () { m_x = new tuple3d(1.0f, 0.0f, 0.0f); m_y = new tuple3d(0.0f, 1.0f, 0.0f); m_z = new tuple3d(0.0f, 0.0f, 1.0f); } /** Scale by f in all dimensions */ public void scale(float f) { m_x.scale(f); m_xo *= f; m_y.scale(f); m_yo *= f; m_z.scale(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_x.scale(xf); m_xo *= xf; m_y.scale(yf); m_yo *= yf; m_z.scale(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_y.x() * ct + m_z.x() * st); float Nyy = (float) (m_y.y() * ct + m_z.y() * st); float Nyz = (float) (m_y.z() * ct + m_z.z() * st); float Nyo = (float) (m_yo * ct + m_zo * st); float Nzx = (float) (m_z.x() * ct - m_y.x() * st); float Nzy = (float) (m_z.y() * ct - m_y.y() * st); float Nzz = (float) (m_z.z() * ct - m_y.z() * st); float Nzo = (float) (m_zo * ct - m_yo * st); m_y.set(Nyx, Nyy, Nyz); m_yo = Nyo; m_z.set(Nzx, Nzy, 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_x.x() * ct + m_z.x() * st); float Nxy = (float) (m_x.y() * ct + m_z.y() * st); float Nxz = (float) (m_x.z() * ct + m_z.z() * st); float Nxo = (float) (m_xo * ct + m_zo * st); float Nzx = (float) (m_z.x() * ct - m_x.x() * st); float Nzy = (float) (m_z.y() * ct - m_x.y() * st); float Nzz = (float) (m_z.z() * ct - m_x.z() * st); float Nzo = (float) (m_zo * ct - m_xo * st); m_x.set(Nxx, Nxy, Nxz); m_xo = Nxo; m_z.set(Nzx, Nzy, 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_x.x() * ct - m_y.x() * st); float Nxy = (float) (m_x.y() * ct - m_y.y() * st); float Nxz = (float) (m_x.z() * ct - m_y.z() * st); float Nxo = (float) (m_xo * ct - m_yo * st); float Nyx = (float) (m_y.x() * ct + m_x.x() * st); float Nyy = (float) (m_y.y() * ct + m_x.y() * st); float Nyz = (float) (m_y.z() * ct + m_x.z() * st); float Nyo = (float) (m_yo * ct + m_xo * st); m_x.set(Nxx, Nxy, Nxz); m_xo = Nxo; m_y.set(Nyx, Nyy, 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_x.x() + Axy * m_y.x() + Axz * m_z.x(); float Nxy = Axx * m_x.y() + Axy * m_y.y() + Axz * m_z.y(); float Nxz = Axx * m_x.z() + Axy * m_y.z() + Axz * m_z.z(); float Nxo = Axx * m_xo + Axy * m_yo + Axz * m_zo; float Nyx = Ayx * m_x.x() + Ayy * m_y.x() + Ayz * m_z.x(); float Nyy = Ayx * m_x.y() + Ayy * m_y.y() + Ayz * m_z.y(); float Nyz = Ayx * m_x.z() + Ayy * m_y.z() + Ayz * m_z.z(); float Nyo = Ayx * m_xo + Ayy * m_yo + Ayz * m_zo; float Nzx = Azx * m_x.x() + Azy * m_y.x() + Azz * m_z.x(); float Nzy = Azx * m_x.y() + Azy * m_y.y() + Azz * m_z.y(); float Nzz = Azx * m_x.z() + Azy * m_y.z() + Azz * m_z.z(); float Nzo = Azx * m_xo + Azy * m_yo + Azz * m_zo; m_x.set(Nxx, Nxy, Nxz); m_xo = Nxo; m_y.set(Nyx, Nyy, Nyz); m_yo = Nyo; m_z.set(Nzx, Nzy, 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 m) { float Nxx = m.m_x.x()*m_x.x() + m.m_x.y()*m_y.x() + m.m_x.z()*m_z.x(); float Nxy = m.m_x.x()*m_x.y() + m.m_x.y()*m_y.y() + m.m_x.z()*m_z.y(); float Nxz = m.m_x.x()*m_x.z() + m.m_x.y()*m_y.z() + m.m_x.z()*m_z.z(); float Nxo = m.m_x.x()*m_xo + m.m_x.y()*m_yo + m.m_x.z()*m_zo + m.m_xo; float Nyx = m.m_y.x()*m_x.x() + m.m_y.y()*m_y.x() + m.m_y.z()*m_z.x(); float Nyy = m.m_y.x()*m_x.y() + m.m_y.y()*m_y.y() + m.m_y.z()*m_z.y(); float Nyz = m.m_y.x()*m_x.z() + m.m_y.y()*m_y.z() + m.m_y.z()*m_z.z(); float Nyo = m.m_y.x()*m_xo + m.m_y.y()*m_yo + m.m_y.z()*m_zo + m.m_yo; float Nzx = m.m_z.x()*m_x.x() + m.m_z.y()*m_y.x() + m.m_z.z()*m_z.x(); float Nzy = m.m_z.x()*m_x.y() + m.m_z.y()*m_y.y() + m.m_z.z()*m_z.y(); float Nzz = m.m_z.x()*m_x.z() + m.m_z.y()*m_y.z() + m.m_z.z()*m_z.z(); float Nzo = m.m_z.x()*m_xo + m.m_z.y()*m_yo + m.m_z.z()*m_zo + m.m_zo; m_x.set(Nxx, Nxy, Nxz); m_xo = Nxo; m_y.set(Nyx, Nyy, Nyz); m_yo = Nyo; m_z.set(Nzx, Nzy, Nzz); m_zo = Nzo; } /** Reinitialize to the unit matrix */ public void unit() { m_x.set(1.0f, 0.0f, 0.0f); m_xo = 0.0f; m_y.set(0.0f, 1.0f, 0.0f); m_yo = 0.0f; m_z.set(0.0f, 0.0f, 1.0f); m_zo = 0.0f; } /** 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_x.x() + m_xo, m_y.x() + m_yo, m_z.x() + m_zo), (0, 1, 0) to (m_x.y() + m_xo, m_y.y() + m_yo, m_z.y() + m_zo) and (0, 0, 1) to (m_x.z() + m_xo, m_y.z() + m_yo, m_z.z() + 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_x.x() + y*m_x.y() + z*m_x.z() + m_xo); tv[i + 1] = (int) (x*m_y.x() + y*m_y.y() + z*m_y.z() + m_yo); tv[i + 2] = (int) (x*m_z.x() + y*m_z.y() + z*m_z.z() + m_zo); } } /** Transform nvert tuple3ds from v into tv. */ public void transform(tuple3d v[], tuple3d tv[], int nvert) { for (int i = 0; i < nvert; i++) { tv[i].set(v[i].dot(m_x) + m_xo, v[i].dot(m_y) + m_yo, v[i].dot(m_z) + m_zo); } } /** Generate a String representation of this matrix3d. */ public String toString() { return "[" + m_x.x() + "," + m_x.y() + "," + m_x.z() + "," + m_xo + ";" + m_y.x() + "," + m_y.y() + "," + m_y.z() + "," + m_yo + ";" + m_z.x() + "," + m_z.y() + "," + m_z.z() + "," + m_zo + "]" ; } }