Added methods to concatenate a translation, rotation or scale directly onto an
existing transform.
This commit is contained in:
@@ -53,6 +53,50 @@ public abstract class AbstractTransform implements Transform
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform uniformScale (float scale) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scale (float scaleX, float scaleY) {
|
||||
scaleX(scaleX);
|
||||
scaleY(scaleY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (float scaleX) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (float scaleY) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (float angle) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translate (float tx, float ty) {
|
||||
translateX(tx);
|
||||
translateY(ty);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateX (float tx) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform translateY (float ty) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform setTx (float tx) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -141,16 +141,9 @@ public class AffineTransform extends AbstractTransform
|
||||
public Transform setRotation (float angle) {
|
||||
// extract the scale, then reapply rotation and scale together
|
||||
float sx = getScaleX(), sy = getScaleY();
|
||||
float sin = FloatMath.sin(angle), cos = FloatMath.cos(angle);
|
||||
if (Math.abs(cos) < ZERO) {
|
||||
cos = 0;
|
||||
sin = sin > 0 ? 1f : -1f;
|
||||
} else if (Math.abs(sin) < ZERO) {
|
||||
sin = 0;
|
||||
cos = cos > 0 ? 1f : -1f;
|
||||
}
|
||||
m00 = cos * sx; m01 = -sin * sx;
|
||||
m10 = sin * sy; m11 = cos * sy;
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
m00 = cosa * sx; m01 = -sina * sx;
|
||||
m10 = sina * sy; m11 = cosa * sy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -184,6 +177,45 @@ public class AffineTransform extends AbstractTransform
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform uniformScale (float scale) {
|
||||
return scale(scale, scale);
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleX (float scaleX) {
|
||||
m00 *= scaleX;
|
||||
m01 *= scaleX;
|
||||
tx *= scaleX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform scaleY (float scaleY) {
|
||||
m10 *= scaleY;
|
||||
m11 *= scaleY;
|
||||
ty *= scaleY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
public Transform rotate (float angle) {
|
||||
float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
|
||||
return multiply(cosa, -sina, sina, cosa, 0, 0, this, 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 () {
|
||||
// compute the determinant, storing the subdeterminants for later use
|
||||
@@ -204,8 +236,12 @@ public class AffineTransform extends AbstractTransform
|
||||
if (generality() < other.generality()) {
|
||||
return other.preConcatenate(this);
|
||||
}
|
||||
return multiply((other instanceof AffineTransform) ?
|
||||
(AffineTransform)other : new AffineTransform(other));
|
||||
if (other instanceof AffineTransform) {
|
||||
return multiply(this, (AffineTransform)other, new AffineTransform());
|
||||
} else {
|
||||
AffineTransform oaff = new AffineTransform(other);
|
||||
return multiply(this, oaff, oaff);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
@@ -213,8 +249,12 @@ public class AffineTransform extends AbstractTransform
|
||||
if (generality() < other.generality()) {
|
||||
return other.concatenate(this);
|
||||
}
|
||||
return ((other instanceof AffineTransform) ?
|
||||
(AffineTransform)other : new AffineTransform(other)).multiply(this);
|
||||
if (other instanceof AffineTransform) {
|
||||
return multiply((AffineTransform)other, this, new AffineTransform());
|
||||
} else {
|
||||
AffineTransform oaff = new AffineTransform(other);
|
||||
return multiply(oaff, this, oaff);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Transform
|
||||
@@ -281,7 +321,8 @@ public class AffineTransform extends AbstractTransform
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "affine [" + m00 + " " + m01 + " " + m10 + " " + m11 + " " + getTranslation() + "]";
|
||||
return "affine [" + FloatMath.toString(m00) + " " + FloatMath.toString(m01) + " " +
|
||||
FloatMath.toString(m10) + " " + FloatMath.toString(m11) + " " + getTranslation() + "]";
|
||||
}
|
||||
|
||||
// we don't publicize this because it might encourage someone to do something stupid like
|
||||
@@ -291,18 +332,36 @@ public class AffineTransform extends AbstractTransform
|
||||
other.getTx(), other.getTy());
|
||||
}
|
||||
|
||||
protected AffineTransform multiply (AffineTransform other) {
|
||||
return multiply(other.m00, other.m01, other.m10, other.m11, other.tx, other.ty);
|
||||
protected static AffineTransform multiply (
|
||||
AffineTransform a, AffineTransform b, AffineTransform into) {
|
||||
return multiply(a.m00, a.m01, a.m10, a.m11, a.tx, a.ty,
|
||||
b.m00, b.m01, b.m10, b.m11, b.tx, b.ty, into);
|
||||
}
|
||||
|
||||
protected AffineTransform multiply (float m00, float m01, float m10, float m11,
|
||||
float tx, float ty) {
|
||||
return new AffineTransform(
|
||||
this.m00 * m00 + this.m01 * m10, this.m00 * m01 + this.m01 * m11,
|
||||
this.m10 * m00 + this.m11 * m10, this.m10 * m01 + this.m11 * m11,
|
||||
this.m00 * tx + this.m01 * ty + this.tx, this.m10 * tx + this.m11 * ty + this.ty);
|
||||
protected static AffineTransform multiply (
|
||||
AffineTransform a, float m00, float m01, float m10, float m11, float tx, float ty,
|
||||
AffineTransform into) {
|
||||
return multiply(a.m00, a.m01, a.m10, a.m11, a.tx, a.ty,
|
||||
m00, m01, m10, m11, tx, ty, into);
|
||||
}
|
||||
|
||||
/** The min value equivalent to zero. An absolute value < ZERO is considered to be zero. */
|
||||
private static final float ZERO = 1E-7f;
|
||||
protected static AffineTransform multiply (
|
||||
float m00, float m01, float m10, float m11, float tx, float ty,
|
||||
AffineTransform b, AffineTransform into) {
|
||||
return multiply(m00, m01, m10, m11, tx, ty,
|
||||
b.m00, b.m01, b.m10, b.m11, b.tx, b.ty, into);
|
||||
}
|
||||
|
||||
protected static AffineTransform multiply (
|
||||
float am00, float am01, float am10, float am11, float atx, float aty,
|
||||
float bm00, float bm01, float bm10, float bm11, float btx, float bty,
|
||||
AffineTransform into) {
|
||||
into.m00 = am00 * bm00 + am01 * bm10;
|
||||
into.m01 = am00 * bm01 + am01 * bm11;
|
||||
into.m10 = am10 * bm00 + am11 * bm10;
|
||||
into.m11 = am10 * bm01 + am11 * bm11;
|
||||
into.tx = am00 * btx + am01 * bty + atx;
|
||||
into.ty = am10 * btx + am11 * bty + aty;
|
||||
return into;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,4 +381,15 @@ public class FloatMath
|
||||
{
|
||||
return (a > 0f ? PI : -PI) - a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ~0 if the value is very close to zero, the string value of the float otherwise.
|
||||
*/
|
||||
public static String toString (float value)
|
||||
{
|
||||
return Math.abs(value) < ZERO ? "~0" : String.valueOf(value);
|
||||
}
|
||||
|
||||
/** The min value equivalent to zero. An absolute value < ZERO is rendered as ~0. */
|
||||
private static final float ZERO = 1E-7f;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,51 @@ public class NonUniformTransform extends AbstractTransform
|
||||
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);
|
||||
|
||||
@@ -58,9 +58,9 @@ public class Points
|
||||
public static String pointToString (float x, float y) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (x >= 0) buf.append("+");
|
||||
buf.append(x);
|
||||
buf.append(FloatMath.toString(x));
|
||||
if (y >= 0) buf.append("+");
|
||||
buf.append(y);
|
||||
buf.append(FloatMath.toString(y));
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,30 @@ public class RigidTransform extends AbstractTransform
|
||||
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 = getTranslation().negateLocal().rotateLocal(-rotation);
|
||||
|
||||
@@ -48,19 +48,19 @@ public interface Transform
|
||||
/** Sets the x and y scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if either supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform setScale (float scaleX, float scaleY);
|
||||
|
||||
/** Sets the x scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform setScaleX (float scaleX);
|
||||
|
||||
/** Sets the y scale of this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not affine or greater. */
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform setScaleY (float scaleY);
|
||||
|
||||
/** Sets the rotation component of this transform.
|
||||
@@ -89,6 +89,50 @@ public interface Transform
|
||||
Transform setTransform (float m00, float m01, float m10, float m11,
|
||||
float tx, float ty);
|
||||
|
||||
/** Scales this transform in a uniform manner by the specified amount.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not uniform or greater. */
|
||||
Transform uniformScale (float scale);
|
||||
|
||||
/** Scales this transform by the specified amount in the x and y dimensions.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if either supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform scale (float scaleX, float scaleY);
|
||||
|
||||
/** Scales this transform by the specified amount in the x dimension.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform scaleX (float scaleX);
|
||||
|
||||
/** Scales this transform by the specified amount in the y dimension.
|
||||
* @return this instance, for chaining.
|
||||
* @throws IllegalArgumentException if the supplied scale is zero.
|
||||
* @throws UnsupportedOperationException if the transform is not non-uniform or greater. */
|
||||
Transform scaleY (float scaleY);
|
||||
|
||||
/** Rotates this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform rotate (float angle);
|
||||
|
||||
/** Translates this transform.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform translate (float tx, float ty);
|
||||
|
||||
/** Translates this transform in the x dimension.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform translateX (float tx);
|
||||
|
||||
/** Translates this transform in the y dimension.
|
||||
* @return this instance, for chaining.
|
||||
* @throws UnsupportedOperationException if the transform is not rigid body or greater. */
|
||||
Transform translateY (float ty);
|
||||
|
||||
/** Returns a new transform that represents the inverse of this transform.
|
||||
* @throws NoninvertibleTransformException if the transform is not invertible. */
|
||||
Transform invert ();
|
||||
|
||||
@@ -88,6 +88,39 @@ public class UniformTransform extends AbstractTransform
|
||||
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;
|
||||
|
||||
@@ -67,15 +67,18 @@ public class TransformTest
|
||||
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);
|
||||
}
|
||||
@@ -129,15 +132,18 @@ public class TransformTest
|
||||
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.mult(scale);
|
||||
test(t, vector, expect);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
@@ -191,15 +197,18 @@ public class TransformTest
|
||||
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).multLocal(scale);
|
||||
test(t, vector, expect);
|
||||
test(tpost, vector, expect);
|
||||
test(tpre, vector, expect);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user