001/*
002 *   Entity112_ParSplineCurve  -- An entity representing a Parametric Spline Curve Entity.
003 *
004 *   Copyright (C) 2013-2025, Joseph A. Huwaldt. All rights reserved.
005 *   
006 *   part library is free software; you can redistribute it and/or
007 *   modify it under the terms of the GNU Lesser General Public
008 *   License as published by the Free Software Foundation; either
009 *   version 2.1 of the License, or (at your option) any later version.
010 *   
011 *   part library is distributed in the hope that it will be useful,
012 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *   Lesser General Public License for more details.
015 *
016 *   You should have received a copy of the GNU Lesser General Public License
017 *   along with part program; if not, write to the Free Software
018 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
020 */
021package geomss.geom.reader.iges;
022
023import geomss.geom.GeomElement;
024import geomss.geom.Transformable;
025import geomss.geom.nurbs.BasicNurbsCurve;
026import geomss.geom.nurbs.ControlPoint;
027import geomss.geom.nurbs.KnotVector;
028import geomss.geom.nurbs.NurbsCurve;
029import java.io.IOException;
030import java.io.RandomAccessFile;
031import java.text.MessageFormat;
032import java.util.ArrayList;
033import java.util.List;
034import javax.measure.unit.Unit;
035import org.jscience.mathematics.vector.Float64Matrix;
036
037/**
038 * <b><i>PARAMETRIC SPLINE CURVE ENTITY</i></b> - This entity represents a Parametric
039 * Spline Curve that may be isolated or used as a component of a Composite Curve Entity or
040 * a Subfigure Entity. The parametric spline curve is a sequence of parametric polynomial
041 * segments.
042 * 
043 * <p>
044 * This entity, when read from an IGES file, is converted to a NURBS curve. This entity
045 * type can not be written out to an IGES file. The spline parameters are stored in the
046 * user data with the prefix "IGES_112_" followed by the parameter name.
047 * </p>
048 *
049 * <p> Modified by: Joseph A. Huwaldt </p>
050 * 
051 * @author Joseph A. Huwaldt, Date: March 9, 2013
052 * @version February 22, 2025
053 */
054public class Entity112_ParSplineCurve extends GeomSSEntity {
055
056    private int ctype = 3;          //  1=linear, 2=quadratic, 3=cubic, 4=Wilson-Fowler, 5=Modified Wilson-Fowler, 6=B-spline
057    private int H = 1;              //  Degree of continuity with respect to arc length
058    private int NDIM = 3;           //  Number of dimemsions (2=planar, 3=non-planar)
059    private int N = 1;              //  Number of segments
060    private double[] T = null;      //  Break points of piecewise polynomial
061    private double[][] A = null;    //  Polynomial in each direction for each segment
062    private double[][] B = null;
063    private double[][] C = null;
064    private double[][] D = null;
065    private double TP[][] = null;
066
067    private static final double[][] Bmat
068            =   {{1.,   0.,         0.,     0.},
069                {1.,    1. / 3.,    0.,     0.},
070                {1.,    2. / 3.,    1. / 3., 0.},
071                {1.,    1.,         1.,     1.}};
072    private static final Float64Matrix Bm = Float64Matrix.valueOf(Bmat);
073
074    private NurbsCurve curve;   //  The GeomSS curve this entity represents.
075
076    /**
077     * Default constructor.
078     *
079     * @param p  part to which this entity is contained
080     * @param de Directory Entry for this entity
081     */
082    public Entity112_ParSplineCurve(Part p, DirEntry de) {
083        super(p, de);
084
085        if (Constants.DEBUG) {
086            System.out.println("Entity112 constructor called");
087        }
088
089    }
090
091    /**
092     * Checks to see if the entity is correct. The following restrictions are imposed:
093     *
094     * - The Label Display Pointer shall be 0
095     */
096    @Override
097    public void check() {
098        DirEntry DE = getDirectoryEntry();
099
100        // DE LblDsp shall be 0
101        if (DE.getLblDsp() != 0) {
102            String msg = MessageFormat.format(RESOURCES.getString("labelDisplay"), DE.getLblDsp());
103            addErrorMessage(getWarningString(msg));
104        }
105
106    }
107
108    /**
109     * Read the Parameter Data from the String read in by the superclass.
110     *
111     * @param in input file
112     * @throws java.io.IOException if the parameter data could not be read in.
113     */
114    @Override
115    public void read(RandomAccessFile in) throws IOException {
116
117        if (Constants.DEBUG) {
118            System.out.println("Entity112.read() called");
119        }
120
121        super.read(in);
122        String s = getPDString();
123
124        if (Constants.DEBUG) {
125            System.out.println("PD String = \"" + s + "\"");
126        }
127
128        ctype = getInt(s);          //  Spline Type before conversion to Type 112
129        H = getInt(s);              //  Degree of continuity with respect to arc length
130        NDIM = getInt(s);           //  Number of dimensions
131        N = getInt(s);              //  Number of segments
132
133        //  Read in breakpoints of piecewise polynomial
134        T = new double[N + 1];        //  Allocate storage for the breakpoints
135        for (int i = 0; i <= N; ++i) {
136            double u = getReal(s);
137            T[i] = u;
138        }
139
140        //  Allocate memory for the polynomial segment coefficients.
141        A = new double[N][NDIM];
142        B = new double[N][NDIM];
143        C = new double[N][NDIM];
144        D = new double[N][NDIM];
145
146        //  Loop over each segment.
147        for (int i = 0; i < N; ++i) {
148            //  Read in the coefficients in each coordinate direction.
149            for (int j = 0; j < NDIM; ++j) {
150                A[i][j] = getReal(s);
151                B[i][j] = getReal(s);
152                C[i][j] = getReal(s);
153                D[i][j] = getReal(s);
154            }
155        }
156
157        //  Read in the remaining evaluation points.
158        TP = new double[NDIM][4];
159        for (int i = 0; i < NDIM; ++i) {
160            for (int j = 0; j < 4; ++j) {
161                TP[i][j] = getReal(s);
162            }
163        }
164
165        super.read_additional();
166    }
167
168    /**
169     * The GeomSS geometry element is created from the IGES parameters when this method is
170     * called.
171     */
172    @Override
173    void createGeometry() throws IOException {
174
175        //  Convert from polynomial coefficient form to B-Spline control points.
176        //      Reference:  Handbook of Grid Generation, Chapter 30, Section 30.3.0.
177        
178        //  Bezier control polygon: bmat[0..3] = Bm*Cmat
179        final int degree = 3;
180        final int order = degree + 1;
181        List<ControlPoint> cps = new ArrayList();
182        double[][] Cmat = new double[order][NDIM];
183        boolean firstPass = true;
184        for (int i = 0; i < N; ++i) {
185
186            //  Calculate the reparameterization factor.
187            double h = T[i + 1] - T[i];
188            double h2 = h * h;
189            double h3 = h2 * h;
190
191            //  Build up the polynomial coefficient matrix.
192            for (int j = 0; j < NDIM; ++j) {
193                Cmat[0][j] = A[i][j];
194                Cmat[1][j] = B[i][j] * h;
195                Cmat[2][j] = C[i][j] * h2;
196                Cmat[3][j] = D[i][j] * h3;
197            }
198
199            //  bmat = Bmat*Cmat
200            Float64Matrix Cm = Float64Matrix.valueOf(Cmat);
201            Float64Matrix bm = Bm.times(Cm);
202
203            //  Convert bm into a list of control points.
204            List<ControlPoint> cpList = cpMatrix2ControlPoints(bm, Constants.unit);
205            if (firstPass) {
206                //  Only add the 1st control point on the 1st segment.
207                firstPass = false;
208                cps.add(cpList.get(0));
209            }
210            for (int j = 1; j < order; ++j) {
211                cps.add(cpList.get(j));
212            }
213        }
214
215        //  Create a knot list and put a degree+1 touple knot at the start.
216        List<Double> knots = new ArrayList();
217        for (int i = 0; i <= degree; i++) {
218            knots.add(ZERO);
219        }
220
221        //  Add degree+1 touple knots where each Bezier segment is joined.
222        double ds = 1. / N;
223        double sv = 0;
224        for (int j = 1; j < N; ++j) {
225            sv += ds;
226            for (int i = 0; i < degree; i++) {
227                knots.add(sv);
228            }
229        }
230
231        //  Add a degree+1 touble set of knots at the end.
232        for (int i = 0; i <= degree; i++) {
233            knots.add(ONE);
234        }
235
236        //  Create the knot vector.
237        KnotVector kv = KnotVector.newInstance(degree, knots);
238
239        //  Create the curve.
240        curve = BasicNurbsCurve.newInstance(cps, kv);
241
242    }
243
244    /**
245     * Method used to apply IGES meta-data to GeomSS elements. This implementation stores
246     * in the user data, in addition to the header info, all the parameter information for
247     * this entity type prefixed by "IGES_112_".
248     */
249    @Override
250    protected void applyMetaData(GeomElement element) {
251        super.applyMetaData(element);
252        element.putUserData("IGES_112_CTYPE", ctype);
253        element.putUserData("IGES_112_H", H);
254        element.putUserData("IGES_112_N", N);
255        element.putUserData("IGES_112_T", T);
256        element.putUserData("IGES_112_A", A);
257        element.putUserData("IGES_112_B", B);
258        element.putUserData("IGES_112_C", C);
259        element.putUserData("IGES_112_D", D);
260        element.putUserData("IGES_112_TP", TP);
261        element.putUserData("IGES_U0", T[0]);
262        element.putUserData("IGES_U1", T[N]);
263    }
264
265    /**
266     * Return a list of control points from the input matrix of control point coordinates
267     * (points in rows, dimensions in columns).
268     *
269     */
270    private static List<ControlPoint> cpMatrix2ControlPoints(Float64Matrix Pmat, Unit units) {
271
272        //  Create a list of control points from the matrix of control point positions.
273        List<ControlPoint> cpList = new ArrayList();
274        int numPoints = Pmat.getNumberOfRows();
275        for (int i = 0; i < numPoints; i++) {
276            ControlPoint pnt = ControlPoint.valueOf(Pmat.getRow(i), 1., units);
277            cpList.add(pnt);
278        }
279
280        return cpList;
281    }
282
283    /**
284     * Return a reference to the Transformable GeomElement contained in this IGES Entity.
285     *
286     * @return A reference to the Transformable GeomElement contained in this IGES Entity.
287     */
288    @Override
289    protected Transformable getGeomElement() {
290        return curve;
291    }
292
293    /**
294     * Returns a short String describing this Entity object's type.
295     *
296     * @return A short String describing this Entity object's type.
297     */
298    @Override
299    public String getTypeString() {
300        return "Entity112 - Parametric Spline Curve";
301    }
302
303    /**
304     * Dump to String.
305     *
306     * @return String containing the resulting text.
307     */
308    @Override
309    public String toString() {
310        StringBuilder outStr = new StringBuilder(super.toString());
311        outStr.append("\n");
312
313        return outStr.toString();
314    }
315
316}