001/**
002 * GeomPointTrans -- A GeomTransform that has a GeomPoint for a child.
003 *
004 * Copyright (C) 2009-2018, 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.*;
021import java.text.MessageFormat;
022import java.util.Objects;
023import static java.util.Objects.requireNonNull;
024import javax.measure.converter.ConversionException;
025import javax.measure.quantity.Angle;
026import javax.measure.quantity.Length;
027import javax.measure.unit.SI;
028import javax.measure.unit.Unit;
029import javax.swing.event.ChangeListener;
030import javolution.context.ObjectFactory;
031import javolution.lang.Immutable;
032import javolution.xml.XMLFormat;
033import javolution.xml.stream.XMLStreamException;
034import org.jscience.mathematics.vector.Float64Vector;
035
036/**
037 * A {@link GeomTransform} element that refers to a {@link GeomPoint} object and
038 * masquerades as a GeomPoint object itself.
039 *
040 * <p> Modified by: Joseph A. Huwaldt </p>
041 *
042 * @author Joseph A. Huwaldt, Date: April 30, 2009
043 * @version April 10, 2018
044 */
045@SuppressWarnings({"serial", "CloneableImplementsClone"})
046public final class GeomPointTrans extends GeomPoint implements GeomTransform<GeomPoint> {
047
048    /**
049     * The transformation represented by this transformation element.
050     */
051    private GTransform _TM;
052
053    /**
054     * The object that is the child of this transform.
055     */
056    private GeomPoint _child;
057
058    //  The child point with the transformation applied to it (an optimization).
059    private Point _pTrans;
060
061    /**
062     * Reference to a change listener for this object's child.
063     */
064    private ChangeListener _childChangeListener = new ForwardingChangeListener(this);
065
066    /**
067     * Returns a 3D {@link GeomPointTrans} instance holding the specified
068     * {@link GeomPoint} and {@link GTransform}.
069     *
070     * @param child     The point that is the child of this transform element (may not be
071     *                  <code>null</code>).
072     * @param transform The transform held by this transform element (may not be
073     *                  <code>null</code>).
074     * @return the transform element having the specified values.
075     * @throws DimensionException if the input element is not 3D.
076     */
077    public static GeomPointTrans newInstance(GeomPoint child, GTransform transform) {
078        requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child"));
079        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
080
081        if (child.getPhyDimension() != 3)
082            throw new DimensionException(
083                    MessageFormat.format(RESOURCES.getString("dimensionNot3"), "point", child.getPhyDimension()));
084
085        GeomPointTrans obj = FACTORY.object();
086        obj._TM = transform;
087        obj._child = child;
088        obj._pTrans = transform.transform(child);
089        child.copyState(obj._pTrans);
090        child.copyState(obj);
091
092        //  Listen for changes to the child object and pass them on.
093        if (!(child instanceof Immutable))
094            child.addChangeListener(obj._childChangeListener);
095
096        return obj;
097    }
098
099    /**
100     * Returns the transformation represented by this transformation element.
101     *
102     * @return The transformation represented by this transformation element.
103     */
104    @Override
105    public GTransform getTransform() {
106        return _TM;
107    }
108
109    /**
110     * Returns the total transformation represented by an entire chain of GeomTransform
111     * objects below this one.
112     *
113     * @return The total transformation represented by an entire chain of GeomTransform
114     *         objects below this one.
115     */
116    @Override
117    public GTransform getTotalTransform() {
118        return GeomUtil.getTotalTransform(this);
119    }
120
121    /**
122     * Sets the transformation represented by this transformation element.
123     *
124     * @param transform The transform to set this transform element to (may not be
125     *                  <code>null</code>).
126     */
127    @Override
128    public void setTransform(GTransform transform) {
129        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
130        _TM = transform;
131        _pTrans = transform.transform(_child);
132        _pTrans.setName(_child.getName());
133        _pTrans.putAllUserData(_child.getAllUserData());
134        fireChangeEvent();
135    }
136
137    /**
138     * Returns the child object transformed by this transform element.
139     *
140     * @return The child object transformed by this transform element.
141     */
142    @Override
143    public GeomPoint getChild() {
144        return _child;
145    }
146
147    /**
148     * Return a copy of the child object transformed by this transformation.
149     *
150     * @return A copy of the child object transformed by this transformation.
151     */
152    @Override
153    public Point copyToReal() {
154        return _pTrans.copy();
155    }
156
157    /**
158     * Recycles a <code>GeomPointTrans</code> instance immediately (on the stack when
159     * executing in a <code>StackContext</code>).
160     *
161     * @param instance The instance to be recycled.
162     */
163    public static void recycle(GeomPointTrans instance) {
164        FACTORY.recycle(instance);
165    }
166
167    /**
168     * Return an immutable version of this point.
169     *
170     * @return An immutable version of this plane.
171     */
172    @Override
173    public Point immutable() {
174        return copyToReal();
175    }
176
177    /**
178     * Returns the number of physical dimensions of the geometry element. This
179     * implementation always returns 3.
180     */
181    @Override
182    public int getPhyDimension() {
183        return 3;
184    }
185
186    /**
187     * Returns the value of the Parameter in this point as a <code>double</code>, stated
188     * in this point's {@link #getUnit unit}.
189     *
190     * @param i the dimension index.
191     * @return the value of the Parameter at <code>i</code>.
192     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; dimension())</code>
193     */
194    @Override
195    public double getValue(int i) {
196        return _pTrans.getValue(i);
197    }
198
199    /**
200     * Returns the value of a coordinate in this point as a <code>double</code>, stated in
201     * the specified unit.
202     *
203     * @param i    the dimension index.
204     * @param unit the unit to return the value in. May not be null.
205     * @return the value of the Parameter at <code>i</code> in the specified unit.
206     * @throws IndexOutOfBoundsException <code>(i &lt; 0) || (i &ge; dimension())</code>
207     */
208    @Override
209    public double getValue(int i, Unit<Length> unit) {
210        return _pTrans.getValue(i, requireNonNull(unit));
211    }
212
213    /**
214     * Returns the square of the Euclidean norm, magnitude, or length value of the vector
215     * from the origin to this point (the dot product of the origin-to-this-point vector
216     * and itself). This is slightly faster than calling <code>normValue</code> if the
217     * squared value is all that is needed.
218     *
219     * @return <code>this.normSq().getValue()</code>.
220     * @see #normValue() 
221     */
222    @Override
223    public double normSqValue() {
224        return _pTrans.normSqValue();
225    }
226    
227    /**
228     * Returns the sum of this point with the one specified. The unit of the output point
229     * will be the units of this point.
230     *
231     * @param that the point to be added. May not be null.
232     * @return <code>this + that</code>.
233     * @throws DimensionException if point dimensions are different.
234     */
235    @Override
236    public Point plus(GeomPoint that) {
237        return _pTrans.plus(that);
238    }
239
240    /**
241     * Adds the specified parameter to each component of this point. The unit of the
242     * output point will be the units of this point.
243     *
244     * @param that the parameter to be added to each component of this point. May not be
245     *             null.
246     * @return <code>this + that</code>.
247     */
248    @Override
249    public Point plus(Parameter<Length> that) {
250        return _pTrans.plus(that);
251    }
252
253    /**
254     * Returns the difference between this point and the one specified. The unit of the
255     * output point will be the units of this point.
256     *
257     * @param that the point to be subtracted from this point. May not be null.
258     * @return <code>this - that</code>.
259     * @throws DimensionException if point dimensions are different.
260     */
261    @Override
262    public Point minus(GeomPoint that) {
263        return _pTrans.minus(that);
264    }
265
266    /**
267     * Subtracts the specified parameter from each component of this point. The unit of
268     * the output point will be the units of this point.
269     *
270     * @param that the parameter to be subtracted from each component of this point. May
271     *             not be null.
272     * @return <code>this - that</code>.
273     */
274    @Override
275    public Point minus(Parameter<Length> that) {
276        return _pTrans.minus(that);
277    }
278
279    /**
280     * Returns the negation of this point (all the values of each dimension negated).
281     *
282     * @return <code>-this</code>
283     */
284    @Override
285    public Point opposite() {
286        return _pTrans.opposite();
287    }
288
289    /**
290     * Returns the product of this point with the specified coefficient.
291     *
292     * @param k the coefficient multiplier.
293     * @return <code>this ยท k</code>
294     */
295    @Override
296    public Point times(double k) {
297        return _pTrans.times(k);
298    }
299
300    /**
301     * Return <code>true</code> if this point contains valid and finite numerical
302     * components. A value of <code>false</code> will be returned if any of the coordinate
303     * values are NaN or Inf.
304     */
305    @Override
306    public boolean isValid() {
307        return _pTrans.isValid();
308    }
309
310    /**
311     * Returns a copy of this GeomPointTrans instance
312     * {@link javolution.context.AllocatorContext allocated} by the calling thread
313     * (possibly on the stack).
314     *
315     * @return an identical and independent copy of this point.
316     */
317    @Override
318    public GeomPointTrans copy() {
319        return copyOf(this);
320    }
321
322    /**
323     * Returns the unit in which the {@link #getValue values} in this point are stated in.
324     */
325    @Override
326    public Unit<Length> getUnit() {
327        return _child.getUnit();
328    }
329
330    /**
331     * Returns the equivalent of this GeomPointTrans object that has a child point in the
332     * specified units.
333     * <p>
334     * WARNING: If the unit changes, then the returned transform element DOES NOT refer
335     * back to the original point (the link with the original point is broken).
336     * </p>
337     *
338     * @param unit the length unit of the point to be returned. May not be null.
339     * @return an equivalent to this point but stated in the specified unit.
340     * @throws ConversionException if the the input unit is not a length unit.
341     */
342    @Override
343    public GeomPointTrans to(Unit<Length> unit) throws ConversionException {
344        if (unit.equals(getUnit()))
345            return this;
346
347        GeomPoint newP = _child.to(unit);
348        GeomPointTrans result = GeomPointTrans.newInstance(newP, _TM);
349
350        return result;
351    }
352
353    /**
354     * Returns a Vector3D representation of this transformed point if possible.
355     *
356     * @return A Vector3D that is equivalent to this transformed point
357     * @throws DimensionException if this point has any number of dimensions other than 3.
358     */
359    @Override
360    public Vector3D<Length> toVector3D() {
361        return _pTrans.toVector3D();
362    }
363
364    /**
365     * Returns the values stored in this transformed point, stated in this point's
366     * {@link #getUnit unit}, as a Float64Vector.
367     *
368     * @return A Float64Vector containing the values stored in this point in the current
369     *         units.
370     */
371    @Override
372    public Float64Vector toFloat64Vector() {
373        return _pTrans.toFloat64Vector();
374    }
375
376    /**
377     * Return the equivalent of this point converted to the specified number of physical
378     * dimensions. This implementation will throw an exception if the specified dimension
379     * is anything other than 3.
380     *
381     * @param newDim The dimension of the point to return. MUST equal 3.
382     * @return The equivalent of this point converted to the new dimensions.
383     * @throws IllegalArgumentException if the new dimension is anything other than 3.
384     */
385    @Override
386    public GeomPointTrans toDimension(int newDim) {
387        if (newDim == 3)
388            return this;
389
390        throw new IllegalArgumentException(
391                MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName()));
392    }
393
394    /**
395     * Compares this GeomPointTrans against the specified object for strict equality (same
396     * values and same units).
397     *
398     * @param obj the object to compare with.
399     * @return <code>true</code> if this point is identical to that point;
400     *         <code>false</code> otherwise.
401     */
402    @Override
403    public boolean equals(Object obj) {
404        if (this == obj)
405            return true;
406        if ((obj == null) || (obj.getClass() != this.getClass()))
407            return false;
408
409        GeomPointTrans that = (GeomPointTrans)obj;
410        return this._TM.equals(that._TM)
411                && this._child.equals(that._child)
412                && super.equals(obj);
413    }
414
415    /**
416     * Returns the hash code for this parameter.
417     *
418     * @return the hash code value.
419     */
420    @Override
421    public int hashCode() {
422        return 31*super.hashCode() + Objects.hash(_TM, _child);
423    }
424
425    /**
426     * Holds the default XML representation for this object.
427     */
428    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
429    protected static final XMLFormat<GeomPointTrans> XML = new XMLFormat<GeomPointTrans>(GeomPointTrans.class) {
430
431        @Override
432        public GeomPointTrans newInstance(Class<GeomPointTrans> cls, InputElement xml) throws XMLStreamException {
433            return FACTORY.object();
434        }
435
436        @Override
437        public void read(InputElement xml, GeomPointTrans obj) throws XMLStreamException {
438            GeomPoint.XML.read(xml, obj);     // Call parent read.
439
440            obj._TM = xml.getNext();
441            GeomPoint child = xml.getNext();
442            obj._child = child;
443
444            //  Listen for changes to the child object and pass them on.
445            if (!(child instanceof Immutable))
446                child.addChangeListener(obj._childChangeListener);
447
448            obj._pTrans = obj._TM.transform(obj._child);
449            obj._pTrans.setName(obj._child.getName());
450            obj._pTrans.putAllUserData(obj._child.getAllUserData());
451        }
452
453        @Override
454        public void write(GeomPointTrans obj, OutputElement xml) throws XMLStreamException {
455            GeomPoint.XML.write(obj, xml);    // Call parent write.
456
457            xml.add(obj._TM);
458            xml.add(obj._child);
459        }
460    };
461
462    ///////////////////////
463    // Factory creation. //
464    ///////////////////////
465    private GeomPointTrans() { }
466
467    @SuppressWarnings("unchecked")
468    private static final ObjectFactory<GeomPointTrans> FACTORY = new ObjectFactory<GeomPointTrans>() {
469        @Override
470        protected GeomPointTrans create() {
471            return new GeomPointTrans();
472        }
473
474        @Override
475        protected void cleanup(GeomPointTrans obj) {
476            obj.reset();
477            obj._TM = null;
478            if (!(obj._child instanceof Immutable))
479                obj._child.removeChangeListener(obj._childChangeListener);
480            obj._child = null;
481            obj._pTrans = null;
482        }
483    };
484
485    @SuppressWarnings("unchecked")
486    private static GeomPointTrans copyOf(GeomPointTrans original) {
487        GeomPointTrans obj = FACTORY.object();
488        obj._TM = original._TM.copy();
489        obj._child = original._child.copy();
490        obj._pTrans = original._pTrans.copy();
491        original.copyState(obj);
492        if (!(obj._child instanceof Immutable))
493            obj._child.addChangeListener(obj._childChangeListener);
494        return obj;
495    }
496
497    /**
498     * Tests the methods in this class.
499     *
500     * @param args Command-line arguments (not used).
501     */
502    public static void main(String args[]) {
503        System.out.println("Testing GeomPointTrans:  test = result [correct result]");
504
505        Parameter<Angle> psi = Parameter.valueOf(60, javax.measure.unit.NonSI.DEGREE_ANGLE);
506        Parameter<Angle> theta = Parameter.ZERO_ANGLE;
507        Parameter<Angle> phi = Parameter.valueOf(20, javax.measure.unit.NonSI.DEGREE_ANGLE);
508        DCMatrix dcm = DCMatrix.getEulerTM(psi, theta, phi);
509        GTransform T1 = GTransform.valueOf(dcm);
510        T1 = T1.applyScale(2.5);
511        Point p1 = Point.valueOf(1, 1, 1, SI.METER);
512
513        GeomPointTrans p1T = GeomPointTrans.newInstance(p1, T1);
514
515        System.out.println("\npsi = " + psi + ", theta = " + theta + ", phi = " + phi);
516        System.out.println("\np1  = " + p1);
517        System.out.println("  T1  = \n" + T1);
518        System.out.println("  p1T = " + p1T + " [-0.0439988715583743 m, 2.9121541062864 m, 3.20428191027894 m]");
519        System.out.println("  p1T.norm() = " + p1T.norm() + " [4.33012701892219 m]");
520
521        GTransform T2 = GTransform.newTranslation(Vector.valueOf(SI.METER, 2, -1, -3));
522        GeomPointTrans p2T = GeomPointTrans.newInstance(p1T, T2);
523        System.out.println("\ntranslate p1T by: 2, -1, -3 m");
524        System.out.println("  p2T = " + p2T + " [1.95600112844163 m, 1.9121541062864 m, 0.20428191027894 m]");
525        System.out.println("  p2T.norm() = " + p2T.norm() + " [2.74299195031995 m]");
526
527        //  Write out XML data.
528        try {
529            System.out.println();
530
531            // Creates some useful aliases for class names.
532            javolution.xml.XMLBinding binding = new GeomXMLBinding();
533
534            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
535            writer.setIndentation("    ");
536            writer.setBinding(binding);
537            writer.write(p1T, "GeomPointTrans", GeomPointTrans.class);
538            writer.flush();
539
540            System.out.println();
541        } catch (Exception e) {
542            e.printStackTrace();
543        }
544
545    }
546
547}