From 1d1e19921ccc1ec10ba3a20212efc1f5c1d0f155 Mon Sep 17 00:00:00 2001
From: Phil Steitz 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
* UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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 UnsupportedOperationException
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;
-
-}
diff --git a/src/mantissa/src/org/spaceroots/mantissa/geometry/Rotation.java b/src/mantissa/src/org/spaceroots/mantissa/geometry/Rotation.java
index 5e84a038b..4a4267ea6 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/geometry/Rotation.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/geometry/Rotation.java
@@ -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;
* Rotation
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:
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:
** double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ); *- *
Focus is more oriented on what a rotation do. Once it - * has been built, and regardless of its representation, a rotation is - * an operator 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.
+ *Focus is oriented on what a rotation do rather than on its + * underlying representation. Once it has been built, and regardless of its + * internal representation, a rotation is an operator 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.
+ *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.
- *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
- * projectVectorIntoDestinationFrame
or
- * computeTransformedDirection
. It provides simpler and
- * more generic methods: {@link #applyTo(Vector3D) applyTo(Vector3D)}
- * and {@link #applyInverseTo(Vector3D) applyInverseTo(Vector3D)}.
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 projectVectorIntoDestinationFrame
or
+ * computeTransformedDirection
. It provides simpler and more generic
+ * methods: {@link #applyTo(Vector3D) applyTo(Vector3D)} and {@link
+ * #applyInverseTo(Vector3D) applyInverseTo(Vector3D)}.
Since a rotation is basically a vectorial operator, several
- * rotations can be composed together and the composite operation
- * r = r1 o r2
(which means that for each vector
- * u
, r(u) = r1(r2(u))
) 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 r1
to
- * r2
and the result we get is r = r1 o
- * r2
. For this purpose, the class provides the methods: {@link
- * #applyTo(Rotation) applyTo(Rotation)} and {@link
- * #applyInverseTo(Rotation) applyInverseTo(Rotation)}.
Since a rotation is basically a vectorial operator, several rotations can be
+ * composed together and the composite operation r = r1 o
+ * r2
(which means that for each vector u
,
+ * r(u) = r1(r2(u))
) 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 r1
to r2
and the result
+ * we get is r = r1 o r2
. For this purpose, the
+ * class provides the methods: {@link #applyTo(Rotation) applyTo(Rotation)} and
+ * {@link #applyInverseTo(Rotation) applyInverseTo(Rotation)}.
Rotations are guaranteed to be immutable objects.
* @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. *A rotation can be built from a normalized quaternion, * i.e. a quaternion for which q02 + * q12 + q22 + * q32 = 1. If the quaternion is not normalized, * the constructor can normalize it in a preprocessing step.
- *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.
* @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. *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.mT = 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.
@@ -290,14 +271,15 @@ public class Rotation /** Build the rotation that transforms a pair of vector into another pair. - *Except for possible scale factors, if the instance were - * applied to the pair (u1, u2) it will produce the pair (v1, - * v2).
+ *Except for possible scale factors, if the instance were applied to + * the pair (u1, u2) it will produce the pair + * (v1, v2).
- *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.
+ *If the angular separation between u1 and u2 is + * not the same as the angular separation between v1 and + * v2, then a corrected v'2 will be used rather than + * v2, the corrected vector will be in the (v1, + * v2) plane.
* @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). + * 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).
* @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 + *This constructor is used only for the sake of Cardan/Euler + * angles handling.
* @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 *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).
+ * 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 π + a1, π + * - a2 and π + a3. 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). *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 * nothing 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; diff --git a/src/mantissa/src/org/spaceroots/mantissa/geometry/Vector3D.java b/src/mantissa/src/org/spaceroots/mantissa/geometry/Vector3D.java index 8bd96d953..1a985ad4b 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/geometry/Vector3D.java +++ b/src/mantissa/src/org/spaceroots/mantissa/geometry/Vector3D.java @@ -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. + *
Vector3D are guaranteed to be immutable objects.
* @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. *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 : *
- * Vector3D k = u;
- * k.normalizeSelf();
+ * Vector3D k = Vector3D.normalize(u);
* Vector3D i = k.orthogonal();
* Vector3D j = Vector3D.crossProduct(k, i);
*
@@ -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;
}
diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegrator.java
index 32c182986..bbc18de5f 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegrator.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegrator.java
@@ -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) {
diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaFehlbergIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaFehlbergIntegrator.java
index e27bbb824..05eaa1da8 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaFehlbergIntegrator.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaFehlbergIntegrator.java
@@ -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
diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaIntegrator.java b/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaIntegrator.java
index 1705f6b66..c44234a48 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaIntegrator.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/ode/RungeKuttaIntegrator.java
@@ -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
diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchState.java b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchState.java
index 7c3879285..1fd262124 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchState.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchState.java
@@ -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.
diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunction.java b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunction.java
index cf254eb8b..c0e231fd7 100644
--- a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunction.java
+++ b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunction.java
@@ -22,18 +22,20 @@ package org.spaceroots.mantissa.ode;
* 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 g
function sign changes.
g
function sign changes, hence
+ * the name switching functions.
*
* Since events are only problem-dependent and are triggered by the * independant time 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).
@@ -52,29 +54,38 @@ public interface SwitchingFunction { */ public static final int STOP = 0; - /** Reset indicator. + /** Reset state indicator. *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).
*/ - public static final int RESET = 1; + public static final int RESET_STATE = 1; + + /** Reset derivatives indicator. + *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).
+ */ + public static final int RESET_DERIVATIVES = 2; /** Continue indicator. *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.
*/ - public static final int CONTINUE = 2; + public static final int CONTINUE = 3; /** Compute the value of the switching function. *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.
+ * function must be continuous (at least in its roots neighborhood), + * as the integrator will need to find its roots to locate the events. * @param t current value of the independant time 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. + * (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. - *If {@link #STOP} is returned, the step handler will be called
- * with the isLast
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.
isLast
flag of the {@link
+ * StepHandler#handleStep handleStep} method set to true and the
+ * integration will be stopped,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.
* @param t current value of the independant time variable diff --git a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunctionsHandler.java b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunctionsHandler.java index 3c7a1756e..0c459a76e 100644 --- a/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunctionsHandler.java +++ b/src/mantissa/src/org/spaceroots/mantissa/ode/SwitchingFunctionsHandler.java @@ -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. */ diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/AllTests.java b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/AllTests.java index e1ce528cc..ab9082b3e 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/AllTests.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/AllTests.java @@ -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; diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/ImmutableVector3DTest.java b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/ImmutableVector3DTest.java index b378ef0c9..e69de29bb 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/ImmutableVector3DTest.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/ImmutableVector3DTest.java @@ -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); - } - -} diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/Vector3DTest.java b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/Vector3DTest.java index 576379fea..324c4ca14 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/Vector3DTest.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/geometry/Vector3DTest.java @@ -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); diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/DormandPrince853IntegratorTest.java b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/DormandPrince853IntegratorTest.java index 3cefb9a07..29287b892 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/DormandPrince853IntegratorTest.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/DormandPrince853IntegratorTest.java @@ -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); } diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GillIntegratorTest.java b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GillIntegratorTest.java index 5aa0272d7..26d3c75f9 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GillIntegratorTest.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GillIntegratorTest.java @@ -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); } diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegratorTest.java b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegratorTest.java index 113fb7aa5..b261c4346 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegratorTest.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/GraggBulirschStoerIntegratorTest.java @@ -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); } diff --git a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/TestProblem4.java b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/TestProblem4.java index 4fe511b22..ec9b3ea47 100644 --- a/src/mantissa/tests-src/org/spaceroots/mantissa/ode/TestProblem4.java +++ b/src/mantissa/tests-src/org/spaceroots/mantissa/ode/TestProblem4.java @@ -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) {