001/**
002 * Vector3D -- A three element vector of Parameter objects that may represent a Cartesian
003 * coordinate.
004 *
005 * Copyright (C) 2008-2025, by Joseph A. Huwaldt. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or modify it under the terms
008 * of the GNU Lesser General Public License as published by the Free Software Foundation;
009 * either version 2 of the License, or (at your option) any later version.
010 *
011 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License along with
016 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
017 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
018 */
019package jahuwaldt.js.param;
020
021import javax.measure.converter.ConversionException;
022import javax.measure.converter.UnitConverter;
023import javax.measure.quantity.*;
024import javax.measure.unit.NonSI;
025import javax.measure.unit.SI;
026import javax.measure.unit.Unit;
027import javolution.context.ImmortalContext;
028import javolution.context.ObjectFactory;
029import javolution.lang.MathLib;
030import javolution.xml.XMLFormat;
031import javolution.xml.XMLSerializable;
032import javolution.xml.stream.XMLStreamException;
033import org.jscience.mathematics.number.Float64;
034import org.jscience.mathematics.vector.*;
035
036/**
037 * This class represents a 3 element {@link Vector vector} of Parameter elements sharing
038 * the same units. In geometrical terms, this vector represents a set of 3 Cartesian
039 * coordinates.
040 *
041 * <p> Modified by: Joseph A. Huwaldt </p>
042 *
043 * @author Joseph A. Huwaldt, Date: November 15, 2008
044 * @version February 23, 2025
045 *
046 * @param <Q> The Quantity (unit type, such as Length or Volume) of this vector.
047 */
048public final class Vector3D<Q extends Quantity> extends Coordinate3D<Q> implements XMLSerializable {
049
050    private static final long serialVersionUID = -306965157071885706L;
051
052    /**
053     * Constant used to identify the X coordinate in the vector.
054     */
055    public static final int X = 0;
056
057    /**
058     * Constant used to identify the Y coordinate in the vector.
059     */
060    public static final int Y = 1;
061
062    /**
063     * Constant used to identify the Z coordinate in the vector.
064     */
065    public static final int Z = 2;
066
067    ///////////////////////
068    // Factory creation. //
069    ///////////////////////
070    private Vector3D() {
071    }
072
073    private static final ObjectFactory<Vector3D> FACTORY = new ObjectFactory<Vector3D>() {
074        @Override
075        protected Vector3D create() {
076            return new Vector3D();
077        }
078    };
079
080    private static <Q extends Quantity> Vector3D<Q> newInstance(Unit<Q> unit) {
081        if (unit == null)
082            throw new NullPointerException("Unit can not be null.");
083        Vector3D<Q> measure = FACTORY.object();
084        measure._unit = unit;
085        return measure;
086    }
087
088    private static <Q extends Quantity> Vector3D<Q> copyOf(Vector3D<Q> original) {
089        Vector3D<Q> measure = Vector3D.newInstance(original._unit);
090        measure._data = original._data.copy();
091        return measure;
092    }
093
094    /**
095     * A Float64Vector with all the elements set to zero.
096     */
097    private static final Float64Vector ZERO_F64VECTOR;
098
099    /**
100     * A dimensionless vector with all the elements set to zero.
101     */
102    public static final Vector3D<Dimensionless> ZERO;
103
104    /**
105     * A position or length vector with all the elements set to zero.
106     */
107    public static final Vector3D<Length> ZERO_POSITION;
108
109    /**
110     * A velocity vector with all the elements set to zero.
111     */
112    public static final Vector3D<Velocity> ZERO_VELOCITY;
113
114    /**
115     * An acceleration vector with all the elements set to zero.
116     */
117    public static final Vector3D<Acceleration> ZERO_ACCELERATION;
118
119    /**
120     * A force vector with all the elements set to zero.
121     */
122    public static final Vector3D<Force> ZERO_FORCE;
123
124    /**
125     * A angle vector with all the elements set to zero.
126     */
127    public static final Vector3D<Angle> ZERO_ANGLE;
128
129    /**
130     * A unit vector in the X axis direction.
131     */
132    public static final Vector3D<Dimensionless> UNIT_X;
133
134    /**
135     * A unit vector in the Y axis direction.
136     */
137    public static final Vector3D<Dimensionless> UNIT_Y;
138
139    /**
140     * A unit vector in the Z axis direction.
141     */
142    public static final Vector3D<Dimensionless> UNIT_Z;
143
144    static {
145        // Forces constants to ImmortalMemory.
146        ImmortalContext.enter();
147        try {
148            ZERO_F64VECTOR = Float64Vector.valueOf(0, 0, 0);
149
150            ZERO = Vector3D.newInstance(Dimensionless.UNIT);
151            ZERO._data = ZERO_F64VECTOR;
152
153            ZERO_POSITION = Vector3D.newInstance(Length.UNIT);
154            ZERO_POSITION._data = ZERO_F64VECTOR;
155
156            ZERO_VELOCITY = Vector3D.newInstance(Velocity.UNIT);
157            ZERO_VELOCITY._data = ZERO_F64VECTOR;
158
159            ZERO_ACCELERATION = Vector3D.newInstance(Acceleration.UNIT);
160            ZERO_ACCELERATION._data = ZERO_F64VECTOR;
161
162            ZERO_FORCE = Vector3D.newInstance(Force.UNIT);
163            ZERO_FORCE._data = ZERO_F64VECTOR;
164
165            ZERO_ANGLE = Vector3D.newInstance(Angle.UNIT);
166            ZERO_ANGLE._data = ZERO_F64VECTOR;
167
168            UNIT_X = Vector3D.newInstance(Dimensionless.UNIT);
169            UNIT_X._data = Float64Vector.valueOf(1, 0, 0);
170
171            UNIT_Y = Vector3D.newInstance(Dimensionless.UNIT);
172            UNIT_Y._data = Float64Vector.valueOf(0, 1, 0);
173
174            UNIT_Z = Vector3D.newInstance(Dimensionless.UNIT);
175            UNIT_Z._data = Float64Vector.valueOf(0, 0, 1);
176
177        } finally {
178            ImmortalContext.exit();
179        }
180    }
181
182    /**
183     * The coordinates are stored in this vector. Serialization is handled by this class
184     * since Float64Vector is not Serializable.
185     */
186    private transient Float64Vector _data;
187
188    /**
189     * Holds the unit of the coordinates.
190     */
191    private Unit<Q> _unit;
192
193    /**
194     * Returns a {@link Vector3D} instance holding the specified <code>double</code>
195     * values stated in the specified units.
196     *
197     * @param <Q>  the Quantity (unit type, e.g. Length or Volume) of this vector.
198     * @param x    the x value stated in the specified unit.
199     * @param y    the y value stated in the specified unit.
200     * @param z    the z value stated in the specified unit.
201     * @param unit the unit in which the coordinates are stated.
202     * @return the vector having the specified values.
203     */
204    public static <Q extends Quantity> Vector3D<Q> valueOf(double x, double y, double z, Unit<Q> unit) {
205        Vector3D<Q> V = Vector3D.newInstance(unit);
206        V._data = Float64Vector.valueOf(x, y, z);
207
208        return V;
209    }
210
211    /**
212     * Returns a {@link Vector3D} instance holding the specified <code>Parameter</code>
213     * values. All the values are converted to the same units as the x value.
214     *
215     * @param <Q> the Quantity (unit type, e.g. Length or Volume) of this vector.
216     * @param x   the x value.
217     * @param y   the y value.
218     * @param z   the z value.
219     * @return the vector having the specified values in the units of x.
220     */
221    public static <Q extends Quantity> Vector3D<Q> valueOf(Parameter<Q> x, Parameter<Q> y, Parameter<Q> z) {
222
223        Unit<Q> unit = x.getUnit();
224        Vector3D<Q> V = Vector3D.newInstance(unit);
225        V._data = Float64Vector.valueOf(x.getValue(), y.getValue(unit), z.getValue(unit));
226
227        return V;
228    }
229
230    /**
231     * Returns a {@link Vector3D} instance containing the specified vector of Float64
232     * values stated in the specified units. The vector must have only 3 elements.
233     *
234     * @param <Q>    the Quantity (unit type, e.g. Length or Volume) of this vector.
235     * @param vector the vector of Float64 values stated in the specified unit (must have
236     *               dimension of 3).
237     * @param unit   the unit in which the coordinates are stated.
238     * @return the vector having the specified values.
239     */
240    public static <Q extends Quantity> Vector3D<Q> valueOf(Vector<Float64> vector, Unit<Q> unit) {
241        if (vector.getDimension() != 3)
242            throw new DimensionException(RESOURCES.getString("v3dBadDimErr"));
243
244        Vector3D<Q> V = Vector3D.newInstance(unit);
245        V._data = Float64Vector.valueOf(vector);
246
247        return V;
248    }
249
250    /**
251     * Returns a {@link Vector3D} instance containing the specified vector of Parameter
252     * values with compatible units. The vector must have only 3 elements. All the values
253     * are converted to the same units as the 1st value.
254     *
255     * @param <Q>    the Quantity (unit type, e.g. Length or Volume) of this vector.
256     * @param vector the vector of Parameter values (must have dimension of 3).
257     * @return the vector having the specified values.
258     */
259    @SuppressWarnings("null")
260    public static <Q extends Quantity> Vector3D<Q> valueOf(Vector<Parameter<Q>> vector) {
261        if (vector instanceof Vector3D)
262            return (Vector3D<Q>)vector;
263
264        if (vector.getDimension() != 3)
265            throw new DimensionException(RESOURCES.getString("v3dBadDimErr"));
266
267        return Vector3D.valueOf(vector.get(X), vector.get(Y), vector.get(Z));
268    }
269
270    /**
271     * Return the specified {@link Vector3D} object as a <code>Vector3D</code> instance.
272     *
273     * @param vector The <code>Vector3D</code> object to be converted to a
274     *               <code>Vector3D</code>.
275     * @return The input object is simply returned.
276     */
277    @Override
278    public Vector3D<Q> fromVector3D(Vector3D<Q> vector) {
279        return vector;
280    }
281
282    /**
283     * Returns the value of a Parameter from this vector.
284     *
285     * @param i the dimension index.
286     * @return the value of the parameter at <code>i</code>.
287     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i > dimension()-1)</code>
288     */
289    @Override
290    public Parameter<Q> get(int i) {
291        return Parameter.valueOf(getValue(i), getUnit());
292    }
293
294    /**
295     * Returns the value of the Parameter in this vector as a <code>double</code>, stated
296     * in this vector's {@link #getUnit unit}.
297     *
298     * @param i the dimension index.
299     * @return the value of the Parameter at <code>i</code>.
300     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i > dimension()-1)</code>
301     */
302    @Override
303    public double getValue(int i) {
304        return _data.getValue(i);
305    }
306
307    /**
308     * Returns the {@link #norm}, magnitude, or length value of this vector.
309     *
310     * @return <code>this.norm().doubleValue()</code>.
311     */
312    @Override
313    public double normValue() {
314        return _data.normValue();
315    }
316
317    /**
318     * Returns the negation of this vector.
319     *
320     * @return <code>-this</code>.
321     */
322    @Override
323    public Vector3D<Q> opposite() {
324        Vector3D<Q> V = Vector3D.newInstance(this._unit);
325        V._data = this._data.opposite();
326        return V;
327    }
328
329    /**
330     * Returns the sum of this vector with the one specified. The unit of the output
331     * vector will be the units of this vector.
332     *
333     * @param that the vector to be added.
334     * @return <code>this + that</code>.
335     * @throws DimensionException if vector dimensions are different.
336     */
337    @Override
338    public Vector3D<Q> plus(Vector<Parameter<Q>> that) {
339
340        //  Convert input vector to a Float64Vector (with unit conversion if necessary).
341        Float64Vector thatData = toFloat64Vector(that, this._unit);
342
343        Vector3D<Q> V = Vector3D.newInstance(this._unit);
344        V._data = this._data.plus(thatData);
345
346        return V;
347    }
348
349    /**
350     * Returns the sum of this vector with the parameter specified. The input parameter is
351     * added to each component of this vector. The unit of the output vector will be the
352     * units of this vector.
353     *
354     * @param that the parameter to be added to each element of this vector.
355     * @return <code>this + that</code>.
356     */
357    @Override
358    public Vector3D<Q> plus(Parameter<Q> that) {
359        Vector3D<Q> V = Vector3D.newInstance(this._unit);
360
361        //  Convert input parameter to the units of this vector.
362        double thatValue = that.getValue(this._unit);
363        double x = this._data.getValue(X) + thatValue;
364        double y = this._data.getValue(Y) + thatValue;
365        double z = this._data.getValue(Z) + thatValue;
366        V._data = Float64Vector.valueOf(x, y, z);
367
368        return V;
369    }
370
371    /**
372     * Returns the difference between this vector and the one specified. The unit of the
373     * output vector will be the units of this vector.
374     *
375     * @param that the vector to be subtracted.
376     * @return <code>this - that</code>.
377     */
378    @Override
379    public Vector3D<Q> minus(Vector<Parameter<Q>> that) {
380
381        //  Convert input vector to a Float64Vector (with unit conversion if necessary).
382        Float64Vector thatData = toFloat64Vector(that, this._unit);
383
384        Vector3D<Q> V = Vector3D.newInstance(this._unit);
385        V._data = this._data.minus(thatData);
386
387        return V;
388    }
389
390    /**
391     * Subtracts the supplied Parameter from each element of this vector and returns the
392     * result. The unit of the output vector will be the units of this vector.
393     *
394     * @param that the Parameter to be subtracted from each element of this vector.
395     * @return <code>this - that</code>.
396     */
397    @Override
398    public Vector3D<Q> minus(Parameter<Q> that) {
399        Vector3D<Q> V = Vector3D.newInstance(this._unit);
400
401        //  Convert input parameter to the units of this vector.
402        double thatValue = that.getValue(this._unit);
403        double x = this._data.getValue(X) - thatValue;
404        double y = this._data.getValue(Y) - thatValue;
405        double z = this._data.getValue(Z) - thatValue;
406        V._data = Float64Vector.valueOf(x, y, z);
407
408        return V;
409    }
410
411    /**
412     * Returns the product of this vector with the specified coefficient.
413     *
414     * @param k the coefficient multiplier.
415     * @return <code>this · k</code>
416     */
417    @Override
418    public Vector3D times(Parameter k) {
419
420        Unit unit = Parameter.productOf(this.getUnit(), k.getUnit());
421        Vector3D<?> V = Vector3D.newInstance(unit);
422        V._data = this._data.times(k.getValue());
423
424        return V;
425    }
426
427    /**
428     * Returns the product of this vector with the specified coefficient.
429     *
430     * @param k the coefficient multiplier.
431     * @return <code>this · k</code>
432     */
433    @Override
434    public Vector3D<Q> times(double k) {
435        Vector3D<Q> V = Vector3D.newInstance(this._unit);
436        V._data = _data.times(k);
437        return V;
438    }
439
440    /**
441     * Returns the dot product of this vector with the one specified.
442     *
443     * @param that the vector multiplier.
444     * @return <code>this · that</code>
445     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
446     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
447     * Wikipedia: Dot Product</a>
448     */
449    @Override
450    public Parameter times(Vector that) {
451
452        //  Convert input vector to a Float64Vector.
453        Float64Vector thatData = toFloat64Vector(that, null);
454
455        Float64 f = this._data.times(thatData);
456        Unit<?> unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
457
458        return Parameter.valueOf(f.doubleValue(), unit);
459    }
460
461    /**
462     * Returns the element-by-element product of this vector with the one specified.
463     *
464     * @param that the vector multiplier.
465     * @return <code>this .* that</code>
466     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
467     */
468    public Vector3D timesEBE(Vector that) {
469
470        //  Convert input vector to a Float64Vector.
471        Float64Vector thatData = toFloat64Vector(that, null);
472
473        Unit<?> unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
474
475        //  Carry out the multiplication.
476        double x = this._data.getValue(X) * thatData.getValue(X);
477        double y = this._data.getValue(Y) * thatData.getValue(Y);
478        double z = this._data.getValue(Z) * thatData.getValue(Z);
479
480        return Vector3D.valueOf(x, y, z, unit);
481    }
482
483    /**
484     * Returns this vector with each element divided by the specified divisor
485     * (dimensionless).
486     *
487     * @param divisor the divisor.
488     * @return <code>this / divisor</code>.
489     */
490    @Override
491    public Vector3D<Q> divide(double divisor) {
492        return times(1. / divisor);
493    }
494
495    /**
496     * Returns the cross product of two 3-dimensional vectors.
497     *
498     * @param that the vector multiplier.
499     * @return <code>this x that</code>
500     * @throws DimensionException if <code>(that.getDimension() != 3)</code>
501     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">
502     * Wikipedia: Cross Product</a>
503     */
504    @Override
505    public Vector3D cross(Vector that) {
506
507        //  Convert input vector to a Float64Vector.
508        Float64Vector thatData = toFloat64Vector(that, null);
509
510        Unit unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
511
512        Vector3D V = Vector3D.newInstance(unit);
513        V._data = this._data.cross(thatData);
514
515        return V;
516    }
517
518    /**
519     * Returns this vector with each element divided by the specified divisor.
520     *
521     * @param that the divisor.
522     * @return <code>this / that</code>.
523     */
524    public Vector3D<?> divide(Parameter<?> that) {
525        return (Vector3D<?>)times(that.inverse());
526    }
527
528    /**
529     * Returns this vector converted to a unit vector by dividing all the vector's
530     * elements by the length ({@link #norm}) of this vector.
531     *
532     * @return this vector converted to a unit vector
533     */
534    @Override
535    public Vector3D<Dimensionless> toUnitVector() {
536        double magnitude = this.normValue();
537        if (this.getUnit().equals(Dimensionless.UNIT) && MathLib.abs(magnitude - 1) <= Parameter.EPS)
538            return (Vector3D<Dimensionless>)this;
539
540        Vector3D<Dimensionless> V = Vector3D.newInstance(Dimensionless.UNIT);
541        V._data = this._data.times(1.0 / magnitude);
542        return V;
543    }
544
545    /**
546     * Returns a copy of this vector {@link javolution.context.AllocatorContext allocated}
547     * by the calling thread (possibly on the stack).
548     *
549     * @return an identical and independent copy of this vector.
550     */
551    @Override
552    public Vector3D<Q> copy() {
553        return copyOf(this);
554    }
555
556    /**
557     * Returns the unit in which the {@link #getValue values} in this vector are stated
558     * in.
559     *
560     * @return the unit in which the values in this vector are stated
561     */
562    @Override
563    public Unit<Q> getUnit() {
564        return _unit;
565    }
566
567    /**
568     * Returns the equivalent to this vector but stated in the specified unit.
569     *
570     * @param <R>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
571     * @param unit the unit of the vector to be returned.
572     * @return a vector equivalent to this vector but stated in the specified unit.
573     * @throws ConversionException if the current model does not allows for conversion to
574     * the specified unit.
575     */
576    @Override
577    public <R extends Quantity> Vector3D<R> to(Unit<R> unit) throws ConversionException {
578        if ((_unit == unit) || this._unit.equals(unit))
579            return (Vector3D<R>)this;
580
581        UnitConverter cvtr = Parameter.converterOf(_unit, unit);
582        if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary.
583            Vector3D<R> result = (Vector3D<R>)Vector3D.copyOf(this);
584            result._unit = unit;
585            return result;
586        }
587        Vector3D<R> result = Vector3D.newInstance(unit);
588        double x = cvtr.convert(_data.getValue(X));
589        double y = cvtr.convert(_data.getValue(Y));
590        double z = cvtr.convert(_data.getValue(Z));
591        result._data = Float64Vector.valueOf(x, y, z);
592
593        return result;
594    }
595
596    /**
597     * Casts this Vector3D to a parameterized unit of specified nature or throw a
598     * <code>ClassCastException</code> if the dimension of the specified quantity and this
599     * parameter's unit dimension do not match.
600     *
601     * @param <T>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
602     * @param type the quantity class identifying the nature of the unit.
603     * @return this Vector3D parameterized with the specified type.
604     * @throws ClassCastException if the dimension of this parameter's unit is different
605     * from the specified quantity dimension.
606     * @throws UnsupportedOperationException if the specified quantity class does not have
607     * a public static field named "UNIT" holding the standard unit for the quantity.
608     */
609    @Override
610    public <T extends Quantity> Vector3D<T> asType(Class<T> type) throws ClassCastException {
611        _unit.asType(type); //  If no exception is thrown, the cast is valid.
612        return (Vector3D<T>)this;
613    }
614
615    /**
616     * Returns a Cartesian Vector3D representation of this vector.
617     *
618     * @return a Cartesian Vector3D representation of this vector
619     */
620    @Override
621    public Vector3D<Q> toVector3D() {
622        return this;
623    }
624
625    /**
626     * Returns double the values stored in this vector, stated in this vector's
627     * {@link #getUnit unit}, as a Float64Vector.
628     *
629     * @return the values stored in this vector as a Float64Vector
630     */
631    public Float64Vector toFloat64Vector() {
632        return _data;
633    }
634
635    /**
636     * Returns a 3x3 skew-symmetric matrix representation of the values in this vector
637     * stated in this vector's {@link #getUnit unit}.
638     * <p>
639     * A skew symmetric representation of a vector is defined by:
640     * <pre>
641     *       | 0 -c  b|      |a|
642     *       | c  0 -a| &lt;--  |b|
643     *       |-b  a  0|      |c|
644     * </pre></p>
645     *
646     * @return a 3x3 skew-symmetric matrix representation of the values in this vector
647     */
648    public Matrix3D<Q> toSkewSymmetric() {
649        return makeSkewSymmetric(_data.getValue(X), _data.getValue(Y), _data.getValue(Z), _unit);
650    }
651
652    /**
653     * Returns a 3x3 skew-symmetric matrix representation of the values in the input 3D
654     * vector stated in the specified units.
655     * <p>
656     * A skew symmetric representation of a vector is defined by:
657     * <pre>
658     *       | 0 -c  b|      |a|
659     *       | c  0 -a| &lt;--  |b|
660     *       |-b  a  0|      |c|
661     * </pre></p>
662     *
663     * <p>
664     * This is a more efficient convenience method equivalent to:
665     * <code>Vector3D.valueOf(a, b, c, Dimensionless.UNIT).toSkewSymmetric();</code></p>
666     *
667     * @param <R>  the Quantity (unit type, e.g. Length or Volume) of the returned matrix.
668     * @param a    The 1st element of the 3D vector
669     * @param b    The 2nd element of the 3D vector
670     * @param c    The 3rd element of the 3D vector
671     * @param unit The unit that the elements are stated in.
672     * @return a 3x3 skew-symmetric matrix representation of the values in the input 3D
673     *         vector.
674     */
675    public static <R extends Quantity> Matrix3D<R> makeSkewSymmetric(double a, double b, double c, Unit<R> unit) {
676        Float64Vector row1 = Float64Vector.valueOf(0, -c, b);
677        Float64Vector row2 = Float64Vector.valueOf(c, 0, -a);
678        Float64Vector row3 = Float64Vector.valueOf(-b, a, 0);
679
680        return Matrix3D.valueOf(row1, row2, row3, unit);
681    }
682
683    /**
684     * Compares this Vector3D against the specified object for strict equality (same
685     * values and same units).
686     *
687     * @param obj the object to compare with.
688     * @return <code>true</code> if this vector is identical to that vector;
689     *         <code>false</code> otherwise.
690     */
691    @Override
692    public boolean equals(Object obj) {
693        if (this == obj)
694            return true;
695        if ((obj == null) || (obj.getClass() != this.getClass()))
696            return false;
697
698        Vector3D<?> that = (Vector3D<?>)obj;
699        if (!this._data.equals(that._data))
700            return false;
701
702        return this._unit.equals(that._unit);
703    }
704
705    /**
706     * Returns the hash code for this parameter.
707     *
708     * @return the hash code value.
709     */
710    @Override
711    public int hashCode() {
712        int hash = 7;
713
714        int var_code = _unit.hashCode();
715        hash = hash * 31 + var_code;
716
717        var_code = _data.hashCode();
718        hash = hash * 31 + var_code;
719
720        return hash;
721    }
722
723    /**
724     * Convert a vector of Parameter objects to a Float64Vector stated in the specified
725     * units. If the units are null, no conversion occurs.
726     */
727    private <Q extends Quantity> Float64Vector toFloat64Vector(Vector<Parameter<Q>> that, Unit<Q> unit) {
728
729        //  Make sure input is a Vector3D instance.
730        if (!(that instanceof Coordinate3D))
731            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
732
733        Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();                       //  Convert to a Vector3D.
734
735        //  Convert that vector's units to the specified units if necessary.
736        Float64Vector thatData = T._data;
737        if (unit != null) {
738            if ((unit != T._unit) && !unit.equals(T._unit)) {
739                UnitConverter cvtr = Parameter.converterOf(T._unit, unit);
740                if (cvtr != UnitConverter.IDENTITY) {
741                    double x = cvtr.convert(thatData.getValue(X));
742                    double y = cvtr.convert(thatData.getValue(Y));
743                    double z = cvtr.convert(thatData.getValue(Z));
744                    thatData = Float64Vector.valueOf(x, y, z);
745                }
746            }
747        }
748
749        return thatData;
750    }
751
752    /**
753     * During serialization, this will write out the Float64Vector as a simple series of
754     * <code>double</code> values. This method is ONLY called by the Java Serialization
755     * mechanism and should not otherwise be used.
756     *
757     * @param out The output stream to serialized this object to.
758     * @throws java.io.IOException if the output stream could not be written to.
759     */
760    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
761
762        // Call the default write object method.
763        out.defaultWriteObject();
764
765        //  Write out the three coordinate values.
766        out.writeDouble(_data.getValue(X));
767        out.writeDouble(_data.getValue(Y));
768        out.writeDouble(_data.getValue(Z));
769
770    }
771
772    /**
773     * During de-serialization, this will handle the reconstruction of the Float64Vector.
774     * This method is ONLY called by the Java Serialization mechanism and should not
775     * otherwise be used.
776     *
777     * @param in The input stream to be de-serialized
778     * @throws java.io.IOException if there is a problem reading from the input stream.
779     * @throws ClassNotFoundException if the class could not be constructed.
780     */
781    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
782
783        // Call the default read object method.
784        in.defaultReadObject();
785
786        //  Read in the three coordinate values.
787        double x = in.readDouble();
788        double y = in.readDouble();
789        double z = in.readDouble();
790
791        _data = Float64Vector.valueOf(x, y, z);
792
793    }
794
795    /**
796     * Holds the default XML representation. For example:
797     * <pre>
798     *   &lt;Vector3D unit = "m"&gt;
799     *       &lt;X value="1.0" /&gt;
800     *       &lt;Y value="0.0" /&gt;
801     *       &lt;Z value="2.0" /&gt;
802     *   &lt;/Vector3D&gt;
803     * </pre>
804     */
805    protected static final XMLFormat<Vector3D> XML = new XMLFormat<Vector3D>(Vector3D.class) {
806
807        @Override
808        public Vector3D<?> newInstance(Class<Vector3D> cls, InputElement xml) throws XMLStreamException {
809            return FACTORY.object();
810        }
811
812        @Override
813        public void read(InputElement xml, Vector3D V) throws XMLStreamException {
814
815            V._unit = Unit.valueOf(xml.getAttribute("unit"));
816            Float64 x = xml.get("X", Float64.class);
817            Float64 y = xml.get("Y", Float64.class);
818            Float64 z = xml.get("Z", Float64.class);
819            V._data = Float64Vector.valueOf(x.doubleValue(), y.doubleValue(), z.doubleValue());
820
821            if (xml.hasNext())
822                throw new XMLStreamException(RESOURCES.getString("toManyXMLElementsErr"));
823        }
824
825        @Override
826        public void write(Vector3D V, OutputElement xml) throws XMLStreamException {
827
828            xml.setAttribute("unit", V._unit.toString());
829            xml.add(V._data.get(X), "X", Float64.class);
830            xml.add(V._data.get(Y), "Y", Float64.class);
831            xml.add(V._data.get(Z), "Z", Float64.class);
832
833        }
834    };
835
836    /**
837     * Tests the methods in this class.
838     *
839     * @param args Command line arguments (ignored).
840     */
841    public static void main(String args[]) {
842        System.out.println("Testing Vector3D:  test = result [correct result]");
843
844        Vector3D<Length> v1 = Vector3D.valueOf(1, 2, 3, NonSI.FOOT);
845        System.out.println("v1 = " + v1);
846        System.out.println("  converted to m       = " + v1.to(SI.METER) + " [{0.3048 m, 0.6096 m, 0.9144 m}]");
847        System.out.println("  v1.norm()            = " + v1.norm() + " [3.74165738677394 ft]");
848        System.out.println("  v1.opposite()        = " + v1.opposite() + " [{-1.0 ft, -2.0 ft, -3.0 ft}]");
849        System.out.println("  v1.toUnitVector()    = " + v1.toUnitVector() + " [{0.267261241912424 , 0.534522483824849 , 0.801783725737273 }]");
850        System.out.println("  v1.toSkewSymmetric() = \n" + v1.toSkewSymmetric());
851
852        Parameter<Length> point = Parameter.valueOf(24, NonSI.INCH);
853        System.out.println("point = " + point);
854        System.out.println("  v1 + point           = " + v1.plus(point) + " [{3.0 ft, 4.0 ft, 5.0 ft}]");
855        System.out.println("  v1 - point           = " + v1.minus(point) + " [{-1.0 ft, 0.0 ft, 1.0 ft}]");
856        System.out.println("  v1 * 2               = " + v1.times(2) + " [{2.0 ft, 4.0 ft, 6.0 ft}]");
857
858        Vector3D<?> areaVector = (Vector3D<?>)v1.times(point);
859        System.out.println("  v1 * point           = " + areaVector);
860        System.out.println("  converted to ft²     = " + areaVector.to(NonSI.SQUARE_FOOT) + " [{2.0 ft², 4.0 ft², 6.0 ft²}]");
861
862        Vector3D<Length> v2 = Vector3D.valueOf(1, 1, 1, SI.METER);
863        Vector3D<?> v1xv2 = (Vector3D<?>)v1.cross(v2);
864        System.out.println("v2 = " + v2);
865        System.out.println("  v1 + v2 = " + v1.plus(v2) + " [{4.28083989501312 ft, 5.28083989501312 ft, 6.28083989501312 ft}]");
866        System.out.println("  v1 - v2 = " + v1.minus(v2) + " [{-2.28083989501312 ft, -1.28083989501312 ft, -0.280839895013123 ft}]");
867        System.out.println("  v1 · v2 = " + v1.times(v2.to(NonSI.FOOT)) + " [19.6850393700787 ft²]");
868        System.out.println("  v1.cross(v2) = " + v1xv2.to(NonSI.FOOT.pow(2)) + " [{-3.28083989501312 ft^2, 6.56167979002625 ft^2, -3.28083989501312 ft^2}]");
869        System.out.println("  v1.angle(v2) = " + v1.angle(v2).to(NonSI.DEGREE_ANGLE) + " [73.6090476746643 deg]");
870
871        //  Write out XML data.
872        try {
873            System.out.println();
874
875            // Creates some useful aliases for class names.
876            javolution.xml.XMLBinding binding = new javolution.xml.XMLBinding();
877            binding.setAlias(org.jscience.mathematics.number.Float64.class, "Float64");
878            binding.setAlias(org.jscience.mathematics.vector.Float64Matrix.class, "Float64Matrix");
879
880            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
881            writer.setIndentation("    ");
882            writer.setBinding(binding);
883            writer.write(v1, "Vector3D", Vector3D.class);
884            writer.flush();
885
886            System.out.println();
887        } catch (Exception e) {
888            e.printStackTrace();
889        }
890
891    }
892}