001/**
002 * Point -- Holds the floating point coordinates of a point in nD space.
003 *
004 * Copyright (C) 2002-2017, Joseph A. Huwaldt. All rights reserved.
005 *
006 * This library is free software; you can redistribute it and/or modify it under the terms
007 * of the GNU Lesser General Public License as published by the Free Software Foundation;
008 * either version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
012 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License along with
015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
017 */
018package geomss.geom;
019
020import jahuwaldt.js.param.Coordinate3D;
021import jahuwaldt.js.param.Parameter;
022import java.text.MessageFormat;
023import java.util.List;
024import static java.util.Objects.requireNonNull;
025import javax.measure.converter.ConversionException;
026import javax.measure.converter.UnitConverter;
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.ArrayFactory;
034import javolution.context.ObjectFactory;
035import javolution.lang.ValueType;
036import javolution.util.FastTable;
037import javolution.xml.XMLFormat;
038import javolution.xml.stream.XMLStreamException;
039import org.jscience.mathematics.number.Float64;
040import org.jscience.mathematics.vector.Float64Vector;
041
042/**
043 * A container that holds the coordinates of a point in n-dimensional space.
044 * 
045 * <p> Modified by: Joseph A. Huwaldt </p>
046 * 
047 * @author Joseph A. Huwaldt, Date: December 11, 1999
048 * @version October 14, 2017
049 */
050@SuppressWarnings({"serial", "CloneableImplementsClone"})
051public final class Point extends GeomPoint implements ValueType {
052
053    /**
054     * The coordinate values are stored in this array.
055     */
056    private transient double[] _data;
057
058    /**
059     * The number of dimensions in the data.
060     */
061    private int _numDims;
062
063    /**
064     * Holds the unit of the coordinates.
065     */
066    private Unit<Length> _unit;
067
068    /**
069     * Returns a {@link Point} instance of the specified dimension with zero meters for
070     * each coordinate value.
071     *
072     * @param dim the physical dimension of the point to create.
073     * @return the point having the specified dimension and zero meters for values.
074     */
075    public static Point newInstance(int dim) {
076        if (dim < 1)
077            throw new IllegalArgumentException(RESOURCES.getString("pointDimGT0"));
078        Point V = Point.newInstancePvt(dim, SI.METER);
079
080        for (int i = 0; i < dim; ++i)
081            V._data[i] = 0;
082
083        return V;
084    }
085
086    /**
087     * Returns a {@link Point} instance of the specified dimension and units with zero for
088     * each coordinate value.
089     *
090     * @param dim  the physical dimension of the point to create.
091     * @param unit The unit for the point to create. May not be null.
092     * @return the point having the specified dimension & units and zero for values.
093     */
094    public static Point newInstance(int dim, Unit<Length> unit) {
095        if (dim < 1)
096            throw new IllegalArgumentException(RESOURCES.getString("pointDimGT0"));
097        Point V = Point.newInstancePvt(dim, requireNonNull(unit));
098
099        for (int i = 0; i < dim; ++i)
100            V._data[i] = 0;
101
102        return V;
103    }
104
105    /**
106     * Returns a {@link Point} instance holding the specified <code>double</code> value or
107     * values stated in meters.
108     *
109     * @param x the coordinate values stated in meters. May not be null.
110     * @return the point having the specified value.
111     */
112    public static Point valueOf(double... x) {
113        return valueOf(SI.METER, x);
114    }
115
116    /**
117     * Returns a 1D {@link Point} instance holding the specified <code>double</code>
118     * values stated in the specified units.
119     *
120     * @param x    the x value stated in the specified unit.
121     * @param unit the unit in which the coordinates are stated. May not be null.
122     * @return the point having the specified value.
123     */
124    public static Point valueOf(double x, Unit<Length> unit) {
125        Point V = Point.newInstancePvt(1, unit);
126
127        V._data[0] = x;
128
129        return V;
130    }
131
132    /**
133     * Returns a 2D {@link Point} instance holding the specified <code>double</code> value
134     * stated in the specified units.
135     *
136     * @param x    the x value stated in the specified unit.
137     * @param y    the y value stated in the specified unit.
138     * @param unit the unit in which the coordinates are stated. May not be null.
139     * @return the point having the specified values.
140     */
141    public static Point valueOf(double x, double y, Unit<Length> unit) {
142        Point V = Point.newInstancePvt(2, unit);
143
144        V._data[0] = x;
145        V._data[1] = y;
146
147        return V;
148    }
149
150    /**
151     * Returns a 3D {@link Point} instance holding the specified <code>double</code>
152     * values stated in the specified units.
153     *
154     * @param x    the x value stated in the specified unit.
155     * @param y    the y value stated in the specified unit.
156     * @param z    the z value stated in the specified unit.
157     * @param unit the unit in which the coordinates are stated. May not be null.
158     * @return the point having the specified values.
159     */
160    public static Point valueOf(double x, double y, double z, Unit<Length> unit) {
161        Point V = Point.newInstancePvt(3, unit);
162
163        V._data[0] = x;
164        V._data[1] = y;
165        V._data[2] = z;
166
167        return V;
168    }
169
170    /**
171     * Returns a {@link Point} instance holding the specified <code>double</code> values
172     * stated in the specified units.
173     *
174     * @param unit   the length unit in which the coordinates are stated. May not be null.
175     * @param values the list of values stated in the specified unit. May not be null.
176     * @return the point having the specified values.
177     */
178    public static Point valueOf(Unit<Length> unit, double... values) {
179        int dim = values.length;
180        Point V = Point.newInstancePvt(dim, unit);
181
182        for (int i = 0; i < dim; ++i)
183            V._data[i] = values[i];
184
185        return V;
186    }
187
188    /**
189     * Returns a {@link Point} instance holding the specified <code>Parameter</code>
190     * values. All the values are converted to the same units as the 1st value.
191     *
192     * @param values The list of values to be stored.  May not be null.
193     * @return the point having the specified values in the units of the 1st value.
194     */
195    public static Point valueOf(Parameter<Length>... values) {
196        Unit<Length> unit = values[0].getUnit();
197        int size = values.length;
198
199        Point V = Point.newInstancePvt(size, unit);
200
201        for (int i = 0; i < size; ++i) {
202            Parameter<Length> param = values[i];
203            V._data[i] = param.getValue(unit);
204        }
205
206        return V;
207    }
208
209    /**
210     * Returns a {@link Point} instance holding the specified <code>Parameter</code>
211     * values. All the values are converted to the same units as the 1st value.
212     *
213     * @param values The list of values to be stored. May not be null and must have at
214     *               least one element..
215     * @return the point having the specified values in the units of the 1st value.
216     */
217    public static Point valueOf(List<Parameter<Length>> values) {
218        Unit<Length> unit = values.get(0).getUnit();
219        int size = values.size();
220
221        Point V = Point.newInstancePvt(size, unit);
222
223        for (int i = 0; i < size; ++i) {
224            Parameter<Length> param = values.get(i);
225            V._data[i] = param.getValue(unit);
226        }
227
228        return V;
229    }
230
231    /**
232     * Returns a {@link Point} instance holding the specified
233     * <code>@link jahuwaldt.js.param.Coordinate3D Coordinate3D</code> values.
234     *
235     * @param coord The {@link jahuwaldt.js.param.Coordinate3D Coordinate3D} to be stored.
236     *              May not be null.
237     * @return the point having the specified values.
238     */
239    public static Point valueOf(Coordinate3D<Length> coord) {
240        int dim = coord.getDimension();
241
242        Point P = Point.newInstancePvt(dim, coord.getUnit());
243        for (int i = 0; i < dim; ++i)
244            P._data[i] = coord.getValue(i);
245
246        return P;
247    }
248
249    /**
250     * Returns a {@link Point} instance containing the specified vector of Float64 values
251     * stated in the specified units.
252     *
253     * @param vector the vector of Float64 values stated in the specified unit. May not be
254     *               null.
255     * @param unit   the unit in which the values are stated. May not be null.
256     * @return the point having the specified values.
257     */
258    public static Point valueOf(org.jscience.mathematics.vector.Vector<Float64> vector, Unit<Length> unit) {
259        int dim = vector.getDimension();
260
261        Point V = Point.newInstancePvt(dim, unit);
262        for (int i = 0; i < dim; ++i)
263            V._data[i] = vector.get(i).doubleValue();
264
265        return V;
266    }
267
268    /**
269     * Returns a {@link Point} instance containing the specified vector of Parameter
270     * values with length units. All the values are converted to the same units as the 1st
271     * value.
272     *
273     * @param <Q>    The Quantity (type of unit) for this Point data. Must be "Length" or
274     *               "Dimensionless".
275     * @param vector The vector of Parameter values stated in length or dimensionless
276     *               units. If in dimensionless units, the values are interpreted as being
277     *               in meters. May not be null.
278     * @return the point having the specified values.
279     * @throws ConversionException if the input vector is not in Length (or Dimensionless)
280     * units.
281     */
282    public static <Q extends Quantity> Point valueOf(org.jscience.mathematics.vector.Vector<Parameter<Q>> vector) {
283
284        Unit unit = vector.get(0).getUnit();
285        if (unit != Dimensionless.UNIT && !Length.UNIT.isCompatible(unit))
286            throw new ConversionException(
287                    MessageFormat.format(RESOURCES.getString("incompatibleUnits"),
288                            "input vector", "length"));
289
290        int size = vector.getDimension();
291        Point P = Point.newInstancePvt(size, (unit == Dimensionless.UNIT ? SI.METER : unit));
292        for (int i = 0; i < size; ++i) {
293            Parameter param = vector.get(i);
294            P._data[i] = param.getValue(unit);
295        }
296
297        return P;
298    }
299
300    /**
301     * Returns a {@link Point} instance that represents the direction elements of the
302     * specified GeomVector given in length or dimensionless units. If the user wishes for
303     * the point to represent the end or tip of the vehicle they should use the following:
304     * <code>Point p = Point.valueOf(vector).plus(vector.getOrigin());</code>
305     *
306     * @param vector the GeomVector stated in length or dimensionless units. If in
307     *               dimensionless units, the values are interpreted as being in meters.
308     *               May not be null.
309     * @return the point having the elements of the vector.
310     * @throws ConversionException if the input vector is not in length (or Dimensionless)
311     * units.
312     */
313    public static Point valueOf(GeomVector<?> vector) {
314
315        Unit unit = vector.getUnit();
316        if (unit != Dimensionless.UNIT && !Length.UNIT.isCompatible(unit))
317            throw new ConversionException(
318                    MessageFormat.format(RESOURCES.getString("incompatibleUnits"),
319                            "input vector", "length"));
320
321        int size = vector.getPhyDimension();
322        Point P = Point.newInstancePvt(size, (unit == Dimensionless.UNIT ? SI.METER : unit));
323        for (int i = 0; i < size; ++i)
324            P._data[i] = vector.getValue(i);
325
326        return P;
327    }
328
329    /**
330     * Returns a {@link Point} instance containing the specified GeomPoint's data.
331     *
332     * @param point the GeomPoint to be copied into a new Point. May not be null.
333     * @return the point having the specified values.
334     */
335    public static Point valueOf(GeomPoint point) {
336        if (point instanceof Point)
337            return (Point)point;
338
339        int dim = point.getPhyDimension();
340
341        Point P = Point.newInstancePvt(dim, point.getUnit());
342        for (int i = 0; i < dim; ++i)
343            P._data[i] = point.getValue(i);
344
345        return P;
346    }
347
348    /**
349     * Returns a {@link Point} instance containing the specified Point's data.
350     *
351     * @param point the Point to be copied into a new Point. May not be null.
352     * @return the point having the specified values.
353     */
354    public static Point valueOf(Point point) {
355        return copyOf(point);
356    }
357
358    /**
359     * Return an immutable version of this point. This implementation simply returns this
360     * Point instance.
361     *
362     * @return A reference to this Point (which is immutable).
363     */
364    @Override
365    public Point immutable() {
366        return this;
367    }
368
369    /**
370     * Return a mutable copy of this point.
371     *
372     * @return A mutable copy of this Point object.
373     */
374    public MutablePoint mutable() {
375        return MutablePoint.valueOf(this);
376    }
377
378    /**
379     * Returns the number of physical dimensions of the geometry element.
380     *
381     * @return The number of physical dimensions of the geometry element.
382     */
383    @Override
384    public int getPhyDimension() {
385        return _numDims;
386    }
387
388    /**
389     * Returns the value of a coordinate in this point as a <code>double</code>, stated in
390     * this point's {@link #getUnit unit}.
391     *
392     * @param i the dimension index.
393     * @return the value of the Parameter at <code>i</code>.
394     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; dimension())</code>
395     */
396    @Override
397    public double getValue(int i) {
398        return _data[i];
399    }
400
401    /**
402     * Returns the value of a coordinate in this point as a <code>double</code>, stated in
403     * the specified unit.
404     *
405     * @param i    the dimension index.
406     * @param unit the unit to return the value in. May not be null.
407     * @return the value of the Parameter at <code>i</code> in the specified unit.
408     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; dimension())</code>
409     */
410    @Override
411    public double getValue(int i, Unit<Length> unit) {
412        double value = _data[i];
413        if (_unit == unit || unit.equals(_unit))
414            return value;
415        UnitConverter cvtr = Parameter.converterOf(_unit, unit);
416        if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary.
417            return value;
418        }
419        return cvtr.convert(value);
420    }
421
422    /**
423     * Returns the square of the Euclidean norm, magnitude, or length value of the vector
424     * from the origin to this point (the dot product of the origin-to-this-point vector
425     * and itself). This is slightly faster than calling <code>normValue</code> if the
426     * squared value is all that is needed.
427     *
428     * @return <code>this.normSq().getValue()</code>.
429     * @see #normValue() 
430     */
431    @Override
432    public double normSqValue() {
433        double[] data = _data;
434        double s2 = data[0];
435        s2 *= s2;
436        for (int i = 1; i < _numDims; ++i) {
437            double vi = data[i];
438            s2 += vi * vi;
439        }
440        return s2;
441    }
442
443    /**
444     * Returns the sum of this point with the one specified. The unit of the output point
445     * will be the units of this point.
446     *
447     * @param that the point to be added. May not be null.
448     * @return <code>this + that</code>.
449     * @throws DimensionException if point dimensions are different.
450     */
451    @Override
452    public Point plus(GeomPoint that) {
453        int numDims = _numDims;
454        if (that.getPhyDimension() != numDims)
455            throw new DimensionException(RESOURCES.getString("dimensionErr"));
456        Unit<Length> unit = _unit;
457
458        Point point = Point.newInstancePvt(numDims, unit);
459        double[] thisData = this._data;
460        for (int i = 0; i < numDims; ++i)
461            point._data[i] = thisData[i] + that.getValue(i, unit);
462
463        return point;
464    }
465
466    /**
467     * Adds the specified parameter to each component of this point. The unit of the
468     * output point will be the units of this point.
469     *
470     * @param that the parameter to be added to each component of this point. May not be
471     *             null.
472     * @return <code>this + that</code>.
473     */
474    @Override
475    public Point plus(Parameter<Length> that) {
476        int numDims = _numDims;
477        Unit<Length> unit = _unit;
478
479        //  Convert input parameter to the units of this point.
480        double thatValue = that.getValue(unit);
481
482        Point point = Point.newInstancePvt(numDims, unit);
483        double[] thisData = this._data;
484        for (int i = 0; i < numDims; ++i)
485            point._data[i] = thisData[i] + thatValue;
486
487        return point;
488    }
489
490    /**
491     * Returns the difference between this point and the one specified. The unit of the
492     * output point will be the units of this point.
493     *
494     * @param that the point to be subtracted from this point. May not be null.
495     * @return <code>this - that</code>.
496     * @throws DimensionException if point dimensions are different.
497     */
498    @Override
499    public Point minus(GeomPoint that) {
500        int numDims = _numDims;
501        if (that.getPhyDimension() != numDims)
502            throw new DimensionException(RESOURCES.getString("dimensionErr"));
503        Unit<Length> unit = _unit;
504
505        Point point = Point.newInstancePvt(numDims, unit);
506        double[] thisData = this._data;
507        for (int i = 0; i < numDims; ++i)
508            point._data[i] = thisData[i] - that.getValue(i, unit);
509
510        return point;
511    }
512
513    /**
514     * Subtracts the specified parameter from each component of this point. The unit of
515     * the output point will be the units of this point.
516     *
517     * @param that the parameter to be subtracted from each component of this point. May
518     *             not be null.
519     * @return <code>this - that</code>.
520     */
521    @Override
522    public Point minus(Parameter<Length> that) {
523        int numDims = _numDims;
524        Unit<Length> unit = _unit;
525
526        //  Convert input parameter to the units of this point.
527        double thatValue = that.getValue(unit);
528
529        Point point = Point.newInstancePvt(numDims, unit);
530        double[] thisData = this._data;
531        for (int i = 0; i < numDims; ++i)
532            point._data[i] = thisData[i] - thatValue;
533
534        return point;
535    }
536
537    /**
538     * Returns the negation of this point (all the values of each dimension negated).
539     *
540     * @return <code>-this</code>
541     */
542    @Override
543    public Point opposite() {
544        int numDims = _numDims;
545
546        Point point = Point.newInstancePvt(numDims, _unit);
547        double[] thisData = this._data;
548        for (int i = 0; i < numDims; ++i)
549            point._data[i] = -thisData[i];
550
551        return point;
552    }
553
554    /**
555     * Returns the product of this point with the specified coefficient.
556     *
557     * @param k the coefficient multiplier.
558     * @return <code>this ยท k</code>
559     */
560    @Override
561    public Point times(double k) {
562        int numDims = _numDims;
563
564        Point point = Point.newInstancePvt(numDims, _unit);
565        double[] thisData = this._data;
566        for (int i = 0; i < numDims; ++i)
567            point._data[i] = thisData[i] * k;
568
569        return point;
570    }
571
572    /**
573     * Returns a copy of this Point instance
574     * {@link javolution.context.AllocatorContext allocated} by the calling thread
575     * (possibly on the stack).
576     *
577     * @return an identical and independent copy of this point.
578     */
579    @Override
580    public Point copy() {
581        return copyOf(this);
582    }
583
584    /**
585     * Return a copy of this object with any transformations or subranges removed
586     * (applied).
587     *
588     * @return A copy of this object with any transformations or subranges removed.
589     */
590    @Override
591    public Point copyToReal() {
592        return copy();
593    }
594
595    /**
596     * Returns the unit in which the {@link #getValue values} in this point are stated in.
597     *
598     * @return The unit in which this Point is stated.
599     */
600    @Override
601    public final Unit<Length> getUnit() {
602        return _unit;
603    }
604
605    /**
606     * Returns the equivalent to this point but stated in the specified unit.
607     *
608     * @param unit the length unit of the point to be returned. May not be null.
609     * @return an equivalent of this point but stated in the specified unit.
610     * @throws ConversionException if the the input unit is not a length unit.
611     */
612    @Override
613    public Point to(Unit<Length> unit) throws ConversionException {
614        if (_unit == unit || unit.equals(_unit))
615            return this;
616
617        UnitConverter cvtr = Parameter.converterOf(_unit, unit);
618        if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary.
619            return this;
620        }
621
622        int numDims = _numDims;
623        Point point = Point.newInstancePvt(numDims, unit);
624        double[] thisData = this._data;
625        for (int i = 0; i < numDims; ++i)
626            point._data[i] = cvtr.convert(thisData[i]);
627
628        return point;
629    }
630
631    /**
632     * Return the equivalent of this point converted to the specified number of physical
633     * dimensions. If the number of dimensions is greater than this element, then zeros
634     * are added to the additional dimensions. If the number of dimensions is less than
635     * this element, then the extra dimensions are simply dropped (truncated). If the new
636     * dimensions are the same as the dimension of this element, then this element is
637     * simply returned.
638     *
639     * @param newDim The dimension of the point to return.
640     * @return A copy of this point converted to the new dimensions.
641     */
642    @Override
643    public Point toDimension(int newDim) {
644        if (newDim < 1)
645            throw new IllegalArgumentException(RESOURCES.getString("pointDimGT0"));
646        int thisDim = this.getPhyDimension();
647        if (newDim == thisDim)
648            return this;
649
650        int numDims = newDim;
651        if (newDim > thisDim)
652            numDims = thisDim;
653
654        Point point = Point.newInstancePvt(newDim, _unit);
655        double[] thisData = this._data;
656        for (int i = 0; i < numDims; ++i)
657            point._data[i] = thisData[i];
658        for (int i = numDims; i < newDim; ++i)
659            point._data[i] = 0;
660
661        return point;
662    }
663
664    /**
665     * Returns the values stored in this point, stated in this point's
666     * {@link #getUnit unit}, as a Float64Vector.
667     *
668     * @return A Float64Vector containing the values stored in this Point in the current
669     *         units.
670     */
671    @Override
672    public final Float64Vector toFloat64Vector() {
673        FastTable<Float64> table = FastTable.newInstance();
674        for (int i = 0; i < _numDims; ++i)
675            table.add(Float64.valueOf(_data[i]));
676
677        Float64Vector f64 = Float64Vector.valueOf(table);
678
679        FastTable.recycle(table);
680        return f64;
681    }
682
683    /**
684     * Compares this Point against the specified object for strict equality (same values
685     * and same units).
686     *
687     * @param obj the object to compare with.
688     * @return <code>true</code> if this point is identical to that point;
689     *         <code>false</code> otherwise.
690     */
691    @Override
692    public boolean equals(Object obj) {
693        if (this == obj)
694            return true;
695        if ((obj == null) || (obj.getClass() != this.getClass()))
696            return false;
697
698        final Point that = (Point)obj;
699        if (this._numDims != that._numDims)
700            return false;
701        if (!this._unit.equals(that._unit))
702            return false;
703        for (int i = 0; i < _numDims; ++i)
704            if (this._data[i] != that._data[i])
705                return false;
706
707        return super.equals(obj);
708    }
709
710    /**
711     * Returns the hash code for this parameter.
712     *
713     * @return the hash code value.
714     */
715    @Override
716    public int hashCode() {
717        int hash = 7;
718
719        int var_code = _unit.hashCode();
720        hash = hash * 31 + var_code;
721
722        for (int i = 0; i < _numDims; ++i)
723            hash = hash * 31 + makeVarCode(_data[i]);
724
725        return hash;
726    }
727
728    private static int makeVarCode(double value) {
729        long bits = Double.doubleToLongBits(value);
730        int var_code = (int)(bits ^ (bits >>> 32));
731        return var_code;
732    }
733
734    /**
735     * During serialization, this will write out the point data as a simple series of
736     * <code>double</code> values. This method is ONLY called by the Java Serialization
737     * mechanism and should not otherwise be used.
738     */
739    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
740
741        // Call the default write object method.
742        out.defaultWriteObject();
743
744        //  Write out the number of elements.
745        int size = _numDims;
746        out.writeInt(size);
747
748        //  Write out the coordinate values.
749        for (int i = 0; i < size; ++i)
750            out.writeDouble(_data[i]);
751
752    }
753
754    /**
755     * During de-serialization, this will handle the reconstruction of the point data.
756     * This method is ONLY called by the Java Serialization mechanism and should not
757     * otherwise be used.
758     */
759    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
760
761        // Call the default read object method.
762        in.defaultReadObject();
763
764        //  Read in the number of elements.
765        int size = in.readInt();
766
767        //  Read in the coordinate values.
768        _data = ArrayFactory.DOUBLES_FACTORY.array(size);
769        for (int i = 0; i < size; ++i) {
770            double value = in.readDouble();
771            _data[i] = value;
772        }
773
774    }
775
776    /**
777     * Holds the default XML representation for this object.
778     */
779    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
780    protected static final XMLFormat<Point> XML = new XMLFormat<Point>(Point.class) {
781
782        @Override
783        public Point newInstance(Class<Point> cls, InputElement xml) throws XMLStreamException {
784            return FACTORY.object();
785        }
786
787        @Override
788        public void read(InputElement xml, Point obj) throws XMLStreamException {
789            Unit unit = Unit.valueOf(xml.getAttribute("unit"));
790            if (!Length.UNIT.isCompatible(unit))
791                throw new XMLStreamException(
792                        MessageFormat.format(RESOURCES.getString("incompatibleUnits"),
793                                "Point", "length"));
794            obj._unit = unit;
795
796            GeomPoint.XML.read(xml, obj);     // Call parent read.
797
798            FastTable<Float64> valueList = FastTable.newInstance();
799            while (xml.hasNext()) {
800                Float64 value = xml.getNext();
801                valueList.add(value);
802            }
803            int numDims = valueList.size();
804            obj._numDims = numDims;
805            obj._data = ArrayFactory.DOUBLES_FACTORY.array(numDims);
806            for (int i = 0; i < numDims; ++i)
807                obj._data[i] = valueList.get(i).doubleValue();
808
809        }
810
811        @Override
812        public void write(Point obj, OutputElement xml) throws XMLStreamException {
813            xml.setAttribute("unit", obj._unit.toString());
814
815            GeomPoint.XML.write(obj, xml);    // Call parent write.
816
817            int size = obj._numDims;
818            for (int i = 0; i < size; ++i)
819                xml.add(Float64.valueOf(obj._data[i]));
820
821        }
822    };
823
824    /**
825     * Allocate a recyclable array that can contain Point objects using factory methods.
826     * <p>
827     * WARNING: The array returned may <I>not</I> be zeroed. Any object references in the
828     * returned array must be assumed to be invalid. Also, the returned array may be
829     * larger than the requested size! The array returned by this method can be recycled
830     * by recycleArray().
831     * </p>
832     *
833     * @param size The minimum number of elements for the returned array to contain.
834     * @return An array that can contain Point objects allocated using factory methods.
835     * @see #recycleArray
836     */
837    public static Point[] allocateArray(int size) {
838        return POINTARRAY_FACTORY.array(size);
839    }
840
841    /**
842     * Recycle an array of Point objects that was created by Point.allocateArray().
843     *
844     * @param arr The array to be recycled. The array must have been created by this the
845     *            allocateArray() method!
846     * @see #allocateArray
847     */
848    public static void recycleArray(Point[] arr) {
849        POINTARRAY_FACTORY.recycle(arr);
850    }
851    
852    ///////////////////////
853    // Factory creation. //
854    ///////////////////////
855    private static ArrayFactory<Point[]> POINTARRAY_FACTORY = new ArrayFactory<Point[]>() {
856        @Override
857        protected Point[] create(int size) {
858            return new Point[size];
859        }
860    };
861
862    private Point() { }
863
864    @SuppressWarnings("unchecked")
865    private static final ObjectFactory<Point> FACTORY = new ObjectFactory<Point>() {
866        @Override
867        protected Point create() {
868            return new Point();
869        }
870
871        @Override
872        protected void cleanup(Point obj) {
873            obj.reset();
874        }
875    };
876
877    @SuppressWarnings("unchecked")
878    private static Point newInstancePvt(int numDims, Unit<Length> unit) {
879        Point measure = FACTORY.object();
880        measure._unit = requireNonNull(unit);
881        measure._numDims = numDims;
882        measure._data = ArrayFactory.DOUBLES_FACTORY.array(numDims);
883        return measure;
884    }
885
886    @SuppressWarnings("unchecked")
887    private static Point copyOf(Point original) {
888        Point measure = newInstancePvt(original._numDims, original._unit);
889        System.arraycopy(original._data, 0, measure._data, 0, original._numDims);
890        original.copyState(measure);
891        return measure;
892    }
893
894    /**
895     * Tests the methods in this class.
896     *
897     * @param args Command-line arguments (not used).
898     */
899    public static void main(String args[]) {
900        System.out.println("Testing Point:  test = result [correct result]");
901
902        Point p1 = Point.valueOf(1, 2, 3, NonSI.FOOT);
903        System.out.println("p1 = " + p1);
904        System.out.println("  converted to m       = " + p1.to(SI.METER) + " [{0.3048 m, 0.6096 m, 0.9144 m}]");
905        System.out.println("  p1.norm()            = " + p1.norm() + " [3.74165738677394 ft]");
906        Point p2 = Point.valueOf(1, 1, 1, SI.METER);
907        System.out.println("p2 = " + p2);
908
909        Point p3 = Point.valueOf(1, 0.5, 1, SI.METER);
910        System.out.println("p3 = " + p3);
911        System.out.println("  p1.min(p3) = " + p1.min(p3) + " [1.0 ft, 1.64041994750656 ft, 3.0 ft]");
912        System.out.println("  p1.max(p3) = " + p1.max(p3) + " [3.28083989501312 ft, 2.0 ft, 3.28083989501312 ft]");
913
914        //  Write out XML data.
915        try {
916            System.out.println();
917
918            // Creates some useful aliases for class names.
919            javolution.xml.XMLBinding binding = new GeomXMLBinding();
920
921            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
922            writer.setIndentation("    ");
923            writer.setBinding(binding);
924            writer.write(p1, "Point", Point.class);
925            writer.flush();
926
927            System.out.println();
928        } catch (Exception e) {
929            e.printStackTrace();
930        }
931
932    }
933
934}