⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rotation.java

📁 Apache的common math数学软件包
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements.  See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.commons.math.geometry;import java.io.Serializable;/** * This class implements rotations in a three-dimensional space. * * <p>Rotations can be represented by several different mathematical * entities (matrices, axe and angle, Cardan or Euler angles, * quaternions). This class presents an higher level abstraction, more * user-oriented and hiding this implementation details. Well, for the * curious, we use quaternions for the internal representation. The * user can build a rotation from any of these representations, and * any of these representations can be retrieved from a * <code>Rotation</code> instance (see the various constructors and * getters). In addition, a rotation can also be built implicitely * from a set of vectors and their image.</p> * <p>This implies that this class can be used to convert from one * representation to another one. For example, converting a rotation * matrix into a set of Cardan angles from can be done using the * followong single line of code:</p> * <pre> * double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ); * </pre> * <p>Focus is oriented on what a rotation <em>do</em> rather than on its * underlying representation. Once it has been built, and regardless of its * internal representation, a rotation is an <em>operator</em> which basically * transforms three dimensional {@link Vector3D vectors} into other three * dimensional {@link Vector3D vectors}. Depending on the application, the * meaning of these vectors may vary and the semantics of the rotation also.</p> * <p>For example in an spacecraft attitude simulation tool, users will often * consider the vectors are fixed (say the Earth direction for example) and the * rotation transforms the coordinates coordinates of this vector in inertial * frame into the coordinates of the same vector in satellite frame. In this * case, the rotation implicitely defines the relation between the two frames. * Another example could be a telescope control application, where the rotation * would transform the sighting direction at rest into the desired observing * direction when the telescope is pointed towards an object of interest. In this * case the rotation transforms the directionf at rest in a topocentric frame * into the sighting direction in the same topocentric frame. In many case, both * approaches will be combined, in our telescope example, we will probably also * need to transform the observing direction in the topocentric frame into the * observing direction in inertial frame taking into account the observatory * location and the Earth rotation.</p> * * <p>These examples show that a rotation is what the user wants it to be, so this * class does not push the user towards one specific definition and hence does not * provide methods like <code>projectVectorIntoDestinationFrame</code> or * <code>computeTransformedDirection</code>. It provides simpler and more generic * methods: {@link #applyTo(Vector3D) applyTo(Vector3D)} and {@link * #applyInverseTo(Vector3D) applyInverseTo(Vector3D)}.</p> * * <p>Since a rotation is basically a vectorial operator, several rotations can be * composed together and the composite operation <code>r = r<sub>1</sub> o * r<sub>2</sub></code> (which means that for each vector <code>u</code>, * <code>r(u) = r<sub>1</sub>(r<sub>2</sub>(u))</code>) is also a rotation. Hence * we can consider that in addition to vectors, a rotation can be applied to other * rotations as well (or to itself). With our previous notations, we would say we * can apply <code>r<sub>1</sub></code> to <code>r<sub>2</sub></code> and the result * we get is <code>r = r<sub>1</sub> o r<sub>2</sub></code>. For this purpose, the * class provides the methods: {@link #applyTo(Rotation) applyTo(Rotation)} and * {@link #applyInverseTo(Rotation) applyInverseTo(Rotation)}.</p> * * <p>Rotations are guaranteed to be immutable objects.</p> * * @version $Revision: 627994 $ $Date: 2008-02-15 03:16:05 -0700 (Fri, 15 Feb 2008) $ * @see Vector3D * @see RotationOrder * @since 1.2 */public class Rotation implements Serializable {  /** Build the identity rotation.   */  public Rotation() {    q0 = 1;    q1 = 0;    q2 = 0;    q3 = 0;  }  /** Build a rotation from the quaternion coordinates.   * <p>A rotation can be built from a <em>normalized</em> quaternion,   * i.e. a quaternion for which q<sub>0</sub><sup>2</sup> +   * q<sub>1</sub><sup>2</sup> + q<sub>2</sub><sup>2</sup> +   * q<sub>3</sub><sup>2</sup> = 1. If the quaternion is not normalized,   * the constructor can normalize it in a preprocessing step.</p>   * @param q0 scalar part of the quaternion   * @param q1 first coordinate of the vectorial part of the quaternion   * @param q2 second coordinate of the vectorial part of the quaternion   * @param q3 third coordinate of the vectorial part of the quaternion   * @param needsNormalization if true, the coordinates are considered   * not to be normalized, a normalization preprocessing step is performed   * before using them   */  public Rotation(double q0, double q1, double q2, double q3,                  boolean needsNormalization) {    if (needsNormalization) {      // normalization preprocessing      double inv = 1.0 / Math.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);      q0 *= inv;      q1 *= inv;      q2 *= inv;      q3 *= inv;    }    this.q0 = q0;    this.q1 = q1;    this.q2 = q2;    this.q3 = q3;  }  /** Build a rotation from an axis and an angle.   * <p>We use the convention that angles are oriented according to   * the effect of the rotation on vectors around the axis. That means   * that if (i, j, k) is a direct frame and if we first provide +k as   * the axis and PI/2 as the angle to this constructor, and then   * {@link #applyTo(Vector3D) apply} the instance to +i, we will get   * +j.</p>   * @param axis axis around which to rotate   * @param angle rotation angle.   * @exception ArithmeticException if the axis norm is zero   */  public Rotation(Vector3D axis, double angle) {    double norm = axis.getNorm();    if (norm == 0) {      throw new ArithmeticException("zero norm for rotation axis");    }    double halfAngle = -0.5 * angle;    double coeff = Math.sin(halfAngle) / norm;    q0 = Math.cos (halfAngle);    q1 = coeff * axis.getX();    q2 = coeff * axis.getY();    q3 = coeff * axis.getZ();  }  /** Build a rotation from a 3X3 matrix.   * <p>Rotation matrices are orthogonal matrices, i.e. unit matrices   * (which are matrices for which m.m<sup>T</sup> = I) with real   * coefficients. The module of the determinant of unit matrices is   * 1, among the orthogonal 3X3 matrices, only the ones having a   * positive determinant (+1) are rotation matrices.</p>   * <p>When a rotation is defined by a matrix with truncated values   * (typically when it is extracted from a technical sheet where only   * four to five significant digits are available), the matrix is not   * orthogonal anymore. This constructor handles this case   * transparently by using a copy of the given matrix and applying a   * correction to the copy in order to perfect its orthogonality. If   * the Frobenius norm of the correction needed is above the given   * threshold, then the matrix is considered to be too far from a   * true rotation matrix and an exception is thrown.<p>   * @param m rotation matrix   * @param threshold convergence threshold for the iterative   * orthogonality correction (convergence is reached when the   * difference between two steps of the Frobenius norm of the   * correction is below this threshold)   * @exception NotARotationMatrixException if the matrix is not a 3X3   * matrix, or if it cannot be transformed into an orthogonal matrix   * with the given threshold, or if the determinant of the resulting   * orthogonal matrix is negative   */  public Rotation(double[][] m, double threshold)    throws NotARotationMatrixException {    // dimension check    if ((m.length != 3) || (m[0].length != 3) ||        (m[1].length != 3) || (m[2].length != 3)) {      throw new NotARotationMatrixException("a {0}x{1} matrix" +                                            " cannot be a rotation matrix",                                            new Object[] {                                              Integer.toString(m.length),                                              Integer.toString(m[0].length)                                            });    }    // compute a "close" orthogonal matrix    double[][] ort = orthogonalizeMatrix(m, threshold);    // check the sign of the determinant    double det = ort[0][0] * (ort[1][1] * ort[2][2] - ort[2][1] * ort[1][2]) -                 ort[1][0] * (ort[0][1] * ort[2][2] - ort[2][1] * ort[0][2]) +                 ort[2][0] * (ort[0][1] * ort[1][2] - ort[1][1] * ort[0][2]);    if (det < 0.0) {      throw new NotARotationMatrixException("the closest orthogonal matrix" +                                            " has a negative determinant {0}",                                            new Object[] {                                              Double.toString(det)                                            });    }    // There are different ways to compute the quaternions elements    // from the matrix. They all involve computing one element from    // the diagonal of the matrix, and computing the three other ones    // using a formula involving a division by the first element,    // which unfortunately can be zero. Since the norm of the    // quaternion is 1, we know at least one element has an absolute    // value greater or equal to 0.5, so it is always possible to    // select the right formula and avoid division by zero and even    // numerical inaccuracy. Checking the elements in turn and using    // the first one greater than 0.45 is safe (this leads to a simple    // test since qi = 0.45 implies 4 qi^2 - 1 = -0.19)    double s = ort[0][0] + ort[1][1] + ort[2][2];    if (s > -0.19) {      // compute q0 and deduce q1, q2 and q3      q0 = 0.5 * Math.sqrt(s + 1.0);      double inv = 0.25 / q0;      q1 = inv * (ort[1][2] - ort[2][1]);      q2 = inv * (ort[2][0] - ort[0][2]);      q3 = inv * (ort[0][1] - ort[1][0]);    } else {      s = ort[0][0] - ort[1][1] - ort[2][2];      if (s > -0.19) {        // compute q1 and deduce q0, q2 and q3        q1 = 0.5 * Math.sqrt(s + 1.0);        double inv = 0.25 / q1;        q0 = inv * (ort[1][2] - ort[2][1]);        q2 = inv * (ort[0][1] + ort[1][0]);        q3 = inv * (ort[0][2] + ort[2][0]);      } else {        s = ort[1][1] - ort[0][0] - ort[2][2];        if (s > -0.19) {          // compute q2 and deduce q0, q1 and q3          q2 = 0.5 * Math.sqrt(s + 1.0);          double inv = 0.25 / q2;          q0 = inv * (ort[2][0] - ort[0][2]);          q1 = inv * (ort[0][1] + ort[1][0]);          q3 = inv * (ort[2][1] + ort[1][2]);        } else {          // compute q3 and deduce q0, q1 and q2          s = ort[2][2] - ort[0][0] - ort[1][1];          q3 = 0.5 * Math.sqrt(s + 1.0);          double inv = 0.25 / q3;          q0 = inv * (ort[0][1] - ort[1][0]);          q1 = inv * (ort[0][2] + ort[2][0]);          q2 = inv * (ort[2][1] + ort[1][2]);        }      }    }  }  /** Build the rotation that transforms a pair of vector into another pair.   * <p>Except for possible scale factors, if the instance were applied to   * the pair (u<sub>1</sub>, u<sub>2</sub>) it will produce the pair   * (v<sub>1</sub>, v<sub>2</sub>).</p>   * <p>If the angular separation between u<sub>1</sub> and u<sub>2</sub> is   * not the same as the angular separation between v<sub>1</sub> and   * v<sub>2</sub>, then a corrected v'<sub>2</sub> will be used rather than   * v<sub>2</sub>, the corrected vector will be in the (v<sub>1</sub>,   * v<sub>2</sub>) plane.</p>   * @param u1 first vector of the origin pair   * @param u2 second vector of the origin pair   * @param v1 desired image of u1 by the rotation   * @param v2 desired image of u2 by the rotation   * @exception IllegalArgumentException if the norm of one of the vectors is zero   */  public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2) {  // norms computation  double u1u1 = Vector3D.dotProduct(u1, u1);  double u2u2 = Vector3D.dotProduct(u2, u2);  double v1v1 = Vector3D.dotProduct(v1, v1);  double v2v2 = Vector3D.dotProduct(v2, v2);  if ((u1u1 == 0) || (u2u2 == 0) || (v1v1 == 0) || (v2v2 == 0)) {    throw new IllegalArgumentException("zero norm for rotation defining vector");  }  double u1x = u1.getX();  double u1y = u1.getY();  double u1z = u1.getZ();  double u2x = u2.getX();  double u2y = u2.getY();  double u2z = u2.getZ();  // normalize v1 in order to have (v1'|v1') = (u1|u1)  double coeff = Math.sqrt (u1u1 / v1v1);  double v1x   = coeff * v1.getX();  double v1y   = coeff * v1.getY();  double v1z   = coeff * v1.getZ();  v1 = new Vector3D(v1x, v1y, v1z);  // adjust v2 in order to have (u1|u2) = (v1|v2) and (v2'|v2') = (u2|u2)  double u1u2   = Vector3D.dotProduct(u1, u2);  double v1v2   = Vector3D.dotProduct(v1, v2);  double coeffU = u1u2 / u1u1;  double coeffV = v1v2 / u1u1;  double beta   = Math.sqrt((u2u2 - u1u2 * coeffU) / (v2v2 - v1v2 * coeffV));  double alpha  = coeffU - beta * coeffV;  double v2x    = alpha * v1x + beta * v2.getX();  double v2y    = alpha * v1y + beta * v2.getY();  double v2z    = alpha * v1z + beta * v2.getZ();  v2 = new Vector3D(v2x, v2y, v2z);  // preliminary computation (we use explicit formulation instead  // of relying on the Vector3D class in order to avoid building lots  // of temporary objects)  Vector3D uRef = u1;  Vector3D vRef = v1;  double dx1 = v1x - u1.getX();  double dy1 = v1y - u1.getY();  double dz1 = v1z - u1.getZ();  double dx2 = v2x - u2.getX();  double dy2 = v2y - u2.getY();  double dz2 = v2z - u2.getZ();  Vector3D k = new Vector3D(dy1 * dz2 - dz1 * dy2,                            dz1 * dx2 - dx1 * dz2,                            dx1 * dy2 - dy1 * dx2);  double c = k.getX() * (u1y * u2z - u1z * u2y) +             k.getY() * (u1z * u2x - u1x * u2z) +             k.getZ() * (u1x * u2y - u1y * u2x);  if (c == 0) {    // the (q1, q2, q3) vector is in the (u1, u2) plane

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -