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