001/*
002 *   Entity128_BSplineSurface  -- An entity representing a Rational B-Spline Surface.
003 *
004 *   Copyright (C) 2010-2016, 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.Point;
025import geomss.geom.Transformable;
026import geomss.geom.nurbs.*;
027import jahuwaldt.tools.math.MathTools;
028import java.io.IOException;
029import java.io.PrintWriter;
030import java.io.RandomAccessFile;
031import java.text.MessageFormat;
032import java.util.List;
033
034/**
035 * <p>
036 * <b><i>RATIONAL B-SPLINE SURFACE ENTITY</i></b> - This entity represents a Rational
037 * B-Spline Surface. This represents a parametric equation obtained by dividing two
038 * summations involving weights (which are real numbers), the control points, and B-Spline
039 * basis functions.</p>
040 *
041 * <p>
042 * This entity, when read from an IGES file, is converted to a NURBS surface. This entity
043 * type <b>can</b> be written out to an IGES file. When reading in this entity, all IGES
044 * parameters are stored in the user data with the prefix "IGES_128_" followed by the
045 * parameter name.
046 * </p>
047 *
048 * <p> Modified by: Joseph A. Huwaldt </p>
049 * 
050 * @author Joseph A. Huwaldt, Date: August 23, 2010
051 * @version April 7, 2016
052 */
053public class Entity128_BSplineSurface extends GeomSSEntity {
054
055    private int prop1 = 0;  //  0=closed in S-direction, 1=not closed
056    private int prop2 = 0;  //  0=closed in T-direction, 1=not closed
057    private int prop3 = 0;  //  0=rational, 1=polynomial
058    private int prop4 = 0;  //  0=non-periodic (clamped) in S-direction, 1=periodic (un-clamped).
059    private int prop5 = 0;  //  0=non-periodic (clamped) in T-direction, 1=periodic (un-clamped).
060    private double[] sKnots = null; //  Knot sequence in S-direction
061    private double[] tKnots = null; //  Knot sequence in T-direction
062    private double uSstart = 0;//   Starting parameter value in S-direction.
063    private double uSend = 1;   //  Ending parameter value in S-direction.
064    private double uTstart = 0;//   Starting parameter value in T-direction.
065    private double uTend = 1;   //  Ending parameter value in T-direction.
066
067    private NurbsSurface surface;   //  The GeomSS surface this entity represents.
068
069    /**
070     * Default constructor.
071     *
072     * @param p  part to which this entity is contained
073     * @param de Directory Entry for this entity
074     */
075    public Entity128_BSplineSurface(Part p, DirEntry de) {
076        super(p, de);
077
078        if (Constants.DEBUG) {
079            System.out.println("Entity128 constructor called");
080        }
081
082    }
083
084    /**
085     * Create this entity from the specified GeomSS geometry element.
086     *
087     * @param part  The Part in which this entity is contained.
088     * @param DEnum The line count from the start of the Directory Entry Section for this
089     *              entry (odd number).
090     * @param geom  The GeomSS geometry to return an Entity for.
091     */
092    public Entity128_BSplineSurface(Part part, int DEnum, NurbsSurface geom) {
093        super(part, new DirEntry(128, 0, DEnum, 0, geom.getName()));
094        surface = geom;
095
096        //  Is the surface closed in S-direction?
097        prop1 = 1;
098        int numCols = surface.getNumberOfColumns();
099        for (int i = 0; i < numCols; ++i) {
100            NurbsCurve curve = surface.getSCurve(i);
101            if (!curve.getRealPoint(0).distance(curve.getRealPoint(1)).isApproxZero()) {
102                prop1 = 0;
103                break;
104            }
105        }
106
107        //  Is the surface closed in T-direction?
108        prop2 = 1;
109        int numRows = surface.getNumberOfRows();
110        for (int i = 0; i < numRows; ++i) {
111            NurbsCurve curve = surface.getTCurve(i);
112            if (!curve.getRealPoint(0).distance(curve.getRealPoint(1)).isApproxZero()) {
113                prop2 = 0;
114                break;
115            }
116        }
117
118        //  Is the surface rational (prop3 = 0) or polynomial (prop3 = 1)?
119        ControlPointNet cpNet = surface.getControlPoints();
120        double w = cpNet.get(0, 0).getWeight();
121        prop3 = 1;
122        for (List<ControlPoint> column : cpNet) {
123            for (ControlPoint cp : column) {
124                if (Math.abs(cp.getWeight() - w) > MathTools.EPS) {
125                    prop3 = 0;
126                    break;
127                }
128            }
129        }
130
131        //  Store the knot vectors of this surface.
132        int K1 = cpNet.getNumberOfRows() - 1;
133        int K2 = cpNet.getNumberOfColumns() - 1;
134        KnotVector kv = surface.getSKnotVector();
135        int M1 = kv.getDegree();
136        int n = kv.length();
137        sKnots = new double[n];
138        for (int i = 0; i < n; ++i)
139            sKnots[i] = kv.getValue(i);
140
141        kv = surface.getTKnotVector();
142        int M2 = kv.getDegree();
143        n = kv.length();
144        tKnots = new double[n];
145        for (int i = 0; i < n; ++i)
146            tKnots[i] = kv.getValue(i);
147
148    }
149
150    /**
151     * Checks to see if the entity is correct. The following restrictions are imposed:
152     *
153     * - The Label Display Pointer shall be 0
154     */
155    @Override
156    public void check() {
157        DirEntry DE = getDirectoryEntry();
158
159        // DE LblDsp shall be 0
160        if (DE.getLblDsp() != 0) {
161            String msg = MessageFormat.format(RESOURCES.getString("labelDisplay"), DE.getLblDsp());
162            addErrorMessage(getWarningString(msg));
163        }
164
165    }
166
167    /**
168     * Read the Parameter Data from the String read in by the superclass.
169     *
170     * @param in input file
171     * @throws java.io.IOException
172     */
173    @Override
174    public void read(RandomAccessFile in) throws IOException {
175
176        if (Constants.DEBUG)
177            System.out.println("Entity128.read() called for " + getHeader());
178
179        super.read(in);
180        String s = getPDString();
181
182        if (Constants.DEBUG)
183            System.out.println("PD String = \"" + s + "\"");
184
185        int K1 = getInt(s);     //  Number of control points.
186        int K2 = getInt(s);     //  Number of control points.
187        int M1 = getInt(s);     //  Degree of basis functions.
188        int M2 = getInt(s);     //  Degree of basis functions.
189        prop1 = getInt(s);
190        prop2 = getInt(s);
191        prop3 = getInt(s);
192        prop4 = getInt(s);
193        prop5 = getInt(s);
194
195        int n = K1 + 1 + M1 + 1;        //  Number of knots.
196        sKnots = new double[n];
197        for (int i = 0; i < n; ++i) {
198            double u = getReal(s);
199            sKnots[i] = u;
200        }
201
202        n = K2 + 1 + M2 + 1;        //  Number of knots.
203        tKnots = new double[n];
204        for (int i = 0; i < n; ++i) {
205            double u = getReal(s);
206            tKnots[i] = u;
207        }
208
209        double[][] weights = new double[K2 + 1][K1 + 1];
210        for (int i = 0; i <= K2; ++i) {
211            for (int j = 0; j <= K1; ++j) {
212                double w = getReal(s);
213                weights[i][j] = w;
214            }
215        }
216
217        Point[][] cps = new Point[K2 + 1][K1 + 1];
218        for (int i = 0; i <= K2; ++i) {
219            for (int j = 0; j <= K1; ++j) {
220                cps[i][j] = getPoint3(s);
221            }
222        }
223
224        uSstart = getReal(s);
225        uSend = getReal(s);
226        uTstart = getReal(s);
227        uTend = getReal(s);
228
229        //  Make sure all the knots read in are in-range.
230        n = sKnots.length;
231        for (int i = 0; i < n; ++i) {
232            double kv = sKnots[i];
233            if (kv < uSstart)
234                sKnots[i] = uSstart;
235            else if (kv > uSend)
236                sKnots[i] = uSend;
237        }
238        n = tKnots.length;
239        for (int i = 0; i < n; ++i) {
240            double kv = tKnots[i];
241            if (kv < uTstart)
242                tKnots[i] = uTstart;
243            else if (kv > uTend)
244                tKnots[i] = uTend;
245        }
246
247        //  Scale the knots into the range 0-1.
248        double[] scaledSKnots = sKnots;
249        if (Math.abs(uSstart) > MathTools.EPS || Math.abs(uSend - 1.0) > MathTools.EPS) {
250            n = sKnots.length;
251            scaledSKnots = new double[n];
252            double m = 1. / (uSend - uSstart);
253            double b = -m * uSstart;
254            for (int i = 0; i < n; ++i) {
255                double kv = sKnots[i];
256                kv = scaleKnot(m, b, kv);
257                scaledSKnots[i] = kv;
258            }
259        }
260        double[] scaledTKnots = tKnots;
261        if (Math.abs(uTstart) > MathTools.EPS || Math.abs(uTend - 1.0) > MathTools.EPS) {
262            n = tKnots.length;
263            scaledTKnots = new double[n];
264            double m = 1. / (uTend - uTstart);
265            double b = -m * uTstart;
266            for (int i = 0; i < n; ++i) {
267                double kv = tKnots[i];
268                kv = scaleKnot(m, b, kv);
269                scaledTKnots[i] = kv;
270            }
271        }
272
273        //  Create the knot vector.
274        KnotVector kvS = KnotVector.newInstance(M1, scaledSKnots);
275        KnotVector kvT = KnotVector.newInstance(M2, scaledTKnots);
276
277        //  Create the control point network.
278        ControlPoint[][] controlPoints = new ControlPoint[K2 + 1][K1 + 1];
279        for (int i = 0; i <= K2; ++i) {
280            for (int j = 0; j <= K1; ++j) {
281                double weight = weights[i][j];
282                Point pnt = cps[i][j];
283                controlPoints[i][j] = ControlPoint.valueOf(pnt, weight);
284            }
285        }
286        ControlPointNet cpNet = ControlPointNet.valueOf(controlPoints);
287
288        //  Create the surface.
289        surface = BasicNurbsSurface.newInstance(cpNet, kvS, kvT);
290
291        super.read_additional();
292    }
293
294    /**
295     * Method used to apply IGES meta-data to GeomSS elements. This implementation stores
296     * in the user data, in addition to the header info, all the parameter information for
297     * this entity type prefixed by "IGES_128_". The range of original U,V parameter
298     * values is also avaliable as "IGES_U0", "IGES_U1", etc.
299     */
300    @Override
301    protected void applyMetaData(GeomElement element) {
302        super.applyMetaData(element);
303        element.putUserData("IGES_128_PROP1", prop1);
304        element.putUserData("IGES_128_PROP2", prop2);
305        element.putUserData("IGES_128_PROP3", prop3);
306        element.putUserData("IGES_128_PROP4", prop4);
307        element.putUserData("IGES_128_PROP5", prop5);
308        element.putUserData("IGES_U0", uSstart);
309        element.putUserData("IGES_U1", uSend);
310        element.putUserData("IGES_V0", uTstart);
311        element.putUserData("IGES_V1", uTend);
312    }
313
314    /**
315     * The GeomSS geometry element is created from the IGES parameters when this method is
316     * called.
317     */
318    @Override
319    void createGeometry() throws IOException {
320        //  Everything was done in "read()".
321    }
322
323    /**
324     * Scale the knot value in the the required range from 0.0 to 1.0.
325     */
326    private double scaleKnot(double m, double b, double value) {
327        //  Scale the knot value.
328        double kv = m * value + b;
329
330        //  Watch for roundoff.
331        if (kv < 0.0)
332            kv = 0.0;
333        else if (kv > 1.0)
334            kv = 1.0;
335
336        return kv;
337    }
338
339    /**
340     * Return a reference to the Transformable GeomElement contained in this IGES Entity.
341     *
342     * @return A reference to the Transformable GeomElement contained in this IGES Entity.
343     */
344    @Override
345    protected Transformable getGeomElement() {
346        return surface;
347    }
348
349    /**
350     * Returns <code>true</code> if the Entity can be written to an exchange file.
351     *
352     * @return true
353     */
354    @Override
355    public boolean canWrite() {
356        return true;
357    }
358
359    /**
360     * Write this entities parameter data to the specified PrintWriter.
361     *
362     * @param writer The PrintWriter to write the parameter data for this entity to.
363     * @param PDnum  The starting Parameter Data row index number.
364     * @return The Parameter Data row index number for the next row.
365     * @throws java.io.IOException
366     */
367    @Override
368    public int write(PrintWriter writer, int PDnum) throws IOException {
369
370        //  Build up the parameter data string.
371        NurbsSurface srf = surface.to(Constants.unit);
372        ControlPointNet cpNet = srf.getControlPoints();
373        int K1 = cpNet.getNumberOfRows() - 1;
374        int K2 = cpNet.getNumberOfColumns() - 1;
375        KnotVector sKV = srf.getSKnotVector();
376        KnotVector tKV = srf.getTKnotVector();
377        int M1 = sKV.getDegree();
378        int M2 = tKV.getDegree();
379
380        StringBuilder buffer = new StringBuilder();
381        buffer.append(128);                 buffer.append(Constants.Delim);
382        buffer.append(K1);                  buffer.append(Constants.Delim);
383        buffer.append(K2);                  buffer.append(Constants.Delim);
384        buffer.append(M1);                  buffer.append(Constants.Delim);
385        buffer.append(M2);                  buffer.append(Constants.Delim);
386        buffer.append(prop1);               buffer.append(Constants.Delim);
387        buffer.append(prop2);               buffer.append(Constants.Delim);
388        buffer.append(prop3);               buffer.append(Constants.Delim);
389        buffer.append(prop4);               buffer.append(Constants.Delim);
390        buffer.append(prop5);               buffer.append(Constants.Delim);
391
392        int n = sKV.length();
393        for (int i = 0; i < n; ++i) {
394            double u = sKV.getValue(i);
395            buffer.append(u);
396            buffer.append(Constants.Delim);
397        }
398
399        n = tKV.length();
400        for (int i = 0; i < n; ++i) {
401            double u = tKV.getValue(i);
402            buffer.append(u);
403            buffer.append(Constants.Delim);
404        }
405
406        for (int i = 0; i <= K2; ++i) {
407            for (int j = 0; j <= K1; ++j) {
408                double w = cpNet.get(j, i).getWeight();
409                buffer.append(w);
410                buffer.append(Constants.Delim);
411            }
412        }
413
414        for (int i = 0; i <= K2; ++i) {
415            for (int j = 0; j <= K1; ++j) {
416                Point cp = cpNet.get(j, i).getPoint();
417                appendPoint3(buffer, cp);
418            }
419        }
420
421        buffer.append(uSstart);             buffer.append(Constants.Delim);
422        buffer.append(uSend);               buffer.append(Constants.Delim);
423        buffer.append(uTstart);             buffer.append(Constants.Delim);
424        buffer.append(uTend);               buffer.append(Constants.Term);
425
426        //  Write it out.
427        int oldPDnum = PDnum;
428        PDnum = Constants.writeSection(writer, PDnum, Constants.makeSequenceNumber(getDENum()),
429                'P', buffer);
430
431        //  Store the PD line number and line count in the directory entry.
432        getDirectoryEntry().setPDNumber(oldPDnum, PDnum - oldPDnum);
433
434        return PDnum;
435    }
436
437    /**
438     * Returns a short String describing this Entity object's type.
439     *
440     * @return A short String describing this Entity object's type.
441     */
442    @Override
443    public String getTypeString() {
444        return "Entity128 - Rational B-Spline Surface";
445    }
446
447    /**
448     * Dump to String.
449     *
450     * @return String containing the resulting text.
451     */
452    @Override
453    public String toString() {
454        if (surface == null)
455            return super.toString();
456        
457        StringBuilder outStr = new StringBuilder(super.toString());
458        outStr.append("\n");
459
460        ControlPointNet cpNet = surface.getControlPoints();
461        int K1 = cpNet.getNumberOfRows()-1;
462        int K2 = cpNet.getNumberOfColumns()-1;
463        int M1 = surface.getSDegree();
464        int M2 = surface.getTDegree();
465        
466        outStr.append("K1    = ");  outStr.append(K1);      outStr.append("\n");
467        outStr.append("K2    = ");  outStr.append(K2);      outStr.append("\n");
468        outStr.append("M1    = ");  outStr.append(M1);      outStr.append("\n");
469        outStr.append("M2    = ");  outStr.append(M2);      outStr.append("\n");
470        outStr.append("prop1 = ");  outStr.append(prop1);   outStr.append("\n");
471        outStr.append("prop2 = ");  outStr.append(prop2);   outStr.append("\n");
472        outStr.append("prop3 = ");  outStr.append(prop3);   outStr.append("\n");
473        outStr.append("prop4 = ");  outStr.append(prop4);   outStr.append("\n");
474        outStr.append("prop5 = ");  outStr.append(prop5);   outStr.append("\n");
475        
476        int n = sKnots.length;
477        for (int i = 0; i < n; i++) {
478            outStr.append("S(");            outStr.append(i);   outStr.append("):  ");
479            outStr.append(sKnots[i]);       outStr.append("\n");
480        }
481        outStr.append("\n");
482        
483        n = tKnots.length;
484        for (int i = 0; i < n; i++) {
485            outStr.append("T(");            outStr.append(i);   outStr.append("):  ");
486            outStr.append(tKnots[i]);   outStr.append("\n");
487        }
488        outStr.append("\n");
489        
490        for (int i=0; i <= K2; i++) {
491            for (int j=0; j <= K1; j++) {
492                double weight = cpNet.get(j,i).getWeight();
493                outStr.append("W(");            outStr.append(j);   outStr.append(",");
494                outStr.append(i);               outStr.append("):  ");
495                outStr.append(weight);          outStr.append("\n");
496            }
497        }
498
499        for (int i=0; i <= K2; i++) {
500            for (int j=0; j <= K1; j++) {
501                Point pnt = cpNet.get(j,i).getPoint();
502                outStr.append("X(");            outStr.append(j);   outStr.append(",");
503                outStr.append(i);               outStr.append("):  ");
504                outStr.append(pnt.get(0));      outStr.append("\n");
505                outStr.append("Y(");            outStr.append(j);   outStr.append(",");
506                outStr.append(i);               outStr.append("):  ");
507                outStr.append(pnt.get(1));      outStr.append("\n");
508                outStr.append("Z(");            outStr.append(j);   outStr.append(",");
509                outStr.append(i);               outStr.append("):  ");
510                outStr.append(pnt.get(2));      outStr.append("\n");
511            }
512        }
513        
514        outStr.append("uSstart = ");    outStr.append(uSstart); outStr.append("\n");
515        outStr.append("uSend   = ");    outStr.append(uSend);   outStr.append("\n");
516        outStr.append("uTstart = ");    outStr.append(uTstart); outStr.append("\n");
517        outStr.append("uTend   = ");    outStr.append(uTend);   outStr.append("\n");
518        
519        return outStr.toString();
520    }
521
522}