/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved.
*
* http://www.object-refinery.com/orsoncharts/index.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package com.orsoncharts.graphics3d;
import java.io.Serializable;
/**
* Performs rotations about along an arbitrary axis (defined by two
* {@link Point3D} instances). This file is derived from code published in
* "Computer Graphics for Java Programmers (Second Edition)" by Leen Ammeraal
* and Kang Zhang.
* <br><br>
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Rotate3D implements Serializable {
/** The first point defining the axis of rotation. */
Point3D a;
/** The second point defining the axis of rotation. */
Point3D b;
/** The angle of rotation in radians. */
double angle;
/**
* Values in the transformation matrix (these need to be recalculated if
* the axis of rotation or the angle are changed).
*/
double r11, r12, r13;
double r21, r22, r23;
double r31, r32, r33;
double r41, r42, r43;
/**
* Creates a new instance that will rotate points about the axis passing
* through points a and b, by the specified angle.
*
* @param a the first point defining the axis of rotation
* ({@code null} not permitted).
* @param b the second point defining the axis of rotation
* ({@code null} not permitted).
* @param angle the rotation angle (in radians).
*/
public Rotate3D(Point3D a, Point3D b, double angle) {
this.a = a;
this.b = b;
this.angle = angle;
double v1 = b.x - a.x;
double v2 = b.y - a.y;
double v3 = b.z - a.z;
double theta = Math.atan2(v2, v1);
double phi = Math.atan2(Math.sqrt(v1 * v1 + v2 * v2), v3);
initRotate(a, theta, phi, angle);
}
/**
* Returns the angle of rotation, in radians.
*
* @return The angle of rotation, in radians.
*/
public double getAngle() {
return this.angle;
}
/**
* Sets the angle of rotation (in radians) and (internally) updates the
* values of the transformation matrix ready for the next call(s) to
* the {@code applyRotation} methods.
*
* @param angle the angle (in radians).
*/
public void setAngle(double angle) {
this.angle = angle;
double v1 = b.x - a.x;
double v2 = b.y - a.y;
double v3 = b.z - a.z;
double theta = Math.atan2(v2, v1);
double phi = Math.atan2(Math.sqrt(v1 * v1 + v2 * v2), v3);
initRotate(this.a, theta, phi, angle);
}
/**
* Computes the transformation matrix values.
*
* @param a the start point of the axis of rotation.
* @param theta theta (defines direction of axis).
* @param phi phi (defines direction of axis).
* @param alpha alpha (angle of rotation).
*/
private void initRotate(Point3D a, double theta, double phi, double alpha) {
double cosAlpha = Math.cos(alpha);
double sinAlpha = Math.sin(alpha);
double cosPhi = Math.cos(phi);
double cosPhi2 = cosPhi * cosPhi;
double sinPhi = Math.sin(phi);
double sinPhi2 = sinPhi * sinPhi;
double cosTheta = Math.cos(theta);
double cosTheta2 = cosTheta * cosTheta;
double sinTheta = Math.sin(theta);
double sinTheta2 = sinTheta * sinTheta;
double a1 = a.x;
double a2 = a.y;
double a3 = a.z;
double c = 1.0 - cosAlpha;
this.r11 = cosTheta2 * (cosAlpha * cosPhi2 + sinPhi2)
+ cosAlpha * sinTheta2;
this.r12 = sinAlpha * cosPhi + c * sinPhi2 * cosTheta * sinTheta;
this.r13 = sinPhi * (cosPhi * cosTheta * c - sinAlpha * sinTheta);
this.r21 = sinPhi2 * cosTheta * sinTheta * c - sinAlpha * cosPhi;
this.r22 = sinTheta2 * (cosAlpha * cosPhi2 + sinPhi2)
+ cosAlpha * cosTheta2;
this.r23 = sinPhi * (cosPhi * sinTheta * c + sinAlpha * cosTheta);
this.r31 = sinPhi * (cosPhi * cosTheta * c + sinAlpha * sinTheta);
this.r32 = sinPhi * (cosPhi * sinTheta * c - sinAlpha * cosTheta);
this.r33 = cosAlpha * sinPhi2 + cosPhi2;
this.r41 = a1 - a1 * this.r11 - a2 * this.r21 - a3 * this.r31;
this.r42 = a2 - a1 * this.r12 - a2 * this.r22 - a3 * this.r32;
this.r43 = a3 - a1 * this.r13 - a2 * this.r23 - a3 * this.r33;
}
/**
* Creates and returns a new point that is the rotation of {@code p}
* about the axis that was specified via the constructor.
*
* @param p the point ({@code null} not permitted).
*
* @return A new point.
*/
public Point3D applyRotation(Point3D p) {
return applyRotation(p.x, p.y, p.z);
}
/**
* Creates an returns a new point that is the rotation of the point
* {@code (x, y, z)} about the axis that was specified via the
* constructor.
*
* @param x the x-coordinate of the point to transform.
* @param y the y-coordinate of the point to transform.
* @param z the z-coordinate of the point to transform.
*
* @return The transformed point (never {@code null}).
*/
public Point3D applyRotation(double x, double y, double z) {
return new Point3D(
x * this.r11 + y * this.r21 + z * this.r31 + this.r41,
x * this.r12 + y * this.r22 + z * this.r32 + this.r42,
x * this.r13 + y * this.r23 + z * this.r33 + this.r43);
}
/**
* Returns the coordinates of a point that is the rotation of the point
* {@code (x, y, z)} about the axis that was specified via the
* constructor.
*
* @param x the x-coordinate of the point to transform.
* @param y the y-coordinate of the point to transform.
* @param z the z-coordinate of the point to transform.
* @param result an array to carry the result ({@code null} permitted).
*
* @return The transformed coordinates (in the {@code result} array if
* one was supplied, otherwise in a newly allocated array).
*/
public double[] applyRotation(double x, double y, double z,
double[] result) {
if (result == null) {
result = new double[3];
}
result[0] = x * this.r11 + y * this.r21 + z * this.r31 + this.r41;
result[1] = x * this.r12 + y * this.r22 + z * this.r32 + this.r42;
result[2] = x * this.r13 + y * this.r23 + z * this.r33 + this.r43;
return result;
}
}