/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.vecmath;
import java.util.Arrays;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
/**
* Provides temporary variables and methods for computing various transforms:
* <ul>
* <li>Shear</li>
* <li>Orient</li>
* <li>Local-to-local</li>
* </ul>
* TransformComputer3f is not synchronized!
*/
public class TransformComputer3f {
private static final Point3f ZEROF = new Point3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public float[] m = new float[16];
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Matrix4f x1 = new Matrix4f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Matrix4f x2 = new Matrix4f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Point3f p1 = new Point3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Point3f p2 = new Point3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v1 = new Vector3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v2 = new Vector3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v3 = new Vector3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v4 = new Vector3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v5 = new Vector3f();
/**
* This is used as a temporary in instance methods, but it is public so that
* you can use it instead of wasting memory by allocating more temporaries.
*/
public Vector3f v6 = new Vector3f();
/**
* Creates a transform that orients an object from one coordinate reference
* frame to another.<br>
* <br>
*
* @param oldOrigin
* the origin of the old reference frame.
* @param oldX
* the x axis of the old reference frame (whatever direction you
* want; it doesn't have to be (1, 0, 0))
* @param newOrigin
* the origin of the new reference frame.
* @param newX
* the x axis of the new reference frame.
* @param result
* the Matrix4f to set such that (ignoring floating point
* inaccuracy):
* <ul>
* <li><code>result.transform( oldOrigin )</code> will equal
* <code>newOrigin</code></li>
* <li><code>result.transform( oldX )</code> will equal
* <code>newX</code></li>
* <li><code>result.transform( n )</code> will equal
* <code>n</code></li> for any vector <code>n</code>
* perpendicular to <code>oldX</code> and <code>newX</code>.
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if <code>oldX</code> or <code>newX</code> is zero.
*
* @see #orient(Point3f, Vector3f, Vector3f, Point3f, Vector3f, Vector3f,
* Matrix4f)
*/
public Matrix4f orient(Point3f oldOrigin, Vector3f oldX, Point3f newOrigin, Vector3f newX, Matrix4f result) {
v1.normalize(oldX);
v4.normalize(newX);
// pick y normal to place of rotation for oldY and newY
v2.cross(v1, v4);
v5.set(v2);
if (v2.equals(ZEROF)) {
// no rotation necessary; just translate
Arrays.fill(m, 0);
m[0] = 1;
m[3] = newOrigin.x - oldOrigin.x;
m[5] = 1;
m[7] = newOrigin.y - oldOrigin.y;
m[10] = 1;
m[11] = newOrigin.z - oldOrigin.z;
m[15] = 1;
result.set(m);
} else {
// compute oldZ and newZ
v3.cross(v1, v2);
v6.cross(v4, v5);
shear(oldOrigin, v1, v2, v3, newOrigin, v4, v5, v6, result);
}
return result;
}
/**
* Creates a transform that orients an object from one coordinate reference
* frame to another.<br>
* <br>
*
* For example, let's say you want to pin a poster on a wall. you've
* unrolled the poster on the ground facing up and the top edge of the
* poster is facing north. The wall you want to put it on is facing east.
* The only problem is you have to use a mathematical transform to put it on
* the wall! What should the transform do? If you transform the center of
* the poster, it should move from the ground to the wall. If you transform
* the direction the poster is facing (up), it should turn east. If you
* transform the direction of the top edge of the poster, it should turn up.
* This method creates such a transform. In this example:
* <ul>
* <li><code>oldOrigin</code> is the center of the poster on the ground</li>
* <li><code>oldX</code> is the direction the poster is facing (up)</li>
* <li><code>oldY</code> is the direction the top edge of the poster is
* facing (north)</li>
* <li><code>newOrigin</code> is the point on the wall where you want the
* poster to be centered</li>
* <li><code>newX</code> is the direction the wall is facing (east)</li>
* <li><code>newY</code> is the direction you want the top edge of the
* poster to be facing when you put it on the wall (up)</li>
* </ul>
*
* In other words, if you create an orient transform and apply it to an
* object, the part of the object at <code>oldOrigin</code> will now be at
* <code>newOrigin</code>, the part of the object facing in the
* <code>oldX</code> direction will now face in the <code>newX</code>
* direction, and the part of the object facing in the <code>oldY</code>
* direction will now face in the <code>newY</code> direction.
*
* @param oldOrigin
* the origin of the old reference frame.
* @param oldX
* the x axis of the old reference frame (whatever direction you
* want; it doesn't have to be (1, 0, 0))
* @param oldY
* the y axis of the old reference frame (if not perpendicular to
* <code>oldX</code>, it will be replaced with a vector
* perpendicular to <code>oldX</code> at the same angle around
* <code>oldX</code>).
* @param newOrigin
* the origin of the new reference frame.
* @param newX
* the x axis of the new reference frame.
* @param newY
* the y axis of the new reference frame (if not perpendicular to
* <code>newX</code>, it will be replaced with a vector
* perpendicular to <code>newX</code> at the same angle around
* <code>newX</code>).
* @param result
* the Matrix4f to set such that (ignoring floating point
* inaccuracy):
* <ul>
* <li><code>result.transform( oldOrigin )</code> will equal
* <code>newOrigin</code></li>
* <li><code>result.transform( oldX )</code> will equal
* <code>newX</code></li>
* <li><code>result.transform( oldY )</code> will equal
* <code>newY</code></li>
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if:
* <ul>
* <li><code>oldX, oldY, newX,</code> or <code>newY</code> is
* zero,</li>
* <li><code>oldX</code> and <code>oldY</code> are parallel, or
* </li>
* <li><code>newX</code> and <code>newY</code> are parallel.
* </li>
* </ul>
*/
public Matrix4f orient(Point3f oldOrigin, Vector3f oldX, Vector3f oldY, Point3f newOrigin, Vector3f newX,
Vector3f newY, Matrix4f result) {
if (oldX.equals(ZEROF) || oldY.equals(ZEROF) || newX.equals(ZEROF) || newY.equals(ZEROF)) {
throw new IllegalArgumentException("oldX, oldY, newX, and newY must be nonzero");
}
v1.normalize(oldX); // corrected oldX
v3.cross(oldX, oldY); // oldZ
if (v3.equals(ZEROF)) {
throw new IllegalArgumentException("oldX and oldY must not be parallel");
}
v3.normalize();
v2.cross(v3, v1); // corrected oldY
v4.normalize(newX); // corrected newX
v6.cross(newX, newY); // newZ
if (v6.equals(ZEROF)) {
throw new IllegalArgumentException("newX and newY must not be parallel");
}
v6.normalize();
v5.cross(v6, v4); // corrected newY
shear(oldOrigin, v1, v2, v3, newOrigin, v4, v5, v6, result);
return result;
}
/**
* Creates a transform that orients an object from one coordinate reference
* frame to another.<br>
* <br>
*
* @param oldX
* the x axis of the old reference frame (whatever direction you
* want; it doesn't have to be (1, 0, 0))
* @param newX
* the x axis of the new reference frame.
* @param result
* the Matrix4f to set such that (ignoring floating point
* inaccuracy):
* <ul>
* <li><code>result.transform( oldX )</code> will equal
* <code>newX</code></li>
* <li><code>result.transform( n )</code> will equal
* <code>n</code></li> for any vector <code>n</code>
* perpendicular to <code>oldX</code> and <code>newX</code>.
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if <code>oldX</code> or <code>newX</code> is zero.
*
* @see #orient(Point3f, Vector3f, Vector3f, Point3f, Vector3f, Vector3f,
* Matrix4f)
*/
public Matrix4f orient(Vector3f oldX, Vector3f newX, Matrix4f result) {
return orient(ZEROF, oldX, ZEROF, newX, result);
}
/**
* Creates a transform that orients and object from one coordinate reference
* frame to another without translation.
*
* @see #orient(Point3f, Vector3f, Vector3f, Point3f, Vector3f, Vector3f,
* Matrix4f)
* @param oldX
* the x axis of the old reference frame (whatever direction you
* want; it doesn't have to be (1, 0, 0))
* @param oldY
* the y axis of the old reference frame (if not perpendicular to
* <code>oldX</code>, it will be replaced with a vector
* perpendicular to <code>oldX</code> at the same angle around
* <code>oldX</code>).
* @param newX
* the x axis of the new reference frame.
* @param newY
* the y axis of the new reference frame (if not perpendicular to
* <code>newX</code>, it will be replaced with a vector
* perpendicular to <code>newX</code> at the same angle around
* <code>newX</code>).
* @param result
* the Matrix4f to set such that (ignoring floating point
* inaccuracy):
* <ul>
* <li><code>result.transform( oldX )</code> will equal
* <code>newX</code></li>
* <li><code>result.transform( oldY )</code> will equal
* <code>newY</code></li>
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if:
* <ul>
* <li><code>oldX, oldY, newX,</code> or <code>newY</code> is
* zero,</li>
* <li><code>oldX</code> and <code>oldY</code> are parallel, or
* </li>
* <li><code>newX</code> and <code>newY</code> are parallel.
* </li>
* </ul>
*/
public Matrix4f orient(Vector3f oldX, Vector3f oldY, Vector3f newX, Vector3f newY, Matrix4f result) {
return orient(ZEROF, oldX, oldY, ZEROF, newX, newY, result);
}
/**
* Computes a shear from the public instance variables.
*
* @return shear( p1 , v1 , v2 , v3 , p2 , v4 , v5 , v6 , result ).
*
* @see #shear(Point3d, Vector3d, Vector3d, Vector3d, Point3d, Vector3d,
* Vector3d, Vector3d, Matrix4f)
*/
public Matrix4f shear(Matrix4f result) {
return shear(p1, v1, v2, v3, p2, v4, v5, v6, result);
}
/**
* Transform from the origin and unit x, y, and z axes to a new origin and
* x, y, and z axes. If newX, newY, and newZ are not perpendicular, shearing
* will result. If newX, newY, and newZ are not unitary, scaling will
* result.
*
* @param newOrigin
* the new origin point.
* @param newX
* the new x axis.
* @param newY
* the new y axis.
* @param newZ
* the new z axis.
* @param result
* the Matrix4f to set such that (ignoring floating-point
* inaccuracy):
* <ul>
* <li><code>result.transform( new Point3d( 0, 0, 0 ) )</code>
* equals <code>newOrigin</code>,</li>
* <li><code>result.transform( new Vector3d( 1, 0, 0 ) )</code>
* equals <code>newX</code>,</li>
* <li><code>result.transform( new Vector3d( 0, 1, 0 ) )</code>
* equals <code>newY</code>, and</li>
* <li><code>result.transform( new Vector3d( 0, 0, 1 ) )</code>
* equals <code>newZ</code>.</li>
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if <code>newX</code>, <code>newY</code>, or <code>newZ</code>
* is zero
*/
public Matrix4f shear(Point3f newOrigin, Vector3f newX, Vector3f newY, Vector3f newZ, Matrix4f result) {
if (newX.equals(ZEROF) || newY.equals(ZEROF) || newZ.equals(ZEROF)) {
throw new IllegalArgumentException("newX, newY, and newZ must be nonzero");
}
m[0] = newX.x;
m[1] = newY.x;
m[2] = newZ.x;
m[3] = newOrigin.x;
m[4] = newX.y;
m[5] = newY.y;
m[6] = newZ.y;
m[7] = newOrigin.y;
m[8] = newX.z;
m[9] = newY.z;
m[10] = newZ.z;
m[11] = newOrigin.z;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
result.set(m);
return result;
}
/**
* Shears from an old origin and x, y, and z axes to a new origin and x, y,
* and z axes.
*
* @param oldOrigin
* the old origin point.
* @param oldX
* the old x axis.
* @param oldY
* the old y axis.
* @param oldZ
* the old z axis.
* @param newOrigin
* the new origin point.
* @param newX
* the new x axis.
* @param newY
* the new y axis.
* @param newZ
* the new z axis.
* @param result
* the Matrix4f to set such that (ignoring floating-point
* inaccuracy):
* <ul>
* <li><code>result.transform( oldOrigin )</code> equals
* <code>newOrigin</code>,</li>
* <li><code>result.transform( oldX )</code> equals
* <code>newX</code>,</li>
* <li><code>result.transform( oldY )</code> equals
* <code>newY</code>, and</li>
* <li><code>result.transform( oldZ )</code> equals
* <code>newZ</code>.</li>
* </ul>
* @return <code>result</code>
*
* @throws IllegalArgumentException
* if <code>newX</code>, <code>newY</code>, or <code>newZ</code>
* is zero
*/
public Matrix4f shear(Point3f oldOrigin, Vector3f oldX, Vector3f oldY, Vector3f oldZ, Point3f newOrigin,
Vector3f newX, Vector3f newY, Vector3f newZ, Matrix4f result) {
if (oldX.equals(ZEROF) || oldY.equals(ZEROF) || oldZ.equals(ZEROF) || newX.equals(ZEROF) || newY.equals(ZEROF)
|| newZ.equals(ZEROF)) {
throw new IllegalArgumentException("oldX, oldY, oldZ, newX, newY, and newZ must be nonzero");
}
shear(newOrigin, newX, newY, newZ, result);
shear(oldOrigin, oldX, oldY, oldZ, x1);
x1.invert();
result.mul(x1);
return result;
}
}