001/*
002 *   Polar3D -- A 3D polar coordinate of azimuth & elevation angle and magnitude.
003 *
004 *   Copyright (C) 2008-2015, by Joseph A. Huwaldt.
005 *   All rights reserved.
006 *   
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2 of the License, or (at your option) any later version.
011 *   
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Lesser General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public License
018 *   along with this program; if not, write to the Free Software
019 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package jahuwaldt.js.param;
023
024import jahuwaldt.tools.math.MathTools;
025import java.text.MessageFormat;
026import javax.measure.converter.ConversionException;
027import javax.measure.quantity.*;
028import javax.measure.unit.NonSI;
029import javax.measure.unit.SI;
030import javax.measure.unit.Unit;
031import javolution.context.ObjectFactory;
032import javolution.context.StackContext;
033import static javolution.lang.MathLib.*;
034import javolution.xml.XMLFormat;
035import javolution.xml.XMLSerializable;
036import javolution.xml.stream.XMLStreamException;
037import org.jscience.mathematics.vector.*;
038
039/**
040 * This class represents a 3 element {@link Vector vector} of Parameter elements
041 * representing a geometrical polar coordinate with elements magnitude, azimuth angle and
042 * elevation angle where elevation is measured positive above the reference plane (similar
043 * to latitude).
044 *
045 * <p> Modified by: Joseph A. Huwaldt </p>
046 *
047 * @author Joseph A. Huwaldt, Date: November 21, 2008
048 * @version November 4, 2015
049 *
050 * @param <Q> The Quantity (unit type, such as Length or Volume) of this vector.
051 */
052public final class Polar3D<Q extends Quantity> extends Coordinate3D<Q> implements XMLSerializable {
053    //  Reference:  http://mathworld.wolfram.com/SphericalCoordinates.html
054    
055    private static final long serialVersionUID = -2077967266275152872L;
056
057    /**
058     * Constant used to identify the magnitude of in the vector.
059     */
060    public static final int MAGNITUDE = 0;
061
062    /**
063     * Constant used to identify the azimuth angle of the vector.
064     */
065    public static final int AZIMUTH = 1;
066
067    /**
068     * Constant used to identify the elevation angle of the vector.
069     */
070    public static final int ELEVATION = 2;
071
072    /**
073     * The magnitude of this vector.
074     */
075    private Parameter<Q> _mag;
076
077    /**
078     * The azimuth angle of this vector.
079     */
080    private Parameter<Angle> _azimuth;
081
082    /**
083     * The elevation angle of this vector.
084     */
085    private Parameter<Angle> _elevation;
086
087    /**
088     * Returns a {@link Polar3D} instance holding the specified <code>double</code> values
089     * stated in the specified units.
090     *
091     * @param <Q>       the Quantity (unit type, e.g. Length or Volume) of this vector.
092     * @param magnitude the magnitude (length) of the vector stated in the specified unit.
093     * @param azimuth   the vector azimuth angle stated in the specified angle unit.
094     * @param elevation the vector elevation angle value stated in the specified angle
095     *                  unit.
096     * @param magUnit   the unit in which the magnitude is stated.
097     * @param angleUnit the unit in which the azimuth and elevation angles are stated.
098     * @return the vector having the specified values.
099     */
100    public static <Q extends Quantity> Polar3D<Q> valueOf(double magnitude, double azimuth, double elevation,
101            Unit<Q> magUnit, Unit<Angle> angleUnit) {
102        Polar3D<Q> V = Polar3D.newInstance();
103
104        V._mag = Parameter.valueOf(magnitude, magUnit);
105        V._azimuth = Parameter.valueOf(azimuth, angleUnit);
106        V._elevation = Parameter.valueOf(elevation, angleUnit);
107
108        return V;
109    }
110
111    /**
112     * Returns a {@link Polar3D} instance holding the specified <code>Parameter</code>
113     * values. All the values are converted to the same units as the x value.
114     *
115     * @param <Q>       the Quantity (unit type, e.g. Length or Volume) of this vector.
116     * @param magnitude the vector magnitude (length) value.
117     * @param azimuth   the vector azimuth angle value.
118     * @param elevation the vector elevation angle value.
119     * @return the vector having the specified values in the units of magnitude.
120     */
121    public static <Q extends Quantity> Polar3D<Q> valueOf(Parameter<Q> magnitude, Parameter<Angle> azimuth, Parameter<Angle> elevation) {
122        Polar3D<Q> V = Polar3D.newInstance();
123        V._mag = magnitude;
124        V._azimuth = azimuth;
125        V._elevation = elevation.to(azimuth.getUnit());
126
127        return V;
128    }
129
130    /**
131     * Returns a {@link Polar3D} instance containing the polar coordinate representation
132     * of the specified {@link Coordinate3D coordinate}. The azimuth and elevation
133     * components of the resulting vector have units of radians.
134     * <p>
135     * The polar representation of a Cartesian coordinate is given by:
136     * <pre>
137     *         magnitude = |V|
138     *         azimuth   = atan2(Y,X)
139     *         elevation = 90° - acos(Z/|V|) = latitude
140     * </pre></p>
141     *
142     * @param <Q>        the Quantity (unit type, e.g. Length or Volume) of this vector.
143     * @param coordinate the input coordinate.
144     * @return the polar coordinate having the specified values.
145     */
146    public static <Q extends Quantity> Polar3D<Q> valueOf(Coordinate3D<Q> coordinate) {
147
148        if (coordinate instanceof Polar3D)
149            return (Polar3D)coordinate;
150
151        Vector3D<Q> vector = coordinate.toVector3D();
152        double x = vector.getValue(Vector3D.X);
153        double y = vector.getValue(Vector3D.Y);
154        double z = vector.getValue(Vector3D.Z);
155
156        double mag = sqrt(x * x + y * y + z * z);
157        double azim = atan2(y, x);
158        double phi = acos(z/mag);
159        double elev = HALF_PI - phi;
160
161        Polar3D<Q> V = Polar3D.newInstance();
162        V._mag = Parameter.valueOf(mag, vector.getUnit());
163        V._azimuth = Parameter.valueOf(azim, SI.RADIAN);
164        V._elevation = Parameter.valueOf(elev, SI.RADIAN);
165
166        return V;
167    }
168
169    /**
170     * Returns a {@link Polar3D} instance containing the polar coordinate representation
171     * of the specified {@link Coordinate3D coordinate}.
172     * <p>
173     * The polar representation of a Cartesian coordinate is given by:
174     * <pre>
175     *         magnitude = |V|
176     *         azimuth   = atan2(Y,X)
177     *         elevation = atan2(-Z,sqrt(X^2+Y^2))
178     * </pre></p>
179     *
180     * @param <Q>       the Quantity (unit type, e.g. Length or Volume) of this vector.
181     * @param coord     the coordinate to convert.
182     * @param angleUnit the unit to use for the azimuth and elevation components.
183     * @return the polar coordinate vector having the specified values.
184     */
185    public static <Q extends Quantity> Polar3D<Q> valueOf(Coordinate3D<Q> coord, Unit<Angle> angleUnit) {
186        Polar3D<Q> V = Polar3D.valueOf(coord);
187        V._azimuth = V._azimuth.to(angleUnit);
188        V._elevation = V._elevation.to(angleUnit);
189        return V;
190    }
191
192    /**
193     * Return the specified {@link Vector3D} object as a <code>Polar3D</code> instance.
194     *
195     * @param vector The Vector3D object to be converted to a Polar3D.
196     * @return A Polar3D instance that is equivalent to the supplied Vector3D object.
197     */
198    @Override
199    public Polar3D<Q> fromVector3D(Vector3D<Q> vector) {
200        return Polar3D.valueOf(vector, _azimuth.getUnit());
201    }
202
203    /**
204     * Returns the value of a Parameter from this vector. The dimensions are defined as:
205     * magnitude, azimuth angle, and elevation angle in that order.
206     *
207     * @param i the dimension index (0=magnitude, 1=azimuth, 2=elevation).
208     * @return the value of the parameter at <code>i</code>.
209     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i > dimension()-1)</code>
210     */
211    @Override
212    public Parameter get(int i) {
213        Parameter value = null;
214        switch (i) {
215            case 0:
216                value = _mag;
217                break;
218            case 1:
219                value = _azimuth;
220                break;
221            case 2:
222                value = _elevation;
223                break;
224            default:
225                throw new IndexOutOfBoundsException(
226                        MessageFormat.format(RESOURCES.getString("p3dIndexErr"), i));
227        }
228        return value;
229    }
230
231    /**
232     * Returns the magnitude of this vector as a <code>double</code>, stated in this
233     * vector's {@link #getUnit() unit}.
234     *
235     * @return the magnitude of this vector in this vector's units.
236     */
237    public double getMagnitudeValue() {
238        return _mag.getValue();
239    }
240
241    /**
242     * Returns the magnitude of this vector as a {@link Parameter}.
243     *
244     * @return the magnitude of this vector.
245     */
246    public Parameter<Q> getMagnitude() {
247        return _mag;
248    }
249
250    /**
251     * Returns the azimuth angle of this vector as a {@link Parameter}.
252     *
253     * @return the azimuth angle of this vector.
254     */
255    public Parameter<Angle> getAzimuth() {
256        return _azimuth;
257    }
258
259    /**
260     * Returns the elevation angle of this vector as a {@link Parameter}.
261     *
262     * @return the elevation angle of this vector.
263     */
264    public Parameter<Angle> getElevation() {
265        return _elevation;
266    }
267
268    /**
269     * Returns the Euclidian norm, magnitude, or length of this vector (square root of the
270     * dot product of this vector and itself).
271     *
272     * @return <code>sqrt(this · this)</code>.
273     */
274    @Override
275    public Parameter<Q> norm() {
276        return _mag;
277    }
278
279    /**
280     * Returns the {@link #norm() norm}, magnitude, or length value of this vector.
281     *
282     * @return <code>this.norm().getValue()</code>.
283     */
284    @Override
285    public double normValue() {
286        return _mag.getValue();
287    }
288
289    /**
290     * Returns the negation of this vector.
291     *
292     * @return <code>-this</code>.
293     */
294    @Override
295    public Polar3D<Q> opposite() {
296
297        //  Convert to cartesian coordinates, negate that, and then convert back to polar.
298        Vector3D<Q> V3D = toVector3D().opposite();
299        Polar3D<Q> V = fromVector3D(V3D);
300
301        return V;
302    }
303
304    /**
305     * Returns the sum of this vector with the one specified. The unit of the output
306     * vector will be the units of this vector.
307     *
308     * @param that the vector to be added.
309     * @return <code>this + that</code>.
310     * @throws DimensionException if vector dimensions are different.
311     */
312    @Override
313    public Polar3D<Q> plus(Vector<Parameter<Q>> that) {
314
315        //  Convert to Cartesian coordinates, add those, then convert back.
316        if (!(that instanceof Coordinate3D))
317            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
318
319        StackContext.enter();
320        try {
321            Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();
322            Vector3D<Q> V3D = toVector3D().plus(T);
323            Polar3D<Q> V = fromVector3D(V3D);
324
325            return StackContext.outerCopy(V);
326        } finally {
327            StackContext.exit();
328        }
329    }
330
331    /**
332     * Returns the sum of this vector with the parameter specified. The input parameter is
333     * added to the magnitude of this vector; the direction is left unchanged. The unit of
334     * the output vector will be the units of this vector.
335     *
336     * @param that the parameter to be added to the magnitude of this vector.
337     * @return <code>this + that</code>.
338     */
339    @Override
340    public Polar3D<Q> plus(Parameter<Q> that) {
341
342        StackContext.enter();
343        try {
344            Polar3D<Q> V = Polar3D.newInstance();
345            Parameter<Q> mag = _mag.plus(that);
346            V._mag = mag;
347            V._azimuth = _azimuth;
348            V._elevation = _elevation;
349            if (mag.getValue() < 0) {
350                V._mag = mag.opposite();
351                V = V.opposite();
352            }
353
354            return StackContext.outerCopy(V);
355        } finally {
356            StackContext.exit();
357        }
358    }
359
360    /**
361     * Returns the difference between this vector and the one specified.
362     *
363     * @param that the vector to be subtracted.
364     * @return <code>this - that</code>.
365     */
366    @Override
367    public Polar3D<Q> minus(Vector<Parameter<Q>> that) {
368
369        //  Convert to Cartesian coordinates, subtract those, then convert back.
370        if (!(that instanceof Coordinate3D))
371            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
372
373        StackContext.enter();
374        try {
375            Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();
376            Vector3D<Q> V3D = toVector3D().minus(T);
377            Polar3D<Q> V = fromVector3D(V3D);
378
379            return StackContext.outerCopy(V);
380        } finally {
381            StackContext.exit();
382        }
383    }
384
385    /**
386     * Subtracts the supplied Parameter from this vector's magnitude and returns the
387     * result. The direction of the vector is left unchanged. The unit of the output
388     * vector will be the units of this vector.
389     *
390     * @param that the Parameter to be subtracted from the magnitude of this vector.
391     * @return <code>this - that</code>.
392     */
393    @Override
394    public Polar3D<Q> minus(Parameter<Q> that) {
395
396        StackContext.enter();
397        try {
398            Polar3D<Q> V = Polar3D.newInstance();
399            Parameter<Q> mag = _mag.minus(that);
400            V._mag = mag;
401            V._azimuth = _azimuth;
402            V._elevation = _elevation;
403            if (mag.getValue() < 0) {
404                V._mag = mag.opposite();
405                V = V.opposite();
406            }
407
408            return StackContext.outerCopy(V);
409        } finally {
410            StackContext.exit();
411        }
412    }
413
414    /**
415     * Returns the product of this vector with the specified coefficient. The magnitude of
416     * this vector is scaled by the input coefficient and the direction is left unchanged.
417     *
418     * @param k the coefficient multiplier.
419     * @return <code>this · k</code>
420     */
421    @Override
422    public Polar3D times(Parameter k) {
423        Polar3D V = Polar3D.newInstance();
424        V._mag = _mag.times(k);
425        V._azimuth = _azimuth;
426        V._elevation = _elevation;
427        return V;
428    }
429
430    /**
431     * Returns the product of this vector with the specified coefficient. The magnitude of
432     * this vector is scaled by the input coefficient and the direction is left unchanged.
433     *
434     * @param k the coefficient multiplier.
435     * @return <code>this · k</code>
436     */
437    @Override
438    public Polar3D<Q> times(double k) {
439        Polar3D<Q> V = Polar3D.newInstance();
440        V._mag = _mag.times(k);
441        V._azimuth = _azimuth;
442        V._elevation = _elevation;
443        return V;
444    }
445
446    /**
447     * Returns the dot product of this vector with the one specified.
448     *
449     * @param that the vector multiplier.
450     * @return <code>this · that</code>
451     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
452     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
453     * Wikipedia: Dot Product</a>
454     */
455    @Override
456    public Parameter times(Vector that) {
457
458        //  Convert to Cartesian coordinates and multiply that.
459        if (!(that instanceof Coordinate3D))
460            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
461
462        StackContext.enter();
463        try {
464            Vector3D T = ((Coordinate3D)that).toVector3D();
465            Parameter product = toVector3D().times(T);
466
467            return StackContext.outerCopy(product);
468        } finally {
469            StackContext.exit();
470        }
471    }
472
473    /**
474     * Returns the result of this vector divided by the specified divisor. The magnitude
475     * of this vector is divided by the input coefficient and the direction is left
476     * unchanged.
477     *
478     * @param that the divisor.
479     * @return <code>this / that</code>.
480     */
481    public Polar3D<?> divide(Parameter<?> that) {
482        return (Polar3D<?>)times(that.inverse());
483    }
484
485    /**
486     * Returns the cross product of two 3-dimensional vectors.
487     *
488     * @param that the vector multiplier.
489     * @return <code>this x that</code>
490     * @throws DimensionException if <code>(that.getDimension() != 3)</code>
491     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">
492     * Wikipedia: Cross Product</a>
493     */
494    @Override
495    public Polar3D cross(Vector that) {
496
497        //  Convert to Cartesian coordinates, multiply those, then convert back.
498        if (!(that instanceof Coordinate3D))
499            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
500
501        StackContext.enter();
502        try {
503            Vector3D<?> T = ((Coordinate3D<?>)that).toVector3D();
504            Vector3D<?> V3D = toVector3D().cross(T);
505            Polar3D<?> V = Polar3D.valueOf(V3D, _azimuth.getUnit());
506
507            return StackContext.outerCopy(V);
508        } finally {
509            StackContext.exit();
510        }
511    }
512
513    /**
514     * Returns this vector converted to a unit vector with a vector magnitude of 1.0.
515     *
516     * @return this vector converted to a unit vector
517     */
518    public Polar3D<Dimensionless> toUnitVector() {
519        double magnitude = this.normValue();
520        if (this.getUnit().equals(Dimensionless.UNIT) && MathTools.isApproxEqual(magnitude, 1.0))
521            return (Polar3D<Dimensionless>)this;
522
523        Polar3D<Dimensionless> V = Polar3D.newInstance();
524        V._azimuth = this._azimuth;
525        V._elevation = this._elevation;
526        V._mag = Parameter.ONE;
527        
528        return V;
529    }
530
531    /**
532     * Returns a copy of this vector {@link javolution.context.AllocatorContext allocated}
533     * by the calling thread (possibly on the stack).
534     *
535     * @return an identical and independent copy of this vector.
536     */
537    @Override
538    public Polar3D<Q> copy() {
539        return copyOf(this);
540    }
541
542    /**
543     * Returns the unit in which the magnitude in this vector are stated in.
544     *
545     * @return the unit in which the magnitude in this vector are stated in
546     */
547    @Override
548    public Unit<Q> getUnit() {
549        return _mag.getUnit();
550    }
551
552    /**
553     * Returns the unit in which the angles in this vector are stated in.
554     *
555     * @return the unit in which the angles in this vector are stated in
556     */
557    public Unit<Angle> getAnglesUnit() {
558        return _azimuth.getUnit();
559    }
560
561    /**
562     * Returns the equivalent to this vector but with the magnitude stated in the
563     * specified unit.
564     *
565     * @param <R>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
566     * @param unit the unit of the magnitude of the vector to be returned.
567     * @return a vector equivalent to this vector but with the magnitude stated in the
568     *         specified unit.
569     * @throws ConversionException if the current model does not allows for conversion to
570     * the specified unit.
571     */
572    @Override
573    public <R extends Quantity> Polar3D<R> to(Unit<R> unit) throws ConversionException {
574        Unit<?> thisUnit = getUnit();
575        if ((thisUnit == unit) || thisUnit.equals(unit))
576            return (Polar3D<R>)this;
577        Polar3D<R> result = Polar3D.newInstance();
578        result._mag = _mag.to(unit);
579        result._azimuth = _azimuth;
580        result._elevation = _elevation;
581        return result;
582    }
583
584    /**
585     * Returns the equivalent to this vector but with the angles stated in the specified
586     * unit.
587     *
588     * @param unit the angle unit of the angles of the vector to be returned.
589     * @return a coordinate equivalent to this vector but with the angles stated in the
590     *         specified unit.
591     * @throws ConversionException if the current model does not allows for conversion to
592     * the specified unit.
593     */
594    public Polar3D<Q> anglesTo(Unit<Angle> unit) {
595        Unit<Angle> thisUnit = _azimuth.getUnit();
596        if ((thisUnit == unit) || thisUnit.equals(unit))
597            return this;
598        Polar3D<Q> result = Polar3D.newInstance();
599        result._mag = _mag;
600        result._azimuth = _azimuth.to(unit);
601        result._elevation = _elevation.to(unit);
602        return result;
603    }
604
605    /**
606     * Casts this Polar3D to a parameterized unit of specified nature or throw a
607     * <code>ClassCastException</code> if the dimension of the specified quantity and this
608     * parameter's unit dimension do not match.
609     *
610     * @param <T>  the Quantity (unit type, e.g. Length or Volume) of the returned vector.
611     * @param type the quantity class identifying the nature of the unit.
612     * @return this Polar3D parameterized with the specified type.
613     * @throws ClassCastException if the dimension of this parameter's unit is different
614     * from the specified quantity dimension.
615     * @throws UnsupportedOperationException if the specified quantity class does not have
616     * a public static field named "UNIT" holding the standard unit for the quantity.
617     */
618    @Override
619    public <T extends Quantity> Polar3D<T> asType(Class<T> type) throws ClassCastException {
620        Unit<T> u = getUnit().asType(type); //  If no exception is thrown, the cast is valid.
621        return (Polar3D<T>)this;
622    }
623
624    /**
625     * Returns a Cartesian Vector3D representation of this vector.
626     * <p>
627     * The polar to Cartesian transformation is defined by:
628     * <pre>
629     *    |x|             | cos(elevation)*cos(azimuth)  |
630     *    |y| = magnitude*| cos(elevation)*sin(azimuth)  |
631     *    |z|             |     -sin(elevation)          |
632     * </pre></p>
633     *
634     * @return a Cartesian Vector3D representation of this vector
635     */
636    @Override
637    public Vector3D<Q> toVector3D() {
638
639        double mag = _mag.getValue();
640        double azim = _azimuth.to(SI.RADIAN).getValue();
641        double elev = _elevation.to(SI.RADIAN).getValue();
642        //double phi = HALF_PI - elev;
643
644        double sPhi = cos(elev), cPhi = sin(elev);
645        double x = mag * sPhi * cos(azim);
646        double y = mag * sPhi * sin(azim);
647        double z = -mag * cPhi;
648
649        Vector3D<Q> V = Vector3D.valueOf(x, y, z, getUnit());
650
651        return V;
652    }
653
654    /**
655     * Compares this Polar3D against the specified object for strict equality (same values
656     * and same units).
657     *
658     * @param obj the object to compare with.
659     * @return <code>true</code> if this vector is identical to that vector;
660     *         <code>false</code> otherwise.
661     */
662    @Override
663    public boolean equals(Object obj) {
664        if (this == obj)
665            return true;
666        if ((obj == null) || (obj.getClass() != this.getClass()))
667            return false;
668
669        Polar3D<?> that = (Polar3D<?>)obj;
670        if (!this._mag.equals(that._mag))
671            return false;
672        if (!this._azimuth.equals(that._azimuth))
673            return false;
674
675        return this._elevation.equals(that._elevation);
676    }
677
678    /**
679     * Returns the hash code for this parameter.
680     *
681     * @return the hash code value.
682     */
683    @Override
684    public int hashCode() {
685        int hash = 7;
686
687        int var_code = _mag.hashCode();
688        hash = hash * 31 + var_code;
689
690        var_code = _azimuth.hashCode();
691        hash = hash * 31 + var_code;
692
693        var_code = _elevation.hashCode();
694        hash = hash * 31 + var_code;
695
696        return hash;
697    }
698
699    /**
700     * Holds the default XML representation. For example:
701     * <pre>
702     *    &lt;Magnitude value="1.73205080756888" unit="m"/&gt;
703     *    &lt;Azimuth value="45.0" unit="°" /&gt;
704     *    &lt;Elevation value="35.2643896827547" unit="°" /&gt;
705     * </pre>
706     */
707    protected static final XMLFormat<Polar3D> XML = new XMLFormat<Polar3D>(Polar3D.class) {
708
709        @Override
710        public Polar3D<?> newInstance(Class<Polar3D> cls, InputElement xml) throws XMLStreamException {
711            return FACTORY.object();
712        }
713
714        @Override
715        public void read(InputElement xml, Polar3D V) throws XMLStreamException {
716            V._mag = xml.get("Magnitude", Parameter.class);
717            V._azimuth = xml.get("Azimuth", Parameter.class);
718            V._elevation = xml.get("Elevation", Parameter.class);
719        }
720
721        @Override
722        public void write(Polar3D V, OutputElement xml) throws XMLStreamException {
723            xml.add(V._mag, "Magnitude", Parameter.class);
724            xml.add(V._azimuth, "Azimuth", Parameter.class);
725            xml.add(V._elevation, "Elevation", Parameter.class);
726        }
727    };
728
729    ///////////////////////
730    // Factory creation. //
731    ///////////////////////
732    private Polar3D() {
733    }
734
735    private static final ObjectFactory<Polar3D<? extends Quantity>> FACTORY = new ObjectFactory<Polar3D<? extends Quantity>>() {
736        @Override
737        protected Polar3D<? extends Quantity> create() {
738            return new Polar3D();
739        }
740    };
741
742    private static <Q extends Quantity> Polar3D<Q> newInstance() {
743        Polar3D<Q> measure = (Polar3D<Q>)FACTORY.object();
744        return measure;
745    }
746
747    private static <Q extends Quantity> Polar3D<Q> copyOf(Polar3D<Q> original) {
748        Polar3D<Q> measure = Polar3D.newInstance();
749        measure._mag = original._mag.copy();
750        measure._azimuth = original._azimuth.copy();
751        measure._elevation = original._elevation.copy();
752        return measure;
753    }
754
755    /**
756     * Tests the methods in this class.
757     *
758     * @param args Command line arguments (ignored)
759     */
760    public static void main(String args[]) {
761        System.out.println("Testing Polar3D:  test = result [correct result]");
762
763        Polar3D<Length> v1 = Polar3D.valueOf(1, 20, 30, NonSI.FOOT, NonSI.DEGREE_ANGLE);
764        System.out.println("v1 = " + v1);
765        System.out.println("v1 (as Vector3D) = " + v1.toVector3D());
766        System.out.println("  converted to m       = " + v1.to(SI.METER) + " [{0.3048 m, 20.0 °, 30.0 °}]");
767        System.out.println("  v1.norm()            = " + v1.norm() + " [1.0 ft]");
768        System.out.println("  v1.opposite()        = " + v1.opposite() + " [{1.0 ft, 200.0 °, 330.0 °}]");
769
770        Parameter<Length> point = Parameter.valueOf(24, NonSI.INCH);
771        System.out.println("point = " + point);
772        System.out.println("  v1 + point           = " + v1.plus(point) + " [{3.0 ft, 20.0 °, 30.0 °}]");
773        System.out.println("  v1 - point           = " + v1.minus(point) + " [{1.0 ft, 200.0 °, 330.0 °}]");
774        System.out.println("  v1 * 2               = " + v1.times(2) + " [{2.0 ft, 20.0 °, 30.0 °}]");
775
776        Polar3D<?> areaVector = (Polar3D<?>)v1.times(point);
777        System.out.println("  v1 * point           = " + areaVector);
778        System.out.println("  converted to ft²     = " + areaVector.to(NonSI.SQUARE_FOOT) + " [{2.0 ft², 20.0 °, 30.0 °}]");
779
780        Vector3D<Length> v2 = Vector3D.valueOf(1, 1, -1, SI.METER);
781        System.out.println("v2 = " + v2);
782        System.out.println("v2 (as Polar3D)= " + Polar3D.valueOf(v2, NonSI.DEGREE_ANGLE) + " [{1.73205080756888 m, 45.0 °, 35.2643896827547 °}]");
783        System.out.println("  v1 + v2 = " + v1.plus(v2) + " [{6.62238689940225 ft, 41.1401661546401 °, 34.8142656403426 °}]");
784        System.out.println("  v1 - v2 = " + v1.minus(v2) + " [{4.76733198496641 ft, 230.423579133255 °, 324.316200627242 °}]");
785        System.out.println("  v1 · v2 = " + v1.times(v2.to(NonSI.FOOT)) + " [5.2821384976227 ft²]");
786        System.out.println("  v1.cross(v2) = " + v1.cross(v2) + " [{2.09541025626521 ft, 56.9975252490327 °, 305.863065923747 °}]");
787        System.out.println("  v1.angle(v2) = " + v1.angle(Polar3D.valueOf(v2)).to(NonSI.DEGREE_ANGLE) + " [21.638095609098805 deg]");
788
789        //  Write out XML data.
790        try {
791            System.out.println();
792
793            // Creates some useful aliases for class names.
794            javolution.xml.XMLBinding binding = new javolution.xml.XMLBinding();
795            binding.setAlias(org.jscience.mathematics.number.Float64.class, "Float64");
796            binding.setAlias(org.jscience.mathematics.vector.Float64Matrix.class, "Float64Matrix");
797
798            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
799            writer.setIndentation("    ");
800            writer.setBinding(binding);
801            writer.write(v1, "Polar3D", Polar3D.class);
802            writer.flush();
803
804            System.out.println();
805        } catch (Exception e) {
806            e.printStackTrace();
807        }
808
809    }
810}