Quaternion.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.numbers.quaternion;

  18. import java.util.Arrays;
  19. import java.util.function.ToDoubleFunction;
  20. import java.util.function.BiPredicate;
  21. import java.io.Serializable;
  22. import org.apache.commons.numbers.core.Precision;

  23. /**
  24.  * This class implements <a href="http://mathworld.wolfram.com/Quaternion.html">
  25.  * quaternions</a> (Hamilton's hypercomplex numbers).
  26.  *
  27.  * <p>Wherever quaternion components are listed in sequence, this class follows the
  28.  * convention of placing the scalar ({@code w}) component first, e.g. [{@code w, x, y, z}].
  29.  * Other libraries and textbooks may place the {@code w} component last.</p>
  30.  *
  31.  * <p>Instances of this class are guaranteed to be immutable.</p>
  32.  */
  33. public final class Quaternion implements Serializable {
  34.     /** Zero quaternion. */
  35.     public static final Quaternion ZERO = of(0, 0, 0, 0);
  36.     /** Identity quaternion. */
  37.     public static final Quaternion ONE = new Quaternion(Type.POSITIVE_POLAR_FORM, 1, 0, 0, 0);
  38.     /** i. */
  39.     public static final Quaternion I = new Quaternion(Type.POSITIVE_POLAR_FORM, 0, 1, 0, 0);
  40.     /** j. */
  41.     public static final Quaternion J = new Quaternion(Type.POSITIVE_POLAR_FORM, 0, 0, 1, 0);
  42.     /** k. */
  43.     public static final Quaternion K = new Quaternion(Type.POSITIVE_POLAR_FORM, 0, 0, 0, 1);

  44.     /** Serializable version identifier. */
  45.     private static final long serialVersionUID = 20170118L;
  46.     /** Error message. */
  47.     private static final String ILLEGAL_NORM_MSG = "Illegal norm: ";

  48.     /** {@link #toString() String representation}. */
  49.     private static final String FORMAT_START = "[";
  50.     /** {@link #toString() String representation}. */
  51.     private static final String FORMAT_END = "]";
  52.     /** {@link #toString() String representation}. */
  53.     private static final String FORMAT_SEP = " ";

  54.     /** The number of dimensions for the vector part of the quaternion. */
  55.     private static final int VECTOR_DIMENSIONS = 3;
  56.     /** The number of parts when parsing a text representation of the quaternion. */
  57.     private static final int NUMBER_OF_PARTS = 4;

  58.     /** For enabling specialized method implementations. */
  59.     private final Type type;
  60.     /** First component (scalar part). */
  61.     private final double w;
  62.     /** Second component (first vector part). */
  63.     private final double x;
  64.     /** Third component (second vector part). */
  65.     private final double y;
  66.     /** Fourth component (third vector part). */
  67.     private final double z;

  68.     /**
  69.      * For enabling optimized implementations.
  70.      */
  71.     private enum Type {
  72.         /** Default implementation. */
  73.         DEFAULT(Default.NORMSQ,
  74.                 Default.NORM,
  75.                 Default.IS_UNIT),
  76.         /** Quaternion has unit norm. */
  77.         NORMALIZED(Normalized.NORM,
  78.                    Normalized.NORM,
  79.                    Normalized.IS_UNIT),
  80.         /** Quaternion has positive scalar part. */
  81.         POSITIVE_POLAR_FORM(Normalized.NORM,
  82.                             Normalized.NORM,
  83.                             Normalized.IS_UNIT);

  84.         /** {@link Quaternion#normSq()}. */
  85.         private final ToDoubleFunction<Quaternion> normSq;
  86.         /** {@link Quaternion#norm()}. */
  87.         private final ToDoubleFunction<Quaternion> norm;
  88.         /** {@link Quaternion#isUnit(double)}. */
  89.         private final BiPredicate<Quaternion, Double> testIsUnit;

  90.         /** Default implementations. */
  91.         private static final class Default {
  92.             /** {@link Quaternion#normSq()}. */
  93.             static final ToDoubleFunction<Quaternion> NORMSQ = q ->
  94.                 q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z;

  95.             /** {@link Quaternion#norm()}. */
  96.             private static final ToDoubleFunction<Quaternion> NORM = q ->
  97.                 Math.sqrt(NORMSQ.applyAsDouble(q));

  98.             /** {@link Quaternion#isUnit(double)}. */
  99.             private static final BiPredicate<Quaternion, Double> IS_UNIT = (q, eps) ->
  100.                 Precision.equals(NORM.applyAsDouble(q), 1d, eps);
  101.         }

  102.         /** Implementations for normalized quaternions. */
  103.         private static final class Normalized {
  104.             /** {@link Quaternion#norm()} returns 1. */
  105.             static final ToDoubleFunction<Quaternion> NORM = q -> 1;
  106.             /** {@link Quaternion#isUnit(double)} returns 1. */
  107.             static final BiPredicate<Quaternion, Double> IS_UNIT = (q, eps) -> true;
  108.         }

  109.         /**
  110.          * @param normSq {@code normSq} method.
  111.          * @param norm {@code norm} method.
  112.          * @param isUnit {@code isUnit} method.
  113.          */
  114.         Type(ToDoubleFunction<Quaternion> normSq,
  115.              ToDoubleFunction<Quaternion> norm,
  116.              BiPredicate<Quaternion, Double> isUnit)  {
  117.             this.normSq = normSq;
  118.             this.norm = norm;
  119.             this.testIsUnit = isUnit;
  120.         }

  121.         /**
  122.          * @param q Quaternion.
  123.          * @return the norm squared.
  124.          */
  125.         double normSq(Quaternion q) {
  126.             return normSq.applyAsDouble(q);
  127.         }
  128.         /**
  129.          * @param q Quaternion.
  130.          * @return the norm.
  131.          */
  132.         double norm(Quaternion q) {
  133.             return norm.applyAsDouble(q);
  134.         }
  135.         /**
  136.          * @param q Quaternion.
  137.          * @param eps Tolerance.
  138.          * @return whether {@code q} has unit norm within the allowed tolerance.
  139.          */
  140.         boolean isUnit(Quaternion q,
  141.                        double eps) {
  142.             return testIsUnit.test(q, eps);
  143.         }
  144.     }

  145.     /**
  146.      * Builds a quaternion from its components.
  147.      *
  148.      * @param type Quaternion type.
  149.      * @param w Scalar component.
  150.      * @param x First vector component.
  151.      * @param y Second vector component.
  152.      * @param z Third vector component.
  153.      */
  154.     private Quaternion(Type type,
  155.                        final double w,
  156.                        final double x,
  157.                        final double y,
  158.                        final double z) {
  159.         this.type = type;
  160.         this.w = w;
  161.         this.x = x;
  162.         this.y = y;
  163.         this.z = z;
  164.     }

  165.     /**
  166.      * Copies the given quaternion, but change its {@link Type}.
  167.      *
  168.      * @param type Quaternion type.
  169.      * @param q Quaternion whose components will be copied.
  170.      */
  171.     private Quaternion(Type type,
  172.                        Quaternion q) {
  173.         this.type = type;
  174.         w = q.w;
  175.         x = q.x;
  176.         y = q.y;
  177.         z = q.z;
  178.     }

  179.     /**
  180.      * Builds a quaternion from its components.
  181.      *
  182.      * @param w Scalar component.
  183.      * @param x First vector component.
  184.      * @param y Second vector component.
  185.      * @param z Third vector component.
  186.      * @return a quaternion instance.
  187.      */
  188.     public static Quaternion of(final double w,
  189.                                 final double x,
  190.                                 final double y,
  191.                                 final double z) {
  192.         return new Quaternion(Type.DEFAULT,
  193.                               w, x, y, z);
  194.     }

  195.     /**
  196.      * Builds a quaternion from scalar and vector parts.
  197.      *
  198.      * @param scalar Scalar part of the quaternion.
  199.      * @param v Components of the vector part of the quaternion.
  200.      * @return a quaternion instance.
  201.      *
  202.      * @throws IllegalArgumentException if the array length is not 3.
  203.      */
  204.     public static Quaternion of(final double scalar,
  205.                                 final double[] v) {
  206.         if (v.length != VECTOR_DIMENSIONS) {
  207.             throw new IllegalArgumentException("Size of array must be 3");
  208.         }

  209.         return of(scalar, v[0], v[1], v[2]);
  210.     }

  211.     /**
  212.      * Builds a pure quaternion from a vector (assuming that the scalar
  213.      * part is zero).
  214.      *
  215.      * @param v Components of the vector part of the pure quaternion.
  216.      * @return a quaternion instance.
  217.      */
  218.     public static Quaternion of(final double[] v) {
  219.         return of(0, v);
  220.     }

  221.     /**
  222.      * Returns the conjugate of this quaternion number.
  223.      * The conjugate of {@code a + bi + cj + dk} is {@code a - bi -cj -dk}.
  224.      *
  225.      * @return the conjugate of this quaternion object.
  226.      */
  227.     public Quaternion conjugate() {
  228.         return of(w, -x, -y, -z);
  229.     }

  230.     /**
  231.      * Returns the Hamilton product of two quaternions.
  232.      *
  233.      * @param q1 First quaternion.
  234.      * @param q2 Second quaternion.
  235.      * @return the product {@code q1} and {@code q2}, in that order.
  236.      */
  237.     public static Quaternion multiply(final Quaternion q1,
  238.                                       final Quaternion q2) {
  239.         // Components of the first quaternion.
  240.         final double q1a = q1.w;
  241.         final double q1b = q1.x;
  242.         final double q1c = q1.y;
  243.         final double q1d = q1.z;

  244.         // Components of the second quaternion.
  245.         final double q2a = q2.w;
  246.         final double q2b = q2.x;
  247.         final double q2c = q2.y;
  248.         final double q2d = q2.z;

  249.         // Components of the product.
  250.         final double w = q1a * q2a - q1b * q2b - q1c * q2c - q1d * q2d;
  251.         final double x = q1a * q2b + q1b * q2a + q1c * q2d - q1d * q2c;
  252.         final double y = q1a * q2c - q1b * q2d + q1c * q2a + q1d * q2b;
  253.         final double z = q1a * q2d + q1b * q2c - q1c * q2b + q1d * q2a;

  254.         return of(w, x, y, z);
  255.     }

  256.     /**
  257.      * Returns the Hamilton product of the instance by a quaternion.
  258.      *
  259.      * @param q Quaternion.
  260.      * @return the product of this instance with {@code q}, in that order.
  261.      */
  262.     public Quaternion multiply(final Quaternion q) {
  263.         return multiply(this, q);
  264.     }

  265.     /**
  266.      * Computes the sum of two quaternions.
  267.      *
  268.      * @param q1 Quaternion.
  269.      * @param q2 Quaternion.
  270.      * @return the sum of {@code q1} and {@code q2}.
  271.      */
  272.     public static Quaternion add(final Quaternion q1,
  273.                                  final Quaternion q2) {
  274.         return of(q1.w + q2.w,
  275.                   q1.x + q2.x,
  276.                   q1.y + q2.y,
  277.                   q1.z + q2.z);
  278.     }

  279.     /**
  280.      * Computes the sum of the instance and another quaternion.
  281.      *
  282.      * @param q Quaternion.
  283.      * @return the sum of this instance and {@code q}.
  284.      */
  285.     public Quaternion add(final Quaternion q) {
  286.         return add(this, q);
  287.     }

  288.     /**
  289.      * Subtracts two quaternions.
  290.      *
  291.      * @param q1 First Quaternion.
  292.      * @param q2 Second quaternion.
  293.      * @return the difference between {@code q1} and {@code q2}.
  294.      */
  295.     public static Quaternion subtract(final Quaternion q1,
  296.                                       final Quaternion q2) {
  297.         return of(q1.w - q2.w,
  298.                   q1.x - q2.x,
  299.                   q1.y - q2.y,
  300.                   q1.z - q2.z);
  301.     }

  302.     /**
  303.      * Subtracts a quaternion from the instance.
  304.      *
  305.      * @param q Quaternion.
  306.      * @return the difference between this instance and {@code q}.
  307.      */
  308.     public Quaternion subtract(final Quaternion q) {
  309.         return subtract(this, q);
  310.     }

  311.     /**
  312.      * Computes the dot-product of two quaternions.
  313.      *
  314.      * @param q1 Quaternion.
  315.      * @param q2 Quaternion.
  316.      * @return the dot product of {@code q1} and {@code q2}.
  317.      */
  318.     public static double dot(final Quaternion q1,
  319.                              final Quaternion q2) {
  320.         return q1.w * q2.w +
  321.             q1.x * q2.x +
  322.             q1.y * q2.y +
  323.             q1.z * q2.z;
  324.     }

  325.     /**
  326.      * Computes the dot-product of the instance by a quaternion.
  327.      *
  328.      * @param q Quaternion.
  329.      * @return the dot product of this instance and {@code q}.
  330.      */
  331.     public double dot(final Quaternion q) {
  332.         return dot(this, q);
  333.     }

  334.     /**
  335.      * Computes the norm of the quaternion.
  336.      *
  337.      * @return the norm.
  338.      */
  339.     public double norm() {
  340.         return type.norm(this);
  341.     }

  342.     /**
  343.      * Computes the square of the norm of the quaternion.
  344.      *
  345.      * @return the square of the norm.
  346.      */
  347.     public double normSq() {
  348.         return type.normSq(this);
  349.     }

  350.     /**
  351.      * Computes the normalized quaternion (the versor of the instance).
  352.      * The norm of the quaternion must not be near zero.
  353.      *
  354.      * @return a normalized quaternion.
  355.      * @throws IllegalStateException if the norm of the quaternion is NaN, infinite,
  356.      *      or near zero.
  357.      */
  358.     public Quaternion normalize() {
  359.         switch (type) {
  360.         case NORMALIZED:
  361.         case POSITIVE_POLAR_FORM:
  362.             return this;
  363.         case DEFAULT:
  364.             final double norm = norm();

  365.             if (norm < Precision.SAFE_MIN ||
  366.                 !Double.isFinite(norm)) {
  367.                 throw new IllegalStateException(ILLEGAL_NORM_MSG + norm);
  368.             }

  369.             final Quaternion unit = divide(norm);

  370.             return w >= 0 ?
  371.                 new Quaternion(Type.POSITIVE_POLAR_FORM, unit) :
  372.                 new Quaternion(Type.NORMALIZED, unit);
  373.         default:
  374.             throw new IllegalStateException(); // Should never happen.
  375.         }
  376.     }

  377.     /**
  378.      * {@inheritDoc}
  379.      */
  380.     @Override
  381.     public boolean equals(Object other) {
  382.         if (this == other) {
  383.             return true;
  384.         }
  385.         if (other instanceof Quaternion) {
  386.             final Quaternion q = (Quaternion) other;
  387.             return ((Double) w).equals(q.w) &&
  388.                 ((Double) x).equals(q.x) &&
  389.                 ((Double) y).equals(q.y) &&
  390.                 ((Double) z).equals(q.z);
  391.         }

  392.         return false;
  393.     }

  394.     /**
  395.      * {@inheritDoc}
  396.      */
  397.     @Override
  398.     public int hashCode() {
  399.         return Arrays.hashCode(new double[] {w, x, y, z});
  400.     }

  401.     /**
  402.      * Checks whether this instance is equal to another quaternion
  403.      * within a given tolerance.
  404.      *
  405.      * @param q Quaternion with which to compare the current quaternion.
  406.      * @param eps Tolerance.
  407.      * @return {@code true} if the each of the components are equal
  408.      * within the allowed absolute error.
  409.      */
  410.     public boolean equals(final Quaternion q,
  411.                           final double eps) {
  412.         return Precision.equals(w, q.w, eps) &&
  413.             Precision.equals(x, q.x, eps) &&
  414.             Precision.equals(y, q.y, eps) &&
  415.             Precision.equals(z, q.z, eps);
  416.     }

  417.     /**
  418.      * Checks whether the instance is a unit quaternion within a given
  419.      * tolerance.
  420.      *
  421.      * @param eps Tolerance (absolute error).
  422.      * @return {@code true} if the norm is 1 within the given tolerance,
  423.      * {@code false} otherwise
  424.      */
  425.     public boolean isUnit(double eps) {
  426.         return type.isUnit(this, eps);
  427.     }

  428.     /**
  429.      * Checks whether the instance is a pure quaternion within a given
  430.      * tolerance.
  431.      *
  432.      * @param eps Tolerance (absolute error).
  433.      * @return {@code true} if the scalar part of the quaternion is zero.
  434.      */
  435.     public boolean isPure(double eps) {
  436.         return Math.abs(w) <= eps;
  437.     }

  438.     /**
  439.      * Returns the polar form of the quaternion.
  440.      *
  441.      * @return the unit quaternion with positive scalar part.
  442.      */
  443.     public Quaternion positivePolarForm() {
  444.         switch (type) {
  445.         case POSITIVE_POLAR_FORM:
  446.             return this;
  447.         case NORMALIZED:
  448.             return w >= 0 ?
  449.                 new Quaternion(Type.POSITIVE_POLAR_FORM, this) :
  450.                 new Quaternion(Type.POSITIVE_POLAR_FORM, negate());
  451.         case DEFAULT:
  452.             return w >= 0 ?
  453.                 normalize() :
  454.                 // The quaternion of rotation (normalized quaternion) q and -q
  455.                 // are equivalent (i.e. represent the same rotation).
  456.                 negate().normalize();
  457.         default:
  458.             throw new IllegalStateException(); // Should never happen.
  459.         }
  460.     }

  461.     /**
  462.      * Returns the opposite of this instance.
  463.      *
  464.      * @return the quaternion for which all components have an opposite
  465.      * sign to this one.
  466.      */
  467.     public Quaternion negate() {
  468.         switch (type) {
  469.         case POSITIVE_POLAR_FORM:
  470.         case NORMALIZED:
  471.             return new Quaternion(Type.NORMALIZED, -w, -x, -y, -z);
  472.         case DEFAULT:
  473.             return new Quaternion(Type.DEFAULT, -w, -x, -y, -z);
  474.         default:
  475.             throw new IllegalStateException(); // Should never happen.
  476.         }
  477.     }

  478.     /**
  479.      * Returns the inverse of this instance.
  480.      * The norm of the quaternion must not be zero.
  481.      *
  482.      * @return the inverse.
  483.      * @throws IllegalStateException if the norm (squared) of the quaternion is NaN,
  484.      *      infinite, or near zero.
  485.      */
  486.     public Quaternion inverse() {
  487.         switch (type) {
  488.         case POSITIVE_POLAR_FORM:
  489.         case NORMALIZED:
  490.             return new Quaternion(type, w, -x, -y, -z);
  491.         case DEFAULT:
  492.             final double squareNorm = normSq();
  493.             if (squareNorm < Precision.SAFE_MIN ||
  494.                 !Double.isFinite(squareNorm)) {
  495.                 throw new IllegalStateException(ILLEGAL_NORM_MSG + Math.sqrt(squareNorm));
  496.             }

  497.             return of(w / squareNorm,
  498.                       -x / squareNorm,
  499.                       -y / squareNorm,
  500.                       -z / squareNorm);
  501.         default:
  502.             throw new IllegalStateException(); // Should never happen.
  503.         }
  504.     }

  505.     /**
  506.      * Gets the first component of the quaternion (scalar part).
  507.      *
  508.      * @return the scalar part.
  509.      */
  510.     public double getW() {
  511.         return w;
  512.     }

  513.     /**
  514.      * Gets the second component of the quaternion (first component
  515.      * of the vector part).
  516.      *
  517.      * @return the first component of the vector part.
  518.      */
  519.     public double getX() {
  520.         return x;
  521.     }

  522.     /**
  523.      * Gets the third component of the quaternion (second component
  524.      * of the vector part).
  525.      *
  526.      * @return the second component of the vector part.
  527.      */
  528.     public double getY() {
  529.         return y;
  530.     }

  531.     /**
  532.      * Gets the fourth component of the quaternion (third component
  533.      * of the vector part).
  534.      *
  535.      * @return the third component of the vector part.
  536.      */
  537.     public double getZ() {
  538.         return z;
  539.     }

  540.     /**
  541.      * Gets the scalar part of the quaternion.
  542.      *
  543.      * @return the scalar part.
  544.      * @see #getW()
  545.      */
  546.     public double getScalarPart() {
  547.         return getW();
  548.     }

  549.     /**
  550.      * Gets the three components of the vector part of the quaternion.
  551.      *
  552.      * @return the vector part.
  553.      * @see #getX()
  554.      * @see #getY()
  555.      * @see #getZ()
  556.      */
  557.     public double[] getVectorPart() {
  558.         return new double[] {x, y, z};
  559.     }

  560.     /**
  561.      * Multiplies the instance by a scalar.
  562.      *
  563.      * @param alpha Scalar factor.
  564.      * @return a scaled quaternion.
  565.      */
  566.     public Quaternion multiply(final double alpha) {
  567.         return of(alpha * w,
  568.                   alpha * x,
  569.                   alpha * y,
  570.                   alpha * z);
  571.     }

  572.     /**
  573.      * Divides the instance by a scalar.
  574.      *
  575.      * @param alpha Scalar factor.
  576.      * @return a scaled quaternion.
  577.      */
  578.     public Quaternion divide(final double alpha) {
  579.         return of(w / alpha,
  580.                   x / alpha,
  581.                   y / alpha,
  582.                   z / alpha);
  583.     }

  584.     /**
  585.      * Parses a string that would be produced by {@link #toString()}
  586.      * and instantiates the corresponding object.
  587.      *
  588.      * @param s String representation.
  589.      * @return an instance.
  590.      * @throws NumberFormatException if the string does not conform
  591.      * to the specification.
  592.      */
  593.     public static Quaternion parse(String s) {
  594.         final int startBracket = s.indexOf(FORMAT_START);
  595.         if (startBracket != 0) {
  596.             throw new QuaternionParsingException("Expected start string: " + FORMAT_START);
  597.         }
  598.         final int len = s.length();
  599.         final int endBracket = s.indexOf(FORMAT_END);
  600.         if (endBracket != len - 1) {
  601.             throw new QuaternionParsingException("Expected end string: " + FORMAT_END);
  602.         }
  603.         final String[] elements = s.substring(1, s.length() - 1).split(FORMAT_SEP);
  604.         if (elements.length != NUMBER_OF_PARTS) {
  605.             throw new QuaternionParsingException("Incorrect number of parts: Expected 4 but was " +
  606.                                                  elements.length +
  607.                                                  " (separator is '" + FORMAT_SEP + "')");
  608.         }

  609.         final double a;
  610.         try {
  611.             a = Double.parseDouble(elements[0]);
  612.         } catch (NumberFormatException ex) {
  613.             throw new QuaternionParsingException("Could not parse scalar part" + elements[0], ex);
  614.         }
  615.         final double b;
  616.         try {
  617.             b = Double.parseDouble(elements[1]);
  618.         } catch (NumberFormatException ex) {
  619.             throw new QuaternionParsingException("Could not parse i part" + elements[1], ex);
  620.         }
  621.         final double c;
  622.         try {
  623.             c = Double.parseDouble(elements[2]);
  624.         } catch (NumberFormatException ex) {
  625.             throw new QuaternionParsingException("Could not parse j part" + elements[2], ex);
  626.         }
  627.         final double d;
  628.         try {
  629.             d = Double.parseDouble(elements[3]);
  630.         } catch (NumberFormatException ex) {
  631.             throw new QuaternionParsingException("Could not parse k part" + elements[3], ex);
  632.         }

  633.         return of(a, b, c, d);
  634.     }

  635.     /**
  636.      * {@inheritDoc}
  637.      */
  638.     @Override
  639.     public String toString() {
  640.         final StringBuilder s = new StringBuilder();
  641.         s.append(FORMAT_START)
  642.             .append(w).append(FORMAT_SEP)
  643.             .append(x).append(FORMAT_SEP)
  644.             .append(y).append(FORMAT_SEP)
  645.             .append(z)
  646.             .append(FORMAT_END);

  647.         return s.toString();
  648.     }

  649.     /** See {@link #parse(String)}. */
  650.     private static class QuaternionParsingException extends NumberFormatException {
  651.         /** Serializable version identifier. */
  652.         private static final long serialVersionUID = 20181128L;

  653.         /**
  654.          * @param msg Error message.
  655.          */
  656.         QuaternionParsingException(String msg) {
  657.             super(msg);
  658.         }

  659.         /**
  660.          * @param msg Error message.
  661.          * @param cause Cause of the exception.
  662.          */
  663.         QuaternionParsingException(String msg, Throwable cause) {
  664.             super(msg);
  665.             initCause(cause);
  666.         }
  667.     }
  668. }