001/**
002 * CSTCurveTrans -- A GeomTransform that has a CSTCurve for a child.
003 *
004 * Copyright (C) 2015, by 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 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 Library 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.cst;
019
020import geomss.geom.*;
021import geomss.geom.nurbs.NurbsCurve;
022import jahuwaldt.js.param.Parameter;
023import java.text.MessageFormat;
024import java.util.List;
025import java.util.Objects;
026import static java.util.Objects.requireNonNull;
027import javax.measure.converter.ConversionException;
028import javax.measure.quantity.Dimensionless;
029import javax.measure.quantity.Length;
030import javax.measure.unit.Unit;
031import javax.swing.event.ChangeListener;
032import javolution.context.ObjectFactory;
033import javolution.context.StackContext;
034import javolution.lang.Immutable;
035import javolution.xml.XMLFormat;
036import javolution.xml.stream.XMLStreamException;
037
038/**
039 * A {@link GeomTransform} object that refers to a {@link CSTCurve} object and masquerades
040 * as a CSTCurve object itself.
041 *
042 * <p> Modified by: Joseph A. Huwaldt </p>
043 *
044 * @author Joseph A. Huwaldt, Date: October 7, 2015
045 * @version November 28, 2015
046 */
047@SuppressWarnings({"serial", "CloneableImplementsClone"})
048public final class CSTCurveTrans extends CSTCurve implements GeomTransform<CSTCurve> {
049
050    /**
051     * The transformation represented by this transformation element.
052     */
053    private GTransform _TM;
054
055    /**
056     * The object that is the child of this transform.
057     */
058    private CSTCurve _child;
059
060    /**
061     * Reference to a change listener for this object's child.
062     */
063    private ChangeListener _childChangeListener = new ForwardingChangeListener(this);
064
065    /**
066     * Returns a 3D {@link CSTCurveTrans} instance holding the specified {@link CSTCurve}
067     * and {@link GTransform}.
068     *
069     * @param child     The CSTCurve that is the child of this transform element (may not
070     *                  be <code>null</code>).
071     * @param transform The transform held by this transform element (may not be
072     *                  <code>null</code>).
073     * @return the transform element having the specified values.
074     */
075    public static CSTCurveTrans newInstance(CSTCurve child, GTransform transform) {
076        requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child"));
077        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
078
079        if (child.getPhyDimension() != 3)
080            throw new DimensionException(
081                    MessageFormat.format(RESOURCES.getString("dimensionNot3"), "CST curve", child.getPhyDimension()));
082
083        CSTCurveTrans obj = FACTORY.object();
084        obj._TM = transform;
085        obj._child = child;
086
087        //  Listen for changes to the child object and pass them on.
088        if (!(child instanceof Immutable))
089            child.addChangeListener(obj._childChangeListener);
090
091        return obj;
092    }
093
094    /**
095     * Returns the transformation represented by this transformation element.
096     *
097     * @return The transformation represented by this element.
098     */
099    @Override
100    public GTransform getTransform() {
101        return _TM;
102    }
103
104    /**
105     * Returns the total transformation represented by an entire chain of GeomTransform
106     * objects below this one.
107     *
108     * @return The total transformation represented by the entire chain of transform
109     *         objects.
110     */
111    @Override
112    public GTransform getTotalTransform() {
113        return GeomUtil.getTotalTransform(this);
114    }
115
116    /**
117     * Sets the transformation represented by this transformation element.
118     *
119     * @param transform The transform to set this transform element to (may not be
120     *                  <code>null</code>).
121     */
122    @Override
123    public void setTransform(GTransform transform) {
124        requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform"));
125        _TM = transform;
126        fireChangeEvent();
127    }
128
129    /**
130     * Returns the child object transformed by this transform element.
131     *
132     * @return The child object transformed by this element.
133     */
134    @Override
135    public CSTCurve getChild() {
136        return _child;
137    }
138
139    /**
140     * Return the class function associated with this CST curve.
141     *
142     * @return The class function associated with this CST curve.
143     */
144    @Override
145    public CSTClassFunction getCFunction() {
146        return _child.getCFunction();
147    }
148
149    /**
150     * Return the shape function associated with this CST curve.
151     *
152     * @return The shape function associated with this CST curve.
153     */
154    @Override
155    public CSTShapeFunction getSFunction() {
156        return _child.getSFunction();
157    }
158
159    /**
160     * Return the offset of the trailing edge of the curve from the chord line.
161     *
162     * @return The offset of the trailing edge of the curve from the chord line.
163     */
164    @Override
165    public Parameter<Length> getOffsetTE() {
166        //  It is possible that the transform scales the offset length.
167        StackContext.enter();
168        try {
169            Parameter<Length> dy = _child.getOffsetTE();
170            GeomVector<Length> vec = _child.getYhat().times(dy).to(dy.getUnit());
171            vec = vec.getTransformed(_TM);
172            return StackContext.outerCopy(vec.mag());
173        } finally {
174            StackContext.exit();
175        }
176    }
177
178    /**
179     * Return the chord length of the curve. This defines the scale or size of the curve.
180     *
181     * @return The chord length of the curve.
182     */
183    @Override
184    public Parameter<Length> getChord() {
185        //  It is possible that the transform scales the chord length.
186        StackContext.enter();
187        try {
188            Parameter<Length> chord = _child.getChord();
189            GeomVector<Length> vec = _child.getXhat().times(chord).to(chord.getUnit());
190            vec = vec.getTransformed(_TM);
191            return StackContext.outerCopy(vec.mag());
192        } finally {
193            StackContext.exit();
194        }
195    }
196
197    /**
198     * Return the origin of the leading edge of this curve. This defines the s=0 location
199     * of the curve in space.
200     *
201     * @return The origin of the leading edge of this curve.
202     */
203    @Override
204    public Point getOrigin() {
205        return _TM.transform(_child.getOrigin());
206    }
207
208    /**
209     * Return the chord-wise direction of this curve (the curve X-direction).
210     *
211     * @return The chord-wise direction of this curve.
212     */
213    @Override
214    public Vector<Dimensionless> getXhat() {
215        return _child.getXhat().getTransformed(_TM).immutable();
216    }
217
218    /**
219     * Return the in-plane direction of this curve perpendicular to the chord-wise
220     * direction (the curve Y-direction).
221     *
222     * @return The in-plane direction of this curve perpendicular to Xhat.
223     */
224    @Override
225    public Vector<Dimensionless> getYhat() {
226        return _child.getYhat().getTransformed(_TM).immutable();
227    }
228
229    /**
230     * Return a copy of the child object transformed by this transformation.
231     *
232     * @return A copy of this object with any transformations or subranges removed
233     *         (applied).
234     */
235    @Override
236    public BasicCSTCurve copyToReal() {
237        StackContext.enter();
238        try {
239            //  Get the transformed piece-parts.
240            CSTClassFunction Cf = getCFunction();
241            CSTShapeFunction Sf = getSFunction();
242            Parameter<Length> DZTE = getOffsetTE();
243            Parameter<Length> chord = getChord();
244            Point origin = getOrigin();
245            Vector<Dimensionless> xhat = getXhat();
246            Vector<Dimensionless> yhat = getYhat();
247
248            //  Create and return a new CST curve.
249            BasicCSTCurve crv = BasicCSTCurve.newInstance(Cf, Sf, DZTE, origin, chord, xhat, yhat);
250            return StackContext.outerCopy(crv);
251        } finally {
252            StackContext.exit();
253        }
254    }
255
256    /**
257     * Return an immutable version of this CST curve.
258     *
259     * @return an immutable version of this curve.
260     */
261    @Override
262    public BasicCSTCurve immutable() {
263        return copyToReal();
264    }
265
266    /**
267     * Return <code>true</code> if this curve contains valid and finite numerical
268     * components. A value of <code>false</code> will be returned if any of the coordinate
269     * values are NaN or Inf.
270     *
271     * @return true if this element contains valid and finite numerical values.
272     */
273    @Override
274    public boolean isValid() {
275        return _child.isValid();
276    }
277
278    /**
279     * Returns the number of physical dimensions of the geometry element.
280     */
281    @Override
282    public int getPhyDimension() {
283        return _child.getPhyDimension();
284    }
285
286    /**
287     * Calculate a point on the curve for the given parametric distance along the curve,
288     * <code>p(s)</code>.
289     *
290     * @param s parametric distance to calculate a point for (0.0 to 1.0 inclusive).
291     * @return the calculated point
292     */
293    @Override
294    public Point getRealPoint(double s) {
295        return _TM.transform(_child.getRealPoint(s));
296    }
297
298    /**
299     * Calculate all the derivatives from <code>0</code> to <code>grade</code> with
300     * respect to parametric distance on the curve for the given parametric distance along
301     * the curve, <code>d^{grade}p(s)/d^{grade}s</code>.
302     * <p>
303     * Example:<br>
304     * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br>
305     * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc.
306     * </p>
307     *
308     * @param s     Parametric distance to calculate derivatives for (0.0 to 1.0
309     *              inclusive).
310     * @param grade The maximum grade to calculate the derivatives for (1=1st derivative,
311     *              2=2nd derivative, etc)
312     * @return A list of derivatives up to the specified grade of the curve at the
313     *         specified parametric position.
314     * @throws IllegalArgumentException if the grade is &lt; 0.
315     */
316    @Override
317    public List<Vector<Length>> getSDerivatives(double s, int grade) {
318        BasicCSTCurve trans = copyToReal();
319        List<Vector<Length>> ders = trans.getSDerivatives(s, grade);
320        BasicCSTCurve.recycle(trans);
321        return ders;
322    }
323
324    /**
325     * Return the coordinate point representing the minimum bounding box corner of this
326     * geometry element (e.g.: min X, min Y, min Z).
327     *
328     * @return The minimum bounding box coordinate for this geometry element.
329     * @throws IndexOutOfBoundsException if this list contains no elements.
330     */
331    @Override
332    public Point getBoundsMin() {
333        BasicCSTCurve trans = copyToReal();
334        Point minPoint = trans.getBoundsMin();
335        BasicCSTCurve.recycle(trans);
336        return minPoint;
337    }
338
339    /**
340     * Return the coordinate point representing the maximum bounding box corner (e.g.: max
341     * X, max Y, max Z).
342     *
343     * @return The maximum bounding box coordinate for this geometry element.
344     * @throws IndexOutOfBoundsException if this list contains no elements.
345     */
346    @Override
347    public Point getBoundsMax() {
348        BasicCSTCurve trans = copyToReal();
349        Point maxPoint = trans.getBoundsMax();
350        BasicCSTCurve.recycle(trans);
351        return maxPoint;
352    }
353
354    /**
355     * Return a new curve that is identical to this one, but with the parameterization
356     * reversed. This does not change the origin or axis directions, but only the curve
357     * parameterization; what was s=0 will become s=1, etc
358     *
359     * @return A new curve identical to this one, but with the parameterization reversed.
360     */
361    @Override
362    public CSTCurveTrans reverse() {
363        CSTCurveTrans crv = CSTCurveTrans.newInstance(_child.reverse(), _TM);
364        return copyState(crv);
365    }
366
367    /**
368     * Split this curve at the specified parametric position returning a list containing
369     * two curves (a lower curve with smaller parametric positions than "s" and an upper
370     * curve with larger parametric positions). The origin, chord length, and trailing
371     * edge offset from chord-line will be identical for the the returned segments and
372     * this curve.  The only change is in the parameterization.
373     *
374     * @param s The parametric position where this curve should be split (must not be 0 or
375     *          1!).
376     * @return A list containing two curves: 0 == the lower curve, 1 == the upper curve.
377     */
378    @Override
379    public GeomList<CSTCurve> splitAt(double s) {
380
381        //  Split the parametric position curve.
382        GeomList<CSTCurve> pCrvs = _child.splitAt(s);
383
384        //  Create the output list.
385        GeomList<CSTCurve> output = GeomList.newInstance();
386
387        //  Create the lower curve.
388        output.add(CSTCurveTrans.newInstance(pCrvs.getFirst(), _TM));
389        output.add(CSTCurveTrans.newInstance(pCrvs.getLast(), _TM));
390
391        GeomList.recycle(pCrvs);
392
393        return output;
394    }
395    
396    /**
397     * Returns the unit in which the geometry of this curve is stated.
398     */
399    @Override
400    public Unit<Length> getUnit() {
401        return _child.getUnit();
402    }
403
404    /**
405     * Returns the equivalent to this curve but stated in the specified unit.
406     *
407     * @param unit the length unit of the curve to be returned. May not be null.
408     * @return an equivalent to this curve but stated in the specified unit.
409     * @throws ConversionException if the the input unit is not a length unit.
410     */
411    @Override
412    public CSTCurveTrans to(Unit<Length> unit) throws ConversionException {
413        if (unit.equals(getUnit()))
414            return this;
415
416        return CSTCurveTrans.newInstance(_child.to(unit), _TM);
417    }
418
419    /**
420     * Return the equivalent of this curve converted to the specified number of physical
421     * dimensions. This implementation will throw an exception if the specified dimension
422     * is anything other than 3.
423     *
424     * @param newDim The dimension of the curve to return. MUST equal 3.
425     * @return The equivalent of this curve converted to the new dimensions.
426     * @throws IllegalArgumentException if the new dimension is anything other than 3.
427     */
428    @Override
429    public CSTCurveTrans toDimension(int newDim) {
430        if (newDim == 3)
431            return this;
432
433        throw new IllegalArgumentException(
434                MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName()));
435    }
436
437    /**
438     * Return a NURBS curve representation of this curve to within the specified
439     * tolerance.
440     *
441     * @param tol The greatest possible difference between this curve and the NURBS
442     *            representation returned.
443     * @return A NURBS curve that represents this curve to within the specified tolerance.
444     */
445    @Override
446    public NurbsCurve toNurbs(Parameter<Length> tol) {
447        BasicCSTCurve crv = copyToReal();
448        NurbsCurve ncrv = crv.toNurbs(tol);
449        BasicCSTCurve.recycle(crv);
450        return ncrv;
451    }
452
453    /**
454     * Returns a copy of this CSTCurveTrans instance
455     * {@link javolution.context.AllocatorContext allocated} by the calling thread
456     * (possibly on the stack).
457     *
458     * @return an identical and independent copy of this point.
459     */
460    @Override
461    public CSTCurveTrans copy() {
462        return copyOf(this);
463    }
464
465    /**
466     * Compares this NurbsCurveTrans against the specified object for strict equality.
467     *
468     * @param obj the object to compare with.
469     * @return <code>true</code> if this transform is identical to that transform;
470     *         <code>false</code> otherwise.
471     */
472    @Override
473    public boolean equals(Object obj) {
474        if (this == obj)
475            return true;
476        if ((obj == null) || (obj.getClass() != this.getClass()))
477            return false;
478
479        CSTCurveTrans that = (CSTCurveTrans)obj;
480        return this._TM.equals(that._TM)
481                && this._child.equals(that._child)
482                && super.equals(obj);
483    }
484
485    /**
486     * Returns the hash code for this parameter.
487     *
488     * @return the hash code value.
489     */
490    @Override
491    public int hashCode() {
492        return 31*super.hashCode() + Objects.hash(_TM, _child);
493    }
494
495    /**
496     * Recycles a <code>CSTCurveTrans</code> instance immediately (on the stack when
497     * executing in a StackContext).
498     *
499     * @param instance The instance to be recycled.
500     */
501    public static void recycle(CSTCurveTrans instance) {
502        FACTORY.recycle(instance);
503    }
504
505    /**
506     * Holds the default XML representation for this object.
507     */
508    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
509    protected static final XMLFormat<CSTCurveTrans> XML = new XMLFormat<CSTCurveTrans>(CSTCurveTrans.class) {
510
511        @Override
512        public CSTCurveTrans newInstance(Class<CSTCurveTrans> cls, XMLFormat.InputElement xml) throws XMLStreamException {
513            return FACTORY.object();
514        }
515
516        @Override
517        public void read(XMLFormat.InputElement xml, CSTCurveTrans obj) throws XMLStreamException {
518            CSTCurve.XML.read(xml, obj);     // Call parent read.
519
520            obj._TM = xml.getNext();
521            CSTCurve child = xml.getNext();
522            obj._child = child;
523
524            //  Listen for changes to the child object and pass them on.
525            if (!(child instanceof Immutable))
526                child.addChangeListener(obj._childChangeListener);
527        }
528
529        @Override
530        public void write(CSTCurveTrans obj, XMLFormat.OutputElement xml) throws XMLStreamException {
531            CSTCurve.XML.write(obj, xml);    // Call parent write.
532
533            xml.add(obj._TM);
534            xml.add(obj._child);
535
536        }
537    };
538
539    //////////////////////
540    // Factory Creation //
541    //////////////////////
542    private static final ObjectFactory<CSTCurveTrans> FACTORY = new ObjectFactory<CSTCurveTrans>() {
543        @Override
544        protected CSTCurveTrans create() {
545            return new CSTCurveTrans();
546        }
547
548        @Override
549        protected void cleanup(CSTCurveTrans obj) {
550            obj.reset();
551            obj._TM = null;
552            if (Objects.nonNull(obj._childChangeListener))
553                obj._child.removeChangeListener(obj._childChangeListener);
554            obj._child = null;
555        }
556    };
557
558    @SuppressWarnings("unchecked")
559    private static CSTCurveTrans copyOf(CSTCurveTrans original) {
560        CSTCurveTrans obj = FACTORY.object();
561        obj._TM = original._TM.copy();
562        obj._child = original._child.copy();
563        original.copyState(obj);
564        if (!(obj._child instanceof Immutable))
565            obj._child.addChangeListener(obj._childChangeListener);
566        return obj;
567    }
568
569    /**
570     * Do not allow the default constructor to be used.
571     */
572    private CSTCurveTrans() { }
573
574}