/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* Licensed 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.
*/
package com.jaamsim.math;
/**
* The Transform class represents a linear transform consisting of a rotation, scale and translation
* in 3-space. Internally it stores all 3 as discrete transforms but allows for arbitrary transforms to be chained,
* collapsed and inverted. The primary limitation is it only allows for uniform (isotropic) scaling
* @author Matt Chudleigh
*
*/
public class Transform {
private final Quaternion _rot;
private final Vec3d _trans;
private double _scale;
private final Mat4d _mat4d = new Mat4d();
private boolean _matrixDirty;
public static final Transform ident = new Transform(); // Static Identity transform
public Transform() {
_trans = new Vec3d();
_rot = new Quaternion(); // Identity
_scale = 1;
_matrixDirty = false;
}
public Transform(Transform t) {
_trans = new Vec3d(t._trans);
_rot = new Quaternion(t._rot);
_scale = t._scale;
_matrixDirty = true;
}
public Transform(Vec3d trans, Quaternion rot, double scale)
{
if (trans == null)
_trans = new Vec3d();
else
_trans = new Vec3d(trans);
if (rot == null)
_rot = new Quaternion();
else
_rot = new Quaternion(rot);
_scale = scale;
_matrixDirty = true;
}
public Transform(Vec3d trans) {
this(trans, null, 1.0d);
}
public void copyFrom(Transform t) {
_trans.set3(t._trans);
_rot.set(t._rot);
_scale = t._scale;
if (!t._matrixDirty) {
_mat4d.set4(t._mat4d);
}
_matrixDirty = t._matrixDirty;
}
public void setTrans(Vec3d trans) {
if (_trans.equals3(trans)) {
return;
}
_trans.set3(trans);
_matrixDirty = true;
}
public void setRot(Quaternion q) {
if (_rot.equals(q)) {
return;
}
_rot.set(q);
_matrixDirty = true;
}
public void setScale(double s) {
if (MathUtils.near(_scale, s)) {
return;
}
_scale = s;
_matrixDirty = true;
}
public void getRot(Quaternion out) {
out.set(_rot);
}
public Quaternion getRotRef() {
return _rot;
}
public Vec3d getTransRef() {
return _trans;
}
public double getScale() {
return _scale;
}
/**
* Calculated the 4x4 matrix corresponding to this transform
* @param out - the 4x4 matrix
*/
public void getMat4d(Mat4d out) {
if (_matrixDirty) {
updateMatrix();
}
out.set4(_mat4d);
}
public Mat4d getMat4dRef() {
if (_matrixDirty) {
updateMatrix();
}
return _mat4d;
}
private void updateMatrix() {
assert(!Double.isNaN(_trans.x));
assert(!Double.isNaN(_trans.y));
assert(!Double.isNaN(_trans.z));
_mat4d.setRot4(_rot);
_mat4d.setTranslate3(_trans);
_mat4d.scale3(_scale);
_matrixDirty = false;
}
/**
* Populates a transform that is the merging of this and 'rhs'
* The matrix from this new transform will be the same as if the matrices of both transforms
* were multiplied (see the unit tests)
* @param rhs - the right hand matrix to merge with
* @param out
*/
public void merge(Transform a, Transform b) {
Vec3d temp = new Vec3d(a._trans);
Mat4d rotTemp = new Mat4d();
rotTemp.setRot3(a._rot);
_trans.mult3(rotTemp, b._trans);
_trans.scale3(a._scale);
_trans.add3(temp);
_matrixDirty = true;
_rot.mult(a._rot, b._rot);
_scale = a._scale * b._scale;
}
/**
* Apply this transform to a vector
* @param vect - the vector to transform
* @param out - the transformed vector
*/
public void apply(Vec4d vect, Vec4d out) {
if (_matrixDirty) {
updateMatrix();
}
out.mult4(_mat4d, vect);
}
public void multAndTrans(Vec3d vect, Vec3d out) {
if (_matrixDirty) {
updateMatrix();
}
out.multAndTrans3(_mat4d, vect);
}
/**
* Returns a transform that is the inverse of this transform, merging the two in any order will
* result in the identity transform
* @param out
*/
public void inverse(Transform out) {
out._scale = 1/_scale;
out._rot.conjugate(_rot);
out._trans.set3(_trans);
out._trans.scale3(-out._scale);
Mat4d rotTemp = new Mat4d();
rotTemp.setRot3(out._rot);
out._trans.mult3(rotTemp, out._trans);
out._matrixDirty = true;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Transform)) return false;
Transform t = (Transform)o;
return _trans.equals3(t._trans) && _rot.equals(t._rot) && MathUtils.near(_scale, t._scale);
}
public boolean near(Transform t) {
return _trans.near3(t._trans) && _rot.equals(t._rot) && MathUtils.near(_scale, t._scale);
}
@Override
public int hashCode() {
assert false : "hashCode not designed";
return 42; // any arbitrary constant will do
}
@Override
public String toString()
{
return "T: " + _trans.toString() + " R: " + _rot.toString() + " S: " + Double.toString(_scale);
}
} // class