001/*
002 *   TriangleTrans  -- A GeomTransform that has a Triangle for a child.
003 *
004 *   Copyright (C) 2015-2018, by Joseph A. Huwaldt
005 *   All rights reserved.
006 *   
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2 of the License, or (at your option) any later version.
011 *   
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Library General Public License for more details.
016 *
017 *  You should have received a copy of the GNU Lesser General Public License
018 *  along with this program; if not, write to the Free Software
019 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *  Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package geomss.geom;
023
024import jahuwaldt.js.param.Parameter;
025import java.text.MessageFormat;
026import java.util.Objects;
027import static java.util.Objects.requireNonNull;
028import javax.measure.converter.ConversionException;
029import javax.measure.quantity.Area;
030import javax.measure.quantity.Dimensionless;
031import javax.measure.quantity.Length;
032import javax.measure.unit.Unit;
033import javax.swing.event.ChangeListener;
034import javolution.context.ObjectFactory;
035import javolution.lang.Immutable;
036import javolution.xml.XMLFormat;
037import javolution.xml.stream.XMLStreamException;
038
039/**
040 * A {@link GeomTransform} object that refers to a {@link Triangle} object
041 * and masquerades as a Triangle object itself.
042 *
043 * <p> Modified by: Joseph A. Huwaldt</p>
044 *
045 * @author Joseph A. Huwaldt, Date: August 26, 2015
046 * @version April 10, 2018
047 */
048@SuppressWarnings({"serial", "CloneableImplementsClone"})
049public final class TriangleTrans extends GeomTriangle implements GeomTransform<GeomTriangle> {
050
051    /**
052     * The transformation represented by this transformation element.
053     */
054    private GTransform _TM;
055
056    /**
057     * The object that is the child of this transform.
058     */
059    private GeomTriangle _child;
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 TriangleTrans} instance holding the specified
068     * {@link GeomTriangle} and {@link GTransform}.
069     *
070     * @param child     The GeomTriangle that is the child of this transform element (may
071     *                  not be <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     */
076    public static TriangleTrans newInstance(GeomTriangle child, GTransform transform) {
077        requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child"));
078        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
079
080        if (child.getPhyDimension() != 3)
081            throw new DimensionException(
082                    MessageFormat.format(RESOURCES.getString("dimensionNot3"), 
083                            "triangle", String.valueOf(child.getPhyDimension())));
084        
085        TriangleTrans obj = FACTORY.object();
086        obj._TM = transform;
087        obj._child = child;
088        child.copyState(obj);
089
090        //  Listen for changes to the child object and pass them on.
091        if (!(child instanceof Immutable))
092            child.addChangeListener(obj._childChangeListener);
093
094        return obj;
095    }
096
097    /**
098     * Returns the transformation represented by this transformation element.
099     *
100     * @return The transformation from the child object represented by this
101     * element.
102     */
103    @Override
104    public GTransform getTransform() {
105        return _TM;
106    }
107
108    /**
109     * Returns the total transformation represented by an entire chain of
110     * GeomTransform objects below this one.
111     *
112     * @return The total transformation represented by the entire chain of
113     * objects below this one.
114     */
115    @Override
116    public GTransform getTotalTransform() {
117        return GeomUtil.getTotalTransform(this);
118    }
119
120    /**
121     * Sets the transformation represented by this transformation element.
122     *
123     * @param transform The transform to set this transform element to (may not
124     * be <code>null</code>).
125     */
126    @Override
127    public void setTransform(GTransform transform) {
128        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
129        _TM = transform;
130        fireChangeEvent();
131    }
132
133    /**
134     * Returns the child object transformed by this transform element.
135     *
136     * @return The child object transformed by this transform element.
137     */
138    @Override
139    public GeomTriangle getChild() {
140        return _child;
141    }
142
143    /**
144     * Return a copy of the child object transformed by this transformation.
145     *
146     * @return A copy of the child object transformed by this transformation.
147     */
148    @Override
149    public Triangle copyToReal() {
150        Point p1 = getP1().copyToReal();
151        Point p2 = getP2().copyToReal();
152        Point p3 = getP3().copyToReal();
153        Triangle T = Triangle.valueOf(p1, p2, p3);
154        return copyState(T);    //  Copy over the super-class state for this object to the new one.
155    }
156
157    /**
158     * Return the first vertex in this triangle.
159     *
160     * @return The first vertex in this triangle.
161     */
162    @Override
163    public GeomPoint getP1() {
164        return _child.getP1().getTransformed(_TM);
165    }
166
167    /**
168     * Return the second vertex in this triangle.
169     *
170     * @return The second vertex in this triangle.
171     */
172    @Override
173    public GeomPoint getP2() {
174        return _child.getP2().getTransformed(_TM);
175    }
176
177    /**
178     * Return the third and last vertex in this triangle.
179     *
180     * @return The third and last vertex in this triangle.
181     */
182    @Override
183    public GeomPoint getP3() {
184        return _child.getP3().getTransformed(_TM);
185    }
186
187    /**
188     * Return the surface unit normal vector for this triangle.
189     * If the triangle is degenerate (zero area), then the
190     * normal vector will have zero length.
191     * 
192     * @return The surface normal vector for this triangle.
193     */
194    @Override
195    public VectorTrans<Dimensionless> getNormal() {
196        return _child.getNormal().getTransformed(_TM);
197    }
198    
199    /**
200     * Return the surface area of one side of this triangle.
201     * The returned area is always positive, but can be zero.
202     * 
203     * @return The surface area of one side of this triangle.
204     */
205    @Override
206    public Parameter<Area> getArea() {
207        return _child.getArea();
208    }
209    
210    /**
211     * Returns the number of physical dimensions of the geometry element.
212     */
213    @Override
214    public int getPhyDimension() {
215        return _child.getPhyDimension();
216    }
217
218    /**
219     * Return a new triangle that is identical to this one, but with the order
220     * of the points (and the surface normal direction) reversed.
221     *
222     * @return A new Triangle that is identical to this one, but with the order
223     * of the points reversed.
224     */
225    @Override
226    public TriangleTrans reverse() {
227        return TriangleTrans.newInstance(_child.reverse(), _TM);
228    }
229
230    /**
231     * Return the coordinate point representing the minimum bounding box corner
232     * of this geometry element (e.g.: min X, min Y, min Z).
233     *
234     * @return The minimum bounding box coordinate for this geometry element.
235     * @throws IndexOutOfBoundsException if this list contains no elements.
236     */
237    @Override
238    public Point getBoundsMin() {
239        Triangle trans = copyToReal();
240        Point minPoint = trans.getBoundsMin();
241        return minPoint;
242    }
243
244    /**
245     * Return the coordinate point representing the maximum bounding box corner
246     * (e.g.: max X, max Y, max Z).
247     *
248     * @return The maximum bounding box coordinate for this geometry element.
249     * @throws IndexOutOfBoundsException if this list contains no elements.
250     */
251    @Override
252    public Point getBoundsMax() {
253        Triangle trans = copyToReal();
254        Point maxPoint = trans.getBoundsMax();
255        return maxPoint;
256    }
257
258    /**
259     * Returns transformed version of this element. The returned object
260     * implements {@link GeomTransform} and contains this element as a child.
261     *
262     * @param transform The transformation to apply to this geometry. May not be null.
263     * @return A new line segment that is identical to this one with the
264     * specified transformation applied.
265     * @throws DimensionException if this point is not 3D.
266     */
267    @Override
268    public TriangleTrans getTransformed(GTransform transform) {
269        return TriangleTrans.newInstance(this, requireNonNull(transform));
270    }
271
272    /**
273     * Returns the unit in which this triangle is stated.
274     */
275    @Override
276    public Unit<Length> getUnit() {
277        return _child.getUnit();
278    }
279
280    /**
281     * Returns the equivalent to this element but stated in the specified unit.
282     *
283     * @param unit the length unit of the element to be returned. May not be null.
284     * @return an equivalent to this element but stated in the specified unit.
285     * @throws ConversionException if the the input unit is not a length unit.
286     */
287    @Override
288    public TriangleTrans to(Unit<Length> unit) throws ConversionException {
289        if (unit.equals(getUnit()))
290            return this;
291
292        return TriangleTrans.newInstance(_child.to(unit), _TM);
293    }
294
295    /**
296     * Return a copy of this Triangle converted to the specified number of
297     * physical dimensions. If the number of dimensions is greater than this
298     * element, then zeros are added to the additional dimensions. If the number
299     * of dimensions is less than this element, then the extra dimensions are
300     * simply dropped (truncated). If the new dimensions are the same as the
301     * dimension of this element, then this element is simply returned.
302     *
303     * @param newDim The dimension of the Triangle to return.
304     * @return This Triangle converted to the new dimensions.
305     * @throws IllegalArgumentException if the new dimension is anything other
306     * than 3.
307     */
308    @Override
309    public TriangleTrans toDimension(int newDim) {
310        if (newDim == 3)
311            return this;
312
313        throw new IllegalArgumentException(
314                MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName()));
315    }
316
317    /**
318     * Returns a copy of this TriangleTrans instance allocated by the calling
319     * thread (possibly on the stack).
320     *
321     * @return an identical and independent copy of this point.
322     */
323    @Override
324    public TriangleTrans copy() {
325        return TriangleTrans.copyOf(this);
326    }
327
328    /**
329     * Compares this TriangleTrans against the specified object for strict
330     * equality.
331     *
332     * @param obj the object to compare with.
333     * @return <code>true</code> if this transform is identical to that
334     * transform; <code>false</code> otherwise.
335     */
336    @Override
337    public boolean equals(Object obj) {
338        if (this == obj)
339            return true;
340        if ((obj == null) || (obj.getClass() != this.getClass()))
341            return false;
342
343        TriangleTrans that = (TriangleTrans)obj;
344        return this._TM.equals(that._TM)
345                && this._child.equals(that._child)
346                && super.equals(obj);
347    }
348
349    /**
350     * Returns the hash code for this parameter.
351     *
352     * @return the hash code value.
353     */
354    @Override
355    public int hashCode() {
356        return 31*super.hashCode() + Objects.hash(_TM, _child);
357    }
358
359    /**
360     * Holds the default XML representation for this object.
361     */
362    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
363    protected static final XMLFormat<TriangleTrans> XML = new XMLFormat<TriangleTrans>(TriangleTrans.class) {
364
365        @Override
366        public TriangleTrans newInstance(Class<TriangleTrans> cls, InputElement xml) throws XMLStreamException {
367            return FACTORY.object();
368        }
369
370        @Override
371        public void read(InputElement xml, TriangleTrans obj) throws XMLStreamException {
372            GeomTriangle.XML.read(xml, obj);     // Call parent read.
373
374            obj._TM = xml.getNext();
375            GeomTriangle child = xml.getNext();
376            obj._child = xml.getNext();
377
378            //  Listen for changes to the child object and pass them on.
379            if (!(child instanceof Immutable))
380                child.addChangeListener(obj._childChangeListener);
381        }
382
383        @Override
384        public void write(TriangleTrans obj, OutputElement xml) throws XMLStreamException {
385            GeomTriangle.XML.write(obj, xml);    // Call parent write.
386
387            xml.add(obj._TM);
388            xml.add(obj._child);
389
390        }
391    };
392
393    //////////////////////
394    // Factory Creation //
395    //////////////////////
396    private static final ObjectFactory<TriangleTrans> FACTORY = new ObjectFactory<TriangleTrans>() {
397        @Override
398        protected TriangleTrans create() {
399            return new TriangleTrans();
400        }
401
402        @Override
403        protected void cleanup(TriangleTrans obj) {
404            obj.reset();
405            obj._TM = null;
406            if (!(obj._child instanceof Immutable))
407                obj._child.removeChangeListener(obj._childChangeListener);
408            obj._child = null;
409        }
410    };
411
412    /**
413     * Recycles a <code>TriangleTrans</code> instance immediately (on the stack
414     * when executing in a StackContext).
415     *
416     * @param instance The instance to recycle immediately.
417     */
418    public static void recycle(TriangleTrans instance) {
419        FACTORY.recycle(instance);
420    }
421
422    @SuppressWarnings("unchecked")
423    private static TriangleTrans copyOf(TriangleTrans original) {
424        TriangleTrans obj = FACTORY.object();
425        obj._TM = original._TM.copy();
426        obj._child = original._child.copy();
427        original.copyState(obj);
428        if (!(obj._child instanceof Immutable))
429            obj._child.addChangeListener(obj._childChangeListener);
430        return obj;
431    }
432
433    /**
434     * Do not allow the default constructor to be used except by subclasses.
435     */
436    private TriangleTrans() { }
437
438}