From 485abe95e6d260134ac78b7125de3b25a4f347b1 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 12 Jul 2012 12:25:06 -0700 Subject: [PATCH] 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. --- .../java/pythagoras/d/AbstractTransform.java | 7 +- .../java/pythagoras/d/AffineTransform.java | 21 +- .../java/pythagoras/d/IdentityTransform.java | 2 +- .../pythagoras/d/NonUniformTransform.java | 272 ------------ .../java/pythagoras/d/RigidTransform.java | 209 --------- src/main/java/pythagoras/d/Transform.java | 7 +- .../java/pythagoras/d/UniformTransform.java | 238 ----------- .../java/pythagoras/f/AbstractTransform.java | 7 +- .../java/pythagoras/f/AffineTransform.java | 21 +- .../java/pythagoras/f/IdentityTransform.java | 2 +- .../pythagoras/f/NonUniformTransform.java | 272 ------------ .../java/pythagoras/f/RigidTransform.java | 209 --------- src/main/java/pythagoras/f/Transform.java | 7 +- .../java/pythagoras/f/UniformTransform.java | 238 ----------- src/test/java/pythagoras/f/TransformTest.java | 403 ------------------ .../scala/pythagoras/f/TransformTest.scala | 211 +++++++++ 16 files changed, 255 insertions(+), 1871 deletions(-) delete mode 100644 src/main/java/pythagoras/d/NonUniformTransform.java delete mode 100644 src/main/java/pythagoras/d/RigidTransform.java delete mode 100644 src/main/java/pythagoras/d/UniformTransform.java delete mode 100644 src/main/java/pythagoras/f/NonUniformTransform.java delete mode 100644 src/main/java/pythagoras/f/RigidTransform.java delete mode 100644 src/main/java/pythagoras/f/UniformTransform.java delete mode 100644 src/test/java/pythagoras/f/TransformTest.java create mode 100644 src/test/scala/pythagoras/f/TransformTest.scala diff --git a/src/main/java/pythagoras/d/AbstractTransform.java b/src/main/java/pythagoras/d/AbstractTransform.java index fdb09e1..7851473 100644 --- a/src/main/java/pythagoras/d/AbstractTransform.java +++ b/src/main/java/pythagoras/d/AbstractTransform.java @@ -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 (); } diff --git a/src/main/java/pythagoras/d/AffineTransform.java b/src/main/java/pythagoras/d/AffineTransform.java index 76f7779..44bc879 100644 --- a/src/main/java/pythagoras/d/AffineTransform.java +++ b/src/main/java/pythagoras/d/AffineTransform.java @@ -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()); diff --git a/src/main/java/pythagoras/d/IdentityTransform.java b/src/main/java/pythagoras/d/IdentityTransform.java index 14c076b..2cf3da3 100644 --- a/src/main/java/pythagoras/d/IdentityTransform.java +++ b/src/main/java/pythagoras/d/IdentityTransform.java @@ -109,7 +109,7 @@ public class IdentityTransform extends AbstractTransform } @Override // from Transform - public Transform clone () { + public Transform copy () { return this; } diff --git a/src/main/java/pythagoras/d/NonUniformTransform.java b/src/main/java/pythagoras/d/NonUniformTransform.java deleted file mode 100644 index b33ff83..0000000 --- a/src/main/java/pythagoras/d/NonUniformTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/main/java/pythagoras/d/RigidTransform.java b/src/main/java/pythagoras/d/RigidTransform.java deleted file mode 100644 index 28ebf7b..0000000 --- a/src/main/java/pythagoras/d/RigidTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/main/java/pythagoras/d/Transform.java b/src/main/java/pythagoras/d/Transform.java index 4064841..b679960 100644 --- a/src/main/java/pythagoras/d/Transform.java +++ b/src/main/java/pythagoras/d/Transform.java @@ -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. */ diff --git a/src/main/java/pythagoras/d/UniformTransform.java b/src/main/java/pythagoras/d/UniformTransform.java deleted file mode 100644 index 3cdb049..0000000 --- a/src/main/java/pythagoras/d/UniformTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/main/java/pythagoras/f/AbstractTransform.java b/src/main/java/pythagoras/f/AbstractTransform.java index 305e985..0b5fe66 100644 --- a/src/main/java/pythagoras/f/AbstractTransform.java +++ b/src/main/java/pythagoras/f/AbstractTransform.java @@ -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 (); } diff --git a/src/main/java/pythagoras/f/AffineTransform.java b/src/main/java/pythagoras/f/AffineTransform.java index 0c396ac..94ae044 100644 --- a/src/main/java/pythagoras/f/AffineTransform.java +++ b/src/main/java/pythagoras/f/AffineTransform.java @@ -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()); diff --git a/src/main/java/pythagoras/f/IdentityTransform.java b/src/main/java/pythagoras/f/IdentityTransform.java index aec856d..6393c86 100644 --- a/src/main/java/pythagoras/f/IdentityTransform.java +++ b/src/main/java/pythagoras/f/IdentityTransform.java @@ -109,7 +109,7 @@ public class IdentityTransform extends AbstractTransform } @Override // from Transform - public Transform clone () { + public Transform copy () { return this; } diff --git a/src/main/java/pythagoras/f/NonUniformTransform.java b/src/main/java/pythagoras/f/NonUniformTransform.java deleted file mode 100644 index be5efbf..0000000 --- a/src/main/java/pythagoras/f/NonUniformTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/main/java/pythagoras/f/RigidTransform.java b/src/main/java/pythagoras/f/RigidTransform.java deleted file mode 100644 index 2bf421c..0000000 --- a/src/main/java/pythagoras/f/RigidTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/main/java/pythagoras/f/Transform.java b/src/main/java/pythagoras/f/Transform.java index 3a61000..a96a39b 100644 --- a/src/main/java/pythagoras/f/Transform.java +++ b/src/main/java/pythagoras/f/Transform.java @@ -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. */ diff --git a/src/main/java/pythagoras/f/UniformTransform.java b/src/main/java/pythagoras/f/UniformTransform.java deleted file mode 100644 index 4e7c312..0000000 --- a/src/main/java/pythagoras/f/UniformTransform.java +++ /dev/null @@ -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() + "]"; - } -} diff --git a/src/test/java/pythagoras/f/TransformTest.java b/src/test/java/pythagoras/f/TransformTest.java deleted file mode 100644 index 22fc867..0000000 --- a/src/test/java/pythagoras/f/TransformTest.java +++ /dev/null @@ -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); - } - } - } -} diff --git a/src/test/scala/pythagoras/f/TransformTest.scala b/src/test/scala/pythagoras/f/TransformTest.scala new file mode 100644 index 0000000..9df23ff --- /dev/null +++ b/src/test/scala/pythagoras/f/TransformTest.scala @@ -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); + } + } +}