001/**
002 * CSTShapeFunction -- A generic CST Shape Function implementation.
003 *
004 * Copyright (C) 2014-2016, 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.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 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.Point;
021import geomss.geom.Vector;
022import geomss.geom.nurbs.*;
023import java.util.List;
024import java.util.ResourceBundle;
025import javax.measure.quantity.Length;
026import javax.measure.unit.SI;
027import javolution.context.ArrayFactory;
028import javolution.context.ObjectFactory;
029import javolution.context.StackContext;
030import javolution.lang.ValueType;
031import javolution.util.FastTable;
032import javolution.xml.XMLFormat;
033import javolution.xml.XMLSerializable;
034import javolution.xml.stream.XMLStreamException;
035
036/**
037 * A generic Class-Shape-Transform (CST) shape function implementation.
038 *
039 * <p> Modified by: Joseph A. Huwaldt </p>
040 *
041 * @author Joseph A. Huwaldt Date: March 14, 2014
042 * @version January 8, 2016
043 */
044@SuppressWarnings({"serial", "CloneableImplementsClone"})
045public final class CSTShapeFunction implements Cloneable, XMLSerializable, ValueType {
046    // Reference: Kulfan, B.M., Bussoletti, J.E., "'Fundamental' Parametric Geometry
047    //          Representations for Aircraft Component Shapes", AIAA-2006-6948.
048    /**
049     * The resource bundle for this package.
050     */
051    private static final ResourceBundle RESOURCES = CSTCurve.RESOURCES;
052
053    private BasicNurbsCurve Scrv;
054
055    /**
056     * Construct a new shape function of the specified order from the specified list of
057     * Bernstein Polynomial coefficients.
058     *
059     * @param order  The order of the shape function to return.
060     * @param Acoefs The coefficients of the Bernstein Polynomial used to construct the
061     *               shape function. If more than "order" coefficients are provided then
062     *               the additional coefficients are ignored. May not be null.
063     * @return A new shape function of the specified order from the specified list of
064     *         Bernstein Polynomial coefficients.
065     */
066    public static CSTShapeFunction newInstance(int order, double... Acoefs) {
067        if (Acoefs.length < order)
068            throw new IllegalArgumentException(RESOURCES.getString("orderNumCoefsErr"));
069
070        //  Create a Bezier knot vector of the correct degree.
071        int degree = order - 1;
072        KnotVector kv = KnotVector.bezierKnotVector(degree);
073
074        //  Turn the input coefficients into a list of 1D, unweighted, control points.
075        FastTable<ControlPoint> cpLst = FastTable.newInstance();
076        for (int i = 0; i < order; ++i) {
077            double Ai = Acoefs[i];
078            Point p = Point.valueOf(Ai, SI.METER);
079            ControlPoint cp = ControlPoint.valueOf(p, 1);
080            cpLst.add(cp);
081        }
082
083        //  Create the defining Bezier curve.
084        BasicNurbsCurve Scrv = BasicNurbsCurve.newInstance(cpLst, kv);
085        FastTable.recycle(cpLst);
086
087        //  Construct a new CSTShapeFunction and fill in the inputs.
088        CSTShapeFunction obj = FACTORY.object();
089        obj.Scrv = Scrv;
090
091        return obj;
092    }
093
094    /**
095     * Construct a new shape function from the specified 1D basis function Bezier curve.
096     *
097     * @param bfCrv The 1D basis function Bezier curve used to define this shape function.
098     *              May not be null.
099     * @return A new shape function based on the specified 1D basis function Bezier curve.
100     */
101    public static CSTShapeFunction valueOf(NurbsCurve bfCrv) {
102        if (bfCrv.getPhyDimension() > 1)
103            throw new IllegalArgumentException(RESOURCES.getString("oneDCurve"));
104        
105        FastTable<Double> pts = CurveUtils.getBezierStartParameters(bfCrv);
106        if (pts.size() > 2)
107            throw new IllegalArgumentException(RESOURCES.getString("singleBezierSegmentErr"));
108        FastTable.recycle(pts);
109        
110        CSTShapeFunction obj = FACTORY.object();
111        obj.Scrv = bfCrv.immutable().to(SI.METER);
112        
113        return obj;
114    }
115
116    /**
117     * Return the order of this shape function.
118     *
119     * @return order of shape function (degree + 1)
120     * @see #getCoefficients() 
121     */
122    public int getOrder() {
123        return Scrv.getDegree() + 1;
124    }
125
126    /**
127     * Return the basis function for this shape function. The returned NURBS curve will
128     * always represent a 1D Bezier curve.
129     * @return The basis function for this shape function.
130     * @see #getCoefficients() 
131     */
132    public BasicNurbsCurve getBasisFunction() {
133        return Scrv;
134    }
135    
136    /**
137     * Return the value of this shape function at the specified parametric location.
138     *
139     * @param s The parametric distance along the curve to calculate the value of this
140     *          shape function at.
141     * @return The value of this shape function at the specified parametric location.
142     */
143    public double getValue(double s) {
144        double S = Scrv.getRealPoint(s).getValue(Point.X);
145        return S;
146    }
147
148    /**
149     * Return a 2D point that represents the value of this shape function at the specified
150     * parametric position: (s,getValue(s)).
151     *
152     * @param s The parametric distance along the curve to calculate the value of this
153     *          shape function at.
154     * @return A 2D point that represents the value of this shape function at the
155     *         specified parametric position: P(s) = getValue(s).
156     */
157    public Point get2DPoint(double s) {
158        Point p = Point.valueOf(s, getValue(s));
159        return p;
160    }
161
162    /**
163     * Calculate all the derivatives from <code>0</code> to <code>grade</code> with
164     * respect to parametric distance on the shape function for the given parametric
165     * distance along the shape function, <code>d^{grade}p(s)/d^{grade}s</code>.
166     * <p>
167     * Example:<br>
168     * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br>
169     * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc.
170     * </p>
171     *
172     * @param s     Parametric distance to calculate derivatives for (0.0 to 1.0
173     *              inclusive).
174     * @param grade The maximum grade to calculate the derivatives for (1=1st derivative,
175     *              2=2nd derivative, etc)
176     * @return An array of derivatives up to the specified grade of the shape function at
177     *         the specified parametric position. The returned array was allocated using
178     *         javolution.context.ArrayFactory.DOUBLES_FACTORY, could be longer than the
179     *         number of derivatives requested, and could be recycled by the user when no
180     *         longer needed.
181     * @throws IllegalArgumentException if the grade is &lt; 0.
182     */
183    public double[] getDerivatives(double s, int grade) {
184        double[] output = ArrayFactory.DOUBLES_FACTORY.array(grade + 1);
185        
186        StackContext.enter();
187        try {
188            List<Vector<Length>> ders = Scrv.getSDerivatives(s, grade);
189            for (int i = grade; i >= 0; --i)
190                output[i] = ders.get(i).getValue(Point.X);
191        } finally {
192            StackContext.exit();
193        }
194        
195        return output;
196    }
197
198    /**
199     * Return the array of Bernstein Polynomial coefficients for this shape function.
200     * The array will have a length of the order of the polynomial.
201     * 
202     * @return The array of Bernstein Polynomial coefficients for this shape function.
203     * @see #getOrder() 
204     * @see #getBasisFunction() 
205     */
206    public double[] getCoefficients() {
207        List<ControlPoint> cps = Scrv.getControlPoints();
208        int order = cps.size();
209        double[] Acoef = new double[order];
210        
211        for (int i=0; i < order; ++i) {
212            ControlPoint cp = cps.get(i);
213            Point p = cp.getPoint();
214            Acoef[i] = p.getValue(Point.X);
215        }
216        
217        FastTable.recycle((FastTable)cps);
218        
219        return Acoef;
220    }
221    
222    /**
223     * Return <code>true</code> if this shape function contains valid and finite numerical
224     * components. A value of <code>false</code> will be returned if any of the values are
225     * NaN or Inf.
226     *
227     * @return true if this shape function contains valid and finite numerical values.
228     */
229    public boolean isValid() {
230        return Scrv.isValid();
231    }
232
233    /**
234     * Compares this CSTShapeFunction against the specified object for strict equality
235     * (same values).
236     *
237     * @param obj the object to compare with.
238     * @return <code>true</code> if this object is identical to that object;
239     *         <code>false</code> otherwise.
240     */
241    @Override
242    public boolean equals(Object obj) {
243        if (this == obj)
244            return true;
245        if ((obj == null) || (obj.getClass() != this.getClass()))
246            return false;
247
248        CSTShapeFunction that = (CSTShapeFunction)obj;
249        return this.Scrv.equals(that.Scrv);
250    }
251
252    /**
253     * Returns the hash code for this object.
254     *
255     * @return the hash code value.
256     */
257    @Override
258    public int hashCode() {
259        int hash = 7;
260
261        hash = hash * 31 + Scrv.hashCode();
262
263        return hash;
264    }
265
266    /**
267     * Returns a copy of this CSTShapeFunction instance
268     * {@link javolution.context.AllocatorContext allocated} by the calling thread
269     * (possibly on the stack).
270     *
271     * @return an identical and independent copy of this object.
272     */
273    @Override
274    public CSTShapeFunction copy() {
275        return copyOf(this);
276    }
277
278    /**
279     * Holds the default XML representation for this object.
280     */
281    protected static final XMLFormat<CSTShapeFunction> XML = new XMLFormat<CSTShapeFunction>(CSTShapeFunction.class) {
282        @Override
283        public CSTShapeFunction newInstance(Class<CSTShapeFunction> cls, XMLFormat.InputElement xml) throws XMLStreamException {
284            return FACTORY.object();
285        }
286
287        @Override
288        public void read(XMLFormat.InputElement xml, CSTShapeFunction obj) throws XMLStreamException {
289            BasicNurbsCurve Scrv = xml.getNext();
290            obj.Scrv = Scrv;
291        }
292
293        @Override
294        public void write(CSTShapeFunction obj, XMLFormat.OutputElement xml) throws XMLStreamException {
295            xml.add(obj.Scrv);
296        }
297    };
298
299    ///////////////////////
300    // Factory creation. //
301    ///////////////////////
302    private CSTShapeFunction() { }
303
304    @SuppressWarnings("unchecked")
305    private static final ObjectFactory<CSTShapeFunction> FACTORY = new ObjectFactory<CSTShapeFunction>() {
306        @Override
307        protected CSTShapeFunction create() {
308            return new CSTShapeFunction();
309        }
310
311        @Override
312        protected void cleanup(CSTShapeFunction obj) {
313            obj.Scrv = null;
314        }
315    };
316
317    @SuppressWarnings("unchecked")
318    private static CSTShapeFunction copyOf(CSTShapeFunction original) {
319        CSTShapeFunction obj = FACTORY.object();
320        obj.Scrv = original.Scrv.copy();
321        return obj;
322    }
323}