Numerous bits:
- nixed all transforms except identity and affine; the intermediate transforms were more trouble than they were worth - fixed bugs in AffineTransform.translate/scaleX/scaleY/rotate; aiya! - replaced Transform.clone with Transform.copy; deprecated clone - rewrote transform test in Scala and using Java AffineTransform as a reference.
This commit is contained in:
@@ -112,6 +112,11 @@ public abstract class AbstractTransform implements Transform
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Deprecated @Override // from Transform
|
||||
public Transform clone () {
|
||||
return copy();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public abstract Transform clone ();
|
||||
public abstract Transform copy ();
|
||||
}
|
||||
|
||||
@@ -189,26 +189,25 @@ public class AffineTransform extends AbstractTransform
|
||||
return scale(scale, scale);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scale (double scaleX, double scaleY) {
|
||||
return Transforms.multiply(this, scaleX, 0, 0, scaleY, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (double scaleX) {
|
||||
m00 *= scaleX;
|
||||
m01 *= scaleX;
|
||||
tx *= scaleX;
|
||||
return this;
|
||||
return Transforms.multiply(this, scaleX, 0, 0, 1, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (double scaleY) {
|
||||
m10 *= scaleY;
|
||||
m11 *= scaleY;
|
||||
ty *= scaleY;
|
||||
return this;
|
||||
return Transforms.multiply(this, 1, 0, 0, scaleY, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (double angle) {
|
||||
double sina = Math.sin(angle), cosa = Math.cos(angle);
|
||||
return Transforms.multiply(cosa, sina, -sina, cosa, 0, 0, this, this);
|
||||
return Transforms.multiply(this, cosa, sina, -sina, cosa, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
@@ -342,7 +341,7 @@ public class AffineTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
public Transform copy () {
|
||||
return new AffineTransform(m00, m01, m10, m11, tx, ty);
|
||||
}
|
||||
|
||||
@@ -358,7 +357,7 @@ public class AffineTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
// 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()
|
||||
// create a new AffineTransform from another AffineTransform using this instead of copy()
|
||||
protected AffineTransform (Transform other) {
|
||||
this(other.scaleX(), other.scaleY(), other.rotation(),
|
||||
other.tx(), other.ty());
|
||||
|
||||
@@ -109,7 +109,7 @@ public class IdentityTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
public Transform copy () {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.d;
|
||||
|
||||
/**
|
||||
* 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 double scaleX, scaleY;
|
||||
|
||||
/** The rotation component of this transform (in radians). */
|
||||
public double rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public double 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 (double scaleX, double scaleY, double rotation, double tx, double ty) {
|
||||
setScale(scaleX, scaleY);
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double uniformScale () {
|
||||
return (scaleX + scaleY) / 2; // TODO: is this sane
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleX () {
|
||||
return scaleX;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleY () {
|
||||
return scaleY;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (double[] matrix) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
matrix[0] = cosa * scaleX; matrix[1] = sina * scaleY;
|
||||
matrix[2] = -sina * scaleX; matrix[3] = cosa * scaleY;
|
||||
matrix[4] = tx; matrix[5] = ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (double scale) {
|
||||
setScaleX(scale);
|
||||
setScaleY(scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleX (double scaleX) {
|
||||
if (scaleX == 0) throw new IllegalArgumentException("Scale (x) must not be zero.");
|
||||
this.scaleX = scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setScaleY (double scaleY) {
|
||||
if (scaleY == 0) throw new IllegalArgumentException("Scale (y) must not be zero.");
|
||||
this.scaleY = scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (double angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (double tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (double ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform uniformScale (double scale) {
|
||||
return scale(scale, scale);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (double scaleX) {
|
||||
if (scaleX == 0) throw new IllegalArgumentException("Scale (x) must not be zero.");
|
||||
this.tx *= scaleX;
|
||||
this.scaleX *= scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (double scaleY) {
|
||||
if (scaleY == 0) throw new IllegalArgumentException("Scale (y) must not be zero.");
|
||||
this.ty *= scaleX;
|
||||
this.scaleY *= scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (double angle) {
|
||||
double otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
double sina = Math.sin(angle), cosa = Math.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (double tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (double 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).scaleLocal(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);
|
||||
}
|
||||
|
||||
double otx = other.tx(), oty = other.ty();
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
double ntx = (otx*cosa - oty*sina) * scaleX + tx();
|
||||
double nty = (otx*sina + oty*cosa) * scaleY + ty();
|
||||
|
||||
double nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
double nscaleX = scaleX * other.scaleX();
|
||||
double nscaleY = scaleY * other.scaleY();
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform preConcatenate (Transform other) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
|
||||
double tx = tx(), ty = ty();
|
||||
double sina = Math.sin(other.rotation()), cosa = Math.cos(other.rotation());
|
||||
double ntx = (tx*cosa - ty*sina) * other.scaleX() + other.tx();
|
||||
double nty = (tx*sina + ty*cosa) * other.scaleY() + other.ty();
|
||||
double nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
|
||||
double nscaleX = other.scaleX() * scaleX;
|
||||
double nscaleY = other.scaleY() * scaleY;
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, double t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
|
||||
double ntx = MathUtil.lerpa(tx, other.tx(), t);
|
||||
double nty = MathUtil.lerpa(ty, other.ty(), t);
|
||||
double nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
|
||||
double nscaleX = MathUtil.lerp(scaleX, other.scaleX(), t);
|
||||
double nscaleY = MathUtil.lerp(scaleY, other.scaleY(), t);
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint s = src[srcOff++];
|
||||
Points.transform(s.x(), s.y(), scaleX, scaleY, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (double[] src, int srcOff, double[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
double sina = Math.sin(rotation), cosa = Math.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.x(), p.y(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.x(), v.y(), 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=" + scale() + ", rot=" + rotation +
|
||||
", trans=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.d;
|
||||
|
||||
/**
|
||||
* 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 double rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public double 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 (double rotation, double tx, double ty) {
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double uniformScale () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleX () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleY () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (double[] matrix) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
matrix[0] = cosa; matrix[1] = sina;
|
||||
matrix[2] = -sina; matrix[3] = cosa;
|
||||
matrix[4] = tx; matrix[5] = ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (double angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (double tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (double ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (double angle) {
|
||||
double otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
double sina = Math.sin(angle), cosa = Math.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (double tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (double ty) {
|
||||
this.ty += ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
Vector t = translation().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.translation();
|
||||
nt.rotateAndAdd(rotation, translation(), nt);
|
||||
double nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
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 = translation();
|
||||
nt.rotateAndAdd(other.rotation(), other.translation(), nt);
|
||||
double nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
|
||||
return new RigidTransform(nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, double t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
Vector nt = translation().lerpLocal(other.translation(), t);
|
||||
return new RigidTransform(MathUtil.lerpa(rotation, other.rotation(), t), nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), 1, 1, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint s = src[srcOff++];
|
||||
Points.transform(s.x(), s.y(), 1, 1, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (double[] src, int srcOff, double[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
double sina = Math.sin(rotation), cosa = Math.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.x(), p.y(), 1, 1, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), 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=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -197,8 +197,11 @@ public interface Transform
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
Vector inverseTransform (IVector v, Vector into);
|
||||
|
||||
/** Returns a clone of this transform. */
|
||||
Transform clone ();
|
||||
/** @deprecated Use {@link #copy}. */
|
||||
@Deprecated Transform clone ();
|
||||
|
||||
/** Returns a copy of this transform. */
|
||||
Transform copy ();
|
||||
|
||||
/** Returns an integer that increases monotonically with the generality of the transform
|
||||
* implementation. Used internally when combining transforms. */
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.d;
|
||||
|
||||
/**
|
||||
* 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 double scale;
|
||||
|
||||
/** The rotation component of this transform (in radians). */
|
||||
public double rotation;
|
||||
|
||||
/** The translation components of this transform. */
|
||||
public double 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 (double scale, double rotation, double tx, double ty) {
|
||||
setUniformScale(scale);
|
||||
setRotation(rotation);
|
||||
setTranslation(tx, ty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double uniformScale () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleX () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double scaleY () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public double ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (double[] matrix) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
matrix[0] = cosa * scale; matrix[1] = sina * scale;
|
||||
matrix[2] = -sina * scale; matrix[3] = cosa * scale;
|
||||
matrix[4] = tx; matrix[5] = ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setUniformScale (double scale) {
|
||||
if (scale == 0) throw new IllegalArgumentException("Scale must be non-zero.");
|
||||
this.scale = scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setRotation (double angle) {
|
||||
this.rotation = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (double tx) {
|
||||
this.tx = tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTy (double ty) {
|
||||
this.ty = ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform uniformScale (double scale) {
|
||||
if (scale == 0) throw new IllegalArgumentException("Scale must be non-zero.");
|
||||
this.tx *= scale;
|
||||
this.ty *= scale;
|
||||
this.scale *= scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (double angle) {
|
||||
double otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
double sina = Math.sin(angle), cosa = Math.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (double tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (double ty) {
|
||||
this.ty += ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
double nscale = 1f / scale, nrotation = -rotation;
|
||||
Vector t = translation().negateLocal().rotateLocal(nrotation).scaleLocal(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.translation();
|
||||
nt.rotateScaleAndAdd(rotation, scale, translation(), nt);
|
||||
double nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
double nscale = scale * other.uniformScale();
|
||||
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 = translation();
|
||||
nt.rotateScaleAndAdd(other.rotation(), other.uniformScale(),
|
||||
other.translation(), nt);
|
||||
double nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
|
||||
double nscale = other.uniformScale() * scale;
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform lerp (Transform other, double t) {
|
||||
if (generality() < other.generality()) {
|
||||
return other.lerp(this, -t); // TODO: is this correct?
|
||||
}
|
||||
|
||||
Vector nt = translation().lerpLocal(other.translation(), t);
|
||||
double nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
|
||||
double nscale = MathUtil.lerp(scale, other.uniformScale(), t);
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
|
||||
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
|
||||
for (int ii = 0; ii < count; ii++) {
|
||||
IPoint p = src[srcOff++];
|
||||
Points.transform(p.x(), p.y(), scale, scale, sina, cosa, tx, ty, dst[dstOff++]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void transform (double[] src, int srcOff, double[] dst, int dstOff, int count) {
|
||||
Point p = new Point();
|
||||
double sina = Math.sin(rotation), cosa = Math.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.x(), p.y(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector p, Vector into) {
|
||||
return Vectors.transform(p.x(), p.y(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scale, scale, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.x(), v.y(), 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=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,11 @@ public abstract class AbstractTransform implements Transform
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Deprecated @Override // from Transform
|
||||
public Transform clone () {
|
||||
return copy();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public abstract Transform clone ();
|
||||
public abstract Transform copy ();
|
||||
}
|
||||
|
||||
@@ -189,26 +189,25 @@ public class AffineTransform extends AbstractTransform
|
||||
return scale(scale, scale);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scale (float scaleX, float scaleY) {
|
||||
return Transforms.multiply(this, scaleX, 0, 0, scaleY, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (float scaleX) {
|
||||
m00 *= scaleX;
|
||||
m01 *= scaleX;
|
||||
tx *= scaleX;
|
||||
return this;
|
||||
return Transforms.multiply(this, scaleX, 0, 0, 1, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (float scaleY) {
|
||||
m10 *= scaleY;
|
||||
m11 *= scaleY;
|
||||
ty *= scaleY;
|
||||
return this;
|
||||
return Transforms.multiply(this, 1, 0, 0, scaleY, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (float angle) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
return Transforms.multiply(cosa, sina, -sina, cosa, 0, 0, this, this);
|
||||
return Transforms.multiply(this, cosa, sina, -sina, cosa, 0, 0, this);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
@@ -342,7 +341,7 @@ public class AffineTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
public Transform copy () {
|
||||
return new AffineTransform(m00, m01, m10, m11, tx, ty);
|
||||
}
|
||||
|
||||
@@ -358,7 +357,7 @@ public class AffineTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
// 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()
|
||||
// create a new AffineTransform from another AffineTransform using this instead of copy()
|
||||
protected AffineTransform (Transform other) {
|
||||
this(other.scaleX(), other.scaleY(), other.rotation(),
|
||||
other.tx(), other.ty());
|
||||
|
||||
@@ -109,7 +109,7 @@ public class IdentityTransform extends AbstractTransform
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform clone () {
|
||||
public Transform copy () {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
//
|
||||
// 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 uniformScale () {
|
||||
return (scaleX + scaleY) / 2; // TODO: is this sane
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleX () {
|
||||
return scaleX;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleY () {
|
||||
return scaleY;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (float[] matrix) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
matrix[0] = cosa * scaleX; matrix[1] = sina * scaleY;
|
||||
matrix[2] = -sina * scaleX; matrix[3] = cosa * scaleY;
|
||||
matrix[4] = tx; matrix[5] = 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 uniformScale (float scale) {
|
||||
return scale(scale, scale);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (float scaleX) {
|
||||
if (scaleX == 0) throw new IllegalArgumentException("Scale (x) must not be zero.");
|
||||
this.tx *= scaleX;
|
||||
this.scaleX *= scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (float scaleY) {
|
||||
if (scaleY == 0) throw new IllegalArgumentException("Scale (y) must not be zero.");
|
||||
this.ty *= scaleX;
|
||||
this.scaleY *= scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (float angle) {
|
||||
float otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (float tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (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).scaleLocal(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.tx(), oty = other.ty();
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
float ntx = (otx*cosa - oty*sina) * scaleX + tx();
|
||||
float nty = (otx*sina + oty*cosa) * scaleY + ty();
|
||||
|
||||
float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
float nscaleX = scaleX * other.scaleX();
|
||||
float nscaleY = scaleY * other.scaleY();
|
||||
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 = tx(), ty = ty();
|
||||
float sina = FloatMath.sin(other.rotation()), cosa = FloatMath.cos(other.rotation());
|
||||
float ntx = (tx*cosa - ty*sina) * other.scaleX() + other.tx();
|
||||
float nty = (tx*sina + ty*cosa) * other.scaleY() + other.ty();
|
||||
float nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
|
||||
float nscaleX = other.scaleX() * scaleX;
|
||||
float nscaleY = other.scaleY() * 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?
|
||||
}
|
||||
|
||||
float ntx = MathUtil.lerpa(tx, other.tx(), t);
|
||||
float nty = MathUtil.lerpa(ty, other.ty(), t);
|
||||
float nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
|
||||
float nscaleX = MathUtil.lerp(scaleX, other.scaleX(), t);
|
||||
float nscaleY = MathUtil.lerp(scaleY, other.scaleY(), t);
|
||||
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), 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.x(), s.y(), 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.x(), p.y(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.x(), v.y(), 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=" + scale() + ", rot=" + rotation +
|
||||
", trans=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
//
|
||||
// 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 uniformScale () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleX () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleY () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (float[] matrix) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
matrix[0] = cosa; matrix[1] = sina;
|
||||
matrix[2] = -sina; matrix[3] = cosa;
|
||||
matrix[4] = tx; matrix[5] = 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 rotate (float angle) {
|
||||
float otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (float tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (float ty) {
|
||||
this.ty += ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
Vector t = translation().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.translation();
|
||||
nt.rotateAndAdd(rotation, translation(), nt);
|
||||
float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
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 = translation();
|
||||
nt.rotateAndAdd(other.rotation(), other.translation(), nt);
|
||||
float nrotation = MathUtil.normalizeAngle(other.rotation() + 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 = translation().lerpLocal(other.translation(), t);
|
||||
return new RigidTransform(MathUtil.lerpa(rotation, other.rotation(), t), nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), 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.x(), s.y(), 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.x(), p.y(), 1, 1, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), 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=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -197,8 +197,11 @@ public interface Transform
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
Vector inverseTransform (IVector v, Vector into);
|
||||
|
||||
/** Returns a clone of this transform. */
|
||||
Transform clone ();
|
||||
/** @deprecated Use {@link #copy}. */
|
||||
@Deprecated Transform clone ();
|
||||
|
||||
/** Returns a copy of this transform. */
|
||||
Transform copy ();
|
||||
|
||||
/** Returns an integer that increases monotonically with the generality of the transform
|
||||
* implementation. Used internally when combining transforms. */
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
//
|
||||
// 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 uniformScale () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleX () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float scaleY () {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float rotation () {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float tx () {
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public float ty () {
|
||||
return ty;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public void get (float[] matrix) {
|
||||
float sina = FloatMath.sin(rotation), cosa = FloatMath.cos(rotation);
|
||||
matrix[0] = cosa * scale; matrix[1] = sina * scale;
|
||||
matrix[2] = -sina * scale; matrix[3] = cosa * scale;
|
||||
matrix[4] = tx; matrix[5] = 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 uniformScale (float scale) {
|
||||
if (scale == 0) throw new IllegalArgumentException("Scale must be non-zero.");
|
||||
this.tx *= scale;
|
||||
this.ty *= scale;
|
||||
this.scale *= scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (float angle) {
|
||||
float otx = this.tx, oty = this.ty;
|
||||
if (otx != 0 || oty != 0) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
this.tx = otx*cosa - oty*sina;
|
||||
this.ty = otx*sina + oty*cosa;
|
||||
}
|
||||
this.rotation += angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (float tx) {
|
||||
this.tx += tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (float ty) {
|
||||
this.ty += ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform invert () {
|
||||
float nscale = 1f / scale, nrotation = -rotation;
|
||||
Vector t = translation().negateLocal().rotateLocal(nrotation).scaleLocal(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.translation();
|
||||
nt.rotateScaleAndAdd(rotation, scale, translation(), nt);
|
||||
float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
|
||||
float nscale = scale * other.uniformScale();
|
||||
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 = translation();
|
||||
nt.rotateScaleAndAdd(other.rotation(), other.uniformScale(),
|
||||
other.translation(), nt);
|
||||
float nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
|
||||
float nscale = other.uniformScale() * 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 = translation().lerpLocal(other.translation(), t);
|
||||
float nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
|
||||
float nscale = MathUtil.lerp(scale, other.uniformScale(), t);
|
||||
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Point transform (IPoint p, Point into) {
|
||||
return Points.transform(p.x(), p.y(), 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.x(), p.y(), 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.x(), p.y(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transformPoint (IVector p, Vector into) {
|
||||
return Vectors.transform(p.x(), p.y(), scale, scale, rotation, tx, ty, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector transform (IVector v, Vector into) {
|
||||
return Vectors.transform(v.x(), v.y(), scale, scale, rotation, into);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Vector inverseTransform (IVector v, Vector into) {
|
||||
return Vectors.inverseTransform(v.x(), v.y(), 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=" + translation() + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
//
|
||||
// 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 t = t1.clone().rotate(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(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 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 t = t1.clone().uniformScale(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(t, point, expect);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.scale(scale);
|
||||
test(t, vector, expect);
|
||||
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.scale(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 t = t1.clone().uniformScale(scale);
|
||||
Transform tpost = t2.concatenate(t1);
|
||||
Transform tpre = t1.preConcatenate(t2);
|
||||
for (Point point : POINTS) {
|
||||
Point expect = point.rotate(angle).multLocal(scale);
|
||||
test(t, point, expect);
|
||||
test(tpost, point, expect);
|
||||
test(tpre, point, expect);
|
||||
}
|
||||
for (Vector vector : VECTORS) {
|
||||
Vector expect = vector.rotate(angle).scaleLocal(scale);
|
||||
test(t, vector, expect);
|
||||
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.scale(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.scale(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.scale(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.scale(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) {
|
||||
if (Math.abs(p1.x - p2.x) > MathUtil.EPSILON ||
|
||||
Math.abs(p1.y - p2.y) > MathUtil.EPSILON) {
|
||||
fail(desc + " want " + p1 + " got " + p2);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (Math.abs(v1.x - v2.x) > MathUtil.EPSILON ||
|
||||
Math.abs(v1.y - v2.y) > MathUtil.EPSILON) {
|
||||
fail(desc + " want " + v1 + " got " + v2);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// Pythagoras - a collection of geometry classes
|
||||
// http://github.com/samskivert/pythagoras
|
||||
|
||||
package pythagoras.f
|
||||
|
||||
import java.awt.geom.{AffineTransform => JAffineTransform}
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
import org.junit._
|
||||
import org.junit.Assert._
|
||||
|
||||
/**
|
||||
* Tests the various transform implementations.
|
||||
*/
|
||||
class TransformTest
|
||||
{
|
||||
trait Action {
|
||||
def set (t :Transform)
|
||||
def add (t :Transform)
|
||||
def add (xf :JAffineTransform)
|
||||
def info :String
|
||||
}
|
||||
|
||||
val ANGLES = Seq(
|
||||
0, FloatMath.PI/2, FloatMath.PI, FloatMath.PI*3/2,
|
||||
-FloatMath.PI/2, -FloatMath.PI, -FloatMath.PI*3/2
|
||||
).map(angle => new Action() {
|
||||
def set (t :Transform) = t.setRotation(angle)
|
||||
def add (t :Transform) = t.rotate(angle)
|
||||
def add (xf :JAffineTransform) = xf.rotate(angle)
|
||||
def info = "r:" + (180*angle/math.Pi).toInt + "'"
|
||||
})
|
||||
|
||||
val SCALES = Seq(0.5f, 1, 1.5f, -0.5f, -1, -1.5f).map(scale => new Action {
|
||||
def set (t :Transform) = t.setUniformScale(scale)
|
||||
def add (t :Transform) = t.uniformScale(scale)
|
||||
def add (xf :JAffineTransform) = xf.scale(scale, scale)
|
||||
def info = "s:" + scale
|
||||
})
|
||||
|
||||
val DXS = Seq(-25, 0, 25)
|
||||
val DYS = Seq(-25, 0, 25)
|
||||
val TRANS = (for (dx <- DXS; dy <- DYS) yield new Point(dx, dy)).map(trans => new Action {
|
||||
def set (t :Transform) = t.setTranslation(trans.x, trans.y)
|
||||
def add (t :Transform) = t.translate(trans.x, trans.y)
|
||||
def add (xf :JAffineTransform) = xf.translate(trans.x, trans.y)
|
||||
def info = "t:" + trans
|
||||
})
|
||||
|
||||
val XFORMS = Seq(new AffineTransform)
|
||||
|
||||
val POINTS = Seq(
|
||||
new Point(0, 0), new Point(MathUtil.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)
|
||||
)
|
||||
val VECTORS = Seq(
|
||||
new Vector(0, 0), new Vector(MathUtil.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)
|
||||
)
|
||||
|
||||
@Test def testTranslate {
|
||||
test(List(TRANS))
|
||||
}
|
||||
|
||||
@Test def testRotate () {
|
||||
test(List(ANGLES))
|
||||
}
|
||||
|
||||
@Test def testScale () {
|
||||
test(List(SCALES))
|
||||
}
|
||||
|
||||
@Test def testTranslateRotate () {
|
||||
test(List(TRANS, ANGLES))
|
||||
}
|
||||
|
||||
@Test def testRotateTranslate () {
|
||||
test(List(ANGLES, TRANS))
|
||||
}
|
||||
|
||||
@Test def testTranslateScale () {
|
||||
test(List(TRANS, SCALES))
|
||||
}
|
||||
|
||||
@Test def testScaleTranslate () {
|
||||
test(List(SCALES, TRANS))
|
||||
}
|
||||
|
||||
@Test def testRotateScale () {
|
||||
test(List(ANGLES, SCALES))
|
||||
}
|
||||
|
||||
@Test def testScaleRotate () {
|
||||
test(List(SCALES, ANGLES))
|
||||
}
|
||||
|
||||
@Test def testScaleRotateTranslate () {
|
||||
test(List(SCALES, ANGLES, TRANS))
|
||||
}
|
||||
|
||||
def test (actionLists :List[Seq[Action]]) {
|
||||
def test (lists :List[Seq[Action]], toApply :List[Action]) {
|
||||
lists match {
|
||||
case Nil => runTest(toApply);
|
||||
case h :: t => h foreach { a => test(t, toApply :+ a) }
|
||||
}
|
||||
}
|
||||
test(actionLists, Nil)
|
||||
}
|
||||
|
||||
def runTest (actions :List[Action]) {
|
||||
for (proto <- XFORMS) {
|
||||
var descs = ListBuffer[String]()
|
||||
var trans = ListBuffer[Transform]()
|
||||
val t = proto.copy
|
||||
val xf = new JAffineTransform
|
||||
for (a <- actions) { // TODO: check generality
|
||||
descs += a.info
|
||||
a.add(t)
|
||||
a.add(xf)
|
||||
val st = proto.copy
|
||||
a.set(st)
|
||||
trans += st
|
||||
}
|
||||
|
||||
val action = descs.mkString(" ")
|
||||
val tpost = trans.reduceLeft(_ concatenate _)
|
||||
val tpre = trans.reverse.reduceLeft(_ preConcatenate _)
|
||||
|
||||
for (p <- POINTS) test(action, t, tpost, tpre, xf, p)
|
||||
for (v <- VECTORS) test(action, t, tpost, tpre, xf, v)
|
||||
}
|
||||
}
|
||||
|
||||
def transform (xf :JAffineTransform, point :IPoint) = {
|
||||
val dest = Array(0f, 0f)
|
||||
xf.transform(Array(point.x, point.y), 0, dest, 0, 1)
|
||||
new Point(dest(0), dest(1))
|
||||
}
|
||||
|
||||
def transform (xf :JAffineTransform, vec :IVector) = {
|
||||
val dest = Array(0.0, 0.0);
|
||||
xf.deltaTransform(Array(vec.x.toDouble, vec.y.toDouble), 0, dest, 0, 1);
|
||||
new Vector(dest(0).toFloat, dest(1).toFloat)
|
||||
}
|
||||
|
||||
def test (action :String, tseq :Transform, tpost :Transform, tpre :Transform,
|
||||
xf :JAffineTransform, point :Point) {
|
||||
val expect = transform(xf, point)
|
||||
test(action + " seq", tseq, point, expect)
|
||||
test(action + " post", tpost, point, expect)
|
||||
test(action + " pre", tpre, point, expect)
|
||||
}
|
||||
|
||||
def test (form :String, t :Transform, p :Point, expect :Point) {
|
||||
val orig = new Point(p);
|
||||
val desc = form + "\n " + t + " @ " + p;
|
||||
|
||||
// test single point transform and inverse transform
|
||||
val tp = t.transform(p, new Point);
|
||||
val itp = t.inverseTransform(tp, new Point);
|
||||
assertEquals(desc, orig, p);
|
||||
assertPointsEqual(desc, expect, tp);
|
||||
assertPointsEqual(desc, p, itp);
|
||||
|
||||
// test multipoint transform
|
||||
val ps = Array[IPoint](null, p, null);
|
||||
val tps = Array(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));
|
||||
}
|
||||
|
||||
def assertPointsEqual (desc :String, p1 :Point, p2 :Point) {
|
||||
if (math.abs(p1.x - p2.x) > MathUtil.EPSILON || math.abs(p1.y - p2.y) > MathUtil.EPSILON) {
|
||||
fail(desc + "\n wantPoint " + p1 + " got " + p2);
|
||||
}
|
||||
}
|
||||
|
||||
def test (action :String, tseq :Transform, tpost :Transform, tpre :Transform,
|
||||
xf :JAffineTransform, vector :Vector) {
|
||||
val expect = transform(xf, vector)
|
||||
test(action + " seq", tseq, vector, expect)
|
||||
test(action + " post", tpost, vector, expect)
|
||||
test(action + " pre", tpre, vector, expect)
|
||||
}
|
||||
|
||||
def test (form :String, t :Transform, v :Vector, expect :Vector) {
|
||||
val orig = new Vector(v);
|
||||
val desc = form + "\n " + t + " @ " + v;
|
||||
|
||||
// test vector transform and inverse transform
|
||||
val tv = t.transform(v, new Vector);
|
||||
val itv = t.inverseTransform(tv, new Vector);
|
||||
assertEquals(desc, orig, v);
|
||||
assertVectorsEqual(desc, expect, tv);
|
||||
assertVectorsEqual(desc, v, itv);
|
||||
}
|
||||
|
||||
def assertVectorsEqual (desc :String, v1 :Vector, v2 :Vector) {
|
||||
if (math.abs(v1.x - v2.x) > MathUtil.EPSILON || math.abs(v1.y - v2.y) > MathUtil.EPSILON) {
|
||||
fail(desc + "\n wantVec " + v1 + " got " + v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user