jogamp.graph.geom.plane.AffineTransform Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jogl-all-android Show documentation
Show all versions of jogl-all-android Show documentation
Java™ Binding for the OpenGL® API (Android)
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Denis M. Kishenko
*/
package jogamp.graph.geom.plane;
// import jogamp.opengl.util.HashCode;
import com.jogamp.graph.geom.Vertex;
import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.math.geom.AABBox;
public class AffineTransform implements Cloneable {
static final String determinantIsZero = "Determinant is zero";
public static final int TYPE_IDENTITY = 0;
public static final int TYPE_TRANSLATION = 1;
public static final int TYPE_UNIFORM_SCALE = 2;
public static final int TYPE_GENERAL_SCALE = 4;
public static final int TYPE_QUADRANT_ROTATION = 8;
public static final int TYPE_GENERAL_ROTATION = 16;
public static final int TYPE_GENERAL_TRANSFORM = 32;
public static final int TYPE_FLIP = 64;
public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
/**
* The TYPE_UNKNOWN
is an initial type value
*/
static final int TYPE_UNKNOWN = -1;
/**
* The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
*/
static final float ZERO = (float) 1E-10;
/**
* The values of transformation matrix
*/
float m00;
float m10;
float m01;
float m11;
float m02;
float m12;
/**
* The transformation type
*/
transient int type;
public AffineTransform() {
setToIdentity();
}
public AffineTransform(final AffineTransform t) {
this.type = t.type;
this.m00 = t.m00;
this.m10 = t.m10;
this.m01 = t.m01;
this.m11 = t.m11;
this.m02 = t.m02;
this.m12 = t.m12;
}
public AffineTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12) {
this.type = TYPE_UNKNOWN;
this.m00 = m00;
this.m10 = m10;
this.m01 = m01;
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
}
public AffineTransform(final float[] matrix) {
this.type = TYPE_UNKNOWN;
m00 = matrix[0];
m10 = matrix[1];
m01 = matrix[2];
m11 = matrix[3];
if (matrix.length > 4) {
m02 = matrix[4];
m12 = matrix[5];
}
}
/*
* Method returns type of affine transformation.
*
* Transform matrix is
* m00 m01 m02
* m10 m11 m12
*
* According analytic geometry new basis vectors are (m00, m01) and (m10, m11),
* translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1).
* Type transformations classification:
* TYPE_IDENTITY - new basis equals original one and zero translation
* TYPE_TRANSLATION - translation vector isn't zero
* TYPE_UNIFORM_SCALE - vectors length of new basis equals
* TYPE_GENERAL_SCALE - vectors length of new basis doesn't equal
* TYPE_FLIP - new basis vector orientation differ from original one
* TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360 degrees
* TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle
* TYPE_GENERAL_TRANSFORM - transformation can't be inversed
*/
public int getType() {
if (type != TYPE_UNKNOWN) {
return type;
}
int type = 0;
if (m00 * m01 + m10 * m11 != 0.0) {
type |= TYPE_GENERAL_TRANSFORM;
return type;
}
if (m02 != 0.0 || m12 != 0.0) {
type |= TYPE_TRANSLATION;
} else
if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
type = TYPE_IDENTITY;
return type;
}
if (m00 * m11 - m01 * m10 < 0.0) {
type |= TYPE_FLIP;
}
final float dx = m00 * m00 + m10 * m10;
final float dy = m01 * m01 + m11 * m11;
if (dx != dy) {
type |= TYPE_GENERAL_SCALE;
} else
if (dx != 1.0) {
type |= TYPE_UNIFORM_SCALE;
}
if ((m00 == 0.0 && m11 == 0.0) ||
(m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0)))
{
type |= TYPE_QUADRANT_ROTATION;
} else
if (m01 != 0.0 || m10 != 0.0) {
type |= TYPE_GENERAL_ROTATION;
}
return type;
}
public final float getScaleX() {
return m00;
}
public final float getScaleY() {
return m11;
}
public final float getShearX() {
return m01;
}
public final float getShearY() {
return m10;
}
public final float getTranslateX() {
return m02;
}
public final float getTranslateY() {
return m12;
}
public final boolean isIdentity() {
return getType() == TYPE_IDENTITY;
}
public final void getMatrix(final float[] matrix) {
matrix[0] = m00;
matrix[1] = m10;
matrix[2] = m01;
matrix[3] = m11;
if (matrix.length > 4) {
matrix[4] = m02;
matrix[5] = m12;
}
}
public final float getDeterminant() {
return m00 * m11 - m01 * m10;
}
public final AffineTransform setTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12) {
this.type = TYPE_UNKNOWN;
this.m00 = m00;
this.m10 = m10;
this.m01 = m01;
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
return this;
}
public final AffineTransform setTransform(final AffineTransform t) {
type = t.type;
setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
return this;
}
public final AffineTransform setToIdentity() {
type = TYPE_IDENTITY;
m00 = m11 = 1.0f;
m10 = m01 = m02 = m12 = 0.0f;
return this;
}
public final AffineTransform setToTranslation(final float mx, final float my) {
m00 = m11 = 1.0f;
m01 = m10 = 0.0f;
m02 = mx;
m12 = my;
if (mx == 0.0f && my == 0.0f) {
type = TYPE_IDENTITY;
} else {
type = TYPE_TRANSLATION;
}
return this;
}
public final AffineTransform setToScale(final float scx, final float scy) {
m00 = scx;
m11 = scy;
m10 = m01 = m02 = m12 = 0.0f;
if (scx != 1.0f || scy != 1.0f) {
type = TYPE_UNKNOWN;
} else {
type = TYPE_IDENTITY;
}
return this;
}
public final AffineTransform setToShear(final float shx, final float shy) {
m00 = m11 = 1.0f;
m02 = m12 = 0.0f;
m01 = shx;
m10 = shy;
if (shx != 0.0f || shy != 0.0f) {
type = TYPE_UNKNOWN;
} else {
type = TYPE_IDENTITY;
}
return this;
}
public final AffineTransform setToRotation(final float angle) {
float sin = FloatUtil.sin(angle);
float cos = FloatUtil.cos(angle);
if (FloatUtil.abs(cos) < ZERO) {
cos = 0.0f;
sin = sin > 0.0f ? 1.0f : -1.0f;
} else
if (FloatUtil.abs(sin) < ZERO) {
sin = 0.0f;
cos = cos > 0.0f ? 1.0f : -1.0f;
}
m00 = m11 = cos;
m01 = -sin;
m10 = sin;
m02 = m12 = 0.0f;
type = TYPE_UNKNOWN;
return this;
}
public final AffineTransform setToRotation(final float angle, final float px, final float py) {
setToRotation(angle);
m02 = px * (1.0f - m00) + py * m10;
m12 = py * (1.0f - m00) - px * m10;
type = TYPE_UNKNOWN;
return this;
}
public final AffineTransform translate(final float mx, final float my, final AffineTransform tmp) {
return concatenate(tmp.setToTranslation(mx, my));
}
public final AffineTransform scale(final float scx, final float scy, final AffineTransform tmp) {
return concatenate(tmp.setToScale(scx, scy));
}
public final AffineTransform shear(final float shx, final float shy, final AffineTransform tmp) {
return concatenate(tmp.setToShear(shx, shy));
}
public final AffineTransform rotate(final float angle, final AffineTransform tmp) {
return concatenate(tmp.setToRotation(angle));
}
public final AffineTransform rotate(final float angle, final float px, final float py, final AffineTransform tmp) {
return concatenate(tmp.setToRotation(angle, px, py));
}
/**
* Multiply matrix of two AffineTransform objects.
* @param tL - the AffineTransform object is a multiplicand (left argument)
* @param tR - the AffineTransform object is a multiplier (right argument)
*
* @return A new AffineTransform object containing the result of [tL] X [tR].
*/
public final static AffineTransform multiply(final AffineTransform tL, final AffineTransform tR) {
return new AffineTransform(
tR.m00 * tL.m00 + tR.m10 * tL.m01, // m00
tR.m00 * tL.m10 + tR.m10 * tL.m11, // m10
tR.m01 * tL.m00 + tR.m11 * tL.m01, // m01
tR.m01 * tL.m10 + tR.m11 * tL.m11, // m11
tR.m02 * tL.m00 + tR.m12 * tL.m01 + tL.m02, // m02
tR.m02 * tL.m10 + tR.m12 * tL.m11 + tL.m12);// m12
}
/**
* Concatenates the given matrix to this.
*
* Implementations performs the matrix multiplication:
*
* [this] = [this] X [tR]
*
*
* @param tR the right-argument of the matrix multiplication
* @return this transform for chaining
*/
public final AffineTransform concatenate(final AffineTransform tR) {
// setTransform(multiply(this, tR));
type = TYPE_UNKNOWN;
setTransform(
tR.m00 * m00 + tR.m10 * m01, // m00
tR.m00 * m10 + tR.m10 * m11, // m10
tR.m01 * m00 + tR.m11 * m01, // m01
tR.m01 * m10 + tR.m11 * m11, // m11
tR.m02 * m00 + tR.m12 * m01 + m02, // m02
tR.m02 * m10 + tR.m12 * m11 + m12);// m12
return this;
}
/**
* Pre-concatenates the given matrix to this.
*
* Implementations performs the matrix multiplication:
*
* [this] = [tL] X [this]
*
*
* @param tL the left-argument of the matrix multiplication
* @return this transform for chaining
*/
public final AffineTransform preConcatenate(final AffineTransform tL) {
// setTransform(multiply(tL, this));
type = TYPE_UNKNOWN;
setTransform(
m00 * tL.m00 + m10 * tL.m01, // m00
m00 * tL.m10 + m10 * tL.m11, // m10
m01 * tL.m00 + m11 * tL.m01, // m01
m01 * tL.m10 + m11 * tL.m11, // m11
m02 * tL.m00 + m12 * tL.m01 + tL.m02, // m02
m02 * tL.m10 + m12 * tL.m11 + tL.m12);// m12
return this;
}
public final AffineTransform createInverse() throws NoninvertibleTransformException {
final float det = getDeterminant();
if (FloatUtil.abs(det) < ZERO) {
throw new NoninvertibleTransformException(determinantIsZero);
}
return new AffineTransform(
m11 / det, // m00
-m10 / det, // m10
-m01 / det, // m01
m00 / det, // m11
(m01 * m12 - m11 * m02) / det, // m02
(m10 * m02 - m00 * m12) / det // m12
);
}
/**
*
* @param src
* @param dst
* @return dst for chaining
*/
public final AABBox transform(final AABBox src, final AABBox dst) {
final float[] srcLo = src.getLow();
final float[] srcHi = src.getHigh();
dst.setSize(srcLo[0] * m00 + srcLo[1] * m01 + m02, srcLo[0] * m10 + srcLo[1] * m11 + m12, srcLo[2],
srcHi[0] * m00 + srcHi[1] * m01 + m02, srcHi[0] * m10 + srcHi[1] * m11 + m12, srcHi[2]);
return dst;
}
/**
* @param src
* @param dst
* @return dst for chaining
*/
public final Vertex transform(final Vertex src, final Vertex dst) {
final float x = src.getX();
final float y = src.getY();
dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, src.getZ());
return dst;
}
public final void transform(final Vertex[] src, int srcOff, final Vertex[] dst, int dstOff, int length) {
while (--length >= 0) {
final Vertex srcPoint = src[srcOff++];
final Vertex dstPoint = dst[dstOff];
if (dstPoint == null) {
throw new IllegalArgumentException("dst["+dstOff+"] is null");
}
final float x = srcPoint.getX();
final float y = srcPoint.getY();
dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, srcPoint.getZ());
dst[dstOff++] = dstPoint;
}
}
/**
* @param src float[2] source of transformation
* @param dst float[2] destination of transformation, maybe be equal to src
* @return dst for chaining
*/
public final float[] transform(final float[] src, final float[] dst) {
final float x = src[0];
final float y = src[1];
dst[0] = x * m00 + y * m01 + m02;
dst[1] = x * m10 + y * m11 + m12;
return dst;
}
public final void transform(final float[] src, final int srcOff, final float[] dst, final int dstOff) {
final float x = src[srcOff + 0];
final float y = src[srcOff + 1];
dst[dstOff + 0] = x * m00 + y * m01 + m02;
dst[dstOff + 1] = x * m10 + y * m11 + m12;
}
public final void transform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) {
int step = 2;
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
srcOff = srcOff + length * 2 - 2;
dstOff = dstOff + length * 2 - 2;
step = -2;
}
while (--length >= 0) {
final float x = src[srcOff + 0];
final float y = src[srcOff + 1];
dst[dstOff + 0] = x * m00 + y * m01 + m02;
dst[dstOff + 1] = x * m10 + y * m11 + m12;
srcOff += step;
dstOff += step;
}
}
/**
*
* @param src
* @param dst
* @return return dst for chaining
*/
public final Vertex deltaTransform(final Vertex src, final Vertex dst) {
final float x = src.getX();
final float y = src.getY();
dst.setCoord(x * m00 + y * m01, x * m10 + y * m11, src.getZ());
return dst;
}
public final void deltaTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) {
while (--length >= 0) {
final float x = src[srcOff++];
final float y = src[srcOff++];
dst[dstOff++] = x * m00 + y * m01;
dst[dstOff++] = x * m10 + y * m11;
}
}
/**
*
* @param src
* @param dst
* @return return dst for chaining
* @throws NoninvertibleTransformException
*/
public final Vertex inverseTransform(final Vertex src, final Vertex dst) throws NoninvertibleTransformException {
final float det = getDeterminant();
if (FloatUtil.abs(det) < ZERO) {
throw new NoninvertibleTransformException(determinantIsZero);
}
final float x = src.getX() - m02;
final float y = src.getY() - m12;
dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det, src.getZ());
return dst;
}
public final void inverseTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length)
throws NoninvertibleTransformException
{
final float det = getDeterminant();
if (FloatUtil.abs(det) < ZERO) {
throw new NoninvertibleTransformException(determinantIsZero);
}
while (--length >= 0) {
final float x = src[srcOff++] - m02;
final float y = src[srcOff++] - m12;
dst[dstOff++] = (x * m11 - y * m01) / det;
dst[dstOff++] = (y * m00 - x * m10) / det;
}
}
public final Path2D createTransformedShape(final Path2D src) {
if (src == null) {
return null;
}
return src.createTransformedShape(this);
/**
* If !(src instanceof Path2D): (but here it always is)
final PathIterator path = src.iterator(this);
final Path2D dst = new Path2D(path.getWindingRule());
dst.append(path, false);
return dst;
*/
}
@Override
public final String toString() {
return
getClass().getName() +
"[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
@Override
public final AffineTransform clone() {
try {
return (AffineTransform) super.clone();
} catch (final CloneNotSupportedException e) {
throw new InternalError();
}
}
/** @Override
public int hashCode() {
HashCode hash = new HashCode();
hash.append(m00);
hash.append(m01);
hash.append(m02);
hash.append(m10);
hash.append(m11);
hash.append(m12);
return hash.hashCode();
} */
@Override
public final boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AffineTransform) {
final AffineTransform t = (AffineTransform)obj;
return
m00 == t.m00 && m01 == t.m01 &&
m02 == t.m02 && m10 == t.m10 &&
m11 == t.m11 && m12 == t.m12;
}
return false;
}
@Override
public final int hashCode() {
throw new InternalError("hashCode not designed");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy