001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2022, by David Gilbert.  All rights reserved.
006 * 
007 * https://github.com/jfree/orson-charts
008 * 
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published by
011 * the Free Software Foundation, either version 3 of the License, or
012 * (at your option) any later version.
013 *
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
021 * 
022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
023 * Other names may be trademarks of their respective owners.]
024 * 
025 * If you do not wish to be bound by the terms of the GPL, an alternative
026 * commercial license can be purchased.  For details, please see visit the
027 * Orson Charts home page:
028 * 
029 * http://www.object-refinery.com/orsoncharts/index.html
030 * 
031 */
032
033package org.jfree.chart3d.graphics3d;
034
035import java.io.Serializable;
036
037/**
038 * Performs rotations about along an arbitrary axis (defined by two 
039 * {@link Point3D} instances).  This file is derived from code published in 
040 * "Computer Graphics for Java Programmers (Second Edition)" by Leen Ammeraal 
041 * and Kang Zhang.
042 * <br><br>
043 * NOTE: This class is serializable, but the serialization format is subject 
044 * to change in future releases and should not be relied upon for persisting 
045 * instances of this class. 
046 */
047@SuppressWarnings("serial")
048public class Rotate3D implements Serializable {
049
050    /** The first point defining the axis of rotation. */
051    Point3D a;
052    
053    /** The second point defining the axis of rotation. */
054    Point3D b;
055    
056    /** The angle of rotation in radians. */
057    double angle;
058    
059    /** 
060     * Values in the transformation matrix (these need to be recalculated if 
061     * the axis of rotation or the angle are changed).
062     */
063    double r11, r12, r13;
064    double r21, r22, r23;
065    double r31, r32, r33;
066    double r41, r42, r43;
067    
068    /**
069     * Creates a new instance that will rotate points about the axis passing
070     * through points a and b, by the specified angle.
071     * 
072     * @param a  the first point defining the axis of rotation 
073     *     ({@code null} not permitted).
074     * @param b  the second point defining the axis of rotation 
075     *     ({@code null} not permitted).
076     * @param angle  the rotation angle (in radians).
077     */
078    public Rotate3D(Point3D a, Point3D b, double angle) {
079        this.a = a;
080        this.b = b;
081        this.angle = angle;
082        double v1 = b.x - a.x;
083        double v2 = b.y - a.y;
084        double v3 = b.z - a.z;
085        double theta = Math.atan2(v2, v1);
086        double phi = Math.atan2(Math.sqrt(v1 * v1 + v2 * v2), v3);
087        initRotate(a, theta, phi, angle);        
088    }
089
090    /**
091     * Returns the angle of rotation, in radians.
092     * 
093     * @return The angle of rotation, in radians. 
094     */
095    public double getAngle() {
096        return this.angle;
097    }
098    
099    /**
100     * Sets the angle of rotation (in radians) and (internally) updates the 
101     * values of the transformation matrix ready for the next call(s) to 
102     * the {@code applyRotation} methods.
103     * 
104     * @param angle  the angle (in radians). 
105     */
106    public void setAngle(double angle) {
107        this.angle = angle;
108        double v1 = b.x - a.x;
109        double v2 = b.y - a.y;
110        double v3 = b.z - a.z;
111        double theta = Math.atan2(v2, v1);
112        double phi = Math.atan2(Math.sqrt(v1 * v1 + v2 * v2), v3);
113        initRotate(this.a, theta, phi, angle);
114    }
115    
116    /**
117     * Computes the transformation matrix values.
118     * 
119     * @param a  the start point of the axis of rotation.
120     * @param theta  theta (defines direction of axis).
121     * @param phi  phi (defines direction of axis).
122     * @param alpha  alpha (angle of rotation).
123     */
124    private void initRotate(Point3D a, double theta, double phi, double alpha) {
125        double cosAlpha = Math.cos(alpha);
126        double sinAlpha = Math.sin(alpha);
127        double cosPhi = Math.cos(phi);
128        double cosPhi2 = cosPhi * cosPhi;
129        double sinPhi = Math.sin(phi);
130        double sinPhi2 = sinPhi * sinPhi;
131        double cosTheta = Math.cos(theta);
132        double cosTheta2 = cosTheta * cosTheta;
133        double sinTheta = Math.sin(theta);
134        double sinTheta2 = sinTheta * sinTheta;
135        double a1 = a.x;
136        double a2 = a.y;
137        double a3 = a.z;
138        double c = 1.0 - cosAlpha;
139        this.r11 = cosTheta2 * (cosAlpha * cosPhi2 + sinPhi2) 
140                + cosAlpha * sinTheta2;
141        this.r12 = sinAlpha * cosPhi + c * sinPhi2 * cosTheta * sinTheta;
142        this.r13 = sinPhi * (cosPhi * cosTheta * c - sinAlpha * sinTheta);
143        this.r21 = sinPhi2 * cosTheta * sinTheta * c - sinAlpha * cosPhi;
144        this.r22 = sinTheta2 * (cosAlpha * cosPhi2 + sinPhi2) 
145                + cosAlpha * cosTheta2;
146        this.r23 = sinPhi * (cosPhi * sinTheta * c + sinAlpha * cosTheta);
147        this.r31 = sinPhi * (cosPhi * cosTheta * c + sinAlpha * sinTheta);
148        this.r32 = sinPhi * (cosPhi * sinTheta * c - sinAlpha * cosTheta);
149        this.r33 = cosAlpha * sinPhi2 + cosPhi2;
150        this.r41 = a1 - a1 * this.r11 - a2 * this.r21 - a3 * this.r31;
151        this.r42 = a2 - a1 * this.r12 - a2 * this.r22 - a3 * this.r32;
152        this.r43 = a3 - a1 * this.r13 - a2 * this.r23 - a3 * this.r33;
153    }
154    
155    /**
156     * Creates and returns a new point that is the rotation of {@code p}
157     * about the axis that was specified via the constructor.
158     * 
159     * @param p  the point ({@code null} not permitted).
160     * 
161     * @return A new point.
162     */
163    public Point3D applyRotation(Point3D p) {
164        return applyRotation(p.x, p.y, p.z);
165    }
166    
167    /**
168     * Creates an returns a new point that is the rotation of the point 
169     * {@code (x, y, z)} about the axis that was specified via the 
170     * constructor.
171     * 
172     * @param x  the x-coordinate of the point to transform.
173     * @param y  the y-coordinate of the point to transform.
174     * @param z  the z-coordinate of the point to transform.
175     * 
176     * @return The transformed point (never {@code null}). 
177     */
178    public Point3D applyRotation(double x, double y, double z) {
179        return new Point3D(
180                x * this.r11 + y * this.r21 + z * this.r31 + this.r41,
181                x * this.r12 + y * this.r22 + z * this.r32 + this.r42,
182                x * this.r13 + y * this.r23 + z * this.r33 + this.r43);        
183    }
184    
185    /**
186     * Returns the coordinates of a point that is the rotation of the point
187     * {@code (x, y, z)} about the axis that was specified via the 
188     * constructor.
189     * 
190     * @param x  the x-coordinate of the point to transform.
191     * @param y  the y-coordinate of the point to transform.
192     * @param z  the z-coordinate of the point to transform.
193     * @param result  an array to carry the result ({@code null} permitted).
194     * 
195     * @return The transformed coordinates (in the {@code result} array if 
196     * one was supplied, otherwise in a newly allocated array). 
197     */
198    public double[] applyRotation(double x, double y, double z, 
199            double[] result) {
200        if (result == null) {
201            result = new double[3];
202        }
203        result[0] = x * this.r11 + y * this.r21 + z * this.r31 + this.r41;
204        result[1] = x * this.r12 + y * this.r22 + z * this.r32 + this.r42;
205        result[2] = x * this.r13 + y * this.r23 + z * this.r33 + this.r43;
206        return result;
207    }
208}