001/**
002 * Vector3D -- A three element vector of Parameter objects that may represent a Cartesian
003 * coordinate.
004 *
005 * Copyright (C) 2008-2015, 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 October 30, 2015
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    public static <Q extends Quantity> Vector3D<Q> valueOf(Vector<Parameter<Q>> vector) {
260        if (vector instanceof Vector3D)
261            return (Vector3D<Q>)vector;
262
263        if (vector.getDimension() != 3)
264            throw new DimensionException(RESOURCES.getString("v3dBadDimErr"));
265
266        return Vector3D.valueOf(vector.get(X), vector.get(Y), vector.get(Z));
267    }
268
269    /**
270     * Return the specified {@link Vector3D} object as a <code>Vector3D</code> instance.
271     *
272     * @param vector The <code>Vector3D</code> object to be converted to a
273     *               <code>Vector3D</code>.
274     * @return The input object is simply returned.
275     */
276    @Override
277    public Vector3D<Q> fromVector3D(Vector3D<Q> vector) {
278        return vector;
279    }
280
281    /**
282     * Returns the value of a Parameter from this vector.
283     *
284     * @param i the dimension index.
285     * @return the value of the parameter at <code>i</code>.
286     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i > dimension()-1)</code>
287     */
288    @Override
289    public Parameter<Q> get(int i) {
290        return Parameter.valueOf(getValue(i), getUnit());
291    }
292
293    /**
294     * Returns the value of the Parameter in this vector as a <code>double</code>, stated
295     * in this vector's {@link #getUnit unit}.
296     *
297     * @param i the dimension index.
298     * @return the value of the Parameter at <code>i</code>.
299     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i > dimension()-1)</code>
300     */
301    @Override
302    public double getValue(int i) {
303        return _data.getValue(i);
304    }
305
306    /**
307     * Returns the {@link #norm}, magnitude, or length value of this vector.
308     *
309     * @return <code>this.norm().doubleValue()</code>.
310     */
311    @Override
312    public double normValue() {
313        return _data.normValue();
314    }
315
316    /**
317     * Returns the negation of this vector.
318     *
319     * @return <code>-this</code>.
320     */
321    @Override
322    public Vector3D<Q> opposite() {
323        Vector3D<Q> V = Vector3D.newInstance(this._unit);
324        V._data = this._data.opposite();
325        return V;
326    }
327
328    /**
329     * Returns the sum of this vector with the one specified. The unit of the output
330     * vector will be the units of this vector.
331     *
332     * @param that the vector to be added.
333     * @return <code>this + that</code>.
334     * @throws DimensionException if vector dimensions are different.
335     */
336    @Override
337    public Vector3D<Q> plus(Vector<Parameter<Q>> that) {
338
339        //  Convert input vector to a Float64Vector (with unit conversion if necessary).
340        Float64Vector thatData = toFloat64Vector(that, this._unit);
341
342        Vector3D<Q> V = Vector3D.newInstance(this._unit);
343        V._data = this._data.plus(thatData);
344
345        return V;
346    }
347
348    /**
349     * Returns the sum of this vector with the parameter specified. The input parameter is
350     * added to each component of this vector. The unit of the output vector will be the
351     * units of this vector.
352     *
353     * @param that the parameter to be added to each element of this vector.
354     * @return <code>this + that</code>.
355     */
356    @Override
357    public Vector3D<Q> plus(Parameter<Q> that) {
358        Vector3D<Q> V = Vector3D.newInstance(this._unit);
359
360        //  Convert input parameter to the units of this vector.
361        double thatValue = that.getValue(this._unit);
362        double x = this._data.getValue(X) + thatValue;
363        double y = this._data.getValue(Y) + thatValue;
364        double z = this._data.getValue(Z) + thatValue;
365        V._data = Float64Vector.valueOf(x, y, z);
366
367        return V;
368    }
369
370    /**
371     * Returns the difference between this vector and the one specified. The unit of the
372     * output vector will be the units of this vector.
373     *
374     * @param that the vector to be subtracted.
375     * @return <code>this - that</code>.
376     */
377    @Override
378    public Vector3D<Q> minus(Vector<Parameter<Q>> that) {
379
380        //  Convert input vector to a Float64Vector (with unit conversion if necessary).
381        Float64Vector thatData = toFloat64Vector(that, this._unit);
382
383        Vector3D<Q> V = Vector3D.newInstance(this._unit);
384        V._data = this._data.minus(thatData);
385
386        return V;
387    }
388
389    /**
390     * Subtracts the supplied Parameter from each element of this vector and returns the
391     * result. The unit of the output vector will be the units of this vector.
392     *
393     * @param that the Parameter to be subtracted from each element of this vector.
394     * @return <code>this - that</code>.
395     */
396    @Override
397    public Vector3D<Q> minus(Parameter<Q> that) {
398        Vector3D<Q> V = Vector3D.newInstance(this._unit);
399
400        //  Convert input parameter to the units of this vector.
401        double thatValue = that.getValue(this._unit);
402        double x = this._data.getValue(X) - thatValue;
403        double y = this._data.getValue(Y) - thatValue;
404        double z = this._data.getValue(Z) - thatValue;
405        V._data = Float64Vector.valueOf(x, y, z);
406
407        return V;
408    }
409
410    /**
411     * Returns the product of this vector with the specified coefficient.
412     *
413     * @param k the coefficient multiplier.
414     * @return <code>this · k</code>
415     */
416    @Override
417    public Vector3D times(Parameter k) {
418
419        Unit unit = Parameter.productOf(this.getUnit(), k.getUnit());
420        Vector3D<?> V = Vector3D.newInstance(unit);
421        V._data = this._data.times(k.getValue());
422
423        return V;
424    }
425
426    /**
427     * Returns the product of this vector with the specified coefficient.
428     *
429     * @param k the coefficient multiplier.
430     * @return <code>this · k</code>
431     */
432    @Override
433    public Vector3D<Q> times(double k) {
434        Vector3D<Q> V = Vector3D.newInstance(this._unit);
435        V._data = _data.times(k);
436        return V;
437    }
438
439    /**
440     * Returns the dot product of this vector with the one specified.
441     *
442     * @param that the vector multiplier.
443     * @return <code>this · that</code>
444     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
445     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
446     * Wikipedia: Dot Product</a>
447     */
448    @Override
449    public Parameter times(Vector that) {
450
451        //  Convert input vector to a Float64Vector.
452        Float64Vector thatData = toFloat64Vector(that, null);
453
454        Float64 f = this._data.times(thatData);
455        Unit<?> unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
456
457        return Parameter.valueOf(f.doubleValue(), unit);
458    }
459
460    /**
461     * Returns the element-by-element product of this vector with the one specified.
462     *
463     * @param that the vector multiplier.
464     * @return <code>this .* that</code>
465     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
466     */
467    public Vector3D timesEBE(Vector that) {
468
469        //  Convert input vector to a Float64Vector.
470        Float64Vector thatData = toFloat64Vector(that, null);
471
472        Unit<?> unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
473
474        //  Carry out the multiplication.
475        double x = this._data.getValue(X) * thatData.getValue(X);
476        double y = this._data.getValue(Y) * thatData.getValue(Y);
477        double z = this._data.getValue(Z) * thatData.getValue(Z);
478
479        return Vector3D.valueOf(x, y, z, unit);
480    }
481
482    /**
483     * Returns this vector with each element divided by the specified divisor
484     * (dimensionless).
485     *
486     * @param divisor the divisor.
487     * @return <code>this / divisor</code>.
488     */
489    @Override
490    public Vector3D<Q> divide(double divisor) {
491        return times(1. / divisor);
492    }
493
494    /**
495     * Returns the cross product of two 3-dimensional vectors.
496     *
497     * @param that the vector multiplier.
498     * @return <code>this x that</code>
499     * @throws DimensionException if <code>(that.getDimension() != 3)</code>
500     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">
501     * Wikipedia: Cross Product</a>
502     */
503    @Override
504    public Vector3D cross(Vector that) {
505
506        //  Convert input vector to a Float64Vector.
507        Float64Vector thatData = toFloat64Vector(that, null);
508
509        Unit unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(X)).getUnit());
510
511        Vector3D V = Vector3D.newInstance(unit);
512        V._data = this._data.cross(thatData);
513
514        return V;
515    }
516
517    /**
518     * Returns this vector with each element divided by the specified divisor.
519     *
520     * @param that the divisor.
521     * @return <code>this / that</code>.
522     */
523    public Vector3D<?> divide(Parameter<?> that) {
524        return (Vector3D<?>)times(that.inverse());
525    }
526
527    /**
528     * Returns this vector converted to a unit vector by dividing all the vector's
529     * elements by the length ({@link #norm}) of this vector.
530     *
531     * @return this vector converted to a unit vector
532     */
533    public Vector3D<Dimensionless> toUnitVector() {
534        double magnitude = this.normValue();
535        if (this.getUnit().equals(Dimensionless.UNIT) && MathLib.abs(magnitude - 1) <= Parameter.EPS)
536            return (Vector3D<Dimensionless>)this;
537
538        Vector3D<Dimensionless> V = Vector3D.newInstance(Dimensionless.UNIT);
539        V._data = this._data.times(1.0 / magnitude);
540        return V;
541    }
542
543    /**
544     * Returns a copy of this vector {@link javolution.context.AllocatorContext allocated}
545     * by the calling thread (possibly on the stack).
546     *
547     * @return an identical and independent copy of this vector.
548     */
549    @Override
550    public Vector3D<Q> copy() {
551        return copyOf(this);
552    }
553
554    /**
555     * Returns the unit in which the {@link #getValue values} in this vector are stated
556     * in.
557     *
558     * @return the unit in which the values in this vector are stated
559     */
560    @Override
561    public Unit<Q> getUnit() {
562        return _unit;
563    }
564
565    /**
566     * Returns the equivalent to this vector but stated in the specified unit.
567     *
568     * @param <R>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
569     * @param unit the unit of the vector to be returned.
570     * @return a vector equivalent to this vector but stated in the specified unit.
571     * @throws ConversionException if the current model does not allows for conversion to
572     * the specified unit.
573     */
574    @Override
575    public <R extends Quantity> Vector3D<R> to(Unit<R> unit) throws ConversionException {
576        if ((_unit == unit) || this._unit.equals(unit))
577            return (Vector3D<R>)this;
578
579        UnitConverter cvtr = Parameter.converterOf(_unit, unit);
580        if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary.
581            Vector3D<R> result = (Vector3D<R>)Vector3D.copyOf(this);
582            result._unit = unit;
583            return result;
584        }
585        Vector3D<R> result = Vector3D.newInstance(unit);
586        double x = cvtr.convert(_data.getValue(X));
587        double y = cvtr.convert(_data.getValue(Y));
588        double z = cvtr.convert(_data.getValue(Z));
589        result._data = Float64Vector.valueOf(x, y, z);
590
591        return result;
592    }
593
594    /**
595     * Casts this Vector3D to a parameterized unit of specified nature or throw a
596     * <code>ClassCastException</code> if the dimension of the specified quantity and this
597     * parameter's unit dimension do not match.
598     *
599     * @param <T>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
600     * @param type the quantity class identifying the nature of the unit.
601     * @return this Vector3D parameterized with the specified type.
602     * @throws ClassCastException if the dimension of this parameter's unit is different
603     * from the specified quantity dimension.
604     * @throws UnsupportedOperationException if the specified quantity class does not have
605     * a public static field named "UNIT" holding the standard unit for the quantity.
606     */
607    @Override
608    public <T extends Quantity> Vector3D<T> asType(Class<T> type) throws ClassCastException {
609        Unit<T> u = _unit.asType(type); //  If no exception is thrown, the cast is valid.
610        return (Vector3D<T>)this;
611    }
612
613    /**
614     * Returns a Cartesian Vector3D representation of this vector.
615     *
616     * @return a Cartesian Vector3D representation of this vector
617     */
618    @Override
619    public Vector3D<Q> toVector3D() {
620        return this;
621    }
622
623    /**
624     * Returns double the values stored in this vector, stated in this vector's
625     * {@link #getUnit unit}, as a Float64Vector.
626     *
627     * @return the values stored in this vector as a Float64Vector
628     */
629    public Float64Vector toFloat64Vector() {
630        return _data;
631    }
632
633    /**
634     * Returns a 3x3 skew-symmetric matrix representation of the values in this vector
635     * stated in this vector's {@link #getUnit unit}.
636     * <p>
637     * A skew symmetric representation of a vector is defined by:
638     * <pre>
639     *       | 0 -c  b|      |a|
640     *       | c  0 -a| &lt;--  |b|
641     *       |-b  a  0|      |c|
642     * </pre></p>
643     *
644     * @return a 3x3 skew-symmetric matrix representation of the values in this vector
645     */
646    public Matrix3D<Q> toSkewSymmetric() {
647        return makeSkewSymmetric(_data.getValue(X), _data.getValue(Y), _data.getValue(Z), _unit);
648    }
649
650    /**
651     * Returns a 3x3 skew-symmetric matrix representation of the values in the input 3D
652     * vector stated in the specified units.
653     * <p>
654     * A skew symmetric representation of a vector is defined by:
655     * <pre>
656     *       | 0 -c  b|      |a|
657     *       | c  0 -a| &lt;--  |b|
658     *       |-b  a  0|      |c|
659     * </pre></p>
660     *
661     * <p>
662     * This is a more efficient convenience method equivalent to:
663     * <code>Vector3D.valueOf(a, b, c, Dimensionless.UNIT).toSkewSymmetric();</code></p>
664     *
665     * @param <R>  the Quantity (unit type, e.g. Length or Volume) of the returned matrix.
666     * @param a    The 1st element of the 3D vector
667     * @param b    The 2nd element of the 3D vector
668     * @param c    The 3rd element of the 3D vector
669     * @param unit The unit that the elements are stated in.
670     * @return a 3x3 skew-symmetric matrix representation of the values in the input 3D
671     *         vector.
672     */
673    public static <R extends Quantity> Matrix3D<R> makeSkewSymmetric(double a, double b, double c, Unit<R> unit) {
674        Float64Vector row1 = Float64Vector.valueOf(0, -c, b);
675        Float64Vector row2 = Float64Vector.valueOf(c, 0, -a);
676        Float64Vector row3 = Float64Vector.valueOf(-b, a, 0);
677
678        return Matrix3D.valueOf(row1, row2, row3, unit);
679    }
680
681    /**
682     * Compares this Vector3D against the specified object for strict equality (same
683     * values and same units).
684     *
685     * @param obj the object to compare with.
686     * @return <code>true</code> if this vector is identical to that vector;
687     *         <code>false</code> otherwise.
688     */
689    @Override
690    public boolean equals(Object obj) {
691        if (this == obj)
692            return true;
693        if ((obj == null) || (obj.getClass() != this.getClass()))
694            return false;
695
696        Vector3D<?> that = (Vector3D<?>)obj;
697        if (!this._data.equals(that._data))
698            return false;
699
700        return this._unit.equals(that._unit);
701    }
702
703    /**
704     * Returns the hash code for this parameter.
705     *
706     * @return the hash code value.
707     */
708    @Override
709    public int hashCode() {
710        int hash = 7;
711
712        int var_code = _unit.hashCode();
713        hash = hash * 31 + var_code;
714
715        var_code = _data.hashCode();
716        hash = hash * 31 + var_code;
717
718        return hash;
719    }
720
721    /**
722     * Convert a vector of Parameter objects to a Float64Vector stated in the specified
723     * units. If the units are null, no conversion occurs.
724     */
725    private <Q extends Quantity> Float64Vector toFloat64Vector(Vector<Parameter<Q>> that, Unit<Q> unit) {
726
727        //  Make sure input is a Vector3D instance.
728        if (!(that instanceof Coordinate3D))
729            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
730
731        Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();                       //  Convert to a Vector3D.
732
733        //  Convert that vector's units to the specified units if necessary.
734        Float64Vector thatData = T._data;
735        if (unit != null) {
736            if ((unit != T._unit) && !unit.equals(T._unit)) {
737                UnitConverter cvtr = Parameter.converterOf(T._unit, unit);
738                if (cvtr != UnitConverter.IDENTITY) {
739                    double x = cvtr.convert(thatData.getValue(X));
740                    double y = cvtr.convert(thatData.getValue(Y));
741                    double z = cvtr.convert(thatData.getValue(Z));
742                    thatData = Float64Vector.valueOf(x, y, z);
743                }
744            }
745        }
746
747        return thatData;
748    }
749
750    /**
751     * During serialization, this will write out the Float64Vector as a simple series of
752     * <code>double</code> values. This method is ONLY called by the Java Serialization
753     * mechanism and should not otherwise be used.
754     */
755    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
756
757        // Call the default write object method.
758        out.defaultWriteObject();
759
760        //  Write out the three coordinate values.
761        out.writeDouble(_data.getValue(X));
762        out.writeDouble(_data.getValue(Y));
763        out.writeDouble(_data.getValue(Z));
764
765    }
766
767    /**
768     * During de-serialization, this will handle the reconstruction of the Float64Vector.
769     * This method is ONLY called by the Java Serialization mechanism and should not
770     * otherwise be used.
771     */
772    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
773
774        // Call the default read object method.
775        in.defaultReadObject();
776
777        //  Read in the three coordinate values.
778        double x = in.readDouble();
779        double y = in.readDouble();
780        double z = in.readDouble();
781
782        _data = Float64Vector.valueOf(x, y, z);
783
784    }
785
786    /**
787     * Holds the default XML representation. For example:
788     * <pre>
789     *   &lt;Vector3D unit = "m"&gt;
790     *       &lt;X value="1.0" /&gt;
791     *       &lt;Y value="0.0" /&gt;
792     *       &lt;Z value="2.0" /&gt;
793     *   &lt;/Vector3D&gt;
794     * </pre>
795     */
796    protected static final XMLFormat<Vector3D> XML = new XMLFormat<Vector3D>(Vector3D.class) {
797
798        @Override
799        public Vector3D<?> newInstance(Class<Vector3D> cls, InputElement xml) throws XMLStreamException {
800            return FACTORY.object();
801        }
802
803        @Override
804        public void read(InputElement xml, Vector3D V) throws XMLStreamException {
805
806            V._unit = Unit.valueOf(xml.getAttribute("unit"));
807            Float64 x = xml.get("X", Float64.class);
808            Float64 y = xml.get("Y", Float64.class);
809            Float64 z = xml.get("Z", Float64.class);
810            V._data = Float64Vector.valueOf(x.doubleValue(), y.doubleValue(), z.doubleValue());
811
812            if (xml.hasNext())
813                throw new XMLStreamException(RESOURCES.getString("toManyXMLElementsErr"));
814        }
815
816        @Override
817        public void write(Vector3D V, OutputElement xml) throws XMLStreamException {
818
819            xml.setAttribute("unit", V._unit.toString());
820            xml.add(V._data.get(X), "X", Float64.class);
821            xml.add(V._data.get(Y), "Y", Float64.class);
822            xml.add(V._data.get(Z), "Z", Float64.class);
823
824        }
825    };
826
827    /**
828     * Tests the methods in this class.
829     *
830     * @param args Command line arguments (ignored).
831     */
832    public static void main(String args[]) {
833        System.out.println("Testing Vector3D:  test = result [correct result]");
834
835        Vector3D<Length> v1 = Vector3D.valueOf(1, 2, 3, NonSI.FOOT);
836        System.out.println("v1 = " + v1);
837        System.out.println("  converted to m       = " + v1.to(SI.METER) + " [{0.3048 m, 0.6096 m, 0.9144 m}]");
838        System.out.println("  v1.norm()            = " + v1.norm() + " [3.74165738677394 ft]");
839        System.out.println("  v1.opposite()        = " + v1.opposite() + " [{-1.0 ft, -2.0 ft, -3.0 ft}]");
840        System.out.println("  v1.toUnitVector()    = " + v1.toUnitVector() + " [{0.267261241912424 , 0.534522483824849 , 0.801783725737273 }]");
841        System.out.println("  v1.toSkewSymmetric() = \n" + v1.toSkewSymmetric());
842
843        Parameter<Length> point = Parameter.valueOf(24, NonSI.INCH);
844        System.out.println("point = " + point);
845        System.out.println("  v1 + point           = " + v1.plus(point) + " [{3.0 ft, 4.0 ft, 5.0 ft}]");
846        System.out.println("  v1 - point           = " + v1.minus(point) + " [{-1.0 ft, 0.0 ft, 1.0 ft}]");
847        System.out.println("  v1 * 2               = " + v1.times(2) + " [{2.0 ft, 4.0 ft, 6.0 ft}]");
848
849        Vector3D<?> areaVector = (Vector3D<?>)v1.times(point);
850        System.out.println("  v1 * point           = " + areaVector);
851        System.out.println("  converted to ft²     = " + areaVector.to(NonSI.SQUARE_FOOT) + " [{2.0 ft², 4.0 ft², 6.0 ft²}]");
852
853        Vector3D<Length> v2 = Vector3D.valueOf(1, 1, 1, SI.METER);
854        Vector3D<?> v1xv2 = (Vector3D<?>)v1.cross(v2);
855        System.out.println("v2 = " + v2);
856        System.out.println("  v1 + v2 = " + v1.plus(v2) + " [{4.28083989501312 ft, 5.28083989501312 ft, 6.28083989501312 ft}]");
857        System.out.println("  v1 - v2 = " + v1.minus(v2) + " [{-2.28083989501312 ft, -1.28083989501312 ft, -0.280839895013123 ft}]");
858        System.out.println("  v1 · v2 = " + v1.times(v2.to(NonSI.FOOT)) + " [19.6850393700787 ft²]");
859        System.out.println("  v1.cross(v2) = " + v1xv2.to(NonSI.FOOT.pow(2)) + " [{-3.28083989501312 ft^2, 6.56167979002625 ft^2, -3.28083989501312 ft^2}]");
860        System.out.println("  v1.angle(v2) = " + v1.angle(v2).to(NonSI.DEGREE_ANGLE) + " [73.6090476746643 deg]");
861
862        //  Write out XML data.
863        try {
864            System.out.println();
865
866            // Creates some useful aliases for class names.
867            javolution.xml.XMLBinding binding = new javolution.xml.XMLBinding();
868            binding.setAlias(org.jscience.mathematics.number.Float64.class, "Float64");
869            binding.setAlias(org.jscience.mathematics.vector.Float64Matrix.class, "Float64Matrix");
870
871            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
872            writer.setIndentation("    ");
873            writer.setBinding(binding);
874            writer.write(v1, "Vector3D", Vector3D.class);
875            writer.flush();
876
877            System.out.println();
878        } catch (Exception e) {
879            e.printStackTrace();
880        }
881
882    }
883}