package org.newdawn.slick.geom;
import org.newdawn.slick.util.FastTrig;
/**
* A 2 dimensional transformation that can be applied to <code>Shape</code> implemenations.
*
* @author Mark
*/
public class Transform {
/**
* Value for each position in the matrix
*
* |0 1 2|
* |3 4 5|
* |6 7 8|
*/
private float matrixPosition[];
/**
* Create and identity transform
*
*/
public Transform() {
matrixPosition = new float[]{1, 0, 0, 0, 1, 0, 0, 0, 1};
}
/**
* Copy a transform
*
* @param other The other transform to copy
*/
public Transform(Transform other) {
matrixPosition = new float[9];
for (int i=0;i<9;i++) {
matrixPosition[i] = other.matrixPosition[i];
}
}
/**
* Concatanate to transform into one
*
* @param t1 The first transform to join
* @param t2 The second transform to join
*/
public Transform(Transform t1, Transform t2) {
this(t1);
concatenate(t2);
}
/**
* Create a transform for the given positions
*
* @param matrixPosition An array of float[6] to set up a transform
* @throws RuntimeException if the array is not of length 6
*/
public Transform(float matrixPosition[]) {
if(matrixPosition.length != 6) {
throw new RuntimeException("The parameter must be a float array of length 6.");
}
this.matrixPosition = new float[]{matrixPosition[0], matrixPosition[1], matrixPosition[2],
matrixPosition[3], matrixPosition[4], matrixPosition[5],
0, 0, 1};
}
/**
* Create a transform for the given positions
*
* @param point00 float for the first position
* @param point01 float for the second position
* @param point02 float for the third position
* @param point10 float for the fourth position
* @param point11 float for the fifth position
* @param point12 float for the sixth position
*/
public Transform(float point00, float point01, float point02, float point10, float point11, float point12) {
matrixPosition = new float[]{point00, point01, point02, point10, point11, point12, 0, 0, 1};
}
/**
* Transform the point pairs in the source array and store them in the destination array.
* All operations will be done before storing the results in the destination. This way the source
* and destination array can be the same without worry of overwriting information before it is transformed.
*
* @param source Array of floats containing the points to be transformed
* @param sourceOffset Where in the array to start processing
* @param destination Array of floats to store the results.
* @param destOffset Where in the array to start storing
* @param numberOfPoints Number of points to be transformed
* @throws ArrayIndexOutOfBoundsException if sourceOffset + numberOfPoints * 2 > source.length or the same operation on the destination array
*/
public void transform(float source[], int sourceOffset, float destination[], int destOffset, int numberOfPoints) {
//TODO performance can be improved by removing the safety to the destination array
float result[] = new float[numberOfPoints * 2];
for(int i=0;i<numberOfPoints * 2;i+=2) {
for(int j=0;j<6;j+=3) {
result[i + (j / 3)] = source[i + sourceOffset] * matrixPosition[j] + source[i + sourceOffset + 1] * matrixPosition[j + 1] + 1 * matrixPosition[j + 2];
}
}
//for safety of the destination, the results are copied after the entire operation.
for(int i=0;i<numberOfPoints * 2;i+=2) {
destination[i + destOffset] = result[i];
destination[i + destOffset + 1] = result[i + 1];
}
}
/**
* Update this Transform by concatenating the given Transform to this one.
*
* @param tx The Transfrom to concatenate to this one.
* @return The resulting Transform
*/
public Transform concatenate(Transform tx) {
float[] mp = new float[9];
float n00 = matrixPosition[0] * tx.matrixPosition[0] + matrixPosition[1] * tx.matrixPosition[3];
float n01 = matrixPosition[0] * tx.matrixPosition[1] + matrixPosition[1] * tx.matrixPosition[4];
float n02 = matrixPosition[0] * tx.matrixPosition[2] + matrixPosition[1] * tx.matrixPosition[5] + matrixPosition[2];
float n10 = matrixPosition[3] * tx.matrixPosition[0] + matrixPosition[4] * tx.matrixPosition[3];
float n11 = matrixPosition[3] * tx.matrixPosition[1] + matrixPosition[4] * tx.matrixPosition[4];
float n12 = matrixPosition[3] * tx.matrixPosition[2] + matrixPosition[4] * tx.matrixPosition[5] + matrixPosition[5];
mp[0] = n00;
mp[1] = n01;
mp[2] = n02;
mp[3] = n10;
mp[4] = n11;
mp[5] = n12;
//
// mp[0] = matrixPosition[0] * transform.matrixPosition[0] + matrixPosition[0] * transform.matrixPosition[3] + matrixPosition[0] * transform.matrixPosition[6];
// mp[1] = matrixPosition[1] * transform.matrixPosition[1] + matrixPosition[1] * transform.matrixPosition[4] + matrixPosition[1] * transform.matrixPosition[7];
// mp[2] = matrixPosition[2] * transform.matrixPosition[2] + matrixPosition[2] * transform.matrixPosition[5] + matrixPosition[2] * transform.matrixPosition[8];
// mp[3] = matrixPosition[3] * transform.matrixPosition[0] + matrixPosition[3] * transform.matrixPosition[3] + matrixPosition[3] * transform.matrixPosition[6];
// mp[4] = matrixPosition[4] * transform.matrixPosition[1] + matrixPosition[4] * transform.matrixPosition[4] + matrixPosition[4] * transform.matrixPosition[7];
// mp[5] = matrixPosition[5] * transform.matrixPosition[2] + matrixPosition[5] * transform.matrixPosition[5] + matrixPosition[5] * transform.matrixPosition[8];
//
matrixPosition = mp;
return this;
}
/**
* Convert this Transform to a String.
*
* @return This Transform in human readable format.
*/
public String toString() {
String result = "Transform[[" + matrixPosition[0] + "," + matrixPosition[1] + "," + matrixPosition[2] +
"][" + matrixPosition[3] + "," + matrixPosition[4] + "," + matrixPosition[5] +
"][" + matrixPosition[6] + "," + matrixPosition[7] + "," + matrixPosition[8] + "]]";
return result.toString();
}
/**
* Get an array representing this Transform.
*
* @return an array representing this Transform.
*/
public float[] getMatrixPosition() {
return matrixPosition;
}
/**
* Create a new rotation Transform
*
* @param angle The angle in radians to set the transform.
* @return The resulting Transform
*/
public static Transform createRotateTransform(float angle) {
return new Transform((float)FastTrig.cos(angle), -(float)FastTrig.sin(angle), 0, (float)FastTrig.sin(angle), (float)FastTrig.cos(angle), 0);
}
/**
* Create a new rotation Transform around the specified point
*
* @param angle The angle in radians to set the transform.
* @param x The x coordinate around which to rotate.
* @param y The y coordinate around which to rotate.
* @return The resulting Transform
*/
public static Transform createRotateTransform(float angle, float x, float y) {
Transform temp = Transform.createRotateTransform(angle);
float sinAngle = temp.matrixPosition[3];
float oneMinusCosAngle = 1.0f - temp.matrixPosition[4];
temp.matrixPosition[2] = x * oneMinusCosAngle + y * sinAngle;
temp.matrixPosition[5] = y * oneMinusCosAngle - x * sinAngle;
return temp;
}
/**
* Create a new translation Transform
*
* @param xOffset The amount to move in the x direction
* @param yOffset The amount to move in the y direction
* @return The resulting Transform
*/
public static Transform createTranslateTransform(float xOffset, float yOffset) {
return new Transform(1, 0, xOffset, 0, 1, yOffset);
}
/**
* Create an new scaling Transform
*
* @param xScale The amount to scale in the x coordinate
* @param yScale The amount to scale in the x coordinate
* @return The resulting Transform
*/
public static Transform createScaleTransform(float xScale, float yScale) {
return new Transform(xScale, 0, 0, 0, yScale, 0);
}
/**
* Transform the vector2f based on the matrix defined in this transform
*
* @param pt The point to be transformed
* @return The resulting point transformed by this matrix
*/
public Vector2f transform(Vector2f pt) {
float[] in = new float[] {pt.x, pt.y};
float[] out = new float[2];
transform(in, 0, out, 0, 1);
return new Vector2f(out[0], out[1]);
}
}