001/**
002 * GeomVector -- Partial implementation of an n-D vector.
003 *
004 * Copyright (C) 2009-2016, 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.Parameter;
021import jahuwaldt.js.param.ParameterVector;
022import static java.util.Objects.nonNull;
023import static java.util.Objects.requireNonNull;
024import javax.measure.quantity.Angle;
025import javax.measure.quantity.Dimensionless;
026import javax.measure.quantity.Quantity;
027import javax.measure.unit.SI;
028import javax.measure.unit.Unit;
029import javolution.lang.MathLib;
030import javolution.text.Text;
031import javolution.text.TextBuilder;
032import org.jscience.mathematics.vector.Float64Vector;
033
034/**
035 * Partial implementation of an n-dimensional vector which indicates direction, but not
036 * position.
037 * 
038 * <p> Modified by: Joseph A. Huwaldt </p>
039 * 
040 * @author Joseph A. Huwaldt Date: June 13, 2009
041 * @version September 9, 2016
042 *
043 * @param <Q> The Quantity (unit type) associated with this vector's coordinate values.
044 */
045@SuppressWarnings({"serial", "CloneableImplementsClone"})
046public abstract class GeomVector<Q extends Quantity> extends AbstractGeomElement<GeomVector>
047        implements Transformable<GeomVector> {
048
049    /**
050     * Constant used to identify the X coordinate in a 3D vector.
051     */
052    public static final int X = 0;
053
054    /**
055     * Constant used to identify the Y coordinate in a 3D vector.
056     */
057    public static final int Y = 1;
058
059    /**
060     * Constant used to identify the Z coordinate in a 3D vector.
061     */
062    public static final int Z = 2;
063
064    /**
065     * Returns the number of child-elements that make up this geometry element. This
066     * implementation always returns 0 as a vector is not made up of any other elements.
067     */
068    @Override
069    public int size() {
070        return 0;
071    }
072
073    /**
074     * Returns the number of parametric dimensions of the geometry element. This
075     * implementation always returns 0 as a vector is not parametric.
076     */
077    @Override
078    public int getParDimension() {
079        return 0;
080    }
081
082    /**
083     * Returns the value of a Parameter from this vector.
084     *
085     * @param i the dimension index.
086     * @return the value of the parameter at <code>i</code>.
087     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; getPhyDimension())</code>
088     */
089    public Parameter<Q> get(int i) {
090        return Parameter.valueOf(getValue(i), (Unit)getUnit());
091    }
092
093    /**
094     * Returns the value of the Parameter in this vector as a <code>double</code>, stated
095     * in this vector's {@link #getUnit unit}.
096     *
097     * @param i the dimension index.
098     * @return the value of the Parameter at <code>i</code>.
099     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; getPhyDimension())</code>
100     */
101    public abstract double getValue(int i);
102
103    /**
104     * Returns the value of the Parameter in this vector as a <code>double</code>, stated
105     * in the specified units.
106     *
107     * @param i    the dimension index.
108     * @param unit the unit to return the value in. May not be null.
109     * @return the value of the Parameter at <code>i</code> in the specified unit.
110     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; getPhyDimension())</code>
111     */
112    public abstract double getValue(int i, Unit<Q> unit);
113
114    /**
115     * Set the origin point for this vector. The origin is used as a reference for drawing
116     * the vector and is <i>not</i> a part of the state of this vector and is not used in
117     * any calculations with this vector.
118     *
119     * @param o The origin point to assign to this vector. May not be null.
120     */
121    public abstract void setOrigin(Point o);
122
123    /**
124     * Return the origin point for this vector. If no origin has been explicitly set, then
125     * the coordinate system origin in the default units is returned.
126     *
127     * @return The origin point associated with this vector.
128     */
129    public abstract Point getOrigin();
130
131    /**
132     * Returns the Euclidian norm, magnitude, or length of this vector (square root of the
133     * dot product of this vector and itself).
134     *
135     * @return <code>sqrt(this · this)</code>.
136     */
137    public Parameter<Q> norm() {
138        return Parameter.valueOf(normValue(), (Unit)getUnit());
139    }
140
141    /**
142     * Returns the {@link #norm}, magnitude, or length value of this vector (square root
143     * of the dot product of this vector and itself).
144     *
145     * @return <code>this.norm().doubleValue()</code>.
146     */
147    public abstract double normValue();
148
149    /**
150     * Returns the Euclidian norm, magnitude, or length of this vector (square root of the
151     * dot product of this vector and itself).
152     *
153     * @return <code>sqrt(this · this)</code>.
154     */
155    public Parameter<Q> mag() {
156        return norm();
157    }
158
159    /**
160     * Returns the {@link #norm}, magnitude, or length value of this vector.
161     *
162     * @return <code>this.norm().doubleValue()</code>.
163     */
164    public double magValue() {
165        return normValue();
166    }
167
168    /**
169     * Returns the negation of this vector.
170     *
171     * @return <code>-this</code>.
172     */
173    public abstract Vector<Q> opposite();
174
175    /**
176     * Returns the sum of this vector with the one specified. The unit of the output
177     * vector will be the units of this vector.
178     *
179     * @param that the vector to be added. May not be null.
180     * @return <code>this + that</code>.
181     * @throws DimensionException if vector dimensions are different.
182     */
183    public abstract Vector<Q> plus(GeomVector<Q> that);
184
185    /**
186     * Returns the sum of this vector with the parameter specified. The input parameter is
187     * added to each component of this vector. The unit of the output vector will be the
188     * units of this vector.
189     *
190     * @param that the parameter to be added to each element of this vector. May not be
191     *             null.
192     * @return <code>this + that</code>.
193     */
194    public abstract Vector<Q> plus(Parameter<Q> that);
195
196    /**
197     * Returns the difference between this vector and the one specified. The unit of the
198     * output vector will be the units of this vector.
199     *
200     * @param that the vector to be subtracted from this vector. May not be null.
201     * @return <code>this - that</code>.
202     * @throws DimensionException if vector dimensions are different.
203     */
204    public abstract Vector<Q> minus(GeomVector<Q> that);
205
206    /**
207     * Subtracts the supplied Parameter from each element of this vector and returns the
208     * result. The unit of the output vector will be the units of this vector.
209     *
210     * @param that the Parameter to be subtracted from each element of this vector. May
211     *             not be null.
212     * @return <code>this - that</code>.
213     */
214    public abstract Vector<Q> minus(Parameter<Q> that);
215
216    /**
217     * Returns the product of this vector with the specified coefficient (dimensionless).
218     *
219     * @param k the coefficient multiplier.
220     * @return <code>this · k</code>
221     */
222    public abstract Vector<Q> times(double k);
223
224    /**
225     * Returns the product of this vector with the specified coefficient.
226     *
227     * @param k the coefficient multiplier. May not be null.
228     * @return <code>this · k</code>
229     */
230    public abstract Vector<? extends Quantity> times(Parameter<?> k);
231
232    /**
233     * Returns the dot product (scalar product) of this vector with the one specified.
234     *
235     * @param that the vector multiplier. May not be null.
236     * @return <code>this · that</code>
237     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
238     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
239     * Wikipedia: Dot Product</a>
240     */
241    public abstract Parameter<? extends Quantity> times(GeomVector<?> that);
242
243    /**
244     * Returns the dot product (scalar product) of this vector with the one specified.
245     *
246     * @param that the vector multiplier. May not be null.
247     * @return <code>this · that</code>
248     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
249     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
250     * Wikipedia: Dot Product</a>
251     */
252    public Parameter<? extends Quantity> dot(GeomVector<?> that) {
253        return times(that);
254    }
255
256    /**
257     * Returns the element-by-element product of this vector with the one specified.
258     *
259     * @param that the vector multiplier. May not be null.
260     * @return <code>this .* that</code>
261     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
262     */
263    public abstract Vector<? extends Quantity> timesEBE(GeomVector<?> that);
264
265    /**
266     * Returns the cross product of two vectors.
267     *
268     * @param that the vector multiplier. May not be null.
269     * @return <code>this x that</code>
270     * @throws DimensionException if
271     * <code>(that.getDimension() != this.getDimension())</code>
272     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">
273     * Wikipedia: Cross Product</a>
274     */
275    public abstract Vector<? extends Quantity> cross(GeomVector<?> that);
276
277    /**
278     * Returns this vector with each element divided by the specified divisor
279     * (dimensionless).
280     *
281     * @param divisor the divisor.
282     * @return <code>this / divisor</code>.
283     */
284    public abstract Vector<Q> divide(double divisor);
285
286    /**
287     * Returns this vector with each element divided by the specified divisor.
288     *
289     * @param that the divisor. May not be null.
290     * @return <code>this / that</code>.
291     */
292    public abstract Vector<? extends Quantity> divide(Parameter<?> that);
293
294    /**
295     * Returns the angle between this vector and the specified vector.
296     *
297     * @param that the vector to which the angle will be determined. May not be null.
298     * @return <code>acos(this · that)/(norm(this)*norm(that))</code>
299     */
300    public Parameter<Angle> angle(GeomVector<?> that) {
301        that = that.to(getUnit());
302        double scalar = this.dot(that).getValue();
303        double abs1 = this.normValue();
304        double abs2 = that.normValue();
305
306        double argument = 1.;
307        double dum = abs1 * abs2;
308        if (dum >= Parameter.SQRT_EPS)
309            argument = scalar / dum;
310
311        if (argument > 1.)
312            argument = 1.;
313        else if (argument < -1.)
314            argument = -1.;
315
316        return Parameter.valueOf(MathLib.acos(argument), SI.RADIAN);
317    }
318
319    /**
320     * Return an immutable version of this vector.
321     *
322     * @return An immutable version of this vector.
323     */
324    public abstract Vector<Q> immutable();
325
326    /**
327     * Return <code>true</code> if this GeomVector contains valid and finite numerical
328     * components. A value of <code>false</code> will be returned if any of the elements
329     * in this vector, including the origin, are invalid.
330     */
331    @Override
332    public boolean isValid() {
333        //  Check the origin point.
334        GeomPoint o = getOrigin();
335        if (!o.isValid())
336            return false;
337
338        //  Check the vector elements.
339        int dim = getPhyDimension();
340        for (int i = 0; i < dim; ++i) {
341            double value = getValue(i);
342            if (Double.isNaN(value) || Double.isInfinite(value))
343                return false;
344        }
345
346        return true;
347    }
348
349    /**
350     * Returns this vector converted to a unit vector by dividing all the vector's
351     * elements by the length ({@link #norm}) of this vector.
352     *
353     * @return This vector converted to a unit vector by dividing all the vector's
354     *         elements by the length of this vector.
355     */
356    public abstract Vector<Dimensionless> toUnitVector();
357
358    /**
359     * Return the coordinate point representing the minimum bounding box corner (e.g.: min
360     * X, min Y, min Z).
361     *
362     * @return The minimum bounding box coordinate for this geometry element.
363     */
364    @Override
365    public Point getBoundsMin() {
366        Point o = getOrigin();
367        Point tip = Point.valueOf(this).plus(o);
368        return o.min(tip);
369    }
370
371    /**
372     * Return the coordinate point representing the maximum bounding box corner (e.g.: max
373     * X, max Y, max Z).
374     *
375     * @return The maximum bounding box coordinate for this geometry element.
376     */
377    @Override
378    public Point getBoundsMax() {
379        Point o = getOrigin();
380        Point tip = Point.valueOf(this).plus(o);
381        return o.max(tip);
382    }
383
384    /**
385     * Returns the most extreme point, either minimum or maximum, in the specified
386     * coordinate direction on this geometry element. This implementation simply returns
387     * <code>getBoundsMin</code> or <code>getBoundsMax</code> depending on the setting of
388     * the "max" flag.
389     *
390     * @param dim An index indicating the dimension to find the min/max point for (0=X,
391     *            1=Y, 2=Z, etc).
392     * @param max Set to <code>true</code> to return the maximum value, <code>false</code>
393     *            to return the minimum.
394     * @param tol Fractional tolerance to refine the min/max point position to if
395     *            necessary.
396     * @return The point found on this element that is the min or max in the specified
397     *         coordinate direction.
398     * @see #getBoundsMin
399     * @see #getBoundsMax
400     */
401    @Override
402    public Point getLimitPoint(int dim, boolean max, double tol) {
403        if (max)
404            return getBoundsMax();
405        return getBoundsMin();
406    }
407
408    /**
409     * Returns a <code>ParameterVector</code> representation of this vector.
410     *
411     * @return A ParameterVector that is equivalent to this vector.
412     */
413    public abstract ParameterVector<Q> toParameterVector();
414
415    /**
416     * Returns a <code>Float64Vector</code> containing the elements of this vector stated
417     * in the current units.
418     *
419     * @return A Float64Vector that contains the elements of this vector in the current
420     *         units.
421     */
422    public abstract Float64Vector toFloat64Vector();
423
424    /**
425     * Returns the values stored in this vector as a Java array, stated in the
426     * current {@link #getUnit units}.
427     *
428     * @return A new array with the vector element values copied into it.
429     */
430    public double[] toArray() {
431        return toArray(new double[this.getPhyDimension()]);
432    }
433
434    /**
435     * Returns the values stored in this vector, stated in the current
436     * {@link #getUnit units} stored in the input Java array.
437     *
438     * @param array An existing array that has at least as many elements as the physical
439     *              dimension of this vector.
440     * @return A reference to the input array after the vector elements have been copied
441     *         over to it.
442     * @see #getPhyDimension()
443     */
444    public double[] toArray(double[] array) {
445        int numDims = this.getPhyDimension();
446        for (int i = 0; i < numDims; ++i)
447            array[i] = this.getValue(i);
448        return array;
449    }
450
451    /**
452     * Returns transformed version of this element. The returned object implements
453     * {@link GeomTransform} and contains this element as a child.
454     *
455     * @param transform The transformation to apply to this geometry. May not be null.
456     * @return A new triangle that is identical to this one with the specified
457     *         transformation applied.
458     * @throws DimensionException if this point is not 3D.
459     */
460    @Override
461    public VectorTrans<Q> getTransformed(GTransform transform) {
462        return VectorTrans.newInstance(this, requireNonNull(transform));
463    }
464
465    /**
466     * Return a copy of this vector converted to the specified number of physical
467     * dimensions. If the number of dimensions is greater than this element, then zeros
468     * are added to the additional dimensions. If the number of dimensions is less than
469     * this element, then the extra dimensions are simply dropped (truncated). If the new
470     * dimensions are the same as the dimension of this element, then this element is
471     * simply returned.
472     *
473     * @param newDim The dimension of the vector to return.
474     * @return A copy of this vector converted to the new dimensions.
475     */
476    @Override
477    public abstract GeomVector<Q> toDimension(int newDim);
478
479    /**
480     * Returns the text representation of this geometry element that consists of the name
481     * followed by the vector axis values. For example:
482     * <pre>
483     *   {v = {{10 ft, -3 ft, 4.56 ft},o={0 m, 0m, 0m}}}
484     * </pre>
485     * If there is no name, then the output looks like this:
486     * <pre>
487     *   {{10 ft, -3 ft, 4.56 ft},o={0 m, 0 m, 0 m}}}
488     * </pre>
489     *
490     * @return the text representation of this geometry element.
491     */
492    @Override
493    public Text toText() {
494        int dimension = this.getPhyDimension();
495        TextBuilder tmp = TextBuilder.newInstance();
496        tmp.append('{');
497        String nameStr = getName();
498        boolean hasName = nonNull(nameStr);
499        if (hasName) {
500            tmp.append(nameStr);
501            tmp.append(" = {{");
502        } else
503            tmp.append("{");
504        for (int i = 0; i < dimension; i++) {
505            tmp.append(get(i));
506            if (i != dimension - 1) {
507                tmp.append(", ");
508            }
509        }
510        GeomPoint o = getOrigin();
511        if (o == null) {
512            tmp.append("},o={null}");
513        } else {
514            tmp.append("},o=");
515            tmp.append(o);
516        }
517
518        if (hasName)
519            tmp.append('}');
520        tmp.append('}');
521        Text txt = tmp.toText();
522        TextBuilder.recycle(tmp);
523        return txt;
524    }
525
526    /**
527     * Returns a copy of this GeomVector instance
528     * {@link javolution.context.AllocatorContext allocated} by the calling thread
529     * (possibly on the stack).
530     *
531     * @return an identical and independent copy of this vector.
532     */
533    @Override
534    public abstract GeomVector<Q> copy();
535
536    /**
537     * Compares this vector against the specified vector for approximate equality
538     * (coordinate values approximately equal to this one to within the numerical roundoff
539     * tolerance). The origin point is ignored.
540     *
541     * @param obj The vector object to compare with.
542     * @return <code>true</code> if this vector is approximately identical to that vector;
543     *         <code>false</code> otherwise.
544     */
545    public boolean isApproxEqual(GeomVector obj) {
546        if (this == obj)
547            return true;
548        if (obj == null)
549            return false;
550
551        int numDims = getPhyDimension();
552        if (obj.getPhyDimension() != numDims)
553            return false;
554
555        for (int i = 0; i < numDims; ++i) {
556            Parameter thisi = get(i);
557            Parameter thati = obj.get(i);
558            if (!thisi.isApproxEqual(thati))
559                return false;
560        }
561
562        return true;
563    }
564
565    /**
566     * Compares this vector against the specified vector for approximate equality
567     * (coordinate values approximately equal to this one to within the numerical roundoff
568     * tolerance). The origin point is ignored.
569     *
570     * @param obj The vector object to compare with.
571     * @param tol The amount use to define approximate equality. If <code>null</code> then
572     *            exact numerical equality is required.
573     * @return <code>true</code> if this vector is approximately identical to that vector;
574     *         <code>false</code> otherwise.
575     */
576    public boolean isApproxEqual(GeomVector obj, Parameter<Q> tol) {
577        if (this == obj)
578            return true;
579        if (obj == null)
580            return false;
581
582        int numDims = getPhyDimension();
583        if (obj.getPhyDimension() != numDims)
584            return false;
585
586        for (int i = 0; i < numDims; ++i) {
587            Parameter thisi = get(i);
588            Parameter thati = obj.get(i);
589            if (!thisi.isApproxEqual(thati, tol))
590                return false;
591        }
592
593        return true;
594    }
595
596}