001/*
002 *   Cylindrical3D -- A 3D cylindrical coordinate of radius, azimuth, and height.
003 *
004 *   Copyright (C) 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.Quantity;
029import javax.measure.unit.SI;
030import javax.measure.unit.Unit;
031import javolution.context.ObjectFactory;
032import javolution.context.StackContext;
033import static javolution.lang.MathLib.cos;
034import static javolution.lang.MathLib.sin;
035import javolution.xml.XMLFormat;
036import javolution.xml.XMLSerializable;
037import javolution.xml.stream.XMLStreamException;
038import org.jscience.mathematics.vector.DimensionException;
039import org.jscience.mathematics.vector.Vector;
040
041/**
042 * This class represents a 3 element {@link Vector vector} of Parameter elements
043 * representing a geometrical cylindrical coordinate with elements radius, azimuth angle
044 * and height.
045 *
046 * <p> Modified by: Joseph A. Huwaldt </p>
047 *
048 * @author Joseph A. Huwaldt, Date: October 29, 2015
049 * @version February 23, 2025
050 *
051 * @param <Q> The Quantity (unit type, such as Length or Volume) of this vector.
052 */
053public final class Cylindrical3D<Q extends Quantity> extends Coordinate3D<Q> implements XMLSerializable {
054
055    private static final long serialVersionUID = 1L;
056
057    /**
058     * Constant used to identify the radius of in the vector.
059     */
060    public static final int RADIUS = 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 height or altitude of the vector.
069     */
070    public static final int HEIGHT = 2;
071
072    /**
073     * The radius of this vector.
074     */
075    private Parameter<Q> _radius;
076
077    /**
078     * The azimuth angle of this vector.
079     */
080    private Parameter<Angle> _azimuth;
081
082    /**
083     * The height/altitude of this vector.
084     */
085    private Parameter<Q> _height;
086
087    /**
088     * Returns a {@link Cylindrical3D} instance holding the specified <code>double</code>
089     * values stated in the specified units.
090     *
091     * @param <Q>        the Quantity (unit type, e.g. Length or Volume) of this vector.
092     * @param radius     the radius of the vector stated in the specified radius unit.
093     * @param azimuth    the vector azimuth angle stated in the specified angle unit.
094     * @param height     the vector height/altitude value stated in the specified radius
095     *                   unit.
096     * @param radiusUnit the unit in which the radius and height is stated.
097     * @param angleUnit  the unit in which the azimuth angle is stated.
098     * @return the vector having the specified values.
099     */
100    public static <Q extends Quantity> Cylindrical3D<Q> valueOf(double radius, double azimuth, double height,
101            Unit<Q> radiusUnit, Unit<Angle> angleUnit) {
102        Cylindrical3D<Q> V = Cylindrical3D.newInstance();
103
104        V._radius = Parameter.valueOf(radius, radiusUnit);
105        V._azimuth = Parameter.valueOf(azimuth, angleUnit);
106        V._height = Parameter.valueOf(height, radiusUnit);
107
108        return V;
109    }
110
111    /**
112     * Returns a {@link Cylindrical3D} instance holding the specified
113     * <code>Parameter</code> values.
114     *
115     * @param <Q>     the Quantity (unit type, e.g. Length or Volume) of this vector.
116     * @param radius  the vector radius value.
117     * @param azimuth the vector azimuth angle value.
118     * @param height  the vector height/altitude value.
119     * @return the vector having the specified values in the units of magnitude.
120     */
121    public static <Q extends Quantity> Cylindrical3D<Q> valueOf(Parameter<Q> radius, Parameter<Angle> azimuth, Parameter<Q> height) {
122        Cylindrical3D<Q> V = Cylindrical3D.newInstance();
123        V._radius = radius;
124        V._azimuth = azimuth;
125        V._height = height.to(radius.getUnit());
126
127        return V;
128    }
129
130    /**
131     * Returns a {@link Cylindrical3D} instance containing the cylindrical coordinate
132     * representation of the specified {@link Coordinate3D coordinate}. The azimuth
133     * component of the resulting vector will have units of radians.
134     *
135     * @param <Q>        the Quantity (unit type, e.g. Length or Volume) of this vector.
136     * @param coordinate the input coordinate.
137     * @return the cylindrical coordinate having the specified values.
138     */
139    public static <Q extends Quantity> Cylindrical3D<Q> valueOf(Coordinate3D<Q> coordinate) {
140
141        if (coordinate instanceof Cylindrical3D)
142            return (Cylindrical3D)coordinate;
143
144        @SuppressWarnings("null")
145        Vector3D<Q> vector = coordinate.toVector3D();
146        double x = vector.getValue(Vector3D.X);
147        double y = vector.getValue(Vector3D.Y);
148
149        double rho = Math.sqrt(x * x + y * y);
150        double azim = Math.atan2(y, x);
151
152        Cylindrical3D<Q> V = Cylindrical3D.newInstance();
153        V._radius = Parameter.valueOf(rho, vector.getUnit());
154        V._azimuth = Parameter.valueOf(azim, SI.RADIAN);
155        V._height = vector.get(Vector3D.Z);
156
157        return V;
158    }
159
160    /**
161     * Returns a {@link Cylindrical3D} instance containing the cylindrical coordinate
162     * representation of the specified {@link Coordinate3D coordinate}.
163     *
164     * @param <Q>       the Quantity (unit type, e.g. Length or Volume) of this vector.
165     * @param coord     the coordinate to convert.
166     * @param angleUnit the unit to use for the azimuth component.
167     * @return the cylindrical coordinate vector having the specified values.
168     */
169    public static <Q extends Quantity> Cylindrical3D<Q> valueOf(Coordinate3D<Q> coord, Unit<Angle> angleUnit) {
170        Cylindrical3D<Q> V = Cylindrical3D.valueOf(coord);
171        V._azimuth = V._azimuth.to(angleUnit);
172        return V;
173    }
174
175    /**
176     * Return the specified {@link Vector3D} object as a <code>Cylindrical3D</code>
177     * instance.
178     *
179     * @param vector The Vector3D object to be converted to a Cylindrical3D.
180     * @return A Cylindrical3D instance that is equivalent to the supplied Vector3D
181     *         object.
182     */
183    @Override
184    public Cylindrical3D<Q> fromVector3D(Vector3D<Q> vector) {
185        return Cylindrical3D.valueOf(vector, _azimuth.getUnit());
186    }
187
188    /**
189     * Returns the value of a Parameter from this vector. The dimensions are defined as:
190     * radius, azimuth angle, and height or altitude in that order.
191     *
192     * @param i the dimension index (0=radius, 1=azimuth, 2=height/altitude).
193     * @return the value of the parameter at <code>i</code>.
194     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &gt; dimension()-1)</code>
195     */
196    @Override
197    public Parameter get(int i) {
198        Parameter value = null;
199        switch (i) {
200            case 0:
201                value = _radius;
202                break;
203            case 1:
204                value = _azimuth;
205                break;
206            case 2:
207                value = _height;
208                break;
209            default:
210                throw new IndexOutOfBoundsException(
211                        MessageFormat.format(RESOURCES.getString("p3dIndexErr"), i));
212        }
213        return value;
214    }
215
216    /**
217     * Returns the radius element of this vector as a <code>double</code>, stated in this
218     * vector's {@link #getUnit() unit}.
219     *
220     * @return the radius element of this vector in this vector's units.
221     */
222    public double getRadiusValue() {
223        return _radius.getValue();
224    }
225
226    /**
227     * Returns the radius element of this vector as a {@link Parameter}.
228     *
229     * @return the radius element of this vector.
230     */
231    public Parameter<Q> getRadius() {
232        return _radius.pow(2).times(_height.pow(2)).sqrt();
233    }
234
235    /**
236     * Returns the azimuth angle of this vector as a {@link Parameter}.
237     *
238     * @return the azimuth angle of this vector.
239     */
240    public Parameter<Angle> getAzimuth() {
241        return _azimuth;
242    }
243
244    /**
245     * Returns the height or altitude element of this vector as a {@link Parameter}.
246     *
247     * @return the height element of this vector.
248     */
249    public Parameter<Q> getHeight() {
250        return _height;
251    }
252
253    /**
254     * Returns the Euclidian norm, magnitude, or length of this vector (square root of the
255     * dot product of this vector and itself).
256     *
257     * @return <code>sqrt(this · this)</code>.
258     */
259    @Override
260    public Parameter<Q> norm() {
261        return _radius.pow(2).times(_height.pow(2)).sqrt();
262    }
263
264    /**
265     * Returns the {@link #norm() norm}, magnitude, or length value of this vector.
266     *
267     * @return <code>this.norm().getValue()</code>.
268     */
269    @Override
270    public double normValue() {
271        double rho = _radius.getValue();
272        double h = _height.getValue();
273        return Math.sqrt(rho * rho + h * h);
274    }
275
276    /**
277     * Returns the negation of this vector.
278     *
279     * @return <code>-this</code>.
280     */
281    @Override
282    public Cylindrical3D<Q> opposite() {
283
284        //  Convert to cartesian coordinates, negate that, and then convert back to polar.
285        Vector3D<Q> V3D = toVector3D().opposite();
286        Cylindrical3D<Q> V = fromVector3D(V3D);
287
288        return V;
289    }
290
291    /**
292     * Returns the sum of this vector with the one specified. The units of the output
293     * vector will be the units of this vector.
294     *
295     * @param that the vector to be added.
296     * @return <code>this + that</code>.
297     * @throws DimensionException if vector dimensions are different.
298     */
299    @Override
300    public Cylindrical3D<Q> plus(Vector<Parameter<Q>> that) {
301
302        //  Convert to Cartesian coordinates, add those, then convert back.
303        if (!(that instanceof Coordinate3D))
304            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
305
306        StackContext.enter();
307        try {
308            Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();
309            Vector3D<Q> V3D = toVector3D().plus(T);
310            Cylindrical3D<Q> V = fromVector3D(V3D);
311
312            return StackContext.outerCopy(V);
313        } finally {
314            StackContext.exit();
315        }
316    }
317
318    /**
319     * Returns the sum of this vector with the parameter specified. The unit of the output
320     * vector will be the units of this vector.
321     *
322     * @param that the parameter to be added to this vector.
323     * @return <code>this + that</code>.
324     */
325    @Override
326    public Cylindrical3D<Q> plus(Parameter<Q> that) {
327
328        StackContext.enter();
329        try {
330            Vector3D<Q> V3D = toVector3D().plus(that);
331            Cylindrical3D<Q> V = fromVector3D(V3D);
332
333            return StackContext.outerCopy(V);
334        } finally {
335            StackContext.exit();
336        }
337
338    }
339
340    /**
341     * Returns the difference between this vector and the one specified.
342     *
343     * @param that the vector to be subtracted.
344     * @return <code>this - that</code>.
345     */
346    @Override
347    public Cylindrical3D<Q> minus(Vector<Parameter<Q>> that) {
348
349        //  Convert to Cartesian coordinates, subtract those, then convert back.
350        if (!(that instanceof Coordinate3D))
351            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
352
353        StackContext.enter();
354        try {
355            Vector3D<Q> T = ((Coordinate3D<Q>)that).toVector3D();
356            Vector3D<Q> V3D = toVector3D().minus(T);
357            Cylindrical3D<Q> V = fromVector3D(V3D);
358
359            return StackContext.outerCopy(V);
360        } finally {
361            StackContext.exit();
362        }
363    }
364
365    /**
366     * Subtracts the supplied Parameter from this vector's and returns the result. The
367     * unit of the output vector will be the units of this vector.
368     *
369     * @param that the Parameter to be subtracted from this vector.
370     * @return <code>this - that</code>.
371     */
372    @Override
373    public Cylindrical3D<Q> minus(Parameter<Q> that) {
374
375        StackContext.enter();
376        try {
377            Vector3D<Q> V3D = toVector3D().minus(that);
378            Cylindrical3D<Q> V = fromVector3D(V3D);
379
380            return StackContext.outerCopy(V);
381        } finally {
382            StackContext.exit();
383        }
384    }
385
386    /**
387     * Returns the product of this vector with the specified coefficient. The magnitude of
388     * this vector is scaled by the input coefficient and the direction is left unchanged.
389     *
390     * @param k the coefficient multiplier.
391     * @return <code>this · k</code>
392     */
393    @Override
394    public Cylindrical3D times(Parameter k) {
395        Cylindrical3D V = Cylindrical3D.newInstance();
396        V._radius = _radius.times(k);
397        V._azimuth = _azimuth;
398        V._height = _height.times(k);
399        return V;
400    }
401
402    /**
403     * Returns the product of this vector with the specified coefficient. The magnitude of
404     * this vector is scaled by the input coefficient and the direction is left unchanged.
405     *
406     * @param k the coefficient multiplier.
407     * @return <code>this · k</code>
408     */
409    @Override
410    public Cylindrical3D<Q> times(double k) {
411        Cylindrical3D<Q> V = Cylindrical3D.newInstance();
412        V._radius = _radius.times(k);
413        V._azimuth = _azimuth;
414        V._height = _height.times(k);
415        return V;
416    }
417
418    /**
419     * Returns the dot product of this vector with the one specified.
420     *
421     * @param that the vector multiplier.
422     * @return <code>this · that</code>
423     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
424     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
425     * Wikipedia: Dot Product</a>
426     */
427    @Override
428    public Parameter times(Vector that) {
429
430        //  Convert to Cartesian coordinates and multiply that.
431        if (!(that instanceof Coordinate3D))
432            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
433
434        StackContext.enter();
435        try {
436            Vector3D T = ((Coordinate3D)that).toVector3D();
437            Parameter product = toVector3D().times(T);
438
439            return StackContext.outerCopy(product);
440        } finally {
441            StackContext.exit();
442        }
443    }
444
445    /**
446     * Returns the result of this vector divided by the specified divisor. The magnitude
447     * of this vector is divided by the input coefficient and the direction is left
448     * unchanged.
449     *
450     * @param that the divisor.
451     * @return <code>this / that</code>.
452     */
453    public Cylindrical3D<?> divide(Parameter<?> that) {
454        return (Cylindrical3D<?>)times(that.inverse());
455    }
456
457    /**
458     * Returns the cross product of two 3-dimensional vectors.
459     *
460     * @param that the vector multiplier.
461     * @return <code>this x that</code>
462     * @throws DimensionException if <code>(that.getDimension() != 3)</code>
463     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">
464     * Wikipedia: Cross Product</a>
465     */
466    @Override
467    public Cylindrical3D cross(Vector that) {
468
469        //  Convert to Cartesian coordinates, multiply those, then convert back.
470        if (!(that instanceof Coordinate3D))
471            throw new ClassCastException(RESOURCES.getString("notCoordinate3D"));
472
473        StackContext.enter();
474        try {
475            Vector3D<?> T = ((Coordinate3D<?>)that).toVector3D();
476            Vector3D<?> V3D = toVector3D().cross(T);
477            Cylindrical3D<?> V = Cylindrical3D.valueOf(V3D, _azimuth.getUnit());
478
479            return StackContext.outerCopy(V);
480        } finally {
481            StackContext.exit();
482        }
483    }
484
485    /**
486     * Returns this vector converted to a unit vector with a vector magnitude of 1.0.
487     *
488     * @return this vector converted to a unit vector
489     */
490    @Override
491    public Cylindrical3D<Dimensionless> toUnitVector() {
492        double magnitude = this.normValue();
493        if (this.getUnit().equals(Dimensionless.UNIT) && MathTools.isApproxEqual(magnitude, 1.0))
494            return (Cylindrical3D<Dimensionless>)this;
495
496        Cylindrical3D<Dimensionless> V = Cylindrical3D.newInstance();
497        V._azimuth = this._azimuth;
498        V._height = (Parameter<Dimensionless>)this._height.divide(magnitude);
499        V._radius = (Parameter<Dimensionless>)this._radius.divide(magnitude);
500        
501        return V;
502    }
503
504    /**
505     * Returns a copy of this vector {@link javolution.context.AllocatorContext allocated}
506     * by the calling thread (possibly on the stack).
507     *
508     * @return an identical and independent copy of this vector.
509     */
510    @Override
511    public Cylindrical3D<Q> copy() {
512        return copyOf(this);
513    }
514
515    /**
516     * Returns the unit in which the radius and height in this vector are stated in.
517     *
518     * @return the unit in which the radius and height in this vector are stated in
519     */
520    @Override
521    public Unit<Q> getUnit() {
522        return _radius.getUnit();
523    }
524
525    /**
526     * Returns the unit in which the azimuth angle in this vector is stated in.
527     *
528     * @return the unit in which the azimuth angle in this vector is stated in
529     */
530    public Unit<Angle> getAngleUnit() {
531        return _azimuth.getUnit();
532    }
533
534    /**
535     * Returns the equivalent to this vector but with the radius and height stated in the
536     * specified unit.
537     *
538     * @param <R>  the Quantity (physical unit) type of the returned vector.
539     * @param unit the unit of the radius and height of the vector to be returned.
540     * @return a vector equivalent to this vector but with the radius and height stated in
541     *         the specified unit.
542     * @throws ConversionException if the current model does not allows for conversion to
543     * the specified unit.
544     */
545    @Override
546    public <R extends Quantity> Cylindrical3D<R> to(Unit<R> unit) throws ConversionException {
547        Unit<?> thisUnit = getUnit();
548        if ((thisUnit == unit) || thisUnit.equals(unit))
549            return (Cylindrical3D<R>)this;
550        Cylindrical3D<R> result = Cylindrical3D.newInstance();
551        result._radius = _radius.to(unit);
552        result._azimuth = _azimuth;
553        result._height = _height.to(unit);
554        return result;
555    }
556
557    /**
558     * Returns the equivalent to this vector but with the azimuth angle stated in the
559     * specified unit.
560     *
561     * @param unit the angle unit of the azimuth of the vector to be returned.
562     * @return a coordinate equivalent to this vector but with the azimuth stated in the
563     *         specified unit.
564     * @throws ConversionException if the current model does not allows for conversion to
565     * the specified unit.
566     */
567    public Cylindrical3D<Q> angleTo(Unit<Angle> unit) {
568        Unit<Angle> thisUnit = _azimuth.getUnit();
569        if ((thisUnit == unit) || thisUnit.equals(unit))
570            return this;
571        Cylindrical3D<Q> result = Cylindrical3D.newInstance();
572        result._radius = _radius;
573        result._azimuth = _azimuth.to(unit);
574        result._height = _height;
575        return result;
576    }
577
578    /**
579     * Casts this Cylindrical3D to a parameterized unit of specified nature or throw a
580     * <code>ClassCastException</code> if the dimension of the specified quantity and this
581     * parameter's unit dimension do not match.
582     *
583     * @param <T>  the Quantity (physical unit) type of the returned vector.
584     * @param type the quantity class identifying the nature of the unit.
585     * @return this Cylindrical3D parameterized with the specified type.
586     * @throws ClassCastException if the dimension of this parameter's unit is different
587     * from the specified quantity dimension.
588     * @throws UnsupportedOperationException if the specified quantity class does not have
589     * a public static field named "UNIT" holding the standard unit for the quantity.
590     */
591    @Override
592    public <T extends Quantity> Cylindrical3D<T> asType(Class<T> type) throws ClassCastException {
593        getUnit().asType(type); //  If no exception is thrown, the cast is valid.
594        return (Cylindrical3D<T>)this;
595    }
596
597    /**
598     * Returns a Cartesian Vector3D representation of this vector.
599     * <p>
600     * The polar to Cartesian transformation is defined by:
601     * <pre>
602     *    |x|   | radius*cos(azimuth)  |
603     *    |y| = | radius*sin(azimuth)  |
604     *    |z|   |      height          |
605     * </pre>
606     *
607     * @return a Cartesian Vector3D representation of this vector
608     */
609    @Override
610    public Vector3D<Q> toVector3D() {
611
612        double radius = _radius.getValue();
613        double azim = _azimuth.to(SI.RADIAN).getValue();
614        double z = _height.getValue();
615
616        double x = radius * cos(azim);
617        double y = radius * sin(azim);
618
619        Vector3D<Q> V = Vector3D.valueOf(x, y, z, getUnit());
620
621        return V;
622    }
623
624    /**
625     * Compares this Cylindrical3D against the specified object for strict equality (same
626     * values and same units).
627     *
628     * @param obj the object to compare with.
629     * @return <code>true</code> if this vector is identical to that vector;
630     *         <code>false</code> otherwise.
631     */
632    @Override
633    public boolean equals(Object obj) {
634        if (this == obj)
635            return true;
636        if ((obj == null) || (obj.getClass() != this.getClass()))
637            return false;
638
639        Cylindrical3D<?> that = (Cylindrical3D<?>)obj;
640        if (!this._radius.equals(that._radius))
641            return false;
642        if (!this._azimuth.equals(that._azimuth))
643            return false;
644
645        return this._height.equals(that._height);
646    }
647
648    /**
649     * Returns the hash code for this parameter.
650     *
651     * @return the hash code value.
652     */
653    @Override
654    public int hashCode() {
655        int hash = 7;
656
657        int var_code = _radius.hashCode();
658        hash = hash * 31 + var_code;
659
660        var_code = _azimuth.hashCode();
661        hash = hash * 31 + var_code;
662
663        var_code = _height.hashCode();
664        hash = hash * 31 + var_code;
665
666        return hash;
667    }
668
669    /**
670     * Holds the default XML representation. For example:
671     * <pre>
672     *    &lt;Radius value="1.73205080756888" unit="m"/&gt;
673     *    &lt;Azimuth value="45.0" unit="°" /&gt;
674     *    &lt;Height value="35.2643896827547" unit="m" /&gt;
675     * </pre>
676     */
677    protected static final XMLFormat<Cylindrical3D> XML = new XMLFormat<Cylindrical3D>(Cylindrical3D.class) {
678
679        @Override
680        public Cylindrical3D<?> newInstance(Class<Cylindrical3D> cls, InputElement xml) throws XMLStreamException {
681            return FACTORY.object();
682        }
683
684        @Override
685        public void read(InputElement xml, Cylindrical3D V) throws XMLStreamException {
686            V._radius = xml.get("Radius", Parameter.class);
687            V._azimuth = xml.get("Azimuth", Parameter.class);
688            V._height = xml.get("Height", Parameter.class);
689        }
690
691        @Override
692        public void write(Cylindrical3D V, OutputElement xml) throws XMLStreamException {
693            xml.add(V._radius, "Radius", Parameter.class);
694            xml.add(V._azimuth, "Azimuth", Parameter.class);
695            xml.add(V._height, "Height", Parameter.class);
696        }
697    };
698
699    ///////////////////////
700    // Factory creation. //
701    ///////////////////////
702    private Cylindrical3D() {
703    }
704
705    private static final ObjectFactory<Cylindrical3D<? extends Quantity>> FACTORY = new ObjectFactory<Cylindrical3D<? extends Quantity>>() {
706        @Override
707        protected Cylindrical3D<? extends Quantity> create() {
708            return new Cylindrical3D();
709        }
710    };
711
712    private static <Q extends Quantity> Cylindrical3D<Q> newInstance() {
713        Cylindrical3D<Q> measure = (Cylindrical3D<Q>)FACTORY.object();
714        return measure;
715    }
716
717    private static <Q extends Quantity> Cylindrical3D<Q> copyOf(Cylindrical3D<Q> original) {
718        Cylindrical3D<Q> measure = Cylindrical3D.newInstance();
719        measure._radius = original._radius.copy();
720        measure._azimuth = original._azimuth.copy();
721        measure._height = original._height.copy();
722        return measure;
723    }
724
725}