Method from java.awt.geom.AffineTransform Detail: |
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
Returns a copy of this AffineTransform object. |
public void concatenate(AffineTransform Tx) {
double M0, M1;
double T00, T01, T10, T11;
double T02, T12;
int mystate = state;
int txstate = Tx.state;
switch ((txstate < < HI_SHIFT) | mystate) {
/* ---------- Tx == IDENTITY cases ---------- */
case (HI_IDENTITY | APPLY_IDENTITY):
case (HI_IDENTITY | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SCALE):
case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SHEAR):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
return;
/* ---------- this == IDENTITY cases ---------- */
case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
m01 = Tx.m01;
m10 = Tx.m10;
/* NOBREAK */
case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
m00 = Tx.m00;
m11 = Tx.m11;
/* NOBREAK */
case (HI_TRANSLATE | APPLY_IDENTITY):
m02 = Tx.m02;
m12 = Tx.m12;
state = txstate;
type = Tx.type;
return;
case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
m01 = Tx.m01;
m10 = Tx.m10;
/* NOBREAK */
case (HI_SCALE | APPLY_IDENTITY):
m00 = Tx.m00;
m11 = Tx.m11;
state = txstate;
type = Tx.type;
return;
case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
m02 = Tx.m02;
m12 = Tx.m12;
/* NOBREAK */
case (HI_SHEAR | APPLY_IDENTITY):
m01 = Tx.m01;
m10 = Tx.m10;
m00 = m11 = 0.0;
state = txstate;
type = Tx.type;
return;
/* ---------- Tx == TRANSLATE cases ---------- */
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SHEAR):
case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SCALE):
case (HI_TRANSLATE | APPLY_TRANSLATE):
translate(Tx.m02, Tx.m12);
return;
/* ---------- Tx == SCALE cases ---------- */
case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SHEAR):
case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SCALE):
case (HI_SCALE | APPLY_TRANSLATE):
scale(Tx.m00, Tx.m11);
return;
/* ---------- Tx == SHEAR cases ---------- */
case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
T01 = Tx.m01; T10 = Tx.m10;
M0 = m00;
m00 = m01 * T10;
m01 = M0 * T01;
M0 = m10;
m10 = m11 * T10;
m11 = M0 * T01;
type = TYPE_UNKNOWN;
return;
case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SHEAR):
m00 = m01 * Tx.m10;
m01 = 0.0;
m11 = m10 * Tx.m01;
m10 = 0.0;
state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
type = TYPE_UNKNOWN;
return;
case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SCALE):
m01 = m00 * Tx.m01;
m00 = 0.0;
m10 = m11 * Tx.m10;
m11 = 0.0;
state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
type = TYPE_UNKNOWN;
return;
case (HI_SHEAR | APPLY_TRANSLATE):
m00 = 0.0;
m01 = Tx.m01;
m10 = Tx.m10;
m11 = 0.0;
state = APPLY_TRANSLATE | APPLY_SHEAR;
type = TYPE_UNKNOWN;
return;
}
// If Tx has more than one attribute, it is not worth optimizing
// all of those cases...
T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
switch (mystate) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE):
state = mystate | txstate;
/* NOBREAK */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M0 = m00;
M1 = m01;
m00 = T00 * M0 + T10 * M1;
m01 = T01 * M0 + T11 * M1;
m02 += T02 * M0 + T12 * M1;
M0 = m10;
M1 = m11;
m10 = T00 * M0 + T10 * M1;
m11 = T01 * M0 + T11 * M1;
m12 += T02 * M0 + T12 * M1;
type = TYPE_UNKNOWN;
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
M0 = m01;
m00 = T10 * M0;
m01 = T11 * M0;
m02 += T12 * M0;
M0 = m10;
m10 = T00 * M0;
m11 = T01 * M0;
m12 += T02 * M0;
break;
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
M0 = m00;
m00 = T00 * M0;
m01 = T01 * M0;
m02 += T02 * M0;
M0 = m11;
m10 = T10 * M0;
m11 = T11 * M0;
m12 += T12 * M0;
break;
case (APPLY_TRANSLATE):
m00 = T00;
m01 = T01;
m02 += T02;
m10 = T10;
m11 = T11;
m12 += T12;
state = txstate | APPLY_TRANSLATE;
type = TYPE_UNKNOWN;
return;
}
updateState();
}
Concatenates an AffineTransform Tx to
this AffineTransform Cx in the most commonly useful
way to provide a new user space
that is mapped to the former user space by Tx .
Cx is updated to perform the combined transformation.
Transforming a point p by the updated transform Cx' is
equivalent to first transforming p by Tx and then
transforming the result by the original transform Cx like this:
Cx'(p) = Cx(Tx(p))
In matrix notation, if this transform Cx is
represented by the matrix [this] and Tx is represented
by the matrix [Tx] then this method does the following:
[this] = [this] x [Tx]
|
public AffineTransform createInverse() throws NoninvertibleTransformException {
double det;
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
det = m00 * m11 - m01 * m10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
return new AffineTransform( m11 / det, -m10 / det,
-m01 / det, m00 / det,
(m01 * m12 - m11 * m02) / det,
(m10 * m02 - m00 * m12) / det,
(APPLY_SHEAR |
APPLY_SCALE |
APPLY_TRANSLATE));
case (APPLY_SHEAR | APPLY_SCALE):
det = m00 * m11 - m01 * m10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
return new AffineTransform( m11 / det, -m10 / det,
-m01 / det, m00 / det,
0.0, 0.0,
(APPLY_SHEAR | APPLY_SCALE));
case (APPLY_SHEAR | APPLY_TRANSLATE):
if (m01 == 0.0 || m10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
return new AffineTransform( 0.0, 1.0 / m01,
1.0 / m10, 0.0,
-m12 / m10, -m02 / m01,
(APPLY_SHEAR | APPLY_TRANSLATE));
case (APPLY_SHEAR):
if (m01 == 0.0 || m10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
return new AffineTransform(0.0, 1.0 / m01,
1.0 / m10, 0.0,
0.0, 0.0,
(APPLY_SHEAR));
case (APPLY_SCALE | APPLY_TRANSLATE):
if (m00 == 0.0 || m11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
return new AffineTransform( 1.0 / m00, 0.0,
0.0, 1.0 / m11,
-m02 / m00, -m12 / m11,
(APPLY_SCALE | APPLY_TRANSLATE));
case (APPLY_SCALE):
if (m00 == 0.0 || m11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
return new AffineTransform(1.0 / m00, 0.0,
0.0, 1.0 / m11,
0.0, 0.0,
(APPLY_SCALE));
case (APPLY_TRANSLATE):
return new AffineTransform( 1.0, 0.0,
0.0, 1.0,
-m02, -m12,
(APPLY_TRANSLATE));
case (APPLY_IDENTITY):
return new AffineTransform();
}
/* NOTREACHED */
}
Returns an AffineTransform object representing the
inverse transformation.
The inverse transform Tx' of this transform Tx
maps coordinates transformed by Tx back
to their original coordinates.
In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
If this transform maps all coordinates onto a point or a line
then it will not have an inverse, since coordinates that do
not lie on the destination point or line will not have an inverse
mapping.
The getDeterminant method can be used to determine if this
transform has no inverse, in which case an exception will be
thrown if the createInverse method is called. |
public Shape createTransformedShape(Shape pSrc) {
if (pSrc == null) {
return null;
}
return new Path2D.Double(pSrc, this);
}
Returns a new Shape object defined by the geometry of the
specified Shape after it has been transformed by
this transform. |
public Point2D deltaTransform(Point2D ptSrc,
Point2D ptDst) {
if (ptDst == null) {
if (ptSrc instanceof Point2D.Double) {
ptDst = new Point2D.Double();
} else {
ptDst = new Point2D.Float();
}
}
// Copy source coords into local variables in case src == dst
double x = ptSrc.getX();
double y = ptSrc.getY();
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SHEAR | APPLY_SCALE):
ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
return ptDst;
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
ptDst.setLocation(y * m01, x * m10);
return ptDst;
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
ptDst.setLocation(x * m00, y * m11);
return ptDst;
case (APPLY_TRANSLATE):
case (APPLY_IDENTITY):
ptDst.setLocation(x, y);
return ptDst;
}
/* NOTREACHED */
}
Transforms the relative distance vector specified by
ptSrc and stores the result in ptDst .
A relative distance vector is transformed without applying the
translation components of the affine transformation matrix
using the following equations:
[ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
[ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
[ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
If ptDst is null , a new
Point2D object is allocated and then the result of the
transform is stored in this object.
In either case, ptDst , which contains the
transformed point, is returned for convenience.
If ptSrc and ptDst are the same object,
the input point is correctly overwritten with the transformed
point. |
public void deltaTransform(double[] srcPts,
int srcOff,
double[] dstPts,
int dstOff,
int numPts) {
double M00, M01, M10, M11; // For caching
if (dstPts == srcPts &&
dstOff > srcOff && dstOff < srcOff + numPts * 2)
{
// If the arrays overlap partially with the destination higher
// than the source and we transform the coordinates normally
// we would overwrite some of the later source coordinates
// with results of previous transformations.
// To get around this we use arraycopy to copy the points
// to their final destination with correct overwrite
// handling and then transform them in place in the new
// safer location.
System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
// srcPts = dstPts; // They are known to be equal.
srcOff = dstOff;
}
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = x * M00 + y * M01;
dstPts[dstOff++] = x * M10 + y * M11;
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = srcPts[srcOff++] * M01;
dstPts[dstOff++] = x * M10;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
M00 = m00; M11 = m11;
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++] * M00;
dstPts[dstOff++] = srcPts[srcOff++] * M11;
}
return;
case (APPLY_TRANSLATE):
case (APPLY_IDENTITY):
if (srcPts != dstPts || srcOff != dstOff) {
System.arraycopy(srcPts, srcOff, dstPts, dstOff,
numPts * 2);
}
return;
}
/* NOTREACHED */
}
Transforms an array of relative distance vectors by this
transform.
A relative distance vector is transformed without applying the
translation components of the affine transformation matrix
using the following equations:
[ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
[ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
[ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
The two coordinate array sections can be exactly the same or
can be overlapping sections of the same array without affecting the
validity of the results.
This method ensures that no source coordinates are
overwritten by a previous operation before they can be transformed.
The coordinates are stored in the arrays starting at the indicated
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public boolean equals(Object obj) {
if (!(obj instanceof AffineTransform)) {
return false;
}
AffineTransform a = (AffineTransform)obj;
return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&
(m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
}
Returns true if this AffineTransform
represents the same affine coordinate transform as the specified
argument. |
public double getDeterminant() {
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SHEAR | APPLY_SCALE):
return m00 * m11 - m01 * m10;
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
return -(m01 * m10);
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
return m00 * m11;
case (APPLY_TRANSLATE):
case (APPLY_IDENTITY):
return 1.0;
}
}
Returns the determinant of the matrix representation of the transform.
The determinant is useful both to determine if the transform can
be inverted and to get a single value representing the
combined X and Y scaling of the transform.
If the determinant is non-zero, then this transform is
invertible and the various methods that depend on the inverse
transform do not need to throw a
NoninvertibleTransformException .
If the determinant is zero then this transform can not be
inverted since the transform maps all input coordinates onto
a line or a point.
If the determinant is near enough to zero then inverse transform
operations might not carry enough precision to produce meaningful
results.
If this transform represents a uniform scale, as indicated by
the getType method then the determinant also
represents the square of the uniform scale factor by which all of
the points are expanded from or contracted towards the origin.
If this transform represents a non-uniform scale or more general
transform then the determinant is not likely to represent a
value useful for any purpose other than determining if inverse
transforms are possible.
Mathematically, the determinant is calculated using the formula:
| m00 m01 m02 |
| m10 m11 m12 | = m00 * m11 - m01 * m10
| 0 0 1 |
|
public void getMatrix(double[] flatmatrix) {
flatmatrix[0] = m00;
flatmatrix[1] = m10;
flatmatrix[2] = m01;
flatmatrix[3] = m11;
if (flatmatrix.length > 5) {
flatmatrix[4] = m02;
flatmatrix[5] = m12;
}
}
Retrieves the 6 specifiable values in the 3x3 affine transformation
matrix and places them into an array of double precisions values.
The values are stored in the array as
{ m00 m10 m01 m11 m02 m12 }.
An array of 4 doubles can also be specified, in which case only the
first four elements representing the non-transform
parts of the array are retrieved and the values are stored into
the array as { m00 m10 m01 m11 } |
public static AffineTransform getQuadrantRotateInstance(int numquadrants) {
AffineTransform Tx = new AffineTransform();
Tx.setToQuadrantRotation(numquadrants);
return Tx;
}
Returns a transform that rotates coordinates by the specified
number of quadrants.
This operation is equivalent to calling:
AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public static AffineTransform getQuadrantRotateInstance(int numquadrants,
double anchorx,
double anchory) {
AffineTransform Tx = new AffineTransform();
Tx.setToQuadrantRotation(numquadrants, anchorx, anchory);
return Tx;
}
Returns a transform that rotates coordinates by the specified
number of quadrants around the specified anchor point.
This operation is equivalent to calling:
AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0,
anchorx, anchory);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public static AffineTransform getRotateInstance(double theta) {
AffineTransform Tx = new AffineTransform();
Tx.setToRotation(theta);
return Tx;
}
Returns a transform representing a rotation transformation.
The matrix representing the returned transform is:
[ cos(theta) -sin(theta) 0 ]
[ sin(theta) cos(theta) 0 ]
[ 0 0 1 ]
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public static AffineTransform getRotateInstance(double vecx,
double vecy) {
AffineTransform Tx = new AffineTransform();
Tx.setToRotation(vecx, vecy);
return Tx;
}
Returns a transform that rotates coordinates according to
a rotation vector.
All coordinates rotate about the origin by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
an identity transform is returned.
This operation is equivalent to calling:
AffineTransform.getRotateInstance(Math.atan2(vecy, vecx));
|
public static AffineTransform getRotateInstance(double theta,
double anchorx,
double anchory) {
AffineTransform Tx = new AffineTransform();
Tx.setToRotation(theta, anchorx, anchory);
return Tx;
}
AffineTransform Tx = new AffineTransform();
Tx.translate(anchorx, anchory); // S3: final translation
Tx.rotate(theta); // S2: rotate around anchor
Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin
The matrix representing the returned transform is:
[ cos(theta) -sin(theta) x-x*cos+y*sin ]
[ sin(theta) cos(theta) y-x*sin-y*cos ]
[ 0 0 1 ]
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public static AffineTransform getRotateInstance(double vecx,
double vecy,
double anchorx,
double anchory) {
AffineTransform Tx = new AffineTransform();
Tx.setToRotation(vecx, vecy, anchorx, anchory);
return Tx;
}
Returns a transform that rotates coordinates around an anchor
point accordinate to a rotation vector.
All coordinates rotate about the specified anchor coordinates
by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
an identity transform is returned.
This operation is equivalent to calling:
AffineTransform.getRotateInstance(Math.atan2(vecy, vecx),
anchorx, anchory);
|
public static AffineTransform getScaleInstance(double sx,
double sy) {
AffineTransform Tx = new AffineTransform();
Tx.setToScale(sx, sy);
return Tx;
}
Returns a transform representing a scaling transformation.
The matrix representing the returned transform is:
[ sx 0 0 ]
[ 0 sy 0 ]
[ 0 0 1 ]
|
public double getScaleX() {
return m00;
}
Returns the X coordinate scaling element (m00) of the 3x3
affine transformation matrix. |
public double getScaleY() {
return m11;
}
Returns the Y coordinate scaling element (m11) of the 3x3
affine transformation matrix. |
public static AffineTransform getShearInstance(double shx,
double shy) {
AffineTransform Tx = new AffineTransform();
Tx.setToShear(shx, shy);
return Tx;
}
Returns a transform representing a shearing transformation.
The matrix representing the returned transform is:
[ 1 shx 0 ]
[ shy 1 0 ]
[ 0 0 1 ]
|
public double getShearX() {
return m01;
}
Returns the X coordinate shearing element (m01) of the 3x3
affine transformation matrix. |
public double getShearY() {
return m10;
}
Returns the Y coordinate shearing element (m10) of the 3x3
affine transformation matrix. |
public static AffineTransform getTranslateInstance(double tx,
double ty) {
AffineTransform Tx = new AffineTransform();
Tx.setToTranslation(tx, ty);
return Tx;
}
Returns a transform representing a translation transformation.
The matrix representing the returned transform is:
[ 1 0 tx ]
[ 0 1 ty ]
[ 0 0 1 ]
|
public double getTranslateX() {
return m02;
}
Returns the X coordinate of the translation element (m02) of the
3x3 affine transformation matrix. |
public double getTranslateY() {
return m12;
}
Returns the Y coordinate of the translation element (m12) of the
3x3 affine transformation matrix. |
public int getType() {
if (type == TYPE_UNKNOWN) {
calculateType();
}
return type;
}
Retrieves the flag bits describing the conversion properties of
this transform.
The return value is either one of the constants TYPE_IDENTITY
or TYPE_GENERAL_TRANSFORM, or a combination of the
appriopriate flag bits.
A valid combination of flag bits is an exclusive OR operation
that can combine
the TYPE_TRANSLATION flag bit
in addition to either of the
TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits
as well as either of the
TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. |
public int hashCode() {
long bits = Double.doubleToLongBits(m00);
bits = bits * 31 + Double.doubleToLongBits(m01);
bits = bits * 31 + Double.doubleToLongBits(m02);
bits = bits * 31 + Double.doubleToLongBits(m10);
bits = bits * 31 + Double.doubleToLongBits(m11);
bits = bits * 31 + Double.doubleToLongBits(m12);
return (((int) bits) ^ ((int) (bits > > 32)));
}
Returns the hashcode for this transform. |
public Point2D inverseTransform(Point2D ptSrc,
Point2D ptDst) throws NoninvertibleTransformException {
if (ptDst == null) {
if (ptSrc instanceof Point2D.Double) {
ptDst = new Point2D.Double();
} else {
ptDst = new Point2D.Float();
}
}
// Copy source coords into local variables in case src == dst
double x = ptSrc.getX();
double y = ptSrc.getY();
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
x -= m02;
y -= m12;
/* NOBREAK */
case (APPLY_SHEAR | APPLY_SCALE):
double det = m00 * m11 - m01 * m10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
ptDst.setLocation((x * m11 - y * m01) / det,
(y * m00 - x * m10) / det);
return ptDst;
case (APPLY_SHEAR | APPLY_TRANSLATE):
x -= m02;
y -= m12;
/* NOBREAK */
case (APPLY_SHEAR):
if (m01 == 0.0 || m10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
ptDst.setLocation(y / m10, x / m01);
return ptDst;
case (APPLY_SCALE | APPLY_TRANSLATE):
x -= m02;
y -= m12;
/* NOBREAK */
case (APPLY_SCALE):
if (m00 == 0.0 || m11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
ptDst.setLocation(x / m00, y / m11);
return ptDst;
case (APPLY_TRANSLATE):
ptDst.setLocation(x - m02, y - m12);
return ptDst;
case (APPLY_IDENTITY):
ptDst.setLocation(x, y);
return ptDst;
}
/* NOTREACHED */
}
Inverse transforms the specified ptSrc and stores the
result in ptDst .
If ptDst is null , a new
Point2D object is allocated and then the result of the
transform is stored in this object.
In either case, ptDst , which contains the transformed
point, is returned for convenience.
If ptSrc and ptDst are the same
object, the input point is correctly overwritten with the
transformed point. |
public void inverseTransform(double[] srcPts,
int srcOff,
double[] dstPts,
int dstOff,
int numPts) throws NoninvertibleTransformException {
double M00, M01, M02, M10, M11, M12; // For caching
double det;
if (dstPts == srcPts &&
dstOff > srcOff && dstOff < srcOff + numPts * 2)
{
// If the arrays overlap partially with the destination higher
// than the source and we transform the coordinates normally
// we would overwrite some of the later source coordinates
// with results of previous transformations.
// To get around this we use arraycopy to copy the points
// to their final destination with correct overwrite
// handling and then transform them in place in the new
// safer location.
System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
// srcPts = dstPts; // They are known to be equal.
srcOff = dstOff;
}
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
det = M00 * M11 - M01 * M10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
while (--numPts >= 0) {
double x = srcPts[srcOff++] - M02;
double y = srcPts[srcOff++] - M12;
dstPts[dstOff++] = (x * M11 - y * M01) / det;
dstPts[dstOff++] = (y * M00 - x * M10) / det;
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
det = M00 * M11 - M01 * M10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = (x * M11 - y * M01) / det;
dstPts[dstOff++] = (y * M00 - x * M10) / det;
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
if (M01 == 0.0 || M10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
while (--numPts >= 0) {
double x = srcPts[srcOff++] - M02;
dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10;
dstPts[dstOff++] = x / M01;
}
return;
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
if (M01 == 0.0 || M10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = srcPts[srcOff++] / M10;
dstPts[dstOff++] = x / M01;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
if (M00 == 0.0 || M11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
while (--numPts >= 0) {
dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;
dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;
}
return;
case (APPLY_SCALE):
M00 = m00; M11 = m11;
if (M00 == 0.0 || M11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++] / M00;
dstPts[dstOff++] = srcPts[srcOff++] / M11;
}
return;
case (APPLY_TRANSLATE):
M02 = m02; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++] - M02;
dstPts[dstOff++] = srcPts[srcOff++] - M12;
}
return;
case (APPLY_IDENTITY):
if (srcPts != dstPts || srcOff != dstOff) {
System.arraycopy(srcPts, srcOff, dstPts, dstOff,
numPts * 2);
}
return;
}
/* NOTREACHED */
}
Inverse transforms an array of double precision coordinates by
this transform.
The two coordinate array sections can be exactly the same or
can be overlapping sections of the same array without affecting the
validity of the results.
This method ensures that no source coordinates are
overwritten by a previous operation before they can be transformed.
The coordinates are stored in the arrays starting at the specified
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public void invert() throws NoninvertibleTransformException {
double M00, M01, M02;
double M10, M11, M12;
double det;
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
det = M00 * M11 - M01 * M10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
m00 = M11 / det;
m10 = -M10 / det;
m01 = -M01 / det;
m11 = M00 / det;
m02 = (M01 * M12 - M11 * M02) / det;
m12 = (M10 * M02 - M00 * M12) / det;
break;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
det = M00 * M11 - M01 * M10;
if (Math.abs(det) < = Double.MIN_VALUE) {
throw new NoninvertibleTransformException("Determinant is "+
det);
}
m00 = M11 / det;
m10 = -M10 / det;
m01 = -M01 / det;
m11 = M00 / det;
// m02 = 0.0;
// m12 = 0.0;
break;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
if (M01 == 0.0 || M10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
// m00 = 0.0;
m10 = 1.0 / M01;
m01 = 1.0 / M10;
// m11 = 0.0;
m02 = -M12 / M10;
m12 = -M02 / M01;
break;
case (APPLY_SHEAR):
M01 = m01;
M10 = m10;
if (M01 == 0.0 || M10 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
// m00 = 0.0;
m10 = 1.0 / M01;
m01 = 1.0 / M10;
// m11 = 0.0;
// m02 = 0.0;
// m12 = 0.0;
break;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
if (M00 == 0.0 || M11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
m00 = 1.0 / M00;
// m10 = 0.0;
// m01 = 0.0;
m11 = 1.0 / M11;
m02 = -M02 / M00;
m12 = -M12 / M11;
break;
case (APPLY_SCALE):
M00 = m00;
M11 = m11;
if (M00 == 0.0 || M11 == 0.0) {
throw new NoninvertibleTransformException("Determinant is 0");
}
m00 = 1.0 / M00;
// m10 = 0.0;
// m01 = 0.0;
m11 = 1.0 / M11;
// m02 = 0.0;
// m12 = 0.0;
break;
case (APPLY_TRANSLATE):
// m00 = 1.0;
// m10 = 0.0;
// m01 = 0.0;
// m11 = 1.0;
m02 = -m02;
m12 = -m12;
break;
case (APPLY_IDENTITY):
// m00 = 1.0;
// m10 = 0.0;
// m01 = 0.0;
// m11 = 1.0;
// m02 = 0.0;
// m12 = 0.0;
break;
}
}
Sets this transform to the inverse of itself.
The inverse transform Tx' of this transform Tx
maps coordinates transformed by Tx back
to their original coordinates.
In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
If this transform maps all coordinates onto a point or a line
then it will not have an inverse, since coordinates that do
not lie on the destination point or line will not have an inverse
mapping.
The getDeterminant method can be used to determine if this
transform has no inverse, in which case an exception will be
thrown if the invert method is called. |
public boolean isIdentity() {
return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
}
Returns true if this AffineTransform is
an identity transform. |
public void preConcatenate(AffineTransform Tx) {
double M0, M1;
double T00, T01, T10, T11;
double T02, T12;
int mystate = state;
int txstate = Tx.state;
switch ((txstate < < HI_SHIFT) | mystate) {
case (HI_IDENTITY | APPLY_IDENTITY):
case (HI_IDENTITY | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SCALE):
case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SHEAR):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
// Tx is IDENTITY...
return;
case (HI_TRANSLATE | APPLY_IDENTITY):
case (HI_TRANSLATE | APPLY_SCALE):
case (HI_TRANSLATE | APPLY_SHEAR):
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
// Tx is TRANSLATE, this has no TRANSLATE
m02 = Tx.m02;
m12 = Tx.m12;
state = mystate | APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
return;
case (HI_TRANSLATE | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
// Tx is TRANSLATE, this has one too
m02 = m02 + Tx.m02;
m12 = m12 + Tx.m12;
return;
case (HI_SCALE | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_IDENTITY):
// Only these two existing states need a new state
state = mystate | APPLY_SCALE;
/* NOBREAK */
case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SHEAR):
case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SCALE | APPLY_SCALE):
// Tx is SCALE, this is anything
T00 = Tx.m00;
T11 = Tx.m11;
if ((mystate & APPLY_SHEAR) != 0) {
m01 = m01 * T00;
m10 = m10 * T11;
if ((mystate & APPLY_SCALE) != 0) {
m00 = m00 * T00;
m11 = m11 * T11;
}
} else {
m00 = m00 * T00;
m11 = m11 * T11;
}
if ((mystate & APPLY_TRANSLATE) != 0) {
m02 = m02 * T00;
m12 = m12 * T11;
}
type = TYPE_UNKNOWN;
return;
case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SHEAR):
mystate = mystate | APPLY_SCALE;
/* NOBREAK */
case (HI_SHEAR | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_IDENTITY):
case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SCALE):
state = mystate ^ APPLY_SHEAR;
/* NOBREAK */
case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
// Tx is SHEAR, this is anything
T01 = Tx.m01;
T10 = Tx.m10;
M0 = m00;
m00 = m10 * T01;
m10 = M0 * T10;
M0 = m01;
m01 = m11 * T01;
m11 = M0 * T10;
M0 = m02;
m02 = m12 * T01;
m12 = M0 * T10;
type = TYPE_UNKNOWN;
return;
}
// If Tx has more than one attribute, it is not worth optimizing
// all of those cases...
T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
switch (mystate) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M0 = m02;
M1 = m12;
T02 += M0 * T00 + M1 * T01;
T12 += M0 * T10 + M1 * T11;
/* NOBREAK */
case (APPLY_SHEAR | APPLY_SCALE):
m02 = T02;
m12 = T12;
M0 = m00;
M1 = m10;
m00 = M0 * T00 + M1 * T01;
m10 = M0 * T10 + M1 * T11;
M0 = m01;
M1 = m11;
m01 = M0 * T00 + M1 * T01;
m11 = M0 * T10 + M1 * T11;
break;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M0 = m02;
M1 = m12;
T02 += M0 * T00 + M1 * T01;
T12 += M0 * T10 + M1 * T11;
/* NOBREAK */
case (APPLY_SHEAR):
m02 = T02;
m12 = T12;
M0 = m10;
m00 = M0 * T01;
m10 = M0 * T11;
M0 = m01;
m01 = M0 * T00;
m11 = M0 * T10;
break;
case (APPLY_SCALE | APPLY_TRANSLATE):
M0 = m02;
M1 = m12;
T02 += M0 * T00 + M1 * T01;
T12 += M0 * T10 + M1 * T11;
/* NOBREAK */
case (APPLY_SCALE):
m02 = T02;
m12 = T12;
M0 = m00;
m00 = M0 * T00;
m10 = M0 * T10;
M0 = m11;
m01 = M0 * T01;
m11 = M0 * T11;
break;
case (APPLY_TRANSLATE):
M0 = m02;
M1 = m12;
T02 += M0 * T00 + M1 * T01;
T12 += M0 * T10 + M1 * T11;
/* NOBREAK */
case (APPLY_IDENTITY):
m02 = T02;
m12 = T12;
m00 = T00;
m10 = T10;
m01 = T01;
m11 = T11;
state = mystate | txstate;
type = TYPE_UNKNOWN;
return;
}
updateState();
}
Concatenates an AffineTransform Tx to
this AffineTransform Cx
in a less commonly used way such that Tx modifies the
coordinate transformation relative to the absolute pixel
space rather than relative to the existing user space.
Cx is updated to perform the combined transformation.
Transforming a point p by the updated transform Cx' is
equivalent to first transforming p by the original transform
Cx and then transforming the result by
Tx like this:
Cx'(p) = Tx(Cx(p))
In matrix notation, if this transform Cx
is represented by the matrix [this] and Tx is
represented by the matrix [Tx] then this method does the
following:
[this] = [Tx] x [this]
|
public void quadrantRotate(int numquadrants) {
switch (numquadrants & 3) {
case 0:
break;
case 1:
rotate90();
break;
case 2:
rotate180();
break;
case 3:
rotate270();
break;
}
}
Concatenates this transform with a transform that rotates
coordinates by the specified number of quadrants.
This is equivalent to calling:
rotate(numquadrants * Math.PI / 2.0);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public void quadrantRotate(int numquadrants,
double anchorx,
double anchory) {
switch (numquadrants & 3) {
case 0:
return;
case 1:
m02 += anchorx * (m00 - m01) + anchory * (m01 + m00);
m12 += anchorx * (m10 - m11) + anchory * (m11 + m10);
rotate90();
break;
case 2:
m02 += anchorx * (m00 + m00) + anchory * (m01 + m01);
m12 += anchorx * (m10 + m10) + anchory * (m11 + m11);
rotate180();
break;
case 3:
m02 += anchorx * (m00 + m01) + anchory * (m01 - m00);
m12 += anchorx * (m10 + m11) + anchory * (m11 - m10);
rotate270();
break;
}
if (m02 == 0.0 && m12 == 0.0) {
state &= ~APPLY_TRANSLATE;
} else {
state |= APPLY_TRANSLATE;
}
}
Concatenates this transform with a transform that rotates
coordinates by the specified number of quadrants around
the specified anchor point.
This method is equivalent to calling:
rotate(numquadrants * Math.PI / 2.0, anchorx, anchory);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public void rotate(double theta) {
double sin = Math.sin(theta);
if (sin == 1.0) {
rotate90();
} else if (sin == -1.0) {
rotate270();
} else {
double cos = Math.cos(theta);
if (cos == -1.0) {
rotate180();
} else if (cos != 1.0) {
double M0, M1;
M0 = m00;
M1 = m01;
m00 = cos * M0 + sin * M1;
m01 = -sin * M0 + cos * M1;
M0 = m10;
M1 = m11;
m10 = cos * M0 + sin * M1;
m11 = -sin * M0 + cos * M1;
updateState();
}
}
}
Concatenates this transform with a rotation transformation.
This is equivalent to calling concatenate(R), where R is an
AffineTransform represented by the following matrix:
[ cos(theta) -sin(theta) 0 ]
[ sin(theta) cos(theta) 0 ]
[ 0 0 1 ]
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public void rotate(double vecx,
double vecy) {
if (vecy == 0.0) {
if (vecx < 0.0) {
rotate180();
}
// If vecx > 0.0 - no rotation
// If vecx == 0.0 - undefined rotation - treat as no rotation
} else if (vecx == 0.0) {
if (vecy > 0.0) {
rotate90();
} else { // vecy must be < 0.0
rotate270();
}
} else {
double len = Math.sqrt(vecx * vecx + vecy * vecy);
double sin = vecy / len;
double cos = vecx / len;
double M0, M1;
M0 = m00;
M1 = m01;
m00 = cos * M0 + sin * M1;
m01 = -sin * M0 + cos * M1;
M0 = m10;
M1 = m11;
m10 = cos * M0 + sin * M1;
m11 = -sin * M0 + cos * M1;
updateState();
}
}
Concatenates this transform with a transform that rotates
coordinates according to a rotation vector.
All coordinates rotate about the origin by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
no additional rotation is added to this transform.
This operation is equivalent to calling:
rotate(Math.atan2(vecy, vecx));
|
public void rotate(double theta,
double anchorx,
double anchory) {
// REMIND: Simple for now - optimize later
translate(anchorx, anchory);
rotate(theta);
translate(-anchorx, -anchory);
}
translate(anchorx, anchory); // S3: final translation
rotate(theta); // S2: rotate around anchor
translate(-anchorx, -anchory); // S1: translate anchor to origin
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public void rotate(double vecx,
double vecy,
double anchorx,
double anchory) {
// REMIND: Simple for now - optimize later
translate(anchorx, anchory);
rotate(vecx, vecy);
translate(-anchorx, -anchory);
}
Concatenates this transform with a transform that rotates
coordinates around an anchor point according to a rotation
vector.
All coordinates rotate about the specified anchor coordinates
by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
the transform is not modified in any way.
This method is equivalent to calling:
rotate(Math.atan2(vecy, vecx), anchorx, anchory);
|
public void scale(double sx,
double sy) {
int state = this.state;
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SHEAR | APPLY_SCALE):
m00 *= sx;
m11 *= sy;
/* NOBREAK */
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
m01 *= sy;
m10 *= sx;
if (m01 == 0 && m10 == 0) {
state &= APPLY_TRANSLATE;
if (m00 == 1.0 && m11 == 1.0) {
this.type = (state == APPLY_IDENTITY
? TYPE_IDENTITY
: TYPE_TRANSLATION);
} else {
state |= APPLY_SCALE;
this.type = TYPE_UNKNOWN;
}
this.state = state;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
m00 *= sx;
m11 *= sy;
if (m00 == 1.0 && m11 == 1.0) {
this.state = (state &= APPLY_TRANSLATE);
this.type = (state == APPLY_IDENTITY
? TYPE_IDENTITY
: TYPE_TRANSLATION);
} else {
this.type = TYPE_UNKNOWN;
}
return;
case (APPLY_TRANSLATE):
case (APPLY_IDENTITY):
m00 = sx;
m11 = sy;
if (sx != 1.0 || sy != 1.0) {
this.state = state | APPLY_SCALE;
this.type = TYPE_UNKNOWN;
}
return;
}
}
Concatenates this transform with a scaling transformation.
This is equivalent to calling concatenate(S), where S is an
AffineTransform represented by the following matrix:
[ sx 0 0 ]
[ 0 sy 0 ]
[ 0 0 1 ]
|
public void setToIdentity() {
m00 = m11 = 1.0;
m10 = m01 = m02 = m12 = 0.0;
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
Resets this transform to the Identity transform. |
public void setToQuadrantRotation(int numquadrants) {
switch (numquadrants & 3) {
case 0:
m00 = 1.0;
m10 = 0.0;
m01 = 0.0;
m11 = 1.0;
m02 = 0.0;
m12 = 0.0;
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
break;
case 1:
m00 = 0.0;
m10 = 1.0;
m01 = -1.0;
m11 = 0.0;
m02 = 0.0;
m12 = 0.0;
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
break;
case 2:
m00 = -1.0;
m10 = 0.0;
m01 = 0.0;
m11 = -1.0;
m02 = 0.0;
m12 = 0.0;
state = APPLY_SCALE;
type = TYPE_QUADRANT_ROTATION;
break;
case 3:
m00 = 0.0;
m10 = -1.0;
m01 = 1.0;
m11 = 0.0;
m02 = 0.0;
m12 = 0.0;
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
break;
}
}
Sets this transform to a rotation transformation that rotates
coordinates by the specified number of quadrants.
This operation is equivalent to calling:
setToRotation(numquadrants * Math.PI / 2.0);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public void setToQuadrantRotation(int numquadrants,
double anchorx,
double anchory) {
switch (numquadrants & 3) {
case 0:
m00 = 1.0;
m10 = 0.0;
m01 = 0.0;
m11 = 1.0;
m02 = 0.0;
m12 = 0.0;
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
break;
case 1:
m00 = 0.0;
m10 = 1.0;
m01 = -1.0;
m11 = 0.0;
m02 = anchorx + anchory;
m12 = anchory - anchorx;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
} else {
state = APPLY_SHEAR | APPLY_TRANSLATE;
type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
}
break;
case 2:
m00 = -1.0;
m10 = 0.0;
m01 = 0.0;
m11 = -1.0;
m02 = anchorx + anchorx;
m12 = anchory + anchory;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SCALE;
type = TYPE_QUADRANT_ROTATION;
} else {
state = APPLY_SCALE | APPLY_TRANSLATE;
type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
}
break;
case 3:
m00 = 0.0;
m10 = -1.0;
m01 = 1.0;
m11 = 0.0;
m02 = anchorx - anchory;
m12 = anchory + anchorx;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
} else {
state = APPLY_SHEAR | APPLY_TRANSLATE;
type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
}
break;
}
}
Sets this transform to a translated rotation transformation
that rotates coordinates by the specified number of quadrants
around the specified anchor point.
This operation is equivalent to calling:
setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory);
Rotating by a positive number of quadrants rotates points on
the positive X axis toward the positive Y axis. |
public void setToRotation(double theta) {
double sin = Math.sin(theta);
double cos;
if (sin == 1.0 || sin == -1.0) {
cos = 0.0;
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
} else {
cos = Math.cos(theta);
if (cos == -1.0) {
sin = 0.0;
state = APPLY_SCALE;
type = TYPE_QUADRANT_ROTATION;
} else if (cos == 1.0) {
sin = 0.0;
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
} else {
state = APPLY_SHEAR | APPLY_SCALE;
type = TYPE_GENERAL_ROTATION;
}
}
m00 = cos;
m10 = sin;
m01 = -sin;
m11 = cos;
m02 = 0.0;
m12 = 0.0;
}
Sets this transform to a rotation transformation.
The matrix representing this transform becomes:
[ cos(theta) -sin(theta) 0 ]
[ sin(theta) cos(theta) 0 ]
[ 0 0 1 ]
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public void setToRotation(double vecx,
double vecy) {
double sin, cos;
if (vecy == 0) {
sin = 0.0;
if (vecx < 0.0) {
cos = -1.0;
state = APPLY_SCALE;
type = TYPE_QUADRANT_ROTATION;
} else {
cos = 1.0;
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
} else if (vecx == 0) {
cos = 0.0;
sin = (vecy > 0.0) ? 1.0 : -1.0;
state = APPLY_SHEAR;
type = TYPE_QUADRANT_ROTATION;
} else {
double len = Math.sqrt(vecx * vecx + vecy * vecy);
cos = vecx / len;
sin = vecy / len;
state = APPLY_SHEAR | APPLY_SCALE;
type = TYPE_GENERAL_ROTATION;
}
m00 = cos;
m10 = sin;
m01 = -sin;
m11 = cos;
m02 = 0.0;
m12 = 0.0;
}
Sets this transform to a rotation transformation that rotates
coordinates according to a rotation vector.
All coordinates rotate about the origin by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
the transform is set to an identity transform.
This operation is equivalent to calling:
setToRotation(Math.atan2(vecy, vecx));
|
public void setToRotation(double theta,
double anchorx,
double anchory) {
setToRotation(theta);
double sin = m10;
double oneMinusCos = 1.0 - m00;
m02 = anchorx * oneMinusCos + anchory * sin;
m12 = anchory * oneMinusCos - anchorx * sin;
if (m02 != 0.0 || m12 != 0.0) {
state |= APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
}
}
setToTranslation(anchorx, anchory); // S3: final translation
rotate(theta); // S2: rotate around anchor
translate(-anchorx, -anchory); // S1: translate anchor to origin
The matrix representing this transform becomes:
[ cos(theta) -sin(theta) x-x*cos+y*sin ]
[ sin(theta) cos(theta) y-x*sin-y*cos ]
[ 0 0 1 ]
Rotating by a positive angle theta rotates points on the positive
X axis toward the positive Y axis.
Note also the discussion of
Handling 90-Degree Rotations
above. |
public void setToRotation(double vecx,
double vecy,
double anchorx,
double anchory) {
setToRotation(vecx, vecy);
double sin = m10;
double oneMinusCos = 1.0 - m00;
m02 = anchorx * oneMinusCos + anchory * sin;
m12 = anchory * oneMinusCos - anchorx * sin;
if (m02 != 0.0 || m12 != 0.0) {
state |= APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
}
}
Sets this transform to a rotation transformation that rotates
coordinates around an anchor point according to a rotation
vector.
All coordinates rotate about the specified anchor coordinates
by the same amount.
The amount of rotation is such that coordinates along the former
positive X axis will subsequently align with the vector pointing
from the origin to the specified vector coordinates.
If both vecx and vecy are 0.0,
the transform is set to an identity transform.
This operation is equivalent to calling:
setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory);
|
public void setToScale(double sx,
double sy) {
m00 = sx;
m10 = 0.0;
m01 = 0.0;
m11 = sy;
m02 = 0.0;
m12 = 0.0;
if (sx != 1.0 || sy != 1.0) {
state = APPLY_SCALE;
type = TYPE_UNKNOWN;
} else {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
}
Sets this transform to a scaling transformation.
The matrix representing this transform becomes:
[ sx 0 0 ]
[ 0 sy 0 ]
[ 0 0 1 ]
|
public void setToShear(double shx,
double shy) {
m00 = 1.0;
m01 = shx;
m10 = shy;
m11 = 1.0;
m02 = 0.0;
m12 = 0.0;
if (shx != 0.0 || shy != 0.0) {
state = (APPLY_SHEAR | APPLY_SCALE);
type = TYPE_UNKNOWN;
} else {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
}
Sets this transform to a shearing transformation.
The matrix representing this transform becomes:
[ 1 shx 0 ]
[ shy 1 0 ]
[ 0 0 1 ]
|
public void setToTranslation(double tx,
double ty) {
m00 = 1.0;
m10 = 0.0;
m01 = 0.0;
m11 = 1.0;
m02 = tx;
m12 = ty;
if (tx != 0.0 || ty != 0.0) {
state = APPLY_TRANSLATE;
type = TYPE_TRANSLATION;
} else {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
}
Sets this transform to a translation transformation.
The matrix representing this transform becomes:
[ 1 0 tx ]
[ 0 1 ty ]
[ 0 0 1 ]
|
public void setTransform(AffineTransform Tx) {
this.m00 = Tx.m00;
this.m10 = Tx.m10;
this.m01 = Tx.m01;
this.m11 = Tx.m11;
this.m02 = Tx.m02;
this.m12 = Tx.m12;
this.state = Tx.state;
this.type = Tx.type;
}
Sets this transform to a copy of the transform in the specified
AffineTransform object. |
public void setTransform(double m00,
double m10,
double m01,
double m11,
double m02,
double m12) {
this.m00 = m00;
this.m10 = m10;
this.m01 = m01;
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
updateState();
}
Sets this transform to the matrix specified by the 6
double precision values. |
public void shear(double shx,
double shy) {
int state = this.state;
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SHEAR | APPLY_SCALE):
double M0, M1;
M0 = m00;
M1 = m01;
m00 = M0 + M1 * shy;
m01 = M0 * shx + M1;
M0 = m10;
M1 = m11;
m10 = M0 + M1 * shy;
m11 = M0 * shx + M1;
updateState();
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
case (APPLY_SHEAR):
m00 = m01 * shy;
m11 = m10 * shx;
if (m00 != 0.0 || m11 != 0.0) {
this.state = state | APPLY_SCALE;
}
this.type = TYPE_UNKNOWN;
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
case (APPLY_SCALE):
m01 = m00 * shx;
m10 = m11 * shy;
if (m01 != 0.0 || m10 != 0.0) {
this.state = state | APPLY_SHEAR;
}
this.type = TYPE_UNKNOWN;
return;
case (APPLY_TRANSLATE):
case (APPLY_IDENTITY):
m01 = shx;
m10 = shy;
if (m01 != 0.0 || m10 != 0.0) {
this.state = state | APPLY_SCALE | APPLY_SHEAR;
this.type = TYPE_UNKNOWN;
}
return;
}
}
Concatenates this transform with a shearing transformation.
This is equivalent to calling concatenate(SH), where SH is an
AffineTransform represented by the following matrix:
[ 1 shx 0 ]
[ shy 1 0 ]
[ 0 0 1 ]
|
public String toString() {
return ("AffineTransform[["
+ _matround(m00) + ", "
+ _matround(m01) + ", "
+ _matround(m02) + "], ["
+ _matround(m10) + ", "
+ _matround(m11) + ", "
+ _matround(m12) + "]]");
}
Returns a String that represents the value of this
Object . |
public Point2D transform(Point2D ptSrc,
Point2D ptDst) {
if (ptDst == null) {
if (ptSrc instanceof Point2D.Double) {
ptDst = new Point2D.Double();
} else {
ptDst = new Point2D.Float();
}
}
// Copy source coords into local variables in case src == dst
double x = ptSrc.getX();
double y = ptSrc.getY();
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
ptDst.setLocation(x * m00 + y * m01 + m02,
x * m10 + y * m11 + m12);
return ptDst;
case (APPLY_SHEAR | APPLY_SCALE):
ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
return ptDst;
case (APPLY_SHEAR | APPLY_TRANSLATE):
ptDst.setLocation(y * m01 + m02, x * m10 + m12);
return ptDst;
case (APPLY_SHEAR):
ptDst.setLocation(y * m01, x * m10);
return ptDst;
case (APPLY_SCALE | APPLY_TRANSLATE):
ptDst.setLocation(x * m00 + m02, y * m11 + m12);
return ptDst;
case (APPLY_SCALE):
ptDst.setLocation(x * m00, y * m11);
return ptDst;
case (APPLY_TRANSLATE):
ptDst.setLocation(x + m02, y + m12);
return ptDst;
case (APPLY_IDENTITY):
ptDst.setLocation(x, y);
return ptDst;
}
/* NOTREACHED */
}
Transforms the specified ptSrc and stores the result
in ptDst .
If ptDst is null , a new Point2D
object is allocated and then the result of the transformation is
stored in this object.
In either case, ptDst , which contains the
transformed point, is returned for convenience.
If ptSrc and ptDst are the same
object, the input point is correctly overwritten with
the transformed point. |
public void transform(Point2D[] ptSrc,
int srcOff,
Point2D[] ptDst,
int dstOff,
int numPts) {
int state = this.state;
while (--numPts >= 0) {
// Copy source coords into local variables in case src == dst
Point2D src = ptSrc[srcOff++];
double x = src.getX();
double y = src.getY();
Point2D dst = ptDst[dstOff++];
if (dst == null) {
if (src instanceof Point2D.Double) {
dst = new Point2D.Double();
} else {
dst = new Point2D.Float();
}
ptDst[dstOff - 1] = dst;
}
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
dst.setLocation(x * m00 + y * m01 + m02,
x * m10 + y * m11 + m12);
break;
case (APPLY_SHEAR | APPLY_SCALE):
dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
break;
case (APPLY_SHEAR | APPLY_TRANSLATE):
dst.setLocation(y * m01 + m02, x * m10 + m12);
break;
case (APPLY_SHEAR):
dst.setLocation(y * m01, x * m10);
break;
case (APPLY_SCALE | APPLY_TRANSLATE):
dst.setLocation(x * m00 + m02, y * m11 + m12);
break;
case (APPLY_SCALE):
dst.setLocation(x * m00, y * m11);
break;
case (APPLY_TRANSLATE):
dst.setLocation(x + m02, y + m12);
break;
case (APPLY_IDENTITY):
dst.setLocation(x, y);
break;
}
}
/* NOTREACHED */
}
Transforms an array of point objects by this transform.
If any element of the ptDst array is
null , a new Point2D object is allocated
and stored into that element before storing the results of the
transformation.
Note that this method does not take any precautions to
avoid problems caused by storing results into Point2D
objects that will be used as the source for calculations
further down the source array.
This method does guarantee that if a specified Point2D
object is both the source and destination for the same single point
transform operation then the results will not be stored until
the calculations are complete to avoid storing the results on
top of the operands.
If, however, the destination Point2D object for one
operation is the same object as the source Point2D
object for another operation further down the source array then
the original coordinates in that point are overwritten before
they can be converted. |
public void transform(float[] srcPts,
int srcOff,
float[] dstPts,
int dstOff,
int numPts) {
double M00, M01, M02, M10, M11, M12; // For caching
if (dstPts == srcPts &&
dstOff > srcOff && dstOff < srcOff + numPts * 2)
{
// If the arrays overlap partially with the destination higher
// than the source and we transform the coordinates normally
// we would overwrite some of the later source coordinates
// with results of previous transformations.
// To get around this we use arraycopy to copy the points
// to their final destination with correct overwrite
// handling and then transform them in place in the new
// safer location.
System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
// srcPts = dstPts; // They are known to be equal.
srcOff = dstOff;
}
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M00 * x + M01 * y);
dstPts[dstOff++] = (float) (M10 * x + M11 * y);
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (M10 * x + M12);
}
return;
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
dstPts[dstOff++] = (float) (M10 * x);
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
}
return;
case (APPLY_SCALE):
M00 = m00; M11 = m11;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
}
return;
case (APPLY_TRANSLATE):
M02 = m02; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
}
return;
case (APPLY_IDENTITY):
if (srcPts != dstPts || srcOff != dstOff) {
System.arraycopy(srcPts, srcOff, dstPts, dstOff,
numPts * 2);
}
return;
}
/* NOTREACHED */
}
Transforms an array of floating point coordinates by this transform.
The two coordinate array sections can be exactly the same or
can be overlapping sections of the same array without affecting the
validity of the results.
This method ensures that no source coordinates are overwritten by a
previous operation before they can be transformed.
The coordinates are stored in the arrays starting at the specified
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public void transform(double[] srcPts,
int srcOff,
double[] dstPts,
int dstOff,
int numPts) {
double M00, M01, M02, M10, M11, M12; // For caching
if (dstPts == srcPts &&
dstOff > srcOff && dstOff < srcOff + numPts * 2)
{
// If the arrays overlap partially with the destination higher
// than the source and we transform the coordinates normally
// we would overwrite some of the later source coordinates
// with results of previous transformations.
// To get around this we use arraycopy to copy the points
// to their final destination with correct overwrite
// handling and then transform them in place in the new
// safer location.
System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
// srcPts = dstPts; // They are known to be equal.
srcOff = dstOff;
}
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = M00 * x + M01 * y + M02;
dstPts[dstOff++] = M10 * x + M11 * y + M12;
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = M00 * x + M01 * y;
dstPts[dstOff++] = M10 * x + M11 * y;
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
dstPts[dstOff++] = M10 * x + M12;
}
return;
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = M01 * srcPts[srcOff++];
dstPts[dstOff++] = M10 * x;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
}
return;
case (APPLY_SCALE):
M00 = m00; M11 = m11;
while (--numPts >= 0) {
dstPts[dstOff++] = M00 * srcPts[srcOff++];
dstPts[dstOff++] = M11 * srcPts[srcOff++];
}
return;
case (APPLY_TRANSLATE):
M02 = m02; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++] + M02;
dstPts[dstOff++] = srcPts[srcOff++] + M12;
}
return;
case (APPLY_IDENTITY):
if (srcPts != dstPts || srcOff != dstOff) {
System.arraycopy(srcPts, srcOff, dstPts, dstOff,
numPts * 2);
}
return;
}
/* NOTREACHED */
}
Transforms an array of double precision coordinates by this transform.
The two coordinate array sections can be exactly the same or
can be overlapping sections of the same array without affecting the
validity of the results.
This method ensures that no source coordinates are
overwritten by a previous operation before they can be transformed.
The coordinates are stored in the arrays starting at the indicated
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public void transform(float[] srcPts,
int srcOff,
double[] dstPts,
int dstOff,
int numPts) {
double M00, M01, M02, M10, M11, M12; // For caching
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = M00 * x + M01 * y + M02;
dstPts[dstOff++] = M10 * x + M11 * y + M12;
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = M00 * x + M01 * y;
dstPts[dstOff++] = M10 * x + M11 * y;
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
dstPts[dstOff++] = M10 * x + M12;
}
return;
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = M01 * srcPts[srcOff++];
dstPts[dstOff++] = M10 * x;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
}
return;
case (APPLY_SCALE):
M00 = m00; M11 = m11;
while (--numPts >= 0) {
dstPts[dstOff++] = M00 * srcPts[srcOff++];
dstPts[dstOff++] = M11 * srcPts[srcOff++];
}
return;
case (APPLY_TRANSLATE):
M02 = m02; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++] + M02;
dstPts[dstOff++] = srcPts[srcOff++] + M12;
}
return;
case (APPLY_IDENTITY):
while (--numPts >= 0) {
dstPts[dstOff++] = srcPts[srcOff++];
dstPts[dstOff++] = srcPts[srcOff++];
}
return;
}
/* NOTREACHED */
}
Transforms an array of floating point coordinates by this transform
and stores the results into an array of doubles.
The coordinates are stored in the arrays starting at the specified
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public void transform(double[] srcPts,
int srcOff,
float[] dstPts,
int dstOff,
int numPts) {
double M00, M01, M02, M10, M11, M12; // For caching
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M01 = m01; M02 = m02;
M10 = m10; M11 = m11; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
M00 = m00; M01 = m01;
M10 = m10; M11 = m11;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
double y = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M00 * x + M01 * y);
dstPts[dstOff++] = (float) (M10 * x + M11 * y);
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
M01 = m01; M02 = m02;
M10 = m10; M12 = m12;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (M10 * x + M12);
}
return;
case (APPLY_SHEAR):
M01 = m01; M10 = m10;
while (--numPts >= 0) {
double x = srcPts[srcOff++];
dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
dstPts[dstOff++] = (float) (M10 * x);
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
M00 = m00; M02 = m02;
M11 = m11; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
}
return;
case (APPLY_SCALE):
M00 = m00; M11 = m11;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
}
return;
case (APPLY_TRANSLATE):
M02 = m02; M12 = m12;
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
}
return;
case (APPLY_IDENTITY):
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (srcPts[srcOff++]);
dstPts[dstOff++] = (float) (srcPts[srcOff++]);
}
return;
}
/* NOTREACHED */
}
Transforms an array of double precision coordinates by this transform
and stores the results into an array of floats.
The coordinates are stored in the arrays starting at the specified
offset in the order [x0, y0, x1, y1, ..., xn, yn] . |
public void translate(double tx,
double ty) {
switch (state) {
default:
stateError();
/* NOTREACHED */
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
m02 = tx * m00 + ty * m01 + m02;
m12 = tx * m10 + ty * m11 + m12;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SHEAR | APPLY_SCALE;
if (type != TYPE_UNKNOWN) {
type -= TYPE_TRANSLATION;
}
}
return;
case (APPLY_SHEAR | APPLY_SCALE):
m02 = tx * m00 + ty * m01;
m12 = tx * m10 + ty * m11;
if (m02 != 0.0 || m12 != 0.0) {
state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
}
return;
case (APPLY_SHEAR | APPLY_TRANSLATE):
m02 = ty * m01 + m02;
m12 = tx * m10 + m12;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SHEAR;
if (type != TYPE_UNKNOWN) {
type -= TYPE_TRANSLATION;
}
}
return;
case (APPLY_SHEAR):
m02 = ty * m01;
m12 = tx * m10;
if (m02 != 0.0 || m12 != 0.0) {
state = APPLY_SHEAR | APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
}
return;
case (APPLY_SCALE | APPLY_TRANSLATE):
m02 = tx * m00 + m02;
m12 = ty * m11 + m12;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SCALE;
if (type != TYPE_UNKNOWN) {
type -= TYPE_TRANSLATION;
}
}
return;
case (APPLY_SCALE):
m02 = tx * m00;
m12 = ty * m11;
if (m02 != 0.0 || m12 != 0.0) {
state = APPLY_SCALE | APPLY_TRANSLATE;
type |= TYPE_TRANSLATION;
}
return;
case (APPLY_TRANSLATE):
m02 = tx + m02;
m12 = ty + m12;
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
}
return;
case (APPLY_IDENTITY):
m02 = tx;
m12 = ty;
if (tx != 0.0 || ty != 0.0) {
state = APPLY_TRANSLATE;
type = TYPE_TRANSLATION;
}
return;
}
}
Concatenates this transform with a translation transformation.
This is equivalent to calling concatenate(T), where T is an
AffineTransform represented by the following matrix:
[ 1 0 tx ]
[ 0 1 ty ]
[ 0 0 1 ]
|
void updateState() {
if (m01 == 0.0 && m10 == 0.0) {
if (m00 == 1.0 && m11 == 1.0) {
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
} else {
state = APPLY_TRANSLATE;
type = TYPE_TRANSLATION;
}
} else {
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SCALE;
type = TYPE_UNKNOWN;
} else {
state = (APPLY_SCALE | APPLY_TRANSLATE);
type = TYPE_UNKNOWN;
}
}
} else {
if (m00 == 0.0 && m11 == 0.0) {
if (m02 == 0.0 && m12 == 0.0) {
state = APPLY_SHEAR;
type = TYPE_UNKNOWN;
} else {
state = (APPLY_SHEAR | APPLY_TRANSLATE);
type = TYPE_UNKNOWN;
}
} else {
if (m02 == 0.0 && m12 == 0.0) {
state = (APPLY_SHEAR | APPLY_SCALE);
type = TYPE_UNKNOWN;
} else {
state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
type = TYPE_UNKNOWN;
}
}
}
}
Manually recalculates the state of the transform when the matrix
changes too much to predict the effects on the state.
The following table specifies what the various settings of the
state field say about the values of the corresponding matrix
element fields.
Note that the rules governing the SCALE fields are slightly
different depending on whether the SHEAR flag is also set.
SCALE SHEAR TRANSLATE
m00/m11 m01/m10 m02/m12
IDENTITY 1.0 0.0 0.0
TRANSLATE (TR) 1.0 0.0 not both 0.0
SCALE (SC) not both 1.0 0.0 0.0
TR | SC not both 1.0 0.0 not both 0.0
SHEAR (SH) 0.0 not both 0.0 0.0
TR | SH 0.0 not both 0.0 not both 0.0
SC | SH not both 0.0 not both 0.0 0.0
TR | SC | SH not both 0.0 not both 0.0 not both 0.0
|