Great big transform revamp.
We now have a Transform interface and a bevy of implementations of varying generality: identity, rigid body, uniform scaling, non-uniform scaling, and affine. Along the way, Vector was added, APIs were tidied up, bits were twiddled, but no platonic shapes were harmed.
This commit is contained in:
@@ -14,8 +14,9 @@
|
||||
<target name="-prepare" depends="-init-ooo">
|
||||
<mkdir dir="${target.dir}"/>
|
||||
<mavendep pom="pom.xml"/>
|
||||
<mavendep pom="pom.xml" id="test" scope="test"/>
|
||||
<path id="test.classpath">
|
||||
<path refid="pom.xml.path"/>
|
||||
<path refid="test.path"/>
|
||||
<pathelement location="${classes.dir}"/>
|
||||
<pathelement location="${tclasses.dir}"/>
|
||||
</path>
|
||||
@@ -30,7 +31,6 @@
|
||||
<copy todir="${classes.dir}"><fileset dir="src/main/resources" includes="**"/></copy>
|
||||
<ooojavac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="pom.xml.path"/>
|
||||
|
||||
<!--
|
||||
<mkdir dir="${tclasses.dir}"/>
|
||||
<javac srcdir="${test.dir}" destdir="${tclasses.dir}" includeAntRuntime="false"
|
||||
debug="on" optimize="${build.optimize}" source="1.6" target="1.6" encoding="utf-8">
|
||||
@@ -38,7 +38,6 @@
|
||||
<compilerarg value="-Xlint"/>
|
||||
<compilerarg value="-Xlint:-serial"/>
|
||||
</javac>
|
||||
-->
|
||||
</target>
|
||||
|
||||
<target name="javadoc" depends="-prepare" description="Generates javadoc documentation.">
|
||||
@@ -51,10 +50,10 @@
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<target name="tests" depends="compile" description="Runs the unit tests.">
|
||||
<target name="test" depends="compile" description="Runs the unit tests.">
|
||||
<!-- allows passing -Dtest=NamePrefix to restrict to subset of tests -->
|
||||
<property name="test" value=""/>
|
||||
<taskdef name="unit" classpathref="classpath"
|
||||
<taskdef name="unit" classpathref="test.path"
|
||||
classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask"/>
|
||||
<mkdir dir="${target.dir}/test-reports"/>
|
||||
<unit printsummary="off" haltonfailure="yes" fork="${junit.fork}">
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -20,9 +20,8 @@ public abstract class AbstractArc extends RectangularShape implements IArc
|
||||
@Override // from interface IArc
|
||||
public Point getStartPoint (Point target) {
|
||||
float a = FloatMath.toRadians(getAngleStart());
|
||||
target.setLocation(getX() + (1f + FloatMath.cos(a)) * getWidth() / 2f,
|
||||
getY() + (1f - FloatMath.sin(a)) * getHeight() / 2f);
|
||||
return target;
|
||||
return target.set(getX() + (1f + FloatMath.cos(a)) * getWidth() / 2f,
|
||||
getY() + (1f - FloatMath.sin(a)) * getHeight() / 2f);
|
||||
}
|
||||
|
||||
@Override // from interface IArc
|
||||
@@ -33,9 +32,8 @@ public abstract class AbstractArc extends RectangularShape implements IArc
|
||||
@Override // from interface IArc
|
||||
public Point getEndPoint (Point target) {
|
||||
float a = FloatMath.toRadians(getAngleStart() + getAngleExtent());
|
||||
target.setLocation(getX() + (1f + FloatMath.cos(a)) * getWidth() / 2f,
|
||||
getY() + (1f - FloatMath.sin(a)) * getHeight() / 2f);
|
||||
return target;
|
||||
return target.set(getX() + (1f + FloatMath.cos(a)) * getWidth() / 2f,
|
||||
getY() + (1f - FloatMath.sin(a)) * getHeight() / 2f);
|
||||
}
|
||||
|
||||
@Override // from interface IArc
|
||||
@@ -189,7 +187,7 @@ public abstract class AbstractArc extends RectangularShape implements IArc
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at) {
|
||||
public PathIterator getPathIterator (Transform at) {
|
||||
return new Iterator(this, at);
|
||||
}
|
||||
|
||||
@@ -223,7 +221,7 @@ public abstract class AbstractArc extends RectangularShape implements IArc
|
||||
private int type;
|
||||
|
||||
/** The path iterator transformation */
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
|
||||
/** The current segment index */
|
||||
private int index;
|
||||
@@ -259,7 +257,7 @@ public abstract class AbstractArc extends RectangularShape implements IArc
|
||||
/** The y coordinate of the first path point (MOVE_TO) */
|
||||
private float my;
|
||||
|
||||
Iterator (IArc a, AffineTransform t) {
|
||||
Iterator (IArc a, Transform t) {
|
||||
this.width = a.getWidth() / 2f;
|
||||
this.height = a.getHeight() / 2f;
|
||||
this.x = a.getX() + width;
|
||||
|
||||
@@ -111,12 +111,12 @@ public abstract class AbstractCubicCurve implements ICubicCurve
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t) {
|
||||
public PathIterator getPathIterator (Transform t) {
|
||||
return new Iterator(this, t);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at, float flatness) {
|
||||
public PathIterator getPathIterator (Transform at, float flatness) {
|
||||
return new FlatteningPathIterator(getPathIterator(at), flatness);
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ public abstract class AbstractCubicCurve implements ICubicCurve
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private ICubicCurve c;
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
private int index;
|
||||
|
||||
Iterator (ICubicCurve c, AffineTransform t) {
|
||||
Iterator (ICubicCurve c, Transform t) {
|
||||
this.c = c;
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public abstract class AbstractEllipse extends RectangularShape implements IEllip
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at) {
|
||||
public PathIterator getPathIterator (Transform at) {
|
||||
return new Iterator(this, at);
|
||||
}
|
||||
|
||||
@@ -52,10 +52,10 @@ public abstract class AbstractEllipse extends RectangularShape implements IEllip
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private final float x, y, width, height;
|
||||
private final AffineTransform t;
|
||||
private final Transform t;
|
||||
private int index;
|
||||
|
||||
Iterator (IEllipse e, AffineTransform t) {
|
||||
Iterator (IEllipse e, Transform t) {
|
||||
this.x = e.getX();
|
||||
this.y = e.getY();
|
||||
this.width = e.getWidth();
|
||||
|
||||
@@ -19,8 +19,7 @@ public abstract class AbstractLine implements ILine
|
||||
|
||||
@Override // from interface ILine
|
||||
public Point getP1 (Point target) {
|
||||
target.setLocation(getX1(), getY1());
|
||||
return target;
|
||||
return target.set(getX1(), getY1());
|
||||
}
|
||||
|
||||
@Override // from interface ILine
|
||||
@@ -30,8 +29,7 @@ public abstract class AbstractLine implements ILine
|
||||
|
||||
@Override // from interface ILine
|
||||
public Point getP2 (Point target) {
|
||||
target.setLocation(getX2(), getY2());
|
||||
return target;
|
||||
return target.set(getX2(), getY2());
|
||||
}
|
||||
|
||||
@Override // from interface ILine
|
||||
@@ -152,12 +150,12 @@ public abstract class AbstractLine implements ILine
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at) {
|
||||
public PathIterator getPathIterator (Transform at) {
|
||||
return new Iterator(this, at);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at, float flatness) {
|
||||
public PathIterator getPathIterator (Transform at, float flatness) {
|
||||
return new Iterator(this, at);
|
||||
}
|
||||
|
||||
@@ -165,10 +163,10 @@ public abstract class AbstractLine implements ILine
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private float x1, y1, x2, y2;
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
private int index;
|
||||
|
||||
Iterator (ILine l, AffineTransform at) {
|
||||
Iterator (ILine l, Transform at) {
|
||||
this.x1 = l.getX1();
|
||||
this.y1 = l.getY1();
|
||||
this.x2 = l.getX2();
|
||||
|
||||
@@ -12,27 +12,59 @@ import pythagoras.util.Platform;
|
||||
*/
|
||||
public abstract class AbstractPoint implements IPoint
|
||||
{
|
||||
@Override // from interface IPoint
|
||||
@Override // from IPoint
|
||||
public float distanceSq (float px, float py) {
|
||||
return Points.distanceSq(getX(), getY(), px, py);
|
||||
}
|
||||
|
||||
@Override // from interface IPoint
|
||||
@Override // from IPoint
|
||||
public float distanceSq (IPoint p) {
|
||||
return Points.distanceSq(getX(), getY(), p.getX(), p.getY());
|
||||
}
|
||||
|
||||
@Override // from interface IPoint
|
||||
@Override // from IPoint
|
||||
public float distance (float px, float py) {
|
||||
return Points.distance(getX(), getY(), px, py);
|
||||
}
|
||||
|
||||
@Override // from interface IPoint
|
||||
@Override // from IPoint
|
||||
public float distance (IPoint p) {
|
||||
return Points.distance(getX(), getY(), p.getX(), p.getY());
|
||||
}
|
||||
|
||||
@Override // from interface IPoint
|
||||
@Override // from IPoint
|
||||
public Point mult (float s) {
|
||||
return mult(s, new Point());
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point mult (float s, Point result) {
|
||||
return result.set(getX() * s, getY() * s);
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point add (float x, float y) {
|
||||
return new Point(getX() + x, getY() + y);
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point add (float x, float y, Point result) {
|
||||
return result.set(getX() + x, getY() + y);
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point rotate (float angle) {
|
||||
return rotate(angle, new Point());
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point rotate (float angle, Point result) {
|
||||
float x = getX(), y = getY();
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
return result.set(x*cosa - y*sina, x*sina + y*cosa);
|
||||
}
|
||||
|
||||
@Override // from IPoint
|
||||
public Point clone () {
|
||||
return new Point(this);
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ public abstract class AbstractQuadCurve implements IQuadCurve
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t) {
|
||||
public PathIterator getPathIterator (Transform t) {
|
||||
return new Iterator(this, t);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t, float flatness) {
|
||||
public PathIterator getPathIterator (Transform t, float flatness) {
|
||||
return new FlatteningPathIterator(getPathIterator(t), flatness);
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ public abstract class AbstractQuadCurve implements IQuadCurve
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private IQuadCurve c;
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
private int index;
|
||||
|
||||
Iterator (IQuadCurve q, AffineTransform t) {
|
||||
Iterator (IQuadCurve q, Transform t) {
|
||||
this.c = q;
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ public abstract class AbstractRectangle extends RectangularShape implements IRec
|
||||
|
||||
@Override // from interface IRectangle
|
||||
public Point getLocation (Point target) {
|
||||
target.setLocation(getX(), getY());
|
||||
return target;
|
||||
return target.set(getX(), getY());
|
||||
}
|
||||
|
||||
@Override // from interface IRectangle
|
||||
@@ -129,12 +128,12 @@ public abstract class AbstractRectangle extends RectangularShape implements IRec
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t) {
|
||||
public PathIterator getPathIterator (Transform t) {
|
||||
return new Iterator(this, t);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t, float flatness) {
|
||||
public PathIterator getPathIterator (Transform t, float flatness) {
|
||||
return new Iterator(this, t);
|
||||
}
|
||||
|
||||
@@ -167,12 +166,12 @@ public abstract class AbstractRectangle extends RectangularShape implements IRec
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private float x, y, width, height;
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
|
||||
/** The current segment index. */
|
||||
private int index;
|
||||
|
||||
Iterator (IRectangle r, AffineTransform at) {
|
||||
Iterator (IRectangle r, Transform at) {
|
||||
this.x = r.getX();
|
||||
this.y = r.getY();
|
||||
this.width = r.getWidth();
|
||||
|
||||
@@ -75,7 +75,7 @@ public abstract class AbstractRoundRectangle extends RectangularShape implements
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform at) {
|
||||
public PathIterator getPathIterator (Transform at) {
|
||||
return new Iterator(this, at);
|
||||
}
|
||||
|
||||
@@ -83,10 +83,10 @@ public abstract class AbstractRoundRectangle extends RectangularShape implements
|
||||
protected static class Iterator implements PathIterator
|
||||
{
|
||||
private final float x, y, width, height, aw, ah;
|
||||
private final AffineTransform t;
|
||||
private final Transform t;
|
||||
private int index;
|
||||
|
||||
Iterator (IRoundRectangle rr, AffineTransform at) {
|
||||
Iterator (IRoundRectangle rr, Transform at) {
|
||||
this.x = rr.getX();
|
||||
this.y = rr.getY();
|
||||
this.width = rr.getWidth();
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Implements some code shared by the various {@link Transform} implementations.
|
||||
*/
|
||||
public abstract class AbstractTransform implements Transform
|
||||
{
|
||||
@Override // from Transform
|
||||
public Vector getScale () {
|
||||
return new Vector(getScaleX(), getScaleY());
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector getTranslation () {
|
||||
return new Vector(getTx(), getTy());
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (float scale) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScale (float scaleX, float scaleY) {
|
||||
setScaleX(scaleX);
|
||||
setScaleY(scaleY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleX (float scaleX) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleY (float scaleY) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (float angle) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTranslation (float tx, float ty) {
|
||||
setTx(tx);
|
||||
setTy(ty);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (float ty) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTransform (float m00, float m01, float m10, float m11, float tx, float ty) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public abstract Transform clone ();
|
||||
}
|
||||
@@ -4,317 +4,144 @@
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import pythagoras.util.NoninvertibleTransformException;
|
||||
import pythagoras.util.Platform;
|
||||
|
||||
/**
|
||||
* Represents a 2D affine transform, which performs a linear mapping that preserves the
|
||||
* straightness and parallelness of lines.
|
||||
*
|
||||
* See http://download.oracle.com/javase/6/docs/api/java/awt/geom/AffineTransform.html
|
||||
* Implements an affine (3x2 matrix) transform. The transformation matrix has the form:
|
||||
* <pre>{@code
|
||||
* [ m00, m10, 0 ]
|
||||
* [ m01, m11, 0 ]
|
||||
* [ tx, ty, 1 ]
|
||||
* }</pre>
|
||||
*/
|
||||
public class AffineTransform implements Cloneable, Serializable
|
||||
public class AffineTransform extends AbstractTransform
|
||||
{
|
||||
public static final int TYPE_IDENTITY = 0;
|
||||
public static final int TYPE_TRANSLATION = 1;
|
||||
public static final int TYPE_UNIFORM_SCALE = 2;
|
||||
public static final int TYPE_GENERAL_SCALE = 4;
|
||||
public static final int TYPE_QUADRANT_ROTATION = 8;
|
||||
public static final int TYPE_GENERAL_ROTATION = 16;
|
||||
public static final int TYPE_GENERAL_TRANSFORM = 32;
|
||||
public static final int TYPE_FLIP = 64;
|
||||
public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
|
||||
public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
|
||||
/** Identifies the affine transform in {@link #generality}. */
|
||||
public static final int GENERALITY = 4;
|
||||
|
||||
/**
|
||||
* Returns a transform that performs the specified translation.
|
||||
*/
|
||||
public static AffineTransform getTranslateInstance (float tx, float ty) {
|
||||
AffineTransform t = new AffineTransform();
|
||||
t.setToTranslation(tx, ty);
|
||||
return t;
|
||||
}
|
||||
/** The scale, rotation and shear components of this transform. */
|
||||
public float m00, m01, m10, m11;
|
||||
|
||||
/**
|
||||
* Returns a transform that performs the specified scale.
|
||||
*/
|
||||
public static AffineTransform getScaleInstance (float scx, float scY) {
|
||||
AffineTransform t = new AffineTransform();
|
||||
t.setToScale(scx, scY);
|
||||
return t;
|
||||
}
|
||||
/** The translation components of this transform. */
|
||||
public float tx, ty;
|
||||
|
||||
/**
|
||||
* Returns a transform that performs the specified shear.
|
||||
*/
|
||||
public static AffineTransform getShearInstance (float shx, float shy) {
|
||||
AffineTransform m = new AffineTransform();
|
||||
m.setToShear(shx, shy);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transform that performs the specified rotation.
|
||||
*/
|
||||
public static AffineTransform getRotateInstance (float angle) {
|
||||
AffineTransform t = new AffineTransform();
|
||||
t.setToRotation(angle);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transform that performs the specified rotation.
|
||||
*/
|
||||
public static AffineTransform getRotateInstance (float angle, float x, float y) {
|
||||
AffineTransform t = new AffineTransform();
|
||||
t.setToRotation(angle, x, y);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an identity transform.
|
||||
*/
|
||||
/** Creates an affine transform configured with the identity transform. */
|
||||
public AffineTransform () {
|
||||
setToIdentity();
|
||||
this(1, 0, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a transform that is a copy of the supplied transform.
|
||||
*/
|
||||
public AffineTransform (AffineTransform t) {
|
||||
setTransform(t);
|
||||
/** Creates an affine transform from the supplied scale, rotation and translation. */
|
||||
public AffineTransform (float scale, float angle, float tx, float ty) {
|
||||
this(scale, scale, angle, tx, ty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a transform with the specified transformation matrix.
|
||||
*/
|
||||
public AffineTransform (float m00, float m10, float m01, float m11, float m02, float m12) {
|
||||
setTransform(m00, m10, m01, m11, m02, m12);
|
||||
/** Creates an affine transform from the supplied scale, rotation and translation. */
|
||||
public AffineTransform (float scaleX, float scaleY, float angle, float tx, float ty) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
this.m00 = cosa * scaleX; this.m01 = -sina * scaleX;
|
||||
this.m10 = sina * scaleY; this.m11 = cosa * scaleY;
|
||||
this.tx = tx; this.ty = ty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a transform with the specified transformation matrix.
|
||||
*
|
||||
* @param matrix either {@code [m00, m10, m01, m11]} or {@code [m00, m10, m01, m11, m02, m12]}.
|
||||
*/
|
||||
public AffineTransform (float[] matrix) {
|
||||
this.type = TYPE_UNKNOWN;
|
||||
m00 = matrix[0];
|
||||
m10 = matrix[1];
|
||||
m01 = matrix[2];
|
||||
m11 = matrix[3];
|
||||
if (matrix.length > 4) {
|
||||
m02 = matrix[4];
|
||||
m12 = matrix[5];
|
||||
}
|
||||
/** Creates an affine transform with the specified transform matrix. */
|
||||
public AffineTransform (float m00, float m01, float m10, float m11, float tx, float ty) {
|
||||
this.m00 = m00; this.m01 = m01;
|
||||
this.m10 = m10; this.m11 = m11;
|
||||
this.tx = tx; this.ty = ty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this affine transform, which is a bitwise-or of the type flags
|
||||
* ({@link #TYPE_TRANSLATION}, etc.).
|
||||
*/
|
||||
public int getType () {
|
||||
if (type != TYPE_UNKNOWN) {
|
||||
return type;
|
||||
}
|
||||
|
||||
int type = 0;
|
||||
|
||||
if (m00 * m01 + m10 * m11 != 0) {
|
||||
type |= TYPE_GENERAL_TRANSFORM;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (m02 != 0 || m12 != 0) {
|
||||
type |= TYPE_TRANSLATION;
|
||||
} else if (m00 == 1f && m11 == 1f && m01 == 0 && m10 == 0) {
|
||||
type = TYPE_IDENTITY;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (m00 * m11 - m01 * m10 < 0) {
|
||||
type |= TYPE_FLIP;
|
||||
}
|
||||
|
||||
float dx = m00 * m00 + m10 * m10;
|
||||
float dy = m01 * m01 + m11 * m11;
|
||||
if (dx != dy) {
|
||||
type |= TYPE_GENERAL_SCALE;
|
||||
} else if (dx != 1f) {
|
||||
type |= TYPE_UNIFORM_SCALE;
|
||||
}
|
||||
|
||||
if ((m00 == 0 && m11 == 0) || (m10 == 0 && m01 == 0 && (m00 < 0 || m11 < 0))) {
|
||||
type |= TYPE_QUADRANT_ROTATION;
|
||||
} else if (m01 != 0 || m10 != 0) {
|
||||
type |= TYPE_GENERAL_ROTATION;
|
||||
}
|
||||
|
||||
return type;
|
||||
@Override // from Transform
|
||||
public float getUniformScale () {
|
||||
// the square root of the signed area of the parallelogram spanned by the axis vectors
|
||||
float cp = m00*m11 - m01*m10;
|
||||
return (cp < 0f) ? -FloatMath.sqrt(-cp) : FloatMath.sqrt(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x-component of the scale vector.
|
||||
*/
|
||||
@Override // from Transform
|
||||
public float getScaleX () {
|
||||
return m00;
|
||||
return FloatMath.sqrt(m00*m00 + m01*m01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y-component of the scale vector.
|
||||
*/
|
||||
@Override // from Transform
|
||||
public float getScaleY () {
|
||||
return m11;
|
||||
return FloatMath.sqrt(m10*m10 + m11*m11);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x-component of the shear vector.
|
||||
*/
|
||||
public float getShearX () {
|
||||
return m01;
|
||||
}
|
||||
@Override // from Transform
|
||||
public float getRotation () {
|
||||
// use the iterative polar decomposition algorithm described by Ken Shoemake:
|
||||
// http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf
|
||||
|
||||
/**
|
||||
* Returns the y-component of the shear vector.
|
||||
*/
|
||||
public float getShearY () {
|
||||
return m10;
|
||||
}
|
||||
// start with the contents of the upper 2x2 portion of the matrix
|
||||
float n00 = m00, n10 = m10;
|
||||
float n01 = m01, n11 = m11;
|
||||
for (int ii = 0; ii < 10; ii++) {
|
||||
// store the results of the previous iteration
|
||||
float o00 = n00, o10 = n10;
|
||||
float o01 = n01, o11 = n11;
|
||||
|
||||
/**
|
||||
* Returns the x-component of the translation vector.
|
||||
*/
|
||||
public float getTranslateX () {
|
||||
return m02;
|
||||
}
|
||||
// compute average of the matrix with its inverse transpose
|
||||
float det = o00*o11 - o10*o01;
|
||||
if (Math.abs(det) == 0f) {
|
||||
// determinant is zero; matrix is not invertible
|
||||
throw new NoninvertibleTransformException(this.toString());
|
||||
}
|
||||
float hrdet = 0.5f / det;
|
||||
n00 = +o11 * hrdet + o00*0.5f;
|
||||
n10 = -o01 * hrdet + o10*0.5f;
|
||||
|
||||
/**
|
||||
* Returns the y-component of the translation vector.
|
||||
*/
|
||||
public float getTranslateY () {
|
||||
return m12;
|
||||
}
|
||||
n01 = -o10 * hrdet + o01*0.5f;
|
||||
n11 = +o00 * hrdet + o11*0.5f;
|
||||
|
||||
/**
|
||||
* Returns true if this transform is the identity.
|
||||
*/
|
||||
public boolean isIdentity () {
|
||||
return getType() == TYPE_IDENTITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in the supplied matrix with this transform's values.
|
||||
*
|
||||
* @param matrix either a length-4 or length-6 array.
|
||||
*/
|
||||
public void getMatrix (float[] matrix) {
|
||||
matrix[0] = m00;
|
||||
matrix[1] = m10;
|
||||
matrix[2] = m01;
|
||||
matrix[3] = m11;
|
||||
if (matrix.length > 4) {
|
||||
matrix[4] = m02;
|
||||
matrix[5] = m12;
|
||||
// compute the difference; if it's small enough, we're done
|
||||
float d00 = n00 - o00, d10 = n10 - o10;
|
||||
float d01 = n01 - o01, d11 = n11 - o11;
|
||||
if (d00*d00 + d10*d10 + d01*d01 + d11*d11 < FloatMath.EPSILON) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// now that we have a nice orthogonal matrix, we can extract the rotation
|
||||
return FloatMath.atan2(n01, n00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <a href="http://en.wikipedia.org/wiki/Determinant">determinant</a> of this
|
||||
* matrix.
|
||||
*/
|
||||
public float getDeterminant () {
|
||||
return m00 * m11 - m01 * m10;
|
||||
@Override // from Transform
|
||||
public float getTx () {
|
||||
return this.tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform's values.
|
||||
*/
|
||||
public void setTransform (float m00, float m10, float m01, float m11, float m02, float m12) {
|
||||
this.type = TYPE_UNKNOWN;
|
||||
this.m00 = m00;
|
||||
this.m10 = m10;
|
||||
this.m01 = m01;
|
||||
this.m11 = m11;
|
||||
this.m02 = m02;
|
||||
this.m12 = m12;
|
||||
@Override // from Transform
|
||||
public float getTy () {
|
||||
return this.tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform's values to be equal to those of the supplied transform.
|
||||
*/
|
||||
public void setTransform (AffineTransform t) {
|
||||
setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
|
||||
type = t.type;
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (float scale) {
|
||||
return setScale(scale, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to the identity transform. Any existing transform values are
|
||||
* overwritten.
|
||||
*/
|
||||
public void setToIdentity () {
|
||||
type = TYPE_IDENTITY;
|
||||
m00 = m11 = 1f;
|
||||
m10 = m01 = m02 = m12 = 0;
|
||||
@Override // from Transform
|
||||
public Transform setScaleX (float scaleX) {
|
||||
// normalize the scale to 1, then re-apply
|
||||
float osx = getScaleX();
|
||||
m00 /= osx; m01 /= osx;
|
||||
m00 *= scaleX; m01 *= scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to a simple translation using the supplied values. Any existing
|
||||
* transform values are overwritten.
|
||||
*/
|
||||
public void setToTranslation (float tx, float ty) {
|
||||
m00 = m11 = 1f;
|
||||
m01 = m10 = 0;
|
||||
m02 = tx;
|
||||
m12 = ty;
|
||||
if (tx == 0 && ty == 0) {
|
||||
type = TYPE_IDENTITY;
|
||||
} else {
|
||||
type = TYPE_TRANSLATION;
|
||||
}
|
||||
@Override // from Transform
|
||||
public Transform setScaleY (float scaleY) {
|
||||
// normalize the scale to 1, then re-apply
|
||||
float osy = getScaleY();
|
||||
m10 /= osy; m11 /= osy;
|
||||
m10 *= scaleY; m11 *= scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to a simple scale using the supplied values. Any existing transform
|
||||
* values are overwritten.
|
||||
*/
|
||||
public void setToScale (float scx, float scy) {
|
||||
m00 = scx;
|
||||
m11 = scy;
|
||||
m10 = m01 = m02 = m12 = 0;
|
||||
if (scx != 1f || scy != 1f) {
|
||||
type = TYPE_UNKNOWN;
|
||||
} else {
|
||||
type = TYPE_IDENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to a simple shear using the supplied values. Any existing transform
|
||||
* values are overwritten.
|
||||
*/
|
||||
public void setToShear (float shx, float shy) {
|
||||
m00 = m11 = 1f;
|
||||
m02 = m12 = 0;
|
||||
m01 = shx;
|
||||
m10 = shy;
|
||||
if (shx != 0 || shy != 0) {
|
||||
type = TYPE_UNKNOWN;
|
||||
} else {
|
||||
type = TYPE_IDENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to a simple rotation using the supplied values. Any existing transform
|
||||
* values are overwritten.
|
||||
*
|
||||
* @param angle the angle of rotation (in radians).
|
||||
*/
|
||||
public void setToRotation (float angle) {
|
||||
float sin = FloatMath.sin(angle);
|
||||
float cos = FloatMath.cos(angle);
|
||||
@Override // from Transform
|
||||
public Transform setRotation (float angle) {
|
||||
// extract the scale, then reapply rotation and scale together
|
||||
float sx = getScaleX(), sy = getScaleY();
|
||||
float sin = FloatMath.sin(angle), cos = FloatMath.cos(angle);
|
||||
if (Math.abs(cos) < ZERO) {
|
||||
cos = 0;
|
||||
sin = sin > 0 ? 1f : -1f;
|
||||
@@ -322,329 +149,160 @@ public class AffineTransform implements Cloneable, Serializable
|
||||
sin = 0;
|
||||
cos = cos > 0 ? 1f : -1f;
|
||||
}
|
||||
m00 = m11 = cos;
|
||||
m01 = -sin;
|
||||
m10 = sin;
|
||||
m02 = m12 = 0;
|
||||
type = TYPE_UNKNOWN;
|
||||
m00 = cos * sx; m01 = -sin * sx;
|
||||
m10 = sin * sy; m11 = cos * sy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this transform to a simple rotation using the supplied values. Any existing transform
|
||||
* values are overwritten.
|
||||
*
|
||||
* @param angle the angle of rotation (in radians).
|
||||
* @param px the x-coordinate of the point around which to rotate.
|
||||
* @param py the y-coordinate of the point around which to rotate.
|
||||
*/
|
||||
public void setToRotation (float angle, float px, float py) {
|
||||
setToRotation(angle);
|
||||
m02 = px * (1f - m00) + py * m10;
|
||||
m12 = py * (1f - m00) - px * m10;
|
||||
type = TYPE_UNKNOWN;
|
||||
@Override // from Transform
|
||||
public Transform setTranslation (float tx, float ty) {
|
||||
this.tx = tx;
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified translation to this transform.
|
||||
*/
|
||||
public void translate (float tx, float ty) {
|
||||
concatenate(getTranslateInstance(tx, ty));
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified scale to this transform.
|
||||
*/
|
||||
public void scale (float scx, float scy) {
|
||||
concatenate(getScaleInstance(scx, scy));
|
||||
@Override // from Transform
|
||||
public Transform setTy (float ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified shear to this transform.
|
||||
*/
|
||||
public void shear (float shx, float shy) {
|
||||
concatenate(getShearInstance(shx, shy));
|
||||
@Override // from Transform
|
||||
public Transform setTransform (float m00, float m01, float m10, float m11, float tx, float ty) {
|
||||
this.m00 = m00;
|
||||
this.m01 = m01;
|
||||
this.m10 = m10;
|
||||
this.m11 = m11;
|
||||
this.tx = tx;
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified rotation to this transform.
|
||||
*/
|
||||
public void rotate (float angle) {
|
||||
concatenate(getRotateInstance(angle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified rotation to this transform.
|
||||
*/
|
||||
public void rotate (float angle, float px, float py) {
|
||||
concatenate(getRotateInstance(angle, px, py));
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the specified transform to this transform.
|
||||
*/
|
||||
public void concatenate (AffineTransform t) {
|
||||
multiply(t, this, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-concatenates the specified transform to this transform.
|
||||
*/
|
||||
public void preConcatenate (AffineTransform t) {
|
||||
multiply(this, t, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the inverse of this transform and stores it in the supplied target.
|
||||
*
|
||||
* @return the supplied target.
|
||||
* @throws NoninvertibleTransformException if this transform cannot be inverted.
|
||||
*/
|
||||
public AffineTransform createInverse (AffineTransform target)
|
||||
throws NoninvertibleTransformException {
|
||||
float det = getDeterminant();
|
||||
if (Math.abs(det) < ZERO) {
|
||||
throw new NoninvertibleTransformException("Determinant is zero");
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
// compute the determinant, storing the subdeterminants for later use
|
||||
float det = m00*m11 - m10*m01;
|
||||
if (Math.abs(det) == 0f) {
|
||||
// determinant is zero; matrix is not invertible
|
||||
throw new NoninvertibleTransformException(this.toString());
|
||||
}
|
||||
target.setTransform(m11 / det, // m00
|
||||
-m10 / det, // m10
|
||||
-m01 / det, // m01
|
||||
m00 / det, // m11
|
||||
(m01 * m12 - m11 * m02) / det, // m02
|
||||
(m10 * m02 - m00 * m12) / det); // m12
|
||||
return target;
|
||||
float rdet = 1f / det;
|
||||
return new AffineTransform(
|
||||
+m11 * rdet, -m01 * rdet,
|
||||
-m10 * rdet, +m00 * rdet,
|
||||
(m01*ty - m11*tx) * rdet, (m10*tx - m00*ty) * rdet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and returns the inverse of this transform.
|
||||
*
|
||||
* @return the supplied target.
|
||||
* @throws NoninvertibleTransformException if this transform cannot be inverted.
|
||||
*/
|
||||
public AffineTransform createInverse () throws NoninvertibleTransformException {
|
||||
return createInverse(new AffineTransform());
|
||||
@Override // from Transform
|
||||
public Transform concatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.preConcatenate(this);
|
||||
}
|
||||
return multiply((other instanceof AffineTransform) ?
|
||||
(AffineTransform)other : new AffineTransform(other));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied point using this transform's matrix.
|
||||
*
|
||||
* @param src the point to be transformed.
|
||||
* @param dst the point in which to store the transformed values, if null a new instance will
|
||||
* be created. May be {@code src}.
|
||||
* @return the supplied (or created) destination point.
|
||||
*/
|
||||
public Point transform (IPoint src, Point dst) {
|
||||
if (dst == null) {
|
||||
dst = new Point();
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
return ((other instanceof AffineTransform) ?
|
||||
(AffineTransform)other : new AffineTransform(other)).multiply(this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, float t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
|
||||
float x = src.getX(), y = src.getY();
|
||||
dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
|
||||
return dst;
|
||||
AffineTransform ot = (other instanceof AffineTransform) ?
|
||||
(AffineTransform)other : new AffineTransform(other);
|
||||
return new AffineTransform(
|
||||
m00 + t*(ot.m00 - m00), m01 + t*(ot.m01 - m01),
|
||||
m10 + t*(ot.m10 - m10), m11 + t*(ot.m11 - m11),
|
||||
tx + t*(ot.tx - tx ), ty + t*(ot.ty - ty ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied points using this transform's matrix.
|
||||
*
|
||||
* @param src the points to be transformed.
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param length the number of points to transform.
|
||||
*/
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int length) {
|
||||
while (--length >= 0) {
|
||||
IPoint srcPoint = src[srcOff++];
|
||||
float x = srcPoint.getX();
|
||||
float y = srcPoint.getY();
|
||||
Point dstPoint = dst[dstOff];
|
||||
if (dstPoint == null) {
|
||||
dstPoint = new Point();
|
||||
}
|
||||
dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
|
||||
dst[dstOff++] = dstPoint;
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
float x = p.getX(), y = p.getY();
|
||||
return into.set(m00*x + m01*y + tx, m10*x + m11*y + ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
transform(src[srcOff++], dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied points using this transform's matrix.
|
||||
*
|
||||
* @param src the points to be transformed (as {@code [x, y, x, y, ...]}).
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param length the number of points to transform.
|
||||
*/
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int length) {
|
||||
int step = 2;
|
||||
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
|
||||
srcOff = srcOff + length * 2 - 2;
|
||||
dstOff = dstOff + length * 2 - 2;
|
||||
step = -2;
|
||||
}
|
||||
while (--length >= 0) {
|
||||
float x = src[srcOff + 0];
|
||||
float y = src[srcOff + 1];
|
||||
dst[dstOff + 0] = (x * m00 + y * m01 + m02);
|
||||
dst[dstOff + 1] = (x * m10 + y * m11 + m12);
|
||||
srcOff += step;
|
||||
dstOff += step;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied relative distance vector (ignores the translation component).
|
||||
*
|
||||
* @param src the point to be transformed.
|
||||
* @param dst the point in which to store the transformed values, if null a new instance will
|
||||
* be created. May be {@code src}.
|
||||
* @return the supplied (or created) destination point.
|
||||
*/
|
||||
public Point deltaTransform (IPoint src, Point dst) {
|
||||
if (dst == null) {
|
||||
dst = new Point();
|
||||
}
|
||||
float x = src.getX(), y = src.getY();
|
||||
dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied relative distance vectors using this transform's matrix (ignores the
|
||||
* translation component).
|
||||
*
|
||||
* @param src the points to be transformed (as {@code [x, y, x, y, ...]}).
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param length the number of points to transform.
|
||||
*/
|
||||
public void deltaTransform (float[] src, int srcOff, float[] dst, int dstOff, int length) {
|
||||
while (--length >= 0) {
|
||||
@Override // from Transform
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int count) {
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
float x = src[srcOff++], y = src[srcOff++];
|
||||
dst[dstOff++] = x * m00 + y * m01;
|
||||
dst[dstOff++] = x * m10 + y * m11;
|
||||
dst[dstOff++] = m00*x + m01*y + tx;
|
||||
dst[dstOff++] = m10*x + m11*y + ty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied point using the inverse of this transform's matrix.
|
||||
*
|
||||
* @param src the point to be transformed.
|
||||
* @param dst the point in which to store the transformed values, if null a new instance will
|
||||
* be created. May be {@code src}.
|
||||
* @return the supplied (or created) destination point.
|
||||
*/
|
||||
public Point inverseTransform (IPoint src, Point dst) throws NoninvertibleTransformException {
|
||||
float det = getDeterminant();
|
||||
if (Math.abs(det) < ZERO) {
|
||||
throw new NoninvertibleTransformException("Determinant is zero");
|
||||
}
|
||||
if (dst == null) {
|
||||
dst = new Point();
|
||||
}
|
||||
float x = src.getX() - m02, y = src.getY() - m12;
|
||||
dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det);
|
||||
return dst;
|
||||
@Override // from Transform
|
||||
public Point inverseTransform (IPoint p, Point into) {
|
||||
return invert().transform(p, into); // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the supplied points using the inverse of this transform's matrix.
|
||||
*
|
||||
* @param src the points to be transformed (as {@code [x, y, x, y, ...]}).
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param length the number of points to transform.
|
||||
*/
|
||||
public void inverseTransform (float[] src, int srcOff, float[] dst, int dstOff, int length)
|
||||
throws NoninvertibleTransformException {
|
||||
float det = getDeterminant();
|
||||
if (Math.abs(det) < ZERO) {
|
||||
throw new NoninvertibleTransformException("Determinant is zero");
|
||||
}
|
||||
while (--length >= 0) {
|
||||
float x = src[srcOff++] - m02, y = src[srcOff++] - m12;
|
||||
dst[dstOff++] = (x * m11 - y * m01) / det;
|
||||
dst[dstOff++] = (y * m00 - x * m10) / det;
|
||||
}
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
float x = v.getX(), y = v.getY();
|
||||
return into.set(m00*x + m01*y, m10*x + m11*y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new shape that is the supplied shape transformed by this transform's
|
||||
* matrix.
|
||||
*/
|
||||
public IShape createTransformedShape (IShape src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
if (src instanceof Path) {
|
||||
return ((Path)src).createTransformedShape(this);
|
||||
}
|
||||
PathIterator path = src.getPathIterator(this);
|
||||
Path dst = new Path(path.getWindingRule());
|
||||
dst.append(path, false);
|
||||
return dst;
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return invert().transform(v, into); // TODO
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
return new AffineTransform(m00, m01, m10, m11, tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public int generality () {
|
||||
return GENERALITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return getClass().getName() +
|
||||
"[[" + m00 + ", " + m01 + ", " + m02 + "], [" + m10 + ", " + m11 + ", " + m12 + "]]";
|
||||
return "affine [" + m00 + " " + m01 + " " + m10 + " " + m11 + " " + getTranslation() + "]";
|
||||
}
|
||||
|
||||
// @Override // can't declare @Override due to GWT
|
||||
public AffineTransform clone () {
|
||||
return new AffineTransform(this);
|
||||
// we don't publicize this because it might encourage someone to do something stupid like
|
||||
// create a new AffineTransform from another AffineTransform using this instead of clone()
|
||||
protected AffineTransform (Transform other) {
|
||||
this(other.getScaleX(), other.getScaleY(), other.getRotation(),
|
||||
other.getTx(), other.getTy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode () {
|
||||
return Platform.hashCode(m00) ^ Platform.hashCode(m01) ^ Platform.hashCode(m02) ^
|
||||
Platform.hashCode(m10) ^ Platform.hashCode(m11) ^ Platform.hashCode(m12);
|
||||
protected AffineTransform multiply (AffineTransform other) {
|
||||
return multiply(other.m00, other.m01, other.m10, other.m11, other.tx, other.ty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof AffineTransform) {
|
||||
AffineTransform t = (AffineTransform)obj;
|
||||
return m00 == t.m00 && m01 == t.m01 && m02 == t.m02 &&
|
||||
m10 == t.m10 && m11 == t.m11 && m12 == t.m12;
|
||||
}
|
||||
return false;
|
||||
protected AffineTransform multiply (float m00, float m01, float m10, float m11,
|
||||
float tx, float ty) {
|
||||
return new AffineTransform(
|
||||
this.m00 * m00 + this.m01 * m10, this.m00 * m01 + this.m01 * m11,
|
||||
this.m10 * m00 + this.m11 * m10, this.m10 * m01 + this.m11 * m11,
|
||||
this.m00 * tx + this.m01 * ty + this.tx, this.m10 * tx + this.m11 * ty + this.ty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two transforms, storing the result in the target transform.
|
||||
* @return the supplied target transform.
|
||||
*/
|
||||
protected static AffineTransform multiply (AffineTransform t1, AffineTransform t2,
|
||||
AffineTransform into) {
|
||||
into.setTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
|
||||
t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
|
||||
t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
|
||||
t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
|
||||
t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
|
||||
t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12); // m12
|
||||
return into;
|
||||
}
|
||||
|
||||
// the values of transformation matrix
|
||||
private float m00;
|
||||
private float m10;
|
||||
private float m01;
|
||||
private float m11;
|
||||
private float m02;
|
||||
private float m12;
|
||||
|
||||
/** The transformation {@code type}. */
|
||||
private transient int type;
|
||||
|
||||
/** An initial type value. */
|
||||
private static final int TYPE_UNKNOWN = -1;
|
||||
|
||||
/** The min value equivalent to zero. An absolute value < ZERO is considered to be zero. */
|
||||
private static final float ZERO = 1E-10f;
|
||||
private static final float ZERO = 1E-7f;
|
||||
}
|
||||
|
||||
@@ -112,15 +112,15 @@ public class Area implements IShape, Cloneable
|
||||
/**
|
||||
* Transforms this area with the supplied transform.
|
||||
*/
|
||||
public void transform (AffineTransform t) {
|
||||
copy(new Area(t.createTransformedShape(this)), this);
|
||||
public void transform (Transform t) {
|
||||
copy(new Area(Transforms.createTransformedShape(t, this)), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new area equal to this area transformed by the supplied transform.
|
||||
*/
|
||||
public Area createTransformedArea (AffineTransform t) {
|
||||
return new Area(t.createTransformedShape(this));
|
||||
public Area createTransformedArea (Transform t) {
|
||||
return new Area(Transforms.createTransformedShape(t, this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,12 +258,12 @@ public class Area implements IShape, Cloneable
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t) {
|
||||
public PathIterator getPathIterator (Transform t) {
|
||||
return new AreaPathIterator(t);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t, float flatness) {
|
||||
public PathIterator getPathIterator (Transform t, float flatness) {
|
||||
return new FlatteningPathIterator(getPathIterator(t), flatness);
|
||||
}
|
||||
|
||||
@@ -1159,11 +1159,11 @@ public class Area implements IShape, Cloneable
|
||||
// the internal class implements PathIterator
|
||||
private class AreaPathIterator implements PathIterator
|
||||
{
|
||||
private final AffineTransform transform;
|
||||
private final Transform transform;
|
||||
private int curRuleIndex = 0;
|
||||
private int curCoordIndex = 0;
|
||||
|
||||
AreaPathIterator (AffineTransform t) {
|
||||
AreaPathIterator (Transform t) {
|
||||
this.transform = t;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,20 @@ public class FloatMath
|
||||
/** The ratio of a circle's circumference to its diameter. */
|
||||
public static final float PI = (float)Math.PI;
|
||||
|
||||
/** The circle constant, tau (τ) http://tauday.com/ */
|
||||
public static final float TWO_PI = (float)(Math.PI * 2);
|
||||
/** The circle constant, Tau (τ) http://tauday.com/ */
|
||||
public static final float TAU = (float)(Math.PI * 2);
|
||||
|
||||
/** Twice Pi. */
|
||||
public static final float TWO_PI = TAU;
|
||||
|
||||
/** Pi times one half. */
|
||||
public static final float HALF_PI = PI * 0.5f;
|
||||
public static final float HALF_PI = (float)(Math.PI * 0.5);
|
||||
|
||||
/** The base value of the natural logarithm. */
|
||||
public static final float E = (float)Math.E;
|
||||
|
||||
/** A small number. */
|
||||
public static final float EPSILON = 0.000001f;
|
||||
public static final float EPSILON = 0.00001f;
|
||||
|
||||
/**
|
||||
* Computes and returns the sine of the given angle.
|
||||
|
||||
@@ -27,6 +27,31 @@ public interface IPoint extends Cloneable
|
||||
/** Returns the Euclidian distance between this point and the supplied point. */
|
||||
float distance (IPoint p);
|
||||
|
||||
/** Multiplies this point by a scale factor.
|
||||
* @return a new point containing the result. */
|
||||
Point mult (float s);
|
||||
|
||||
/** Multiplies this point by a scale factor and places the result in the supplied object.
|
||||
* @return a reference to the result, for chaining. */
|
||||
Point mult (float s, Point result);
|
||||
|
||||
/** Translates this point by the specified offset.
|
||||
* @return a new point containing the result. */
|
||||
Point add (float x, float y);
|
||||
|
||||
/** Translates this point by the specified offset and stores the result in the object provided.
|
||||
* @return a reference to the result, for chaining. */
|
||||
Point add (float x, float y, Point result);
|
||||
|
||||
/** Rotates this point around the origin by the specified angle.
|
||||
* @return a new point containing the result. */
|
||||
Point rotate (float angle);
|
||||
|
||||
/** Rotates this point around the origin by the specified angle, storing the result in the
|
||||
* point provided.
|
||||
* @return a reference to the result point, for chaining. */
|
||||
Point rotate (float angle, Point result);
|
||||
|
||||
/** Returns a mutable copy of this point. */
|
||||
Point clone ();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public interface IShape
|
||||
*
|
||||
* @param at if supplied, the points in the path are transformed using this.
|
||||
*/
|
||||
PathIterator getPathIterator (AffineTransform at);
|
||||
PathIterator getPathIterator (Transform at);
|
||||
|
||||
/**
|
||||
* Returns an iterator over the path described by this shape.
|
||||
@@ -52,5 +52,5 @@ public interface IShape
|
||||
* distance the lines are allowed to deviate from the approximated curve, thus a higher
|
||||
* flatness value generally allows for a path with fewer segments.
|
||||
*/
|
||||
PathIterator getPathIterator (AffineTransform at, float flatness);
|
||||
PathIterator getPathIterator (Transform at, float flatness);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Implements the identity transform.
|
||||
*/
|
||||
public class IdentityTransform extends AbstractTransform
|
||||
{
|
||||
/** Identifies the identity transform in {@link #generality}. */
|
||||
public static final int GENERALITY = 0;
|
||||
|
||||
@Override // from Transform
|
||||
public float getUniformScale () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleX () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleY () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getRotation () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTx () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTy () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform concatenate (Transform other) {
|
||||
return other;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
return other;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, float t) {
|
||||
throw new UnsupportedOperationException(); // TODO
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return into.set(p);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
transform(src[srcOff++], dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int count) {
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
dst[dstOff++] = src[srcOff++];
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point inverseTransform (IPoint p, Point into) {
|
||||
return into.set(p);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return into.set(v);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return into.set(v);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public int generality () {
|
||||
return GENERALITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "ident";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Implements a uniform (translation, rotation, scaleX, scaleY) transform.
|
||||
*/
|
||||
public class NonUniformTransform extends AbstractTransform
|
||||
{
|
||||
/** Identifies the uniform transform in {@link #generality}. */
|
||||
public static final int GENERALITY = 3;
|
||||
|
||||
/** The scale components of this transform. */
|
||||
public float scaleX, scaleY;
|
||||
|
||||
/** The rotation component of this transform (in radians). */
|
||||
public float rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public float tx, ty;
|
||||
|
||||
/** Creates a uniform transform with zero translation and rotation, and unit scale. */
|
||||
public NonUniformTransform () {
|
||||
this.scaleX = this.scaleY = 1;
|
||||
}
|
||||
|
||||
/** Creates a uniform transform with the specified translation, rotation and scale. */
|
||||
public NonUniformTransform (float scaleX, float scaleY, float rotation, float tx, float ty) {
|
||||
setScale(scaleX, scaleY);
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getUniformScale () {
|
||||
return (scaleX + scaleY) / 2; // TODO: is this sane
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleX () {
|
||||
return scaleX;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleY () {
|
||||
return scaleY;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getRotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTy () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (float scale) {
|
||||
setScaleX(scale);
|
||||
setScaleY(scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleX (float scaleX) {
|
||||
if (scaleX == 0) throw new IllegalArgumentException("Scale (x) must not be zero.");
|
||||
this.scaleX = scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleY (float scaleY) {
|
||||
if (scaleY == 0) throw new IllegalArgumentException("Scale (y) must not be zero.");
|
||||
this.scaleY = scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (float angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (float ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
Vector iscale = new Vector(1f / scaleX, 1f / scaleY);
|
||||
Vector t = new Vector(tx, ty).negateLocal().rotateLocal(-rotation).multLocal(iscale);
|
||||
return new NonUniformTransform(iscale.x, iscale.y, -rotation, t.x, t.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform concatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.preConcatenate(this);
|
||||
}
|
||||
|
||||
float otx = other.getTx(), oty = other.getTy();
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
float ntx = (otx*cosa - oty*sina) * scaleX + getTx();
|
||||
float nty = (otx*sina + oty*cosa) * scaleY + getTy();
|
||||
|
||||
float nrotation = FloatMath.normalizeAngle(rotation + other.getRotation());
|
||||
float nscaleX = scaleX * other.getScaleX();
|
||||
float nscaleY = scaleY * other.getScaleY();
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
|
||||
float tx = getTx(), ty = getTy();
|
||||
float sina = FloatMath.sin(other.getRotation()), cosa = FloatMath.cos(other.getRotation());
|
||||
float ntx = (tx*cosa - ty*sina) * other.getScaleX() + other.getTx();
|
||||
float nty = (tx*sina + ty*cosa) * other.getScaleY() + other.getTy();
|
||||
float nrotation = FloatMath.normalizeAngle(other.getRotation() + rotation);
|
||||
float nscaleX = other.getScaleX() * scaleX;
|
||||
float nscaleY = other.getScaleY() * scaleY;
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, float t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
|
||||
Vector nt = getTranslation().lerpLocal(other.getTranslation(), t);
|
||||
float nrotation = FloatMath.lerpa(rotation, other.getRotation(), t);
|
||||
float nscaleX = FloatMath.lerp(scaleX, other.getScaleX(), t);
|
||||
float nscaleY = FloatMath.lerp(scaleY, other.getScaleY(), t);
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.getX(), p.getY(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint s = src[srcOff++];
|
||||
Points.transform(s.getX(), s.getY(), scaleX, scaleY, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
Points.transform(src[srcOff++], src[srcOff++], scaleX, scaleY, sina, cosa, tx, ty, p);
|
||||
dst[dstOff++] = p.x;
|
||||
dst[dstOff++] = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point inverseTransform (IPoint p, Point into) {
|
||||
return Points.inverseTransform(p.getX(), p.getY(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.getX(), v.getY(), scaleX, scaleY, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.getX(), v.getY(), scaleX, scaleY, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
return new NonUniformTransform(scaleX, scaleY, rotation, tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public int generality () {
|
||||
return GENERALITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "nonunif [scale=" + getScale() + ", rot=" + rotation +
|
||||
", trans=" + getTranslation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -157,11 +157,11 @@ public final class Path implements IShape, Cloneable
|
||||
pointSize = 0;
|
||||
}
|
||||
|
||||
public void transform (AffineTransform t) {
|
||||
public void transform (Transform t) {
|
||||
t.transform(points, 0, points, 0, pointSize / 2);
|
||||
}
|
||||
|
||||
public IShape createTransformedShape (AffineTransform t) {
|
||||
public IShape createTransformedShape (Transform t) {
|
||||
Path p = clone();
|
||||
if (t != null) {
|
||||
p.transform(t);
|
||||
@@ -241,12 +241,12 @@ public final class Path implements IShape, Cloneable
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t) {
|
||||
public PathIterator getPathIterator (Transform t) {
|
||||
return new Iterator(this, t);
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t, float flatness) {
|
||||
public PathIterator getPathIterator (Transform t, float flatness) {
|
||||
return new FlatteningPathIterator(getPathIterator(t), flatness);
|
||||
}
|
||||
|
||||
@@ -309,13 +309,13 @@ public final class Path implements IShape, Cloneable
|
||||
private Path p;
|
||||
|
||||
/** The path iterator transformation. */
|
||||
private AffineTransform t;
|
||||
private Transform t;
|
||||
|
||||
Iterator (Path path) {
|
||||
this(path, null);
|
||||
}
|
||||
|
||||
Iterator (Path path, AffineTransform at) {
|
||||
Iterator (Path path, Transform at) {
|
||||
this.p = path;
|
||||
this.t = at;
|
||||
}
|
||||
|
||||
@@ -27,44 +27,46 @@ public class Point extends AbstractPoint implements Serializable
|
||||
* Constructs a point at the specified coordinates.
|
||||
*/
|
||||
public Point (float x, float y) {
|
||||
setLocation(x, y);
|
||||
set(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a point with coordinates equal to the supplied point.
|
||||
*/
|
||||
public Point (IPoint p) {
|
||||
setLocation(p.getX(), p.getY());
|
||||
set(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coordinates of this point to be equal to those of the supplied point.
|
||||
*/
|
||||
public void setLocation (IPoint p) {
|
||||
setLocation(p.getX(), p.getY());
|
||||
/** Sets the coordinates of this point to be equal to those of the supplied point.
|
||||
* @return a reference to this this, for chaining. */
|
||||
public Point set (IPoint p) {
|
||||
return set(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coordinates of this point to the supplied values.
|
||||
*/
|
||||
public void setLocation (float x, float y) {
|
||||
/** Sets the coordinates of this point to the supplied values.
|
||||
* @return a reference to this this, for chaining. */
|
||||
public Point set (float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A synonym for {@link #setLocation}.
|
||||
*/
|
||||
public void move (float x, float y) {
|
||||
setLocation(x, y);
|
||||
/** Multiplies this point by a scale factor.
|
||||
* @return a a reference to this point, for chaining. */
|
||||
public Point multLocal (float s) {
|
||||
return mult(s, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates this point by the specified offset.
|
||||
*/
|
||||
public void translate (float dx, float dy) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
/** Translates this point by the specified offset.
|
||||
* @return a reference to this point, for chaining. */
|
||||
public Point addLocal (float dx, float dy) {
|
||||
return add(dx, dy, this);
|
||||
}
|
||||
|
||||
/** Rotates this point in-place by the specified angle.
|
||||
* @return a reference to this point, for chaining. */
|
||||
public Point rotateLocal (float angle) {
|
||||
return rotate(angle, this);
|
||||
}
|
||||
|
||||
@Override // from interface IPoint
|
||||
|
||||
@@ -25,6 +25,32 @@ public class Points
|
||||
return (float)Math.sqrt(distanceSq(x1, y1, x2, y2));
|
||||
}
|
||||
|
||||
/** Transforms a point as specified, storing the result in the point provided.
|
||||
* @return a reference to the result point, for chaining. */
|
||||
public static Point transform (float x, float y, float sx, float sy, float rotation,
|
||||
float tx, float ty, Point result) {
|
||||
return transform(x, y, sx, sy, FloatMath.sin(rotation), FloatMath.cos(rotation), tx, ty,
|
||||
result);
|
||||
}
|
||||
|
||||
/** Transforms a point as specified, storing the result in the point provided.
|
||||
* @return a reference to the result point, for chaining. */
|
||||
public static Point transform (float x, float y, float sx, float sy, float sina, float cosa,
|
||||
float tx, float ty, Point result) {
|
||||
return result.set((x*cosa - y*sina) * sx + tx, (x*sina + y*cosa) * sy + ty);
|
||||
}
|
||||
|
||||
/** Inverse transforms a point as specified, storing the result in the point provided.
|
||||
* @return a reference to the result point, for chaining. */
|
||||
public static Point inverseTransform (float x, float y, float sx, float sy, float rotation,
|
||||
float tx, float ty, Point result) {
|
||||
x -= tx; y -= ty; // untranslate
|
||||
float sinnega = FloatMath.sin(-rotation), cosnega = FloatMath.cos(-rotation);
|
||||
float nx = (x * cosnega - y * sinnega); // unrotate
|
||||
float ny = (x * sinnega + y * cosnega);
|
||||
return result.set(nx / sx, ny / sy); // unscale
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing the supplied point, of the form <code>+x+y</code>,
|
||||
* <code>+x-y</code>, <code>-x-y</code>, etc.
|
||||
|
||||
@@ -170,7 +170,7 @@ public abstract class RectangularShape implements IRectangularShape
|
||||
}
|
||||
|
||||
@Override // from interface IShape
|
||||
public PathIterator getPathIterator (AffineTransform t, float flatness) {
|
||||
public PathIterator getPathIterator (Transform t, float flatness) {
|
||||
return new FlatteningPathIterator(getPathIterator(t), flatness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Implements a rigid body (translation, rotation) transform.
|
||||
*/
|
||||
public class RigidTransform extends AbstractTransform
|
||||
{
|
||||
/** Identifies the rigid body transform in {@link #generality}. */
|
||||
public static final int GENERALITY = 1;
|
||||
|
||||
/** The rotation component of this transform (in radians). */
|
||||
public float rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public float tx, ty;
|
||||
|
||||
/** Creates a rigid body transform with zero translation and rotation. */
|
||||
public RigidTransform () {
|
||||
}
|
||||
|
||||
/** Creates a rigid body transform with the specified translation and rotation. */
|
||||
public RigidTransform (float rotation, float tx, float ty) {
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getUniformScale () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleX () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleY () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getRotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTy () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (float angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (float ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
Vector t = getTranslation().negateLocal().rotateLocal(-rotation);
|
||||
return new RigidTransform(-rotation, t.x, t.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform concatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.preConcatenate(this);
|
||||
}
|
||||
|
||||
Vector nt = other.getTranslation();
|
||||
nt.rotateAndAdd(rotation, getTranslation(), nt);
|
||||
float nrotation = FloatMath.normalizeAngle(rotation + other.getRotation());
|
||||
return new RigidTransform(nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
|
||||
Vector nt = getTranslation();
|
||||
nt.rotateAndAdd(other.getRotation(), other.getTranslation(), nt);
|
||||
float nrotation = FloatMath.normalizeAngle(other.getRotation() + rotation);
|
||||
return new RigidTransform(nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, float t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
Vector nt = getTranslation().lerpLocal(other.getTranslation(), t);
|
||||
return new RigidTransform(FloatMath.lerpa(rotation, other.getRotation(), t), nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.getX(), p.getY(), 1, 1, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint s = src[srcOff++];
|
||||
Points.transform(s.getX(), s.getY(), 1, 1, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
Points.transform(src[srcOff++], src[srcOff++], 1, 1, sina, cosa, tx, ty, p);
|
||||
dst[dstOff++] = p.x;
|
||||
dst[dstOff++] = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point inverseTransform (IPoint p, Point into) {
|
||||
return Points.inverseTransform(p.getX(), p.getY(), 1, 1, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return v.rotate(rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return v.rotate(-rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
return new RigidTransform(rotation, tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public int generality () {
|
||||
return GENERALITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "rigid [rot=" + rotation + ", trans=" + getTranslation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -6,52 +6,32 @@ package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Represents a geometric transform. Specialized implementations exist for identity, rigid body,
|
||||
* uniform, affine and general transforms.
|
||||
* uniform, non-uniform, and affine transforms.
|
||||
*/
|
||||
public interface Transform
|
||||
{
|
||||
/** Sets the translation component of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
void setTranslation (float tx, float ty);
|
||||
/** Returns the uniform scale applied by this transform. The uniform scale will be approximated
|
||||
* for non-uniform transforms. */
|
||||
float getUniformScale ();
|
||||
|
||||
/** Sets the x-component of this transform's translation.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
void setTx (float tx);
|
||||
/** Returns the scale vector for this transform. */
|
||||
Vector getScale ();
|
||||
|
||||
/** Sets the y-component of this transform's translation.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
void setTy (float ty);
|
||||
/** Returns the x-component of the scale applied by this transform. Note that this will be
|
||||
* extracted and therefore approximate for affine transforms. */
|
||||
float getScaleX ();
|
||||
|
||||
/** Sets the rotation component of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
void setRotation (float angle);
|
||||
/** Returns the y-component of the scale applied by this transform. Note that this will be
|
||||
* extracted and therefore approximate for affine transforms. */
|
||||
float getScaleY ();
|
||||
|
||||
/** Sets the uniform scale of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not uniform or greater. */
|
||||
void setScale (float scale);
|
||||
/** Returns the rotation applied by this transform. Note that the rotation is extracted and
|
||||
* therefore approximate for affine transforms.
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
float getRotation ();
|
||||
|
||||
/** Sets the x and y scale of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
void setScale (float scaleX, float scaleY);
|
||||
|
||||
/** Sets the x scale of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
void setScaleX (float scaleX);
|
||||
|
||||
/** Sets the y scale of this transform.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
void setScaleY (float scaleY);
|
||||
|
||||
/** Sets the affine transform matrix.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
void setTransform (float m00, float m01, float m10, float m11,
|
||||
float tx, float ty);
|
||||
|
||||
/** Sets the general transform matrix.
|
||||
* @throws UnsupportedOperationException if the transform is not general. */
|
||||
void setTransform (float m00, float m01, float m02,
|
||||
float m10, float m11, float m12,
|
||||
float m20, float m21, float m22);
|
||||
/** Returns the translation vector for this transform. */
|
||||
Vector getTranslation ();
|
||||
|
||||
/** Returns the x-coordinate of the translation component. */
|
||||
float getTx ();
|
||||
@@ -59,45 +39,114 @@ public interface Transform
|
||||
/** Returns the y-coordinate of the translation component. */
|
||||
float getTy ();
|
||||
|
||||
/** Returns the rotation applied by this transform. Note that the rotation is extracted and
|
||||
* therefore approximate for affine and general transforms. */
|
||||
float getRotation (); // will be extracted from affine+
|
||||
/** Sets the uniform scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not uniform or greater. */
|
||||
Transform setUniformScale (float scale);
|
||||
|
||||
/** Returns the uniform scale applied by this transform. Note that the uniform scale will be
|
||||
* approximated for non-uniform transforms (affine and general). */
|
||||
float getScale (); // will be extracted/approximated for affine+
|
||||
/** Sets the x and y scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if either supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
Transform setScale (float scaleX, float scaleY);
|
||||
|
||||
/** Returns the x-component of the scale applied by this transform. */
|
||||
float getScaleX (); // will be extracted from affine+
|
||||
/** Sets the x scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
Transform setScaleX (float scaleX);
|
||||
|
||||
/** Returns the y-component of the scale applied by this transform. */
|
||||
float getScaleY (); // will be extracted from affine+
|
||||
/** Sets the y scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
Transform setScaleY (float scaleY);
|
||||
|
||||
/** Returns the inverse of this transform.
|
||||
/** Sets the rotation component of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform setRotation (float angle);
|
||||
|
||||
/** Sets the translation component of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform setTranslation (float tx, float ty);
|
||||
|
||||
/** Sets the x-component of this transform's translation.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform setTx (float tx);
|
||||
|
||||
/** Sets the y-component of this transform's translation.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform setTy (float ty);
|
||||
|
||||
/** Sets the affine transform matrix.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
Transform setTransform (float m00, float m01, float m10, float m11,
|
||||
float tx, float ty);
|
||||
|
||||
/** Returns a new transform that represents the inverse of this transform.
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
Transform invert ();
|
||||
|
||||
/** Composes this transform with the supplied transform (i.e. {@code this x other}). */
|
||||
Transform compose (Transform other);
|
||||
/** Returns a new transform comprised of the concatenation of {@code other} to this transform
|
||||
* (i.e. {@code this x other}). */
|
||||
Transform concatenate (Transform other);
|
||||
|
||||
/** Returns the linear interpolation between this transform and the specified other. */
|
||||
/** Returns a new transform comprised of the concatenation of this transform to {@code other}
|
||||
* (i.e. {@code other x this}). */
|
||||
Transform preConcatenate (Transform other);
|
||||
|
||||
/** Returns a new transform comprised of the linear interpolation between this transform and
|
||||
* the specified other. */
|
||||
Transform lerp (Transform other, float t);
|
||||
|
||||
/** Transforms the supplied point, writing the result into {@code into}, which may reference
|
||||
* the same object as {@code p}. */
|
||||
void transform (IPoint p, Point into);
|
||||
/** Transforms the supplied point, writing the result into {@code into}.
|
||||
* @param into a point into which to store the result, may be the same object as {@code p}.
|
||||
* @return {@code into} for chaining. */
|
||||
Point transform (IPoint p, Point into);
|
||||
|
||||
/** Inverse transforms the supplied point, writing the result into {@code into}, which may
|
||||
* reference the same object as {@code p}.
|
||||
/** Transforms the supplied points.
|
||||
* @param src the points to be transformed.
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param count the number of points to transform. */
|
||||
void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count);
|
||||
|
||||
/** Transforms the supplied points.
|
||||
* @param src the points to be transformed (as {@code [x, y, x, y, ...]}).
|
||||
* @param srcOff the offset into the {@code src} array at which to start.
|
||||
* @param dst the points into which to store the transformed points. May be {@code src}.
|
||||
* @param dstOff the offset into the {@code dst} array at which to start.
|
||||
* @param count the number of points to transform. */
|
||||
void transform (float[] src, int srcOff, float[] dst, int dstOff, int count);
|
||||
|
||||
/** Inverse transforms the supplied point, writing the result into {@code into}.
|
||||
* @param into a point into which to store the result, may be the same object as {@code p}.
|
||||
* @return {@code into}, for chaining.
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
void inverseTransform (IPoint p, Point into);
|
||||
Point inverseTransform (IPoint p, Point into);
|
||||
|
||||
/** Transforms the supplied vector, writing the result into {@code into}, which may reference
|
||||
* the same object as {@code v}. */
|
||||
void transform (IVector v, Vector into);
|
||||
/** Transforms the supplied vector, writing the result into {@code into}.
|
||||
* @param into a vector into which to store the result, may be the same object as {@code v}.
|
||||
* @return {@code into}, for chaining. */
|
||||
Vector transform (IVector v, Vector into);
|
||||
|
||||
/** Inverse transforms the supplied vector, writing the result into {@code into}, which may
|
||||
* reference the same object as {@code v}.
|
||||
/** Inverse transforms the supplied vector, writing the result into {@code into}.
|
||||
* @param into a vector into which to store the result, may be the same object as {@code v}.
|
||||
* @return {@code into}, for chaining.
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
void inverseTransform (IVector v, Vector into);
|
||||
Vector inverseTransform (IVector v, Vector into);
|
||||
|
||||
/** Returns a clone of this transform. */
|
||||
Transform clone ();
|
||||
|
||||
/** Returns an integer that increases monotonically with the generality of the transform
|
||||
* implementation. Used internally when combining transforms. */
|
||||
int generality ();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* {@link Transform} related utility methods.
|
||||
*/
|
||||
public class Transforms
|
||||
{
|
||||
/**
|
||||
* Creates and returns a new shape that is the supplied shape transformed by this transform's
|
||||
* matrix.
|
||||
*/
|
||||
public static IShape createTransformedShape (Transform t, IShape src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
if (src instanceof Path) {
|
||||
return ((Path)src).createTransformedShape(t);
|
||||
}
|
||||
PathIterator path = src.getPathIterator(t);
|
||||
Path dst = new Path(path.getWindingRule());
|
||||
dst.append(path, false);
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
/**
|
||||
* Implements a uniform (translation, rotation, scale) transform.
|
||||
*/
|
||||
public class UniformTransform extends AbstractTransform
|
||||
{
|
||||
/** Identifies the uniform transform in {@link #generality}. */
|
||||
public static final int GENERALITY = 2;
|
||||
|
||||
/** The uniform scale component of this transform. */
|
||||
public float scale;
|
||||
|
||||
/** The rotation component of this transform (in radians). */
|
||||
public float rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public float tx, ty;
|
||||
|
||||
/** Creates a uniform transform with zero translation and rotation, and unit scale. */
|
||||
public UniformTransform () {
|
||||
setUniformScale(1);
|
||||
}
|
||||
|
||||
/** Creates a uniform transform with the specified translation, rotation and scale. */
|
||||
public UniformTransform (float scale, float rotation, float tx, float ty) {
|
||||
setUniformScale(scale);
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getUniformScale () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleX () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getScaleY () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getRotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float getTy () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (float scale) {
|
||||
if (scale == 0) throw new IllegalArgumentException("Scale must be non-zero.");
|
||||
this.scale = scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (float angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (float ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
float nscale = 1f / scale, nrotation = -rotation;
|
||||
Vector t = getTranslation().negateLocal().rotateLocal(nrotation).multLocal(nscale);
|
||||
return new UniformTransform(nscale, nrotation, t.x, t.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform concatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.preConcatenate(this);
|
||||
}
|
||||
|
||||
Vector nt = other.getTranslation();
|
||||
nt.rotateScaleAndAdd(rotation, scale, getTranslation(), nt);
|
||||
float nrotation = FloatMath.normalizeAngle(rotation + other.getRotation());
|
||||
float nscale = scale * other.getUniformScale();
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
|
||||
Vector nt = getTranslation();
|
||||
nt.rotateScaleAndAdd(other.getRotation(), other.getUniformScale(),
|
||||
other.getTranslation(), nt);
|
||||
float nrotation = FloatMath.normalizeAngle(other.getRotation() + rotation);
|
||||
float nscale = other.getUniformScale() * scale;
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, float t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
|
||||
Vector nt = getTranslation().lerpLocal(other.getTranslation(), t);
|
||||
float nrotation = FloatMath.lerpa(rotation, other.getRotation(), t);
|
||||
float nscale = FloatMath.lerp(scale, other.getUniformScale(), t);
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.getX(), p.getY(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint p = src[srcOff++];
|
||||
Points.transform(p.getX(), p.getY(), scale, scale, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (float[] src, int srcOff, float[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
Points.transform(src[srcOff++], src[srcOff++], scale, scale, sina, cosa, tx, ty, p);
|
||||
dst[dstOff++] = p.x;
|
||||
dst[dstOff++] = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point inverseTransform (IPoint p, Point into) {
|
||||
return Points.inverseTransform(p.getX(), p.getY(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.getX(), v.getY(), scale, scale, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.getX(), v.getY(), scale, scale, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
return new UniformTransform(scale, rotation, tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public int generality () {
|
||||
return GENERALITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "uniform [scale=" + scale + ", rot=" + rotation +
|
||||
", trans=" + getTranslation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -29,16 +29,6 @@ public class Vector extends AbstractVector
|
||||
public Vector () {
|
||||
}
|
||||
|
||||
@Override // from AbstractVector
|
||||
public float getX () {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override // from AbstractVector
|
||||
public float getY () {
|
||||
return y;
|
||||
}
|
||||
|
||||
/** Negates this vector in-place.
|
||||
* @return a reference to this vector, for chaining. */
|
||||
public Vector negateLocal () {
|
||||
@@ -118,4 +108,14 @@ public class Vector extends AbstractVector
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from AbstractVector
|
||||
public float getX () {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override // from AbstractVector
|
||||
public float getY () {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,50 @@ public class Vectors
|
||||
/** A vector containing the maximum floating point value for all components. */
|
||||
public static final IVector MAX_VALUE = new Vector(Float.MAX_VALUE, Float.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* Returns the magnitude of the specified vector.
|
||||
*/
|
||||
public static final float length (float x, float y) {
|
||||
return FloatMath.sqrt(lengthSq(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square of the magnitude of the specified vector.
|
||||
*/
|
||||
public static final float lengthSq (float x, float y) {
|
||||
return (x*x + y*y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a point as specified, storing the result in the point provided.
|
||||
* @return a reference to the result vector, for chaining.
|
||||
*/
|
||||
public static Vector transform (float x, float y, float sx, float sy, float rotation,
|
||||
Vector result) {
|
||||
return transform(x, y, sx, sy, FloatMath.sin(rotation), FloatMath.cos(rotation), result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a vector as specified, storing the result in the vector provided.
|
||||
* @return a reference to the result vector, for chaining.
|
||||
*/
|
||||
public static Vector transform (float x, float y, float sx, float sy, float sina, float cosa,
|
||||
Vector result) {
|
||||
return result.set((x*cosa - y*sina) * sx, (x*sina + y*cosa) * sy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse transforms a point as specified, storing the result in the point provided.
|
||||
* @return a reference to the result vector, for chaining.
|
||||
*/
|
||||
public static Vector inverseTransform (float x, float y, float sx, float sy, float rotation,
|
||||
Vector result) {
|
||||
float sinnega = FloatMath.sin(-rotation), cosnega = FloatMath.cos(-rotation);
|
||||
float nx = (x * cosnega - y * sinnega); // unrotate
|
||||
float ny = (x * sinnega + y * cosnega);
|
||||
return result.set(nx / sx, ny / sy); // unscale
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing the supplied vector, of the form <code>+x+y</code>,
|
||||
* <code>+x-y</code>, <code>-x-y</code>, etc.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package pythagoras.util;
|
||||
|
||||
/**
|
||||
* An exception thrown by {@code AffineTransform} when a request for an inverse transform cannot be
|
||||
* An exception thrown by {@code Transform} when a request for an inverse transform cannot be
|
||||
* satisfied.
|
||||
*/
|
||||
public class NoninvertibleTransformException extends java.lang.RuntimeException
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests the various transform implementations.
|
||||
*/
|
||||
public class TransformTest
|
||||
{
|
||||
@Test public void testTranslate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < RigidTransform.GENERALITY) continue;
|
||||
for (Point trans : TRANS) {
|
||||
Transform t = proto.clone();
|
||||
t.setTranslation(trans.x, trans.y);
|
||||
for (Point point : POINTS) {
|
||||
test(t, point, point.add(trans.x, trans.y));
|
||||
}
|
||||
for (Vector vec : VECTORS) {
|
||||
test(t, vec, vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testRotate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < RigidTransform.GENERALITY) continue;
|
||||
for (float angle : ANGLES) {
|
||||
Transform t = proto.clone();
|
||||
t.setRotation(angle);
|
||||
for (Point point : POINTS) {
|
||||
test(t, point, point.rotate(angle));
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
test(t, vector, vector.rotate(angle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testScale () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (Point point : POINTS) {
|
||||
for (float scale : SCALES) {
|
||||
Transform t = proto.clone();
|
||||
t.setUniformScale(scale);
|
||||
test(t, point, point.mult(scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testTranslateRotate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < RigidTransform.GENERALITY) continue;
|
||||
for (Point trans : TRANS) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setTranslation(trans.x, trans.y);
|
||||
for (float angle : ANGLES) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setRotation(angle);
|
||||
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.add(trans.x, trans.y).rotateLocal(angle);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.rotate(angle);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testRotateTranslate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < RigidTransform.GENERALITY) continue;
|
||||
for (float angle : ANGLES) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setRotation(angle);
|
||||
for (Point trans : TRANS) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setTranslation(trans.x, trans.y);
|
||||
|
||||
// test that a single transform rotates then translates
|
||||
Transform t = proto.clone();
|
||||
t.setRotation(angle);
|
||||
t.setTranslation(trans.x, trans.y);
|
||||
|
||||
// test explicitly via concatenation
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.rotate(angle).addLocal(trans.x, trans.y);
|
||||
test(t, point, expect);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.rotate(angle);
|
||||
test(t, vector, expect);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testTranslateScale () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (Point trans : TRANS) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setTranslation(trans.x, trans.y);
|
||||
for (float scale : SCALES) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setUniformScale(scale);
|
||||
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.add(trans.x, trans.y).multLocal(scale);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testScaleTranslate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (float scale : SCALES) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setUniformScale(scale);
|
||||
for (Point trans : TRANS) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setTranslation(trans.x, trans.y);
|
||||
|
||||
// test that a single transform scales then translates
|
||||
Transform t = proto.clone();
|
||||
t.setUniformScale(scale);
|
||||
t.setTranslation(trans.x, trans.y);
|
||||
|
||||
// test explicitly via concatenation
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.mult(scale).addLocal(trans.x, trans.y);
|
||||
test(t, point, expect);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale);
|
||||
test(t, vector, expect);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testRotateScale () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (float angle : ANGLES) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setRotation(angle);
|
||||
for (float scale : SCALES) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setUniformScale(scale);
|
||||
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.rotate(angle).multLocal(scale);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.rotate(angle).multLocal(scale);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testScaleRotate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (float scale : SCALES) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setUniformScale(scale);
|
||||
for (float angle : ANGLES) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setRotation(angle);
|
||||
|
||||
// test explicitly via concatenation
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.mult(scale).rotateLocal(angle);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale).rotateLocal(angle);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
|
||||
// if we have an affine transform, we cannot set the scale and then set the
|
||||
// rotation, because setting the rotation will first extract the scale and then
|
||||
// reapply it, losing the sign of the scale in the process
|
||||
if (proto.generality() >= AffineTransform.GENERALITY) continue;
|
||||
|
||||
// test that a single transform scales then rotates
|
||||
Transform t = proto.clone();
|
||||
t.setUniformScale(scale);
|
||||
t.setRotation(angle);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.mult(scale).rotateLocal(angle);
|
||||
test(t, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale).rotateLocal(angle);
|
||||
test(t, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testScaleRotateTranslate () {
|
||||
for (Transform proto : createTransforms()) {
|
||||
if (proto.generality() < UniformTransform.GENERALITY) continue;
|
||||
for (float scale : SCALES) {
|
||||
Transform t1 = proto.clone();
|
||||
t1.setUniformScale(scale);
|
||||
for (float angle : ANGLES) {
|
||||
Transform t2 = proto.clone();
|
||||
t2.setRotation(angle);
|
||||
for (Point trans : TRANS) {
|
||||
Transform t3 = proto.clone();
|
||||
t3.setTranslation(trans.x, trans.y);
|
||||
|
||||
// test explicitly via concatenation
|
||||
Transform tpost = t3.concatenate(t2).concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2.preConcatenate(t3));
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.mult(scale).rotateLocal(angle).
|
||||
addLocal(trans.x, trans.y);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale).rotateLocal(angle);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
|
||||
// if we have an affine transform, we cannot set the scale and then set the
|
||||
// rotation, because setting the rotation will first extract the scale and
|
||||
// then reapply it, losing the sign of the scale in the process
|
||||
if (proto.generality() >= AffineTransform.GENERALITY) continue;
|
||||
|
||||
// test that a single transform scales, rotates, then translates
|
||||
Transform t = proto.clone();
|
||||
t.setUniformScale(scale);
|
||||
t.setRotation(angle);
|
||||
t.setTranslation(trans.x, trans.y);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.mult(scale).rotateLocal(angle).
|
||||
addLocal(trans.x, trans.y);
|
||||
test(t, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.mult(scale).rotateLocal(angle);
|
||||
test(t, vector, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void test (Transform t, Point p, Point expect) {
|
||||
Point orig = new Point(p);
|
||||
String desc = t + " @ " + p;
|
||||
|
||||
// test single point transform and inverse transform
|
||||
Point tp = t.transform(p, new Point());
|
||||
Point itp = t.inverseTransform(tp, new Point());
|
||||
assertEquals(desc, orig, p);
|
||||
assertPointsEqual(desc, expect, tp);
|
||||
assertPointsEqual(desc, p, itp);
|
||||
|
||||
// test multipoint transform
|
||||
Point[] ps = new Point[] { null, p, null };
|
||||
Point[] tps = new Point[] { null, new Point(), null };
|
||||
t.transform(ps, 1, tps, 1, 1);
|
||||
assertEquals(desc, orig, p);
|
||||
assertEquals(desc, null, tps[0]);
|
||||
assertPointsEqual(desc, expect, tps[1]);
|
||||
assertEquals(desc, null, tps[2]);
|
||||
}
|
||||
|
||||
protected void assertPointsEqual (String desc, Point p1, Point p2) {
|
||||
assertEquals(desc + " = " + p1, p1.x, p2.x, FloatMath.EPSILON);
|
||||
assertEquals(desc + " = " + p1, p1.y, p2.y, FloatMath.EPSILON);
|
||||
}
|
||||
|
||||
protected void test (Transform t, Vector v, Vector expect) {
|
||||
Vector orig = new Vector(v);
|
||||
String desc = t + " @ " + v;
|
||||
|
||||
// test vector transform and inverse transform
|
||||
Vector tv = t.transform(v, new Vector());
|
||||
Vector itv = t.inverseTransform(tv, new Vector());
|
||||
assertEquals(desc, orig, v);
|
||||
assertVectorsEqual(desc, expect, tv);
|
||||
assertVectorsEqual(desc, v, itv);
|
||||
}
|
||||
|
||||
protected void assertVectorsEqual (String desc, Vector v1, Vector v2) {
|
||||
assertEquals(desc + " = " + v1, v1.x, v2.x, FloatMath.EPSILON);
|
||||
assertEquals(desc + " = " + v1, v1.y, v2.y, FloatMath.EPSILON);
|
||||
}
|
||||
|
||||
protected Transform[] createTransforms () {
|
||||
return new Transform[] {
|
||||
new IdentityTransform(),
|
||||
new RigidTransform(),
|
||||
new UniformTransform(),
|
||||
new NonUniformTransform(),
|
||||
new AffineTransform(),
|
||||
};
|
||||
}
|
||||
|
||||
protected static final Point[] POINTS = {
|
||||
new Point(0, 0), new Point(FloatMath.TAU, FloatMath.E),
|
||||
new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1),
|
||||
new Point(1, 1), new Point(-1, 1), new Point(-1, -1), new Point(1, -1)
|
||||
};
|
||||
protected static final Vector[] VECTORS = {
|
||||
new Vector(0, 0), new Vector(FloatMath.TAU, FloatMath.E),
|
||||
new Vector(1, 0), new Vector(0, 1), new Vector(-1, 0), new Vector(0, -1),
|
||||
new Vector(1, 1), new Vector(-1, 1), new Vector(-1, -1), new Vector(1, -1)
|
||||
};
|
||||
protected static final float[] ANGLES = {
|
||||
0, FloatMath.PI/2, FloatMath.PI, FloatMath.PI*3/2,
|
||||
-FloatMath.PI/2, -FloatMath.PI, -FloatMath.PI*3/2
|
||||
};
|
||||
protected static final float[] SCALES = { 0.5f, 1, 1.5f, -0.5f, -1, -1.5f };
|
||||
|
||||
protected static final float[] DXS = { -25, 0, 25 };
|
||||
protected static final float[] DYS = { -25, 0, 25 };
|
||||
protected static final Point[] TRANS = new Point[DXS.length * DYS.length];
|
||||
static {
|
||||
int ii = 0;
|
||||
for (float dx : DXS) {
|
||||
for (float dy : DYS) {
|
||||
TRANS[ii++] = new Point(dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user