com.sun.javafx.geom.transform.Affine3D Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.geom.transform;
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.Point2D;
import com.sun.javafx.geom.Rectangle;
import com.sun.javafx.geom.Vec3d;
/**
*
*/
public class Affine3D extends AffineBase {
private double mxz;
private double myz;
private double mzx;
private double mzy;
private double mzz;
private double mzt;
public Affine3D() {
mxx = myy = mzz = 1.0;
// mxy = mxz = mxt = 0.0; /* Not needed. */
// myx = myz = myt = 0.0; /* Not needed. */
// mzx = mzy = mzt = 0.0; /* Not needed. */
// type = TYPE_IDENTITY; /* Not needed. */
}
public Affine3D(BaseTransform transform) {
setTransform(transform);
}
public Affine3D(double mxx, double mxy, double mxz, double mxt,
double myx, double myy, double myz, double myt,
double mzx, double mzy, double mzz, double mzt)
{
this.mxx = mxx;
this.mxy = mxy;
this.mxz = mxz;
this.mxt = mxt;
this.myx = myx;
this.myy = myy;
this.myz = myz;
this.myt = myt;
this.mzx = mzx;
this.mzy = mzy;
this.mzz = mzz;
this.mzt = mzt;
updateState();
}
public Affine3D(Affine3D other) {
this.mxx = other.mxx;
this.mxy = other.mxy;
this.mxz = other.mxz;
this.mxt = other.mxt;
this.myx = other.myx;
this.myy = other.myy;
this.myz = other.myz;
this.myt = other.myt;
this.mzx = other.mzx;
this.mzy = other.mzy;
this.mzz = other.mzz;
this.mzt = other.mzt;
this.state = other.state;
this.type = other.type;
}
@Override
public BaseTransform copy() {
return new Affine3D(this);
}
@Override
public Degree getDegree() {
return Degree.AFFINE_3D;
}
@Override
protected void reset3Delements() {
this.mxz = 0.0;
this.myz = 0.0;
this.mzx = 0.0;
this.mzy = 0.0;
this.mzz = 1.0;
this.mzt = 0.0;
}
@Override
protected void updateState() {
super.updateState();
if (!almostZero(mxz) ||
!almostZero(myz) ||
!almostZero(mzx) ||
!almostZero(mzy) ||
!almostOne(mzz) ||
!almostZero(mzt))
{
state |= APPLY_3D;
if (type != TYPE_UNKNOWN) {
type |= TYPE_AFFINE_3D;
}
}
}
@Override public double getMxz() { return mxz; }
@Override public double getMyz() { return myz; }
@Override public double getMzx() { return mzx; }
@Override public double getMzy() { return mzy; }
@Override public double getMzz() { return mzz; }
@Override public double getMzt() { return mzt; }
@Override
public double getDeterminant() {
if ((state & APPLY_3D) == 0) {
return super.getDeterminant();
}
// D=a11{a22a33-a32a23}
// +a12{a23a31-a33a21}
// +a13{a21a32-a31a22}
return (mxx * (myy * mzz - mzy * myz) +
mxy * (myz * mzx - mzz * myx) +
mxz * (myx * mzy - mzx * myy));
}
@Override
public void setTransform(BaseTransform transform) {
this.mxx = transform.getMxx();
this.mxy = transform.getMxy();
this.mxz = transform.getMxz();
this.mxt = transform.getMxt();
this.myx = transform.getMyx();
this.myy = transform.getMyy();
this.myz = transform.getMyz();
this.myt = transform.getMyt();
this.mzx = transform.getMzx();
this.mzy = transform.getMzy();
this.mzz = transform.getMzz();
this.mzt = transform.getMzt();
updateState();
}
public void setTransform(double mxx, double mxy, double mxz, double mxt,
double myx, double myy, double myz, double myt,
double mzx, double mzy, double mzz, double mzt)
{
this.mxx = mxx;
this.mxy = mxy;
this.mxz = mxz;
this.mxt = mxt;
this.myx = myx;
this.myy = myy;
this.myz = myz;
this.myt = myt;
this.mzx = mzx;
this.mzy = mzy;
this.mzz = mzz;
this.mzt = mzt;
updateState();
}
public void setToTranslation(double tx, double ty, double tz) {
this.mxx = 1.0;
this.mxy = 0.0;
this.mxz = 0.0;
this.mxt = tx;
this.myx = 0.0;
this.myy = 1.0;
this.myz = 0.0;
this.myt = ty;
this.mzx = 0.0;
this.mzy = 0.0;
this.mzz = 1.0;
this.mzt = tz;
if (tz == 0.0) {
if (tx == 0.0 && ty == 0.0) {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
} else {
state = APPLY_TRANSLATE;
type = TYPE_TRANSLATION;
}
} else {
if (tx == 0.0 && ty == 0.0) {
state = APPLY_3D;
type = TYPE_AFFINE_3D;
} else {
state = APPLY_TRANSLATE | APPLY_3D;
type = TYPE_TRANSLATION | TYPE_AFFINE_3D;
}
}
}
public void setToScale(double sx, double sy, double sz) {
this.mxx = sx;
this.mxy = 0.0;
this.mxz = 0.0;
this.mxt = 0.0;
this.myx = 0.0;
this.myy = sy;
this.myz = 0.0;
this.myt = 0.0;
this.mzx = 0.0;
this.mzy = 0.0;
this.mzz = sz;
this.mzt = 0.0;
if (sz == 1.0) {
if (sx == 1.0 && sy == 1.0) {
state = APPLY_IDENTITY;
type = TYPE_IDENTITY;
} else {
state = APPLY_SCALE;
type = TYPE_UNKNOWN;
}
} else {
if (sx == 1.0 && sy == 1.0) {
state = APPLY_3D;
type = TYPE_AFFINE_3D;
} else {
state = APPLY_SCALE | APPLY_3D;
type = TYPE_UNKNOWN;
}
}
}
public void setToRotation(double theta,
double axisX, double axisY, double axisZ,
double pivotX, double pivotY, double pivotZ)
{
setToRotation(theta, axisX, axisY, axisZ);
if (pivotX != 0.0 || pivotY != 0.0 || pivotZ != 0.0) {
preTranslate(pivotX, pivotY, pivotZ);
translate(-pivotX, -pivotY, -pivotZ);
}
}
public void setToRotation(double theta, double axisX, double axisY, double axisZ) {
double mag = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
if (almostZero(mag)) {
setToIdentity();
return;
}
mag = 1.0 / mag;
double ax = axisX * mag;
double ay = axisY * mag;
double az = axisZ * mag;
double sinTheta = Math.sin(theta);
double cosTheta = Math.cos(theta);
double t = 1.0 - cosTheta;
double xz = ax * az;
double xy = ax * ay;
double yz = ay * az;
this.mxx = t * ax * ax + cosTheta;
this.mxy = t * xy - sinTheta * az;
this.mxz = t * xz + sinTheta * ay;
this.mxt = 0.0;
this.myx = t * xy + sinTheta * az;
this.myy = t * ay * ay + cosTheta;
this.myz = t * yz - sinTheta * ax;
this.myt = 0.0;
this.mzx = t * xz - sinTheta * ay;
this.mzy = t * yz + sinTheta * ax;
this.mzz = t * az * az + cosTheta;
this.mzt = 0.0;
updateState();
}
@Override
public BaseBounds transform(BaseBounds src, BaseBounds dst) {
if ((state & APPLY_3D) == 0) {
return dst = super.transform(src, dst);
}
switch (state) {
default:
/* NOBREAK */
// TODO: Optimize these cases ... (RT-26800)
case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
/* NOBREAK */
case (APPLY_SHEAR | APPLY_SCALE):
/* NOBREAK */
case (APPLY_SHEAR | APPLY_TRANSLATE):
/* NOBREAK */
case (APPLY_SHEAR):
Vec3d tempV3d = new Vec3d();
dst = TransformHelper.general3dBoundsTransform(this, src, dst, tempV3d);
break;
case (APPLY_SCALE | APPLY_TRANSLATE):
dst = dst.deriveWithNewBoundsAndSort(
(float) (src.getMinX() * mxx + mxt),
(float) (src.getMinY() * myy + myt),
(float) (src.getMinZ() * mzz + mzt),
(float) (src.getMaxX() * mxx + mxt),
(float) (src.getMaxY() * myy + myt),
(float) (src.getMaxZ() * mzz + mzt));
break;
case (APPLY_SCALE):
dst = dst.deriveWithNewBoundsAndSort(
(float) (src.getMinX() * mxx),
(float) (src.getMinY() * myy),
(float) (src.getMinZ() * mzz),
(float) (src.getMaxX() * mxx),
(float) (src.getMaxY() * myy),
(float) (src.getMaxZ() * mzz));
break;
case (APPLY_TRANSLATE):
dst = dst.deriveWithNewBounds(
(float) (src.getMinX() + mxt),
(float) (src.getMinY() + myt),
(float) (src.getMinZ() + mzt),
(float) (src.getMaxX() + mxt),
(float) (src.getMaxY() + myt),
(float) (src.getMaxZ() + mzt));
break;
case (APPLY_IDENTITY):
if (src != dst) {
dst = dst.deriveWithNewBounds(src);
}
break;
}
return dst;
}
@Override
public Vec3d transform(Vec3d src, Vec3d dst) {
if ((state & APPLY_3D) == 0) {
return super.transform(src, dst);
}
if (dst == null) {
dst = new Vec3d();
}
double x = src.x;
double y = src.y;
double z = src.z;
dst.x = mxx * x + mxy * y + mxz * z + mxt;
dst.y = myx * x + myy * y + myz * z + myt;
dst.z = mzx * x + mzy * y + mzz * z + mzt;
return dst;
}
@Override
public Vec3d deltaTransform(Vec3d src, Vec3d dst) {
if ((state & APPLY_3D) == 0) {
return super.deltaTransform(src, dst);
}
if (dst == null) {
dst = new Vec3d();
}
double x = src.x;
double y = src.y;
double z = src.z;
dst.x = mxx * x + mxy * y + mxz * z;
dst.y = myx * x + myy * y + myz * z;
dst.z = mzx * x + mzy * y + mzz * z;
return dst;
}
@Override
public void inverseTransform(float[] srcPts, int srcOff,
float[] dstPts, int dstOff,
int numPts)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
super.inverseTransform(srcPts, srcOff, dstPts, dstOff, numPts);
} else {
// TODO: Optimize... (RT-26800)
createInverse().transform(srcPts, srcOff, dstPts, dstOff, numPts);
}
}
@Override
public void inverseDeltaTransform(float[] srcPts, int srcOff,
float[] dstPts, int dstOff,
int numPts)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
super.inverseDeltaTransform(srcPts, srcOff, dstPts, dstOff, numPts);
} else {
// TODO: Optimize... (RT-26800)
createInverse().deltaTransform(srcPts, srcOff, dstPts, dstOff, numPts);
}
}
@Override
public void inverseTransform(double[] srcPts, int srcOff,
double[] dstPts, int dstOff,
int numPts)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
super.inverseTransform(srcPts, srcOff, dstPts, dstOff, numPts);
} else {
// TODO: Optimize... (RT-26800)
createInverse().transform(srcPts, srcOff, dstPts, dstOff, numPts);
}
}
@Override
public Point2D inverseTransform(Point2D src, Point2D dst)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
return super.inverseTransform(src, dst);
} else {
// TODO: Optimize... (RT-26800)
return createInverse().transform(src, dst);
}
}
@Override
public Vec3d inverseTransform(Vec3d src, Vec3d dst)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
return super.inverseTransform(src, dst);
} else {
// TODO: Optimize... (RT-26800)
return createInverse().transform(src, dst);
}
}
@Override
public Vec3d inverseDeltaTransform(Vec3d src, Vec3d dst)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
return super.inverseDeltaTransform(src, dst);
} else {
// TODO: Optimize... (RT-26800)
return createInverse().deltaTransform(src, dst);
}
}
@Override
public BaseBounds inverseTransform(BaseBounds bounds, BaseBounds result)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
result = super.inverseTransform(bounds, result);
} else {
// TODO: Optimize... (RT-26800)
result = createInverse().transform(bounds, result);
}
return result;
}
@Override
public void inverseTransform(Rectangle bounds, Rectangle result)
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
super.inverseTransform(bounds, result);
} else {
// TODO: Optimize... (RT-26800)
createInverse().transform(bounds, result);
}
}
@Override
public BaseTransform createInverse()
throws NoninvertibleTransformException
{
BaseTransform t = copy();
t.invert();
return t;
}
@Override
public void invert()
throws NoninvertibleTransformException
{
if ((state & APPLY_3D) == 0) {
super.invert();
return;
}
// InvM = Transpose(Cofactor(M)) / det(M)
// Cofactor(M) = matrix of cofactors(0..3,0..3)
// cofactor(r,c) = (-1 if r+c is odd) * minor(r,c)
// minor(r,c) = det(M with row r and col c removed)
// For an Affine3D matrix, minor(r, 3) is {0, 0, 0, det}
// which generates {0, 0, 0, 1} and so can be ignored.
// TODO: Inlining the minor calculations should allow them
// to be simplified... (RT-26800)
double cxx = minor(0, 0);
double cyx = - minor(0, 1);
double czx = minor(0, 2);
double cxy = - minor(1, 0);
double cyy = minor(1, 1);
double czy = - minor(1, 2);
double cxz = minor(2, 0);
double cyz = - minor(2, 1);
double czz = minor(2, 2);
double cxt = - minor(3, 0);
double cyt = minor(3, 1);
double czt = - minor(3, 2);
double det = getDeterminant();
mxx = cxx / det;
mxy = cxy / det;
mxz = cxz / det;
mxt = cxt / det;
myx = cyx / det;
myy = cyy / det;
myz = cyz / det;
myt = cyt / det;
mzx = czx / det;
mzy = czy / det;
mzz = czz / det;
mzt = czt / det;
updateState();
}
private double minor(int row, int col) {
double m00 = mxx, m01 = mxy, m02 = mxz;
double m10 = myx, m11 = myy, m12 = myz;
double m20 = mzx, m21 = mzy, m22 = mzz;
switch (col) {
case 0:
m00 = m01;
m10 = m11;
m20 = m21;
case 1:
m01 = m02;
m11 = m12;
m21 = m22;
case 2:
m02 = mxt;
m12 = myt;
m22 = mzt;
}
switch (row) {
case 0:
m00 = m10;
m01 = m11;
// m02 = m12;
case 1:
m10 = m20;
m11 = m21;
// m12 = m22;
case 2:
// m20 = 0.0;
// m21 = 0.0;
// m22 = 1.0;
break;
case 3:
// This is the only row that requires a full 3x3 determinant
return (m00 * (m11 * m22 - m21 * m12) +
m01 * (m12 * m20 - m22 * m10) +
m02 * (m10 * m21 - m20 * m11));
}
// return (m00 * (m11 * 1.0 - 0.0 * m12) +
// m01 * (m12 * 0.0 - 1.0 * m10) +
// m02 * (m10 * 0.0 - 0.0 * m11));
return (m00 * m11 - m01 * m10);
}
@Override
public Affine3D deriveWithNewTransform(BaseTransform tx) {
setTransform(tx);
return this;
}
@Override
public Affine3D deriveWithTranslation(double tx, double ty) {
translate(tx, ty, 0.0);
return this;
}
@Override
public void translate(double tx, double ty) {
if ((state & APPLY_3D) == 0) {
super.translate(tx, ty);
} else {
translate(tx, ty, 0.0);
}
}
public void translate(double tx, double ty, double tz) {
if ((state & APPLY_3D) == 0) {
super.translate(tx, ty);
if (tz != 0.0) {
this.mzt = tz;
state |= APPLY_3D;
if (type != TYPE_UNKNOWN) {
type |= TYPE_AFFINE_3D;
}
}
return;
}
this.mxt = tx * mxx + ty * mxy + tz * mxz + mxt;
this.myt = tx * myx + ty * myy + tz * myz + myt;
this.mzt = tx * mzx + ty * mzy + tz * mzz + mzt;
updateState();
}
@Override
public Affine3D deriveWithPreTranslation(double mxt, double myt) {
preTranslate(mxt, myt, 0.0);
return this;
}
@Override
public BaseTransform deriveWithTranslation(double mxt, double myt, double mzt) {
translate(mxt, myt, mzt);
return this;
}
@Override
public BaseTransform deriveWithScale(double mxx, double myy, double mzz) {
scale(mxx, myy, mzz);
return this;
}
@Override
public BaseTransform deriveWithRotation(double theta,
double axisX, double axisY, double axisZ) {
rotate(theta, axisX, axisY, axisZ);
return this;
}
public void preTranslate(double mxt, double myt, double mzt) {
this.mxt += mxt;
this.myt += myt;
this.mzt += mzt;
int clearflags = 0;
int setflags = 0;
if (this.mzt == 0.0) {
if ((state & APPLY_3D) != 0) {
// Might have become non-3D...
updateState();
return;
}
} else {
state |= APPLY_3D;
setflags = TYPE_AFFINE_3D;
}
if (this.mxt == 0.0 && this.myt == 0.0) {
state &= ~APPLY_TRANSLATE;
clearflags = TYPE_TRANSLATION;
} else {
state |= APPLY_TRANSLATE;
setflags |= TYPE_TRANSLATION;
}
if (type != TYPE_UNKNOWN) {
type = ((type & ~clearflags) | setflags);
}
}
@Override
public void scale(double sx, double sy) {
if ((state & APPLY_3D) == 0) {
super.scale(sx, sy);
} else {
scale(sx, sy, 1.0);
}
}
public void scale(double sx, double sy, double sz) {
if ((state & APPLY_3D) == 0) {
super.scale(sx, sy);
if (sz != 1.0) {
this.mzz = sz;
state |= APPLY_3D;
if (type != TYPE_UNKNOWN) {
type |= TYPE_AFFINE_3D;
}
}
return;
}
this.mxx *= sx;
this.mxy *= sy;
this.mxz *= sz;
this.myx *= sx;
this.myy *= sy;
this.myz *= sz;
this.mzx *= sx;
this.mzy *= sy;
this.mzz *= sz;
// TODO: Optimize the state... (RT-26800)
updateState();
}
@Override
public void rotate(double theta) {
if ((state & APPLY_3D) == 0) {
super.rotate(theta);
} else {
rotate(theta, 0, 0, 1);
}
}
public void rotate(double theta, double axisX, double axisY, double axisZ) {
if ((state & APPLY_3D) == 0 && almostZero(axisX) && almostZero(axisY)) {
if (axisZ > 0) {
super.rotate(theta);
} else if (axisZ < 0) {
super.rotate(-theta);
} // else rotating about zero vector - NOP
return;
}
double mag = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
if (almostZero(mag)) {
return;
}
mag = 1.0 / mag;
double ax = axisX * mag;
double ay = axisY * mag;
double az = axisZ * mag;
double sinTheta = Math.sin(theta);
double cosTheta = Math.cos(theta);
double t = 1.0 - cosTheta;
double xz = ax * az;
double xy = ax * ay;
double yz = ay * az;
double Txx = t * ax * ax + cosTheta;
double Txy = t * xy - sinTheta * az;
double Txz = t * xz + sinTheta * ay;
double Tyx = t * xy + sinTheta * az;
double Tyy = t * ay * ay + cosTheta;
double Tyz = t * yz - sinTheta * ax;
double Tzx = t * xz - sinTheta * ay;
double Tzy = t * yz + sinTheta * ax;
double Tzz = t * az * az + cosTheta;
double rxx = (mxx * Txx + mxy * Tyx + mxz * Tzx /* + mxt * 0.0 */);
double rxy = (mxx * Txy + mxy * Tyy + mxz * Tzy /* + mxt * 0.0 */);
double rxz = (mxx * Txz + mxy * Tyz + mxz * Tzz /* + mxt * 0.0 */);
double ryx = (myx * Txx + myy * Tyx + myz * Tzx /* + myt * 0.0 */);
double ryy = (myx * Txy + myy * Tyy + myz * Tzy /* + myt * 0.0 */);
double ryz = (myx * Txz + myy * Tyz + myz * Tzz /* + myt * 0.0 */);
double rzx = (mzx * Txx + mzy * Tyx + mzz * Tzx /* + mzt * 0.0 */);
double rzy = (mzx * Txy + mzy * Tyy + mzz * Tzy /* + mzt * 0.0 */);
double rzz = (mzx * Txz + mzy * Tyz + mzz * Tzz /* + mzt * 0.0 */);
this.mxx = rxx;
this.mxy = rxy;
this.mxz = rxz;
this.myx = ryx;
this.myy = ryy;
this.myz = ryz;
this.mzx = rzx;
this.mzy = rzy;
this.mzz = rzz;
updateState();
}
@Override
public void shear(double shx, double shy) {
if ((state & APPLY_3D) == 0) {
super.shear(shx, shy);
return;
}
double rxx = (mxx + mxy * shy);
double rxy = (mxy + mxx * shx);
double ryx = (myx + myy * shy);
double ryy = (myy + myx * shx);
double rzx = (mzx + mzy * shy);
double rzy = (mzy + mzx * shx);
this.mxx = rxx;
this.mxy = rxy;
this.myx = ryx;
this.myy = ryy;
this.mzx = rzx;
this.mzy = rzy;
updateState();
}
@Override
public Affine3D deriveWithConcatenation(BaseTransform transform) {
concatenate(transform);
return this;
}
@Override
public Affine3D deriveWithPreConcatenation(BaseTransform transform) {
preConcatenate(transform);
return this;
}
@Override
public void concatenate(BaseTransform transform) {
switch (transform.getDegree()) {
case IDENTITY:
return;
case TRANSLATE_2D:
translate(transform.getMxt(), transform.getMyt());
return;
case TRANSLATE_3D:
translate(transform.getMxt(), transform.getMyt(), transform.getMzt());
return;
case AFFINE_3D:
if (!transform.is2D()) {
break;
}
/* No Break */
case AFFINE_2D:
if ((state & APPLY_3D) == 0) {
super.concatenate(transform);
return;
}
break;
}
double Txx = transform.getMxx();
double Txy = transform.getMxy();
double Txz = transform.getMxz();
double Txt = transform.getMxt();
double Tyx = transform.getMyx();
double Tyy = transform.getMyy();
double Tyz = transform.getMyz();
double Tyt = transform.getMyt();
double Tzx = transform.getMzx();
double Tzy = transform.getMzy();
double Tzz = transform.getMzz();
double Tzt = transform.getMzt();
double rxx = (mxx * Txx + mxy * Tyx + mxz * Tzx /* + mxt * 0.0 */);
double rxy = (mxx * Txy + mxy * Tyy + mxz * Tzy /* + mxt * 0.0 */);
double rxz = (mxx * Txz + mxy * Tyz + mxz * Tzz /* + mxt * 0.0 */);
double rxt = (mxx * Txt + mxy * Tyt + mxz * Tzt + mxt /* * 1.0 */);
double ryx = (myx * Txx + myy * Tyx + myz * Tzx /* + myt * 0.0 */);
double ryy = (myx * Txy + myy * Tyy + myz * Tzy /* + myt * 0.0 */);
double ryz = (myx * Txz + myy * Tyz + myz * Tzz /* + myt * 0.0 */);
double ryt = (myx * Txt + myy * Tyt + myz * Tzt + myt /* * 1.0 */);
double rzx = (mzx * Txx + mzy * Tyx + mzz * Tzx /* + mzt * 0.0 */);
double rzy = (mzx * Txy + mzy * Tyy + mzz * Tzy /* + mzt * 0.0 */);
double rzz = (mzx * Txz + mzy * Tyz + mzz * Tzz /* + mzt * 0.0 */);
double rzt = (mzx * Txt + mzy * Tyt + mzz * Tzt + mzt /* * 1.0 */);
this.mxx = rxx;
this.mxy = rxy;
this.mxz = rxz;
this.mxt = rxt;
this.myx = ryx;
this.myy = ryy;
this.myz = ryz;
this.myt = ryt;
this.mzx = rzx;
this.mzy = rzy;
this.mzz = rzz;
this.mzt = rzt;
updateState();
}
public void concatenate(double Txx, double Txy, double Txz, double Txt,
double Tyx, double Tyy, double Tyz, double Tyt,
double Tzx, double Tzy, double Tzz, double Tzt)
{
double rxx = (mxx * Txx + mxy * Tyx + mxz * Tzx /* + mxt * 0.0 */);
double rxy = (mxx * Txy + mxy * Tyy + mxz * Tzy /* + mxt * 0.0 */);
double rxz = (mxx * Txz + mxy * Tyz + mxz * Tzz /* + mxt * 0.0 */);
double rxt = (mxx * Txt + mxy * Tyt + mxz * Tzt + mxt /* * 1.0 */);
double ryx = (myx * Txx + myy * Tyx + myz * Tzx /* + myt * 0.0 */);
double ryy = (myx * Txy + myy * Tyy + myz * Tzy /* + myt * 0.0 */);
double ryz = (myx * Txz + myy * Tyz + myz * Tzz /* + myt * 0.0 */);
double ryt = (myx * Txt + myy * Tyt + myz * Tzt + myt /* * 1.0 */);
double rzx = (mzx * Txx + mzy * Tyx + mzz * Tzx /* + mzt * 0.0 */);
double rzy = (mzx * Txy + mzy * Tyy + mzz * Tzy /* + mzt * 0.0 */);
double rzz = (mzx * Txz + mzy * Tyz + mzz * Tzz /* + mzt * 0.0 */);
double rzt = (mzx * Txt + mzy * Tyt + mzz * Tzt + mzt /* * 1.0 */);
this.mxx = rxx;
this.mxy = rxy;
this.mxz = rxz;
this.mxt = rxt;
this.myx = ryx;
this.myy = ryy;
this.myz = ryz;
this.myt = ryt;
this.mzx = rzx;
this.mzy = rzy;
this.mzz = rzz;
this.mzt = rzt;
updateState();
}
@Override
public Affine3D deriveWithConcatenation(double Txx, double Tyx,
double Txy, double Tyy,
double Txt, double Tyt)
{
double rxx = (mxx * Txx + mxy * Tyx /* + mxz * 0.0 + mxt * 0.0 */);
double rxy = (mxx * Txy + mxy * Tyy /* + mxz * 0.0 + mxt * 0.0 */);
// double rxz = (mxz /* * 1.0 + mxx * 0.0 + mxy * 0.0 + mxt * 0.0 */);
double rxt = (mxx * Txt + mxy * Tyt + mxt /* + mxz * 0.0 * 1.0 */);
double ryx = (myx * Txx + myy * Tyx /* + myz * 0.0 + myt * 0.0 */);
double ryy = (myx * Txy + myy * Tyy /* + myz * 0.0 + myt * 0.0 */);
// double ryz = (myz /* * 1.0 + myx * 0.0 + myy * 0.0 + myt * 0.0 */);
double ryt = (myx * Txt + myy * Tyt + myt /* * 1.0 + myz * 0.0 */);
double rzx = (mzx * Txx + mzy * Tyx /* + mzz * 0.0 + mzt * 0.0 */);
double rzy = (mzx * Txy + mzy * Tyy /* + mzz * 0.0 + mzt * 0.0 */);
// double rzz = (mzz /* * 1.0 + mzx * 0.0 + mzy * 0.0 + mzt * 0.0 */);
double rzt = (mzx * Txt + mzy * Tyt + mzt /* * 1.0 + mzz * 0.0 */);
this.mxx = rxx;
this.mxy = rxy;
// this.mxz = rxz; // == mxz anyway
this.mxt = rxt;
this.myx = ryx;
this.myy = ryy;
// this.myz = ryz; // == myz anyway
this.myt = ryt;
this.mzx = rzx;
this.mzy = rzy;
// this.mzz = rzz; // == mzz anyway
this.mzt = rzt;
updateState();
return this;
}
@Override
public BaseTransform deriveWithConcatenation(
double mxx, double mxy, double mxz, double mxt,
double myx, double myy, double myz, double myt,
double mzx, double mzy, double mzz, double mzt) {
concatenate(mxx, mxy, mxz, mxt,
myx, myy, myz, myt,
mzx, mzy, mzz, mzt);
return this;
}
public void preConcatenate(BaseTransform transform) {
switch (transform.getDegree()) {
case IDENTITY:
return;
case TRANSLATE_2D:
preTranslate(transform.getMxt(), transform.getMyt(), 0.0);
return;
case TRANSLATE_3D:
preTranslate(transform.getMxt(), transform.getMyt(), transform.getMzt());
return;
}
double Txx = transform.getMxx();
double Txy = transform.getMxy();
double Txz = transform.getMxz();
double Txt = transform.getMxt();
double Tyx = transform.getMyx();
double Tyy = transform.getMyy();
double Tyz = transform.getMyz();
double Tyt = transform.getMyt();
double Tzx = transform.getMzx();
double Tzy = transform.getMzy();
double Tzz = transform.getMzz();
double Tzt = transform.getMzt();
double rxx = (Txx * mxx + Txy * myx + Txz * mzx /* + Txt * 0.0 */);
double rxy = (Txx * mxy + Txy * myy + Txz * mzy /* + Txt * 0.0 */);
double rxz = (Txx * mxz + Txy * myz + Txz * mzz /* + Txt * 0.0 */);
double rxt = (Txx * mxt + Txy * myt + Txz * mzt + Txt /* * 1.0 */);
double ryx = (Tyx * mxx + Tyy * myx + Tyz * mzx /* + Tyt * 0.0 */);
double ryy = (Tyx * mxy + Tyy * myy + Tyz * mzy /* + Tyt * 0.0 */);
double ryz = (Tyx * mxz + Tyy * myz + Tyz * mzz /* + Tyt * 0.0 */);
double ryt = (Tyx * mxt + Tyy * myt + Tyz * mzt + Tyt /* * 1.0 */);
double rzx = (Tzx * mxx + Tzy * myx + Tzz * mzx /* + Tzt * 0.0 */);
double rzy = (Tzx * mxy + Tzy * myy + Tzz * mzy /* + Tzt * 0.0 */);
double rzz = (Tzx * mxz + Tzy * myz + Tzz * mzz /* + Tzt * 0.0 */);
double rzt = (Tzx * mxt + Tzy * myt + Tzz * mzt + Tzt /* * 1.0 */);
this.mxx = rxx;
this.mxy = rxy;
this.mxz = rxz;
this.mxt = rxt;
this.myx = ryx;
this.myy = ryy;
this.myz = ryz;
this.myt = ryt;
this.mzx = rzx;
this.mzy = rzy;
this.mzz = rzz;
this.mzt = rzt;
updateState();
}
@Override
public void restoreTransform(double mxx, double myx,
double mxy, double myy,
double mxt, double myt)
{
throw new InternalError("must use Affine3D restore method "+
"to prevent loss of information");
}
@Override
public void restoreTransform(double mxx, double mxy, double mxz, double mxt,
double myx, double myy, double myz, double myt,
double mzx, double mzy, double mzz, double mzt)
{
this.mxx = mxx;
this.mxy = mxy;
this.mxz = mxz;
this.mxt = mxt;
this.myx = myx;
this.myy = myy;
this.myz = myz;
this.myt = myt;
this.mzx = mzx;
this.mzy = mzy;
this.mzz = mzz;
this.mzt = mzt;
updateState();
}
/**
* Sets this transform to a viewing transform computed from the specified
* eye point, center point, and up vector.
* The resulting transform can be used as the view transform
* in a 3D camera.
*
* @param eye the eye point
*
* @param center the center point
*
* @param up the up vector
*
* @return this transform
*/
public Affine3D lookAt(Vec3d eye, Vec3d center, Vec3d up) {
double forwardx, forwardy, forwardz, invMag;
double upx, upy, upz;
double sidex, sidey, sidez;
forwardx = eye.x - center.x;
forwardy = eye.y - center.y;
forwardz = eye.z - center.z;
invMag = 1.0 / Math.sqrt(forwardx * forwardx + forwardy * forwardy + forwardz * forwardz);
forwardx = forwardx * invMag;
forwardy = forwardy * invMag;
forwardz = forwardz * invMag;
invMag = 1.0 / Math.sqrt(up.x * up.x + up.y * up.y + up.z * up.z);
upx = up.x * invMag;
upy = up.y * invMag;
upz = up.z * invMag;
// side = Up cross forward
sidex = upy * forwardz - forwardy * upz;
sidey = upz * forwardx - upx * forwardz;
sidez = upx * forwardy - upy * forwardx;
invMag = 1.0 / Math.sqrt(sidex * sidex + sidey * sidey + sidez * sidez);
sidex *= invMag;
sidey *= invMag;
sidez *= invMag;
// recompute up = forward cross side
upx = forwardy * sidez - sidey * forwardz;
upy = forwardz * sidex - forwardx * sidez;
upz = forwardx * sidey - forwardy * sidex;
// transpose because we calculated the inverse of what we want
mxx = sidex;
mxy = sidey;
mxz = sidez;
myx = upx;
myy = upy;
myz = upz;
mzx = forwardx;
mzy = forwardy;
mzz = forwardz;
mxt = -eye.x * mxx + -eye.y * mxy + -eye.z * mxz;
myt = -eye.x * myx + -eye.y * myy + -eye.z * myz;
mzt = -eye.x * mzx + -eye.y * mzy + -eye.z * mzz;
updateState();
return this;
}
static boolean almostOne(double a) {
return ((a < 1+EPSILON_ABSOLUTE) && (a > 1-EPSILON_ABSOLUTE));
}
// Round values to sane precision for printing
// Note that Math.sin(Math.PI) has an error of about 10^-16
private static double _matround(double matval) {
return Math.rint(matval * 1E15) / 1E15;
}
/**
* Returns a String
that represents the value of this
* {@link Object}.
* @return a String
representing the value of this
* Object
.
*/
@Override
public String toString() {
return ("Affine3D[["
+ _matround(mxx) + ", "
+ _matround(mxy) + ", "
+ _matround(mxz) + ", "
+ _matround(mxt) + "], ["
+ _matround(myx) + ", "
+ _matround(myy) + ", "
+ _matround(myz) + ", "
+ _matround(myt) + "], ["
+ _matround(mzx) + ", "
+ _matround(mzy) + ", "
+ _matround(mzz) + ", "
+ _matround(mzt) + "]]");
}
}