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}