001/*
002*   LineSeg  -- A concrete line segment in nD space.
003*
004*   Copyright (C) 2012-2015, 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.1 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*   Lesser 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.util.List;
026import java.util.Objects;
027import javax.measure.converter.ConversionException;
028import javax.measure.quantity.Dimensionless;
029import javax.measure.quantity.Length;
030import javax.measure.unit.Unit;
031import javolution.context.ObjectFactory;
032import javolution.lang.ValueType;
033import javolution.util.FastTable;
034import javolution.xml.XMLFormat;
035import javolution.xml.stream.XMLStreamException;
036
037
038/**
039*  A concrete line segment in n-dimensional space.
040*
041*  <p>  Modified by:  Joseph A. Huwaldt   </p>
042*
043*  @author  Joseph A. Huwaldt    Date:  January 14, 2012
044*  @version November 27, 2015
045*/
046@SuppressWarnings({"serial", "CloneableImplementsClone"})
047public final class LineSeg extends LineSegment implements ValueType {
048
049    
050    /**
051    *  The ends of the line segment.
052    */
053    private Point _p0, _p1;
054    
055    /**
056     *  The length of the line segment.
057     */
058    private Parameter<Length> _length;
059    
060    /**
061     *  Unit direction vector for the line segment.
062     */
063    private Vector<Dimensionless> _u;
064    
065    /**
066     * Dimensional vector for the line segment.
067     */
068    private Vector<Length> _Ds;
069    
070    
071    
072    /**
073    * Returns a <code>LineSeg</code> instance with the specified end points.
074    * The units of the curve will be the units of the start point.
075    *
076    * @param p0  The start (beginning) of the line segment. May not be null.
077    * @param p1  The end of the line segment. May not be null.
078    * @return A <code>LineSeg</code> instance that represents a line between the input points.
079    */
080    public static LineSeg valueOf(Point p0, Point p1) {
081        
082        //  Check the dimensionality.
083        int numDims = Math.max(p0.getPhyDimension(), p1.getPhyDimension());
084        
085        LineSeg L = newInstance();
086        L._p0 = p0.immutable().toDimension(numDims);
087        L._p1 = p1.immutable().to(p0.getUnit()).toDimension(numDims);
088        
089        computeLineData(L);
090        
091        return L;
092    }
093    
094    /**
095     * Returns a <code>LineSeg</code> instance with the specified start point and
096     * direction/length vector. The units of the curve will be the units of the start
097     * point.
098     *
099     * @param p0   The start (beginning) of the line segment. May not be null.
100     * @param Ldir The vector defining the direction and length of the line segment. May
101     *             not be null.
102     * @return A line segment from the input point in the direction and length of the
103     *         input vector.
104     */
105    public static LineSeg valueOf(Point p0, GeomVector<Length> Ldir) {
106        
107        //  Check the dimensionality.
108        int numDims = Math.max(p0.getPhyDimension(), Ldir.getPhyDimension());
109        
110        LineSeg L = newInstance();
111        L._p0 = p0.immutable().toDimension(numDims);
112        L._p1 = L._p0.plus(Point.valueOf(Ldir));
113        
114        computeLineData(L);
115        
116        return L;
117    }
118    
119    /**
120     * Returns a <code>LineSeg</code> that represents a straight line between the end
121     * points of the input curve. WARNING: No check is made that the input curve is truly
122     * a straight line. The end points of the input curve are simply used to define a
123     * straight line segment.
124     *
125     * @param lineCrv A curve from which the end points will be used to define a line
126     *                segment. May not be null.
127     * @return The line segment between the end points of the input curve.
128     * @see Curve#isLine
129     */
130    public static LineSeg valueOf(Curve lineCrv) {
131        
132        LineSeg L = newInstance();
133        L._p0 = lineCrv.getRealPoint(0);
134        L._p1 = lineCrv.getRealPoint(1);
135        
136        computeLineData(L);
137        
138        return L;
139    }
140    
141    /**
142     *  Compute values defined by the end points of the line segment.
143     *  It is assumed that the maximum dimension and units have
144     *  been defined for p0 and p1 before calling this method.
145     */
146    private static void computeLineData(LineSeg L) {
147        Point p0 = L._p0;
148        Point p1 = L._p1;
149        
150        L._length = p0.distance(p1);
151        L._Ds = p1.minus(p0).toGeomVector();
152        L._u = L._Ds.toUnitVector();
153    }
154    
155    /**
156     * Recycles a <code>LineSeg</code> instance immediately (on the stack when
157     * executing in a <code>StackContext</code>).
158     *
159     * @param instance The instance to recycle immediately.
160     */
161    public static void recycle(LineSeg instance) {
162        FACTORY.recycle(instance);
163    }
164    
165
166    /**
167     * Return the starting point of the line segment.
168     *
169     * @return The starting point for this line segment.
170     */
171    @Override
172    public Point getStart() {
173        return _p0;
174    }
175    
176    /**
177     * Return the end point of the line segment.
178     *
179     * @return The end point of this line segment.
180     */
181    @Override
182    public Point getEnd() {
183        return _p1;
184    }
185    
186    /**
187     *  Get a unit direction vector for the line segment.
188     * 
189     * @return A unit vector indicating the direction of this line segment.
190     */
191    @Override
192    public Vector<Dimensionless> getUnitVector() {
193        return _u.copy();
194    }
195    
196    /**
197     *  Return the dimensional derivative vector for this line segment.
198     *  The length of this vector is the length of the line segment,
199     *  the origin is at the start point and the end of the vector is the line end.
200     * 
201     * @return The dimensional derivative vector for this line segment.
202     */
203    @Override
204    public GeomVector<Length> getDerivativeVector() {
205        Vector<Length> D = _Ds.copy();
206        D.setOrigin(_p0);
207        return D;
208    }
209    
210    /**
211    * Calculate a point on the curve for the given parametric distance
212    * along the curve.
213    *
214    * @param s  parametric distance to calculate a point for (0.0 to 1.0 inclusive).
215    * @return the calculated point
216    */
217    @Override
218    public Point getRealPoint(double s) {
219        validateParameter(s);
220        
221        if (parNearStart(s, TOL_S))
222            return _p0;
223        else if (parNearEnd(s, TOL_S))
224            return _p1;
225        
226        Point p = _p0.plus(Point.valueOf(_Ds.times(s)));
227        
228        return p;
229    }
230    
231    /**
232    * Calculate all the derivatives from <code>0</code> to <code>grade</code> with respect
233    * to parametric distance on the curve for the given parametric distance along the curve,
234    * <code>d^{grade}p(s)/d^{grade}s</code>.
235    * <p>
236    * Example:<br>
237    * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br>
238    * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc.
239    * </p>
240    *
241    * @param s     Parametric distance to calculate derivatives for (0.0 to 1.0 inclusive).
242    * @param grade The maximum grade to calculate the derivatives for (1=1st derivative, 2=2nd derivative, etc)
243    * @return A list of derivatives up to the specified grade of the curve at the specified parametric position.
244    * @throws IllegalArgumentException if the grade is < 0.
245    */
246    @Override
247    public List<Vector<Length>> getSDerivatives(double s, int grade) {
248        validateParameter(s);
249        if (grade < 0)
250            throw new IllegalArgumentException(RESOURCES.getString("gradeLTZeroErr"));
251        
252        //  Create a list to hold the output.
253        List<Vector<Length>> output = FastTable.newInstance();
254        
255        //  Calculate the point.
256        Point p = getRealPoint(s);
257        Vector<Length> Pv = p.toGeomVector();
258        output.add(Pv);
259        
260        //  Calculate each requested derivative.
261        int der = 1;
262        for (int i=1; i <= grade; ++i, ++der) {
263            
264            if (i == 1) {
265                //  Copy the derivative vector.
266                Vector<Length> Dv = _Ds.copy();
267                Dv.setOrigin(p);
268                output.add(Dv);
269                
270            } else {
271                //  High derivatives are all zero.
272                Vector<Length> Dv = Vector.newInstance(getPhyDimension(), getUnit());
273                Dv.setOrigin(p);
274                output.add(Dv);
275            }
276        }
277        
278        return output;
279    }
280    
281    /**
282    *  Return the total arc length of this curve.
283    *
284    *  @param eps The desired fractional accuracy on the arc-length.  For this
285    *             curve type, this parameter is ignored and the exact arc
286    *             length is always returned.
287    *  @return The total arc length of the curve.
288    */
289    @Override
290    public Parameter<Length> getArcLength(double eps) {
291        return _length;
292    }
293
294    /**
295     * Return a new curve that is identical to this one, but with the
296     * parameterization reversed.
297     *
298     * @return A new curve that is identical to this one, but with the
299     * parameterization reversed.
300     */
301    @Override
302    public LineSegment reverse() {
303        
304        //  Create the reversed curve.
305        LineSeg crv = LineSeg.valueOf(_p1, _p0);
306        
307        return copyState(crv);  //  Copy over the super-class state for this object to the new one.
308    }
309    
310    /**
311    *  Return the coordinate point representing the
312    *  minimum bounding box corner of this geometry element (e.g.: min X, min Y, min Z).
313    *
314    *  @return The minimum bounding box coordinate for this geometry element.
315    */
316    @Override
317    public Point getBoundsMin() {
318        return _p0.min(_p1);
319    }
320
321    /**
322    *  Return the coordinate point representing the
323    *  maximum bounding box corner (e.g.: max X, max Y, max Z).
324    *
325    *  @return The maximum bounding box coordinate for this geometry element.
326    */
327    @Override
328    public Point getBoundsMax() {
329        return _p0.max(_p1);
330    }
331
332    /**
333    *  Return a copy of this curve converted to the specified number
334    *  of physical dimensions.  If the number of dimensions is greater than
335    *  this element, then zeros are added to the additional dimensions.
336    *  If the number of dimensions is less than this element, then
337    *  the extra dimensions are simply dropped (truncated).  If
338    *  the new dimensions are the same as the dimension of this element,
339    *  then this element is simply returned.
340    *
341    *  @param newDim  The dimension of the curve to return.
342    *  @return This curve converted to the new dimensions.
343    */
344    @Override
345    public LineSeg toDimension(int newDim) {
346        if (getPhyDimension() == newDim)    return this;
347        
348        LineSeg crv = LineSeg.valueOf(_p0.toDimension(newDim), _p1.toDimension(newDim));
349        
350        return copyState(crv);  //  Copy over the super-class state for this object to the new one.
351    }
352    
353    /**
354    * Returns the equivalent to this element but stated in the 
355    * specified unit. 
356    *
357    * @param  unit the length unit of the element to be returned. May not be null.
358    * @return an equivalent to this element but stated in the specified unit.
359    * @throws ConversionException if the the input unit is not a length unit.
360    */
361    @Override
362    public LineSeg to(Unit<Length> unit) throws ConversionException {
363        if (unit.equals(getUnit()))
364            return this;
365        LineSeg crv = LineSeg.valueOf(_p0.to(unit), _p1.to(unit));
366        return copyState(crv);  //  Copy over the super-class state for this object to the new one.
367    }
368    
369    /**
370    * Returns a copy of this LineSeg instance  
371    * {@link javolution.context.AllocatorContext allocated} 
372    * by the calling thread (possibly on the stack).
373    *   
374    * @return an identical and independent copy of this line segment.
375    */
376    @Override
377    public LineSeg copy() {
378        return copyOf(this);
379    }
380    
381    /**
382     * Return a copy of this object with any transformations or subranges
383     * removed (applied).
384     *
385     * @return A copy of this object with any transformations or subranges
386     *      removed (applied).
387     */
388    @Override
389    public LineSeg copyToReal() {
390        return copy();
391    }
392    
393    /**
394    * Compares this LineSeg against the specified object for strict 
395    * equality.
396    *
397    * @param  obj the object to compare with.
398    * @return <code>true</code> if this LineSeg is identical to that
399    *       LineSeg; <code>false</code> otherwise.
400    */
401    @Override
402    public boolean equals(Object obj) {
403        if (this == obj)
404            return true;
405        if ((obj == null) || (obj.getClass() != this.getClass()))
406            return false;
407        
408        LineSeg that = (LineSeg)obj;
409        return this._p0.equals(that._p0)
410                && this._p1.equals(that._p1)
411                && super.equals(obj);
412    }
413
414    /**
415    * Returns the hash code for this parameter.
416    * 
417    * @return the hash code value.
418    */
419    @Override
420    public int hashCode() {
421        return 31*super.hashCode() + Objects.hash(_p0, _p1);
422    }
423
424    /**
425    * Holds the default XML representation for this object.
426    */
427    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
428    protected static final XMLFormat<LineSeg> XML = new XMLFormat<LineSeg>(LineSeg.class) {
429
430        @Override
431        public LineSeg newInstance(Class<LineSeg> cls, InputElement xml) throws XMLStreamException {
432            return FACTORY.object();
433        }
434
435        @Override
436        public void read(InputElement xml, LineSeg obj) throws XMLStreamException {
437            LineSegment.XML.read(xml, obj);     // Call parent read.
438            
439            obj._p0 = xml.getNext();
440            obj._p1 = xml.getNext();
441            
442            computeLineData(obj);
443        }
444
445        @Override
446        public void write(LineSeg obj, OutputElement xml) throws XMLStreamException {
447            LineSegment.XML.write(obj, xml);    // Call parent write.
448            
449            xml.add(obj._p0);
450            xml.add(obj._p1);
451            
452        }
453    };
454    
455    
456    ///////////////////////
457    // Factory creation. //
458    ///////////////////////
459
460    private LineSeg() {}
461    
462    private static final ObjectFactory<LineSeg> FACTORY = new ObjectFactory<LineSeg>() {
463        @Override
464        protected LineSeg create() {
465            return new LineSeg();
466        }
467        @Override
468        protected void cleanup(LineSeg obj) {
469            obj.reset();
470            obj._p0 = null;
471            obj._p1 = null;
472        }
473    };
474
475    private static LineSeg copyOf(LineSeg original) {
476        LineSeg o = FACTORY.object();
477        o._p0 = original._p0.copy();
478        o._p1 = original._p1.copy();
479        computeLineData(o);
480        original.copyState(o);
481        return o;
482    }
483
484    private static LineSeg newInstance() {
485        return FACTORY.object();
486    }
487    
488}
489
490