Collection of patches to initial Mantissa sources:
- Fixed a problem when switching functions triggered derivatives discontinuities - Removed methods and classes that were deprecated in Mantissa and do not need to be preserved in commons-math as backward compatibility is not a problem for this newly integrated code - Changed Vector3D and Rotation to immutable classes for ease of use - Improved some javadoc in class Rotation JIRA: MATH-161 Submitted (with patches) by Luc Maisonobe git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@478458 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
05195b77ca
commit
1d1e19921c
|
@ -91,7 +91,7 @@ public class MessagesResources_fr
|
|||
{ "dimensions mismatch: ODE problem has dimension {0},"
|
||||
+ " state vector has dimension {1}",
|
||||
"incompatibilit\u00e9 de dimensions entre le probl\u00e8me ODE ({0}),"
|
||||
+ " et le vecteur d'\u00e9tat ({1})" },
|
||||
+ " et le vecteur d''\u00e9tat ({1})" },
|
||||
{ "too small integration interval: length = {0}",
|
||||
"intervalle d''int\u00e9gration trop petit : {0}" },
|
||||
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
// 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.spaceroots.mantissa.geometry;
|
||||
|
||||
/**
|
||||
* This class implements immutable vectors in a three-dimensional space.
|
||||
|
||||
* @version $Id: ImmutableVector3D.java 1705 2006-09-17 19:57:39Z luc $
|
||||
* @author L. Maisonobe
|
||||
|
||||
*/
|
||||
|
||||
public class ImmutableVector3D
|
||||
extends Vector3D {
|
||||
|
||||
/** Simple constructor.
|
||||
* Build a vector from its coordinates
|
||||
* @param x abscissa
|
||||
* @param y ordinate
|
||||
* @param z height
|
||||
*/
|
||||
public ImmutableVector3D(double x, double y, double z) {
|
||||
super(x, y, z);
|
||||
computeNorm();
|
||||
}
|
||||
|
||||
/** Simple constructor.
|
||||
* Build a vector from its azimuthal coordinates
|
||||
* @param alpha azimuth around Z
|
||||
* (0 is +X, PI/2 is +Y, PI is -X and 3PI/2 is -Y)
|
||||
* @param delta elevation above (XY) plane, from -PI to +PI
|
||||
*/
|
||||
public ImmutableVector3D(double alpha, double delta) {
|
||||
super(alpha, delta);
|
||||
computeNorm();
|
||||
}
|
||||
|
||||
/** Copy constructor.
|
||||
* Build a copy of a vector
|
||||
* @param v vector to copy
|
||||
*/
|
||||
public ImmutableVector3D(Vector3D v) {
|
||||
super(v);
|
||||
computeNorm();
|
||||
}
|
||||
|
||||
/** Multiplicative constructor
|
||||
* Build a vector from another one and a scale factor.
|
||||
* The vector built will be a * u
|
||||
* @param a scale factor
|
||||
* @param u base (unscaled) vector
|
||||
*/
|
||||
public ImmutableVector3D(double a, Vector3D u) {
|
||||
super(a, u);
|
||||
computeNorm();
|
||||
}
|
||||
|
||||
/** Linear constructor
|
||||
* Build a vector from two other ones and corresponding scale factors.
|
||||
* The vector built will be a * u + b * v
|
||||
* @param a first scale factor
|
||||
* @param u first base (unscaled) vector
|
||||
* @param b second scale factor
|
||||
* @param v second base (unscaled) vector
|
||||
*/
|
||||
public ImmutableVector3D(double a, Vector3D u, double b, Vector3D v) {
|
||||
super(a, u, b, v);
|
||||
computeNorm();
|
||||
}
|
||||
|
||||
/** Set the abscissa of the vector.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param x new abscissa for the vector
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void setX(double x) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Set the ordinate of the vector.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param y new ordinate for the vector
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void setY(double y) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Set the height of the vector.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param z new height for the vector
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void setZ(double z) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Set all coordinates of the vector.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param x new abscissa for the vector
|
||||
* @param y new ordinate for the vector
|
||||
* @param z new height for the vector
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void setCoordinates(double x, double y, double z) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Compute the norm once and for all. */
|
||||
private void computeNorm() {
|
||||
norm = Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/** Get the norm for the vector.
|
||||
* @return euclidian norm for the vector
|
||||
*/
|
||||
public double getNorm() {
|
||||
return norm;
|
||||
}
|
||||
|
||||
/** Add a vector to the instance.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param v vector to add
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void addToSelf(Vector3D v) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Subtract a vector from the instance.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param v vector to subtract
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void subtractFromSelf(Vector3D v) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Normalize the instance.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void normalizeSelf() {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Revert the instance.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void negateSelf() {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Multiply the instance by a scalar
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param a scalar by which the instance should be multiplied
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void multiplySelf(double a) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Reinitialize internal state from the specified array slice data.
|
||||
* This method should not be called for immutable vectors, it always
|
||||
* throws an <code>UnsupportedOperationException</code> exception
|
||||
* @param start start index in the array
|
||||
* @param array array holding the data to extract
|
||||
* @exception UnsupportedOperationException thrown in every case
|
||||
*/
|
||||
public void mapStateFromArray(int start, double[] array) {
|
||||
throw new UnsupportedOperationException("vector is immutable");
|
||||
}
|
||||
|
||||
/** Norm of the vector. */
|
||||
private double norm;
|
||||
|
||||
private static final long serialVersionUID = 5377895850033895270L;
|
||||
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.spaceroots.mantissa.geometry;
|
||||
|
||||
import org.spaceroots.mantissa.utilities.ArraySliceMappable;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
@ -25,59 +24,61 @@ import java.io.Serializable;
|
|||
|
||||
* <p>Rotations can be represented by several different mathematical
|
||||
* entities (matrices, axe and angle, Cardan or Euler angles,
|
||||
* quaternions). This class is an higher level abstraction, more
|
||||
* user-oriented and hiding this implementation detail. Well, for the
|
||||
* 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 before and after it has been applied. This
|
||||
* means that this class can be used to compute transformations from
|
||||
* one representation to another one. For example, extracting a set of
|
||||
* Cardan angles from a rotation matrix can be done using one single
|
||||
* line of code:</p>
|
||||
* from a set of vectors and their image.</p>
|
||||
* <p>This implies that this class can be used to transform one
|
||||
* representation into 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 more oriented on what a rotation <em>do</em>. Once it
|
||||
* has been built, and regardless of its 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 can vary. For example in an attitude simulation tool,
|
||||
* you will often consider the vector is fixed and you transform its
|
||||
* coordinates in one frame into its coordinates in another 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. In this case the frame is the
|
||||
* same (probably a topocentric one) and the raw and transformed
|
||||
* vectors are different. 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>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>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 = r1 o r2</code> (which means that for each vector
|
||||
* <code>u</code>, <code>r(u) = r1(r2(u))</code>) is also a
|
||||
* rotation. Hence we can consider that in addition to vectors, a
|
||||
* rotation can be applied to other rotations (or to itself). With our
|
||||
* previous notations, we would say we can apply <code>r1</code> to
|
||||
* <code>r2</code> and the result we get is <code>r = r1 o
|
||||
* r2</code>. For this purpose, the class provides the methods: {@link
|
||||
* #applyTo(Rotation) applyTo(Rotation)} and {@link
|
||||
* #applyInverseTo(Rotation) applyInverseTo(Rotation)}.</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 $Id: Rotation.java 1705 2006-09-17 19:57:39Z luc $
|
||||
* @author L. Maisonobe
|
||||
|
@ -86,8 +87,7 @@ import java.io.Serializable;
|
|||
|
||||
*/
|
||||
|
||||
public class Rotation
|
||||
implements ArraySliceMappable, Serializable {
|
||||
public class Rotation implements Serializable {
|
||||
|
||||
/** Build the identity rotation.
|
||||
*/
|
||||
|
@ -98,31 +98,12 @@ public class Rotation
|
|||
q3 = 0;
|
||||
}
|
||||
|
||||
/** Build a rotation from the quaternion coordinates.
|
||||
* @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
|
||||
* @deprecated since Mantissa 6.3, this method as been deprecated as it
|
||||
* does not properly handles non-normalized quaternions, it should be
|
||||
* replaced by {@link #Rotation(double, double, double, double, boolean)}
|
||||
*/
|
||||
public Rotation(double q0, double q1, double q2, double q3) {
|
||||
this.q0 = q0;
|
||||
this.q1 = q1;
|
||||
this.q2 = q2;
|
||||
this.q3 = q3;
|
||||
}
|
||||
|
||||
/** 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>
|
||||
* <p>This method replaces the {@link #Rotation(double, double,
|
||||
* double, double) constructor using only 4 doubles} which was deprecated
|
||||
* as of version 6.3 of Mantissa.</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
|
||||
|
@ -181,7 +162,7 @@ public class Rotation
|
|||
/** Build a rotation from a 3X3 matrix.
|
||||
|
||||
* <p>Rotation matrices are orthogonal matrices, i.e. unit matrices
|
||||
* (which are matrices for which m.mT = I) with real
|
||||
* (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>
|
||||
|
@ -290,14 +271,15 @@ public class Rotation
|
|||
|
||||
/** 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 (u1, u2) it will produce the pair (v1,
|
||||
* v2).</p>
|
||||
* <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 u1 and u2 is not the same as
|
||||
* the angular separation between v1 and v2, then a corrected v2'
|
||||
* will be used rather than v2, the corrected vector will be in the
|
||||
* (v1, v2) plane.</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
|
||||
|
@ -477,11 +459,11 @@ public class Rotation
|
|||
* rotations are three successive rotations around the canonical
|
||||
* axes X, Y and Z, the first and last rotations beeing around the
|
||||
* same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
|
||||
* YZY, ZXZ and ZYZ), the most popular one being ZXZ. Beware that
|
||||
* many people routinely use the term Euler angles even for what
|
||||
* really are Cardan angles (this confusion is especially widespread
|
||||
* in the aerospace business where Roll, Pitch and Yaw angles are
|
||||
* often tagged as Euler angles).</p>
|
||||
* YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
|
||||
* <p>Beware that many people routinely use the term Euler angles even
|
||||
* for what really are Cardan angles (this confusion is especially
|
||||
* widespread in the aerospace business where Roll, Pitch and Yaw angles
|
||||
* are often tagged as Euler angles).</p>
|
||||
|
||||
* @param order order of rotations to use
|
||||
* @param alpha1 angle of the first elementary rotation
|
||||
|
@ -490,107 +472,91 @@ public class Rotation
|
|||
*/
|
||||
public Rotation(RotationOrder order,
|
||||
double alpha1, double alpha2, double alpha3) {
|
||||
|
||||
if (order == RotationOrder.XYZ) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.XZY) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.YXZ) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.YZX) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.ZXY) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.ZYX) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.XYX) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.XZX) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.YXY) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.YZY) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
|
||||
} else if (order == RotationOrder.ZXZ) {
|
||||
|
||||
compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
|
||||
} else { // last possibility is ZYZ
|
||||
|
||||
compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Override the instance by the composition of three rotations.
|
||||
* @param r1 last (outermost) rotation to compose
|
||||
* @param r2 intermediate rotation to compose
|
||||
* @param r3 first (innermost) rotation to compose
|
||||
*/
|
||||
private void compose(Rotation r1, Rotation r2, Rotation r3) {
|
||||
Rotation composed = r1.applyTo(r2.applyTo(r3));
|
||||
q0 = composed.q0;
|
||||
q1 = composed.q1;
|
||||
q2 = composed.q2;
|
||||
q3 = composed.q3;
|
||||
this(computeRotation(order, alpha1, alpha2, alpha3));
|
||||
}
|
||||
|
||||
/** Copy constructor.
|
||||
* Build a copy of a rotation
|
||||
* <p>This constructor is used only for the sake of Cardan/Euler
|
||||
* angles handling.</p>
|
||||
* @param r rotation to copy
|
||||
*/
|
||||
public Rotation(Rotation r) {
|
||||
private Rotation(Rotation r) {
|
||||
q0 = r.q0;
|
||||
q1 = r.q1;
|
||||
q2 = r.q2;
|
||||
q3 = r.q3;
|
||||
}
|
||||
|
||||
/** Build a rotation from three Cardan or Euler elementary rotations.
|
||||
* @param order order of rotations to use
|
||||
* @param alpha1 angle of the first elementary rotation
|
||||
* @param alpha2 angle of the second elementary rotation
|
||||
* @param alpha3 angle of the third elementary rotation
|
||||
*/
|
||||
public static Rotation computeRotation(RotationOrder order,
|
||||
double alpha1,
|
||||
double alpha2,
|
||||
double alpha3) {
|
||||
if (order == RotationOrder.XYZ) {
|
||||
return compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
} else if (order == RotationOrder.XZY) {
|
||||
return compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
} else if (order == RotationOrder.YXZ) {
|
||||
return compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
} else if (order == RotationOrder.YZX) {
|
||||
return compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
} else if (order == RotationOrder.ZXY) {
|
||||
return compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
} else if (order == RotationOrder.ZYX) {
|
||||
return compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
} else if (order == RotationOrder.XYX) {
|
||||
return compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
} else if (order == RotationOrder.XZX) {
|
||||
return compose(new Rotation(Vector3D.plusI, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusI, alpha3));
|
||||
} else if (order == RotationOrder.YXY) {
|
||||
return compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
} else if (order == RotationOrder.YZY) {
|
||||
return compose(new Rotation(Vector3D.plusJ, alpha1),
|
||||
new Rotation(Vector3D.plusK, alpha2),
|
||||
new Rotation(Vector3D.plusJ, alpha3));
|
||||
} else if (order == RotationOrder.ZXZ) {
|
||||
return compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusI, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
} else { // last possibility is ZYZ
|
||||
return compose(new Rotation(Vector3D.plusK, alpha1),
|
||||
new Rotation(Vector3D.plusJ, alpha2),
|
||||
new Rotation(Vector3D.plusK, alpha3));
|
||||
}
|
||||
}
|
||||
|
||||
/** Override the instance by the composition of three rotations.
|
||||
* @param r3 last (outermost) rotation to compose
|
||||
* @param r2 intermediate rotation to compose
|
||||
* @param r1 first (innermost) rotation to compose
|
||||
*/
|
||||
private static Rotation compose(Rotation r3, Rotation r2, Rotation r1) {
|
||||
return r3.applyTo(r2.applyTo(r1));
|
||||
}
|
||||
|
||||
/** Revert a rotation.
|
||||
* Build a rotation which reverse the effect of another
|
||||
* rotation. This means that is r(u) = v, then r.revert (v) = u. The
|
||||
|
@ -599,7 +565,7 @@ public class Rotation
|
|||
* of the instance
|
||||
*/
|
||||
public Rotation revert() {
|
||||
return new Rotation(-q0, q1, q2, q3);
|
||||
return new Rotation(-q0, q1, q2, q3, false);
|
||||
}
|
||||
|
||||
/** Get the scalar coordinate of the quaternion.
|
||||
|
@ -647,7 +613,7 @@ public class Rotation
|
|||
}
|
||||
|
||||
/** Get the angle of the rotation.
|
||||
* @return angle of the rotation (between 0 and PI)
|
||||
* @return angle of the rotation (between 0 and π)
|
||||
*/
|
||||
public double getAngle() {
|
||||
if ((q0 < -0.1) || (q0 > 0.1)) {
|
||||
|
@ -663,14 +629,15 @@ public class Rotation
|
|||
|
||||
* <p>The equations show that each rotation can be defined by two
|
||||
* different values of the Cardan or Euler angles set. For example
|
||||
* if Cardan angles are used, the rotation defined by the angles a1,
|
||||
* a2 and a3 is the same as the rotation defined by the angles PI +
|
||||
* a1, PI - a2 and PI + a3. This method implements the following
|
||||
* arbitrary choices. For Cardan angles, the chosen set is the one
|
||||
* for which the second angle is between -PI/2 and PI/2 (i.e its
|
||||
* cosine is positive). For Euler angles, the chosen set is the one
|
||||
* for which the second angle is between 0 and PI (i.e its sine is
|
||||
* positive).</p>
|
||||
* if Cardan angles are used, the rotation defined by the angles
|
||||
* a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
|
||||
* the rotation defined by the angles π + a<sub>1</sub>, π
|
||||
* - a<sub>2</sub> and π + a<sub>3</sub>. This method implements
|
||||
* the following arbitrary choices. For Cardan angles, the chosen
|
||||
* set is the one for which the second angle is between -π/2 and
|
||||
* π/2 (i.e its cosine is positive). For Euler angles, the chosen
|
||||
* set is the one for which the second angle is between 0 and π
|
||||
* (i.e its sine is positive).</p>
|
||||
|
||||
* <p>Cardan and Euler angle have a very disappointing drawback: all
|
||||
* of them have singularities. This means that if the instance is
|
||||
|
@ -678,12 +645,12 @@ public class Rotation
|
|||
* rotation order, it will be impossible to retrieve the angles. For
|
||||
* Cardan angles, this is often called gimbal lock. There is
|
||||
* <em>nothing</em> to do to prevent this, it is an intrisic problem
|
||||
* of Cardan and Euler representation (but not a problem with the
|
||||
* with Cardan and Euler representation (but not a problem with the
|
||||
* rotation itself, which is perfectly well defined). For Cardan
|
||||
* angles, singularities occur when the second angle is close to
|
||||
* -PI/2 or +PI/2, for Euler angle singularities occur when the
|
||||
* second angle is close to 0 or PI, this means that the identity
|
||||
* rotation is always singular for Euler angles !
|
||||
* -π/2 or +π/2, for Euler angle singularities occur when the
|
||||
* second angle is close to 0 or π, this implies that the identity
|
||||
* rotation is always singular for Euler angles!
|
||||
|
||||
* @param order rotation order to use
|
||||
* @return an array of three angles, in the order specified by the set
|
||||
|
@ -988,7 +955,8 @@ public class Rotation
|
|||
return new Rotation(r.q0 * q0 - (r.q1 * q1 + r.q2 * q2 + r.q3 * q3),
|
||||
r.q1 * q0 + r.q0 * q1 + (r.q2 * q3 - r.q3 * q2),
|
||||
r.q2 * q0 + r.q0 * q2 + (r.q3 * q1 - r.q1 * q3),
|
||||
r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1));
|
||||
r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1),
|
||||
false);
|
||||
}
|
||||
|
||||
/** Apply the inverse of the instance to another rotation.
|
||||
|
@ -1006,7 +974,8 @@ public class Rotation
|
|||
return new Rotation(-r.q0 * q0 - (r.q1 * q1 + r.q2 * q2 + r.q3 * q3),
|
||||
-r.q1 * q0 + r.q0 * q1 + (r.q2 * q3 - r.q3 * q2),
|
||||
-r.q2 * q0 + r.q0 * q2 + (r.q3 * q1 - r.q1 * q3),
|
||||
-r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1));
|
||||
-r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1),
|
||||
false);
|
||||
}
|
||||
|
||||
/** Perfect orthogonality on a 3X3 matrix.
|
||||
|
@ -1106,35 +1075,17 @@ public class Rotation
|
|||
});
|
||||
}
|
||||
|
||||
public int getStateDimension() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public void mapStateFromArray(int start, double[] array) {
|
||||
q0 = array[start];
|
||||
q1 = array[start + 1];
|
||||
q2 = array[start + 2];
|
||||
q3 = array[start + 3];
|
||||
}
|
||||
|
||||
public void mapStateToArray(int start, double[] array) {
|
||||
array[start] = q0;
|
||||
array[start + 1] = q1;
|
||||
array[start + 2] = q2;
|
||||
array[start + 3] = q3;
|
||||
}
|
||||
|
||||
/** Scalar coordinate of the quaternion. */
|
||||
private double q0;
|
||||
private final double q0;
|
||||
|
||||
/** First coordinate of the vectorial part of the quaternion. */
|
||||
private double q1;
|
||||
private final double q1;
|
||||
|
||||
/** Second coordinate of the vectorial part of the quaternion. */
|
||||
private double q2;
|
||||
private final double q2;
|
||||
|
||||
/** Third coordinate of the vectorial part of the quaternion. */
|
||||
private double q3;
|
||||
private final double q3;
|
||||
|
||||
private static final long serialVersionUID = 7264384082212242475L;
|
||||
|
||||
|
|
|
@ -18,52 +18,32 @@
|
|||
package org.spaceroots.mantissa.geometry;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.spaceroots.mantissa.utilities.ArraySliceMappable;
|
||||
|
||||
|
||||
/** This class implements vectors in a three-dimensional space.
|
||||
* <p>Vector3D are guaranteed to be immutable objects.</p>
|
||||
* @version $Id: Vector3D.java 1705 2006-09-17 19:57:39Z luc $
|
||||
* @author L. Maisonobe
|
||||
*/
|
||||
public class Vector3D implements Serializable {
|
||||
|
||||
public class Vector3D
|
||||
implements ArraySliceMappable, Serializable {
|
||||
/** First canonical vector (coordinates : 1, 0, 0). */
|
||||
public static final Vector3D plusI = new Vector3D(1, 0, 0);
|
||||
|
||||
/** First canonical vector (coordinates : 1, 0, 0).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D plusI = new ImmutableVector3D(1, 0, 0);
|
||||
/** Opposite of the first canonical vector (coordinates : -1, 0, 0). */
|
||||
public static final Vector3D minusI = new Vector3D(-1, 0, 0);
|
||||
|
||||
/** Opposite of the first canonical vector (coordinates : -1, 0, 0).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D minusI = new ImmutableVector3D(-1, 0, 0);
|
||||
/** Second canonical vector (coordinates : 0, 1, 0). */
|
||||
public static final Vector3D plusJ = new Vector3D(0, 1, 0);
|
||||
|
||||
/** Second canonical vector (coordinates : 0, 1, 0).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D plusJ = new ImmutableVector3D(0, 1, 0);
|
||||
/** Opposite of the second canonical vector (coordinates : 0, -1, 0). */
|
||||
public static final Vector3D minusJ = new Vector3D(0, -1, 0);
|
||||
|
||||
/** Opposite of the second canonical vector (coordinates : 0, -1, 0).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D minusJ = new ImmutableVector3D(0, -1, 0);
|
||||
/** Third canonical vector (coordinates : 0, 0, 1). */
|
||||
public static final Vector3D plusK = new Vector3D(0, 0, 1);
|
||||
|
||||
/** Third canonical vector (coordinates : 0, 0, 1).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D plusK = new ImmutableVector3D(0, 0, 1);
|
||||
|
||||
/** Opposite of the third canonical vector (coordinates : 0, 0, -1).
|
||||
* This is really an {@link ImmutableVector3D ImmutableVector3D},
|
||||
* hence it can't be changed in any way.
|
||||
*/
|
||||
public static final Vector3D minusK = new ImmutableVector3D(0, 0, -1);
|
||||
/** Opposite of the third canonical vector (coordinates : 0, 0, -1). */
|
||||
public static final Vector3D minusK = new Vector3D(0, 0, -1);
|
||||
|
||||
/** Simple constructor.
|
||||
* Build a null vector.
|
||||
|
@ -140,31 +120,30 @@ public class Vector3D
|
|||
* @param c third scale factor
|
||||
* @param w third base (unscaled) vector
|
||||
*/
|
||||
public Vector3D(double a, Vector3D u,
|
||||
double b, Vector3D v,
|
||||
public Vector3D(double a, Vector3D u, double b, Vector3D v,
|
||||
double c, Vector3D w) {
|
||||
this.x = a * u.x + b * v.x + c * w.x;
|
||||
this.y = a * u.y + b * v.y + c * w.y;
|
||||
this.z = a * u.z + b * v.z + c * w.z;
|
||||
}
|
||||
|
||||
/** Copy constructor.
|
||||
* Build a copy of a vector
|
||||
* @param v vector to copy
|
||||
/** Linear constructor
|
||||
* Build a vector from four other ones and corresponding scale factors.
|
||||
* The vector built will be a * t + b * u + c * v + d * w
|
||||
* @param a first scale factor
|
||||
* @param t first base (unscaled) vector
|
||||
* @param b second scale factor
|
||||
* @param u second base (unscaled) vector
|
||||
* @param c third scale factor
|
||||
* @param v third base (unscaled) vector
|
||||
* @param d third scale factor
|
||||
* @param w third base (unscaled) vector
|
||||
*/
|
||||
public Vector3D(Vector3D v) {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
}
|
||||
|
||||
/** Reset the instance.
|
||||
* @param v vector to copy data from
|
||||
*/
|
||||
public void reset(Vector3D v) {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
public Vector3D(double a, Vector3D t, double b, Vector3D u,
|
||||
double c, Vector3D v, double d, Vector3D w) {
|
||||
this.x = a * t.x + b * u.x + c * v.x + d * w.x;
|
||||
this.y = a * t.y + b * u.y + c * v.y + d * w.y;
|
||||
this.z = a * t.z + b * u.z + c * v.z + d * w.z;
|
||||
}
|
||||
|
||||
/** Get the abscissa of the vector.
|
||||
|
@ -176,15 +155,6 @@ public class Vector3D
|
|||
return x;
|
||||
}
|
||||
|
||||
/** Set the abscissa of the vector.
|
||||
* @param x new abscissa for the vector
|
||||
* @see #getX()
|
||||
* @see #setCoordinates(double, double, double)
|
||||
*/
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
/** Get the ordinate of the vector.
|
||||
* @return ordinate of the vector
|
||||
* @see #Vector3D(double, double, double)
|
||||
|
@ -194,15 +164,6 @@ public class Vector3D
|
|||
return y;
|
||||
}
|
||||
|
||||
/** Set the ordinate of the vector.
|
||||
* @param y new ordinate for the vector
|
||||
* @see #getY()
|
||||
* @see #setCoordinates(double, double, double)
|
||||
*/
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/** Get the height of the vector.
|
||||
* @return height of the vector
|
||||
* @see #Vector3D(double, double, double)
|
||||
|
@ -212,27 +173,6 @@ public class Vector3D
|
|||
return z;
|
||||
}
|
||||
|
||||
/** Set the height of the vector.
|
||||
* @param z new height for the vector
|
||||
* @see #getZ()
|
||||
* @see #setCoordinates(double, double, double)
|
||||
*/
|
||||
public void setZ(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/** Set all coordinates of the vector.
|
||||
* @param x new abscissa for the vector
|
||||
* @param y new ordinate for the vector
|
||||
* @param z new height for the vector
|
||||
* @see #Vector3D(double, double, double)
|
||||
*/
|
||||
public void setCoordinates(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/** Get the norm for the vector.
|
||||
* @return euclidian norm for the vector
|
||||
*/
|
||||
|
@ -256,25 +196,18 @@ public class Vector3D
|
|||
return Math.asin(z / getNorm());
|
||||
}
|
||||
|
||||
/** Add a vector to the instance.
|
||||
* Add a vector to the instance. The instance is changed.
|
||||
* @param v vector to add
|
||||
/** Normalize a vector.
|
||||
* @param v vector to normalize
|
||||
* @return a new vector equal to v / ||v||
|
||||
* @exception ArithmeticException if the norm of the instance is null
|
||||
*/
|
||||
public void addToSelf(Vector3D v) {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
z += v.z;
|
||||
}
|
||||
|
||||
/** Add a scaled vector to the instance.
|
||||
* Add a scaled vector to the instance. The instance is changed.
|
||||
* @param factor scale factor to apply to v before adding it
|
||||
* @param v vector to add
|
||||
*/
|
||||
public void addToSelf(double factor, Vector3D v) {
|
||||
x += factor * v.x;
|
||||
y += factor * v.y;
|
||||
z += factor * v.z;
|
||||
public static Vector3D normalize(Vector3D v) {
|
||||
double norm = v.getNorm();
|
||||
if (norm == 0) {
|
||||
throw new ArithmeticException("null norm");
|
||||
}
|
||||
double inv = 1.0 / norm;
|
||||
return new Vector3D(inv * v.x, inv * v.y, inv * v.z);
|
||||
}
|
||||
|
||||
/** Add two vectors.
|
||||
|
@ -287,16 +220,6 @@ public class Vector3D
|
|||
return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
|
||||
}
|
||||
|
||||
/** Subtract a vector from the instance.
|
||||
* Subtract a vector from the instance. The instance is changed.
|
||||
* @param v vector to subtract
|
||||
*/
|
||||
public void subtractFromSelf(Vector3D v) {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
z -= v.z;
|
||||
}
|
||||
|
||||
/** Subtract two vectors.
|
||||
* Subtract two vectors and return the difference as a new vector
|
||||
* @param v1 first vector
|
||||
|
@ -307,22 +230,6 @@ public class Vector3D
|
|||
return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
|
||||
}
|
||||
|
||||
/** Normalize the instance.
|
||||
* Divide the instance by its norm in order to have a unit
|
||||
* vector. The instance is changed.
|
||||
* @exception ArithmeticException if the norm is null
|
||||
*/
|
||||
public void normalizeSelf() {
|
||||
double s = getNorm();
|
||||
if (s == 0) {
|
||||
throw new ArithmeticException("null norm");
|
||||
}
|
||||
double invNorm = 1 / s;
|
||||
x *= invNorm;
|
||||
y *= invNorm;
|
||||
z *= invNorm;
|
||||
}
|
||||
|
||||
/** Get a vector orthogonal to the instance.
|
||||
* <p>There are an infinite number of normalized vectors orthogonal
|
||||
* to the instance. This method picks up one of them almost
|
||||
|
@ -331,8 +238,7 @@ public class Vector3D
|
|||
* following example shows how to build a frame having the k axis
|
||||
* aligned with the known vector u :
|
||||
* <pre><code>
|
||||
* Vector3D k = u;
|
||||
* k.normalizeSelf();
|
||||
* Vector3D k = Vector3D.normalize(u);
|
||||
* Vector3D i = k.orthogonal();
|
||||
* Vector3D j = Vector3D.crossProduct(k, i);
|
||||
* </code></pre></p>
|
||||
|
@ -392,15 +298,6 @@ public class Vector3D
|
|||
|
||||
}
|
||||
|
||||
/** Revert the instance.
|
||||
* Replace the instance u by -u
|
||||
*/
|
||||
public void negateSelf() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
/** Get the opposite of a vector.
|
||||
* @param u vector to revert
|
||||
* @return a new vector which is -u
|
||||
|
@ -409,16 +306,6 @@ public class Vector3D
|
|||
return new Vector3D(-u.x, -u.y, -u.z);
|
||||
}
|
||||
|
||||
/** Multiply the instance by a scalar
|
||||
* Multiply the instance by a scalar. The instance is changed.
|
||||
* @param a scalar by which the instance should be multiplied
|
||||
*/
|
||||
public void multiplySelf(double a) {
|
||||
x *= a;
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
|
||||
/** Multiply a vector by a scalar
|
||||
* Multiply a vectors by a scalar and return the product as a new vector
|
||||
* @param a scalar
|
||||
|
@ -438,18 +325,6 @@ public class Vector3D
|
|||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
/** Set the instance to the result of the cross-product of two vectors.
|
||||
* @param v1 first vector (can be the instance)
|
||||
* @param v2 second vector (can be the instance)
|
||||
*/
|
||||
public void setToCrossProduct(Vector3D v1, Vector3D v2) {
|
||||
double newX = v1.y * v2.z - v1.z * v2.y;
|
||||
double newY = v1.z * v2.x - v1.x * v2.z;
|
||||
z = v1.x * v2.y - v1.y * v2.x;
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
|
||||
/** Compute the cross-product of two vectors.
|
||||
* @param v1 first vector
|
||||
* @param v2 second vector
|
||||
|
@ -461,31 +336,15 @@ public class Vector3D
|
|||
v1.x * v2.y - v1.y * v2.x);
|
||||
}
|
||||
|
||||
public int getStateDimension() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
public void mapStateFromArray(int start, double[] array) {
|
||||
x = array[start];
|
||||
y = array[start + 1];
|
||||
z = array[start + 2];
|
||||
}
|
||||
|
||||
public void mapStateToArray(int start, double[] array) {
|
||||
array[start] = x;
|
||||
array[start + 1] = y;
|
||||
array[start + 2] = z;
|
||||
}
|
||||
|
||||
/** Abscissa. */
|
||||
protected double x;
|
||||
private final double x;
|
||||
|
||||
/** Ordinate. */
|
||||
protected double y;
|
||||
private final double y;
|
||||
|
||||
/** Height. */
|
||||
protected double z;
|
||||
private final double z;
|
||||
|
||||
private static final long serialVersionUID = 4115635019045864211L;
|
||||
private static final long serialVersionUID = 484345009325358136L;
|
||||
|
||||
}
|
||||
|
|
|
@ -880,7 +880,11 @@ public class GraggBulirschStoerIntegrator
|
|||
interpolator.storeTime(currentT);
|
||||
handler.handleStep(interpolator, lastStep);
|
||||
|
||||
switchesHandler.reset(currentT, y);
|
||||
if (switchesHandler.reset(currentT, y) && ! lastStep) {
|
||||
// some switching function has triggered changes that
|
||||
// invalidate the derivatives, we need to recompute them
|
||||
firstStepAlreadyComputed = false;
|
||||
}
|
||||
|
||||
int optimalIter;
|
||||
if (k == 1) {
|
||||
|
|
|
@ -311,7 +311,11 @@ public abstract class RungeKuttaFehlbergIntegrator
|
|||
System.arraycopy(yDotK[stages - 1], 0, yDotK[0], 0, y0.length);
|
||||
}
|
||||
|
||||
switchesHandler.reset(currentT, y);
|
||||
if (switchesHandler.reset(currentT, y) && ! lastStep) {
|
||||
// some switching function has triggered changes that
|
||||
// invalidate the derivatives, we need to recompute them
|
||||
equations.computeDerivatives(currentT, y, yDotK[0]);
|
||||
}
|
||||
|
||||
if (! lastStep) {
|
||||
// stepsize control for next step
|
||||
|
|
|
@ -234,7 +234,11 @@ public abstract class RungeKuttaIntegrator
|
|||
System.arraycopy(yDotK[stages - 1], 0, yDotK[0], 0, y0.length);
|
||||
}
|
||||
|
||||
switchesHandler.reset(currentT, y);
|
||||
if (switchesHandler.reset(currentT, y) && ! lastStep) {
|
||||
// some switching function has triggered changes that
|
||||
// invalidate the derivatives, we need to recompute them
|
||||
equations.computeDerivatives(currentT, y, yDotK[0]);
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
// a switching function has changed the step
|
||||
|
|
|
@ -239,15 +239,23 @@ class SwitchState
|
|||
* beginning of the next step
|
||||
* @param y array were to put the desired state vector at the beginning
|
||||
* of the next step
|
||||
* @return true if the integrator should reset the derivatives too
|
||||
*/
|
||||
public void reset(double t, double[] y) {
|
||||
if (pendingEvent) {
|
||||
if (nextAction == SwitchingFunction.RESET) {
|
||||
function.resetState(t, y);
|
||||
}
|
||||
pendingEvent = false;
|
||||
pendingEventTime = Double.NaN;
|
||||
public boolean reset(double t, double[] y) {
|
||||
|
||||
if (! pendingEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextAction == SwitchingFunction.RESET_STATE) {
|
||||
function.resetState(t, y);
|
||||
}
|
||||
pendingEvent = false;
|
||||
pendingEventTime = Double.NaN;
|
||||
|
||||
return (nextAction == SwitchingFunction.RESET_STATE)
|
||||
|| (nextAction == SwitchingFunction.RESET_DERIVATIVES);
|
||||
|
||||
}
|
||||
|
||||
/** Get the value of the g function at the specified time.
|
||||
|
|
|
@ -22,18 +22,20 @@ package org.spaceroots.mantissa.ode;
|
|||
* <p>A switching function allows to handle discrete events in
|
||||
* integration problems. These events occur for example when the
|
||||
* integration process should be stopped as some value is reached
|
||||
* (G-stop facility), or when the derivatives has
|
||||
* discontinuities. These events are traditionally defined as
|
||||
* occurring when a <code>g</code> function sign changes.</p>
|
||||
* (G-stop facility), or when the derivatives have
|
||||
* discontinuities, or simply when the user wants to monitor some
|
||||
* states boundaries crossings. These events are traditionally defined
|
||||
* as occurring when a <code>g</code> function sign changes, hence
|
||||
* the name <em>switching functions</em>.</p>
|
||||
*
|
||||
* <p>Since events are only problem-dependent and are triggered by the
|
||||
* independant <i>time</i> variable and the state vector, they can
|
||||
* occur at virtually any time. The integrators will take care to
|
||||
* avoid sign changes inside the steps, they will reduce the step size
|
||||
* when such an event is detected in order to put this event exactly
|
||||
* at the end of the current step. This guarantees that step
|
||||
* interpolation (which always has a one step scope) is relevant even
|
||||
* in presence of discontinuities. This is independent from the
|
||||
* occur at virtually any time, unknown in advance. The integrators will
|
||||
* take care to avoid sign changes inside the steps, they will reduce
|
||||
* the step size when such an event is detected in order to put this
|
||||
* event exactly at the end of the current step. This guarantees that
|
||||
* step interpolation (which always has a one step scope) is relevant
|
||||
* even in presence of discontinuities. This is independent from the
|
||||
* stepsize control provided by integrators that monitor the local
|
||||
* error (this feature is available on all integrators, including
|
||||
* fixed step ones).</p>
|
||||
|
@ -52,29 +54,38 @@ public interface SwitchingFunction {
|
|||
*/
|
||||
public static final int STOP = 0;
|
||||
|
||||
/** Reset indicator.
|
||||
/** Reset state indicator.
|
||||
* <p>This value should be used as the return value of the {@link
|
||||
* #eventOccurred eventOccurred} method when the integration should
|
||||
* go on after the event ending the current step, with a new state
|
||||
* vector (which will be retrieved through the {@link #resetState
|
||||
* vector (which will be retrieved thanks to the {@link #resetState
|
||||
* resetState} method).</p>
|
||||
*/
|
||||
public static final int RESET = 1;
|
||||
public static final int RESET_STATE = 1;
|
||||
|
||||
/** Reset derivatives indicator.
|
||||
* <p>This value should be used as the return value of the {@link
|
||||
* #eventOccurred eventOccurred} method when the integration should
|
||||
* go on after the event ending the current step, with a new derivatives
|
||||
* vector (which will be retrieved thanks to the {@link
|
||||
* FirstOrderDifferentialEquations#computeDerivatives} method).</p>
|
||||
*/
|
||||
public static final int RESET_DERIVATIVES = 2;
|
||||
|
||||
/** Continue indicator.
|
||||
* <p>This value should be used as the return value of the {@link
|
||||
* #eventOccurred eventOccurred} method when the integration should go
|
||||
* on after the event ending the current step.</p>
|
||||
*/
|
||||
public static final int CONTINUE = 2;
|
||||
public static final int CONTINUE = 3;
|
||||
|
||||
/** Compute the value of the switching function.
|
||||
|
||||
* <p>Discrete events are generated when the sign of this function
|
||||
* changes, the integrator will take care to change the stepsize in
|
||||
* such a way these events occur exactly at step boundaries. This
|
||||
* function must be continuous, as the integrator will need to find
|
||||
* its roots to locate the events.</p>
|
||||
* function must be continuous (at least in its roots neighborhood),
|
||||
* as the integrator will need to find its roots to locate the events.</p>
|
||||
|
||||
* @param t current value of the independant <i>time</i> variable
|
||||
* @param y array containing the current value of the state vector
|
||||
|
@ -88,22 +99,33 @@ public interface SwitchingFunction {
|
|||
* ending exactly on a sign change of the function, just before the
|
||||
* step handler itself is called. It allows the user to update his
|
||||
* internal data to acknowledge the fact the event has been handled
|
||||
* (for example setting a flag to switch the derivatives computation
|
||||
* in case of discontinuity), and it allows to direct the integrator
|
||||
* to either stop or continue integration, possibly with a reset
|
||||
* state.</p>
|
||||
* (for example setting a flag in the {@link
|
||||
* FirstOrderDifferentialEquations differential equations} to switch
|
||||
* the derivatives computation in case of discontinuity), or to
|
||||
* direct the integrator to either stop or continue integration,
|
||||
* possibly with a reset state or derivatives.</p>
|
||||
|
||||
* <p>If {@link #STOP} is returned, the step handler will be called
|
||||
* with the <code>isLast</code> flag of the {@link
|
||||
* StepHandler#handleStep handleStep} method set to true. If {@link
|
||||
* #RESET} is returned, the {@link #resetState resetState} method
|
||||
* will be called once the step handler has finished its task.</p>
|
||||
* <ul>
|
||||
* <li>if {@link #STOP} is returned, the step handler will be called
|
||||
* with the <code>isLast</code> flag of the {@link
|
||||
* StepHandler#handleStep handleStep} method set to true and the
|
||||
* integration will be stopped,</li>
|
||||
* <li>if {@link #RESET_STATE} is returned, the {@link #resetState
|
||||
* resetState} method will be called once the step handler has
|
||||
* finished its task, and the integrator will also recompute the
|
||||
* derivatives,</li>
|
||||
* <li>if {@link #RESET_DERIVATIVES} is returned, the integrator
|
||||
* will recompute the derivatives,
|
||||
* <li>if {@link #CONTINUE} is returned, no specific action will
|
||||
* be taken (apart from having called this method) and integration
|
||||
* will continue.</li>
|
||||
* </ul>
|
||||
|
||||
* @param t current value of the independant <i>time</i> variable
|
||||
* @param y array containing the current value of the state vector
|
||||
* @return indication of what the integrator should do next, this
|
||||
* value must be one of {@link #STOP}, {@link #RESET} or {@link
|
||||
* #CONTINUE}
|
||||
* value must be one of {@link #STOP}, {@link #RESET_STATE},
|
||||
* {@link #RESET_DERIVATIVES} or {@link #CONTINUE}
|
||||
*/
|
||||
public int eventOccurred(double t, double[] y);
|
||||
|
||||
|
@ -111,11 +133,11 @@ public interface SwitchingFunction {
|
|||
|
||||
* <p>This method is called after the step handler has returned and
|
||||
* before the next step is started, but only when {@link
|
||||
* #eventOccurred} has itself returned the {@link #RESET}
|
||||
* #eventOccurred} has itself returned the {@link #RESET_STATE}
|
||||
* indicator. It allows the user to reset the state vector for the
|
||||
* next step, without perturbing the step handler of the finishing
|
||||
* step. If the {@link #eventOccurred} never returns the {@link
|
||||
* #RESET} indicator, this function will never be called, and it is
|
||||
* #RESET_STATE} indicator, this function will never be called, and it is
|
||||
* safe to leave its body empty.</p>
|
||||
|
||||
* @param t current value of the independant <i>time</i> variable
|
||||
|
|
|
@ -166,11 +166,16 @@ public class SwitchingFunctionsHandler {
|
|||
* beginning of the next step
|
||||
* @param y array were to put the desired state vector at the beginning
|
||||
* of the next step
|
||||
* @return true if the integrator should reset the derivatives too
|
||||
*/
|
||||
public void reset(double t, double[] y) {
|
||||
public boolean reset(double t, double[] y) {
|
||||
boolean resetDerivatives = false;
|
||||
for (Iterator iter = functions.iterator(); iter.hasNext();) {
|
||||
((SwitchState) iter.next()).reset(t, y);
|
||||
if (((SwitchState) iter.next()).reset(t, y)) {
|
||||
resetDerivatives = true;
|
||||
}
|
||||
}
|
||||
return resetDerivatives;
|
||||
}
|
||||
|
||||
/** Switching functions. */
|
||||
|
|
|
@ -26,7 +26,6 @@ public class AllTests {
|
|||
TestSuite suite = new TestSuite("org.spaceroots.mantissa.geometry");
|
||||
|
||||
suite.addTest(Vector3DTest.suite());
|
||||
suite.addTest(ImmutableVector3DTest.suite());
|
||||
suite.addTest(RotationTest.suite());
|
||||
|
||||
return suite;
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
// 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.spaceroots.mantissa.geometry;
|
||||
|
||||
import junit.framework.*;
|
||||
|
||||
public class ImmutableVector3DTest
|
||||
extends TestCase {
|
||||
|
||||
public ImmutableVector3DTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void testCanonical() {
|
||||
try {
|
||||
Vector3D.plusK.normalizeSelf();
|
||||
fail("an exception should have been thrown");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
} catch (Exception e) {
|
||||
fail ("wrong exception caught");
|
||||
}
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new TestSuite(ImmutableVector3DTest.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -43,7 +43,7 @@ public class Vector3DTest
|
|||
|
||||
Vector3D v1 = new Vector3D(1, 2, 3);
|
||||
Vector3D v2 = new Vector3D(-3, -2, -1);
|
||||
v1.subtractFromSelf(v2);
|
||||
v1 = Vector3D.subtract(v1, v2);
|
||||
checkVector(v1, new Vector3D(4, 4, 4));
|
||||
|
||||
checkVector(Vector3D.subtract(v2, v1), new Vector3D(-7, -6, -5));
|
||||
|
@ -53,7 +53,7 @@ public class Vector3DTest
|
|||
public void testAdd() {
|
||||
Vector3D v1 = new Vector3D(1, 2, 3);
|
||||
Vector3D v2 = new Vector3D(-3, -2, -1);
|
||||
v1.addToSelf(v2);
|
||||
v1 = Vector3D.add(v1, v2);
|
||||
checkVector(v1, new Vector3D(-2, 0, 2));
|
||||
|
||||
checkVector(Vector3D.add(v2, v1), new Vector3D(-5, -2, 1));
|
||||
|
@ -62,7 +62,7 @@ public class Vector3DTest
|
|||
|
||||
public void testScalarProduct() {
|
||||
Vector3D v = new Vector3D(1, 2, 3);
|
||||
v.multiplySelf(3);
|
||||
v = Vector3D.multiply(3, v);
|
||||
checkVector(v, new Vector3D(3, 6, 9));
|
||||
|
||||
checkVector(Vector3D.multiply(0.5, v), new Vector3D(1.5, 3, 4.5));
|
||||
|
@ -101,12 +101,10 @@ public class Vector3DTest
|
|||
public void testAngularSeparation() {
|
||||
Vector3D v1 = new Vector3D(2, -1, 4);
|
||||
|
||||
Vector3D k = v1;
|
||||
k.normalizeSelf();
|
||||
Vector3D k = Vector3D.normalize(v1);
|
||||
Vector3D i = k.orthogonal();
|
||||
|
||||
Vector3D v2 = Vector3D.multiply(Math.cos(1.2), k);
|
||||
v2.addToSelf(Vector3D.multiply(Math.sin(1.2), i));
|
||||
Vector3D v2 = new Vector3D(Math.cos(1.2), k, Math.sin(1.2), i);
|
||||
|
||||
assertTrue(Math.abs(Vector3D.angle(v1, v2) - 1.2) < 1.0e-12);
|
||||
|
||||
|
|
|
@ -305,6 +305,17 @@ public class DormandPrince853IntegratorTest
|
|||
|
||||
}
|
||||
|
||||
public void testUnstableDerivative()
|
||||
throws DerivativeException, IntegratorException {
|
||||
final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
|
||||
FirstOrderIntegrator integ =
|
||||
new DormandPrince853Integrator(0.1, 10, 1.0e-12, 0.0);
|
||||
integ.addSwitchingFunction(stepProblem, 1.0, 1.0e-12);
|
||||
double[] y = { Double.NaN };
|
||||
integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
|
||||
assertEquals(8.0, y[0], 1.0e-12);
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new TestSuite(DormandPrince853IntegratorTest.class);
|
||||
}
|
||||
|
|
|
@ -196,6 +196,16 @@ public class GillIntegratorTest
|
|||
pb.getFinalTime(), new double[pb.getDimension()]);
|
||||
}
|
||||
|
||||
public void testUnstableDerivative()
|
||||
throws DerivativeException, IntegratorException {
|
||||
final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
|
||||
FirstOrderIntegrator integ = new GillIntegrator(0.3);
|
||||
integ.addSwitchingFunction(stepProblem, 1.0, 1.0e-12);
|
||||
double[] y = { Double.NaN };
|
||||
integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
|
||||
assertEquals(8.0, y[0], 1.0e-12);
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new TestSuite(GillIntegratorTest.class);
|
||||
}
|
||||
|
|
|
@ -256,6 +256,17 @@ public class GraggBulirschStoerIntegratorTest
|
|||
pb.getFinalTime(), new double[pb.getDimension()]);
|
||||
}
|
||||
|
||||
public void testUnstableDerivative()
|
||||
throws DerivativeException, IntegratorException {
|
||||
final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
|
||||
FirstOrderIntegrator integ =
|
||||
new GraggBulirschStoerIntegrator(0.1, 10, 1.0e-12, 0.0);
|
||||
integ.addSwitchingFunction(stepProblem, 1.0, 1.0e-12);
|
||||
double[] y = { Double.NaN };
|
||||
integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
|
||||
assertEquals(8.0, y[0], 1.0e-12);
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new TestSuite(GraggBulirschStoerIntegratorTest.class);
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ class TestProblem4
|
|||
public int eventOccurred(double t, double[] y) {
|
||||
// this sign change is needed because the state will be reset soon
|
||||
sign = -sign;
|
||||
return SwitchingFunction.RESET;
|
||||
return SwitchingFunction.RESET_STATE;
|
||||
}
|
||||
|
||||
public void resetState(double t, double[] y) {
|
||||
|
|
Loading…
Reference in New Issue