001/*
002 *   Entity142_CurveOnSurface  -- An entity representing a Curve On a Parametric Surface.
003 *
004 *   Copyright (Ccrv) 2013-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.*;
024import geomss.geom.nurbs.BasicNurbsCurve;
025import geomss.geom.nurbs.ControlPoint;
026import geomss.geom.nurbs.CurveFactory;
027import geomss.geom.nurbs.NurbsCurve;
028import jahuwaldt.js.param.Parameter;
029import java.io.IOException;
030import java.io.PrintWriter;
031import java.io.RandomAccessFile;
032import java.util.List;
033import javax.measure.quantity.Length;
034import javax.measure.unit.SI;
035import javax.measure.unit.Unit;
036import javolution.context.StackContext;
037import javolution.util.FastTable;
038
039/**
040 * <b><i>CURVE ON A PARAMETRIC SURFACE ENTITY</i></b> - This entity represents a curve
041 * associated with a surface and identifies the curve as lying on the surface.
042 *
043 * <p>
044 * This entity, when read from an IGES file, is converted to a SubrangeCurve curve. This
045 * entity type can be written out to an IGES file. When reading in this entity, the IGES
046 * parameters are stored in the user data with the prefix "IGES_142_" followed by the
047 * parameter name.
048 * </p>
049 *
050 * <p> Modified by: Joseph A. Huwaldt </p>
051 * 
052 * @author Joseph A. Huwaldt, Date: March 13, 2013
053 * @version April 10, 2016
054 */
055public class Entity142_CurveOnSurface extends GeomSSEntity {
056
057    private int Crtn = 0;   //  Indicates the way the curve on the surface has been created:
058                            //      0 = Unspecified, 1 = Projection of a givencurve on the surface,
059                            //      2 = Intersection of two surfaces,
060                            //      3 = Isoparametric curve, i.e., either a u- parametric or a v- parametric curve.
061    private int Sptr;       //  Pointer to the DE of the surface on which the curve lies
062    private int Bptr;       //  Pointer to the DE of the entity that contains the definition of
063                            //      the curve Bcrv in the parametric space (u, v) of the surface Ssrf
064    private int Cptr;       //  Pointer to the DE of the curve Ccrv
065    private int Pref = 1;   //  Indicates preferred representation in the sending system:
066                            //      0 = Unspecified, 1 = S o B is preferred, 2 = C is preferred,
067                            //      3 = C and S o B are equally preferred
068
069    private GeomList<SubrangeCurve> curves; //  The list of GeomSS curves this entity represents.
070
071    /**
072     * Default constructor.
073     *
074     * @param p  part to which this entity is contained
075     * @param de Directory Entry for this entity
076     */
077    public Entity142_CurveOnSurface(Part p, DirEntry de) {
078        super(p, de);
079
080        if (Constants.DEBUG) {
081            System.out.println("Entity142 constructor called");
082        }
083
084    }
085
086    /**
087     * Create this entity from the specified GeomSS geometry element.
088     *
089     * @param part  The Part to which this entity is contained.
090     * @param DEnum The line count from the start of the Directory Entry Section for this
091     *              entry (odd number).
092     * @param name  The name to assign to the IGES entity or <code>nulL</code> for none.
093     * @param Sptr  Pointer to the DE of the surface on which the curve lies.
094     * @param Bptr  Pointer to the DE of the entity that contains the definition of the
095     *              curve Bcrv in the parametric space (u, v) of the surface Ssrf
096     * @param Cptr  Pointer to the DE of the curve Ccrv
097     */
098    public Entity142_CurveOnSurface(Part part, int DEnum, String name, int Sptr, int Bptr, int Cptr) {
099        super(part, new DirEntry(142, 0, DEnum, 0, name));
100
101        this.Sptr = Sptr;
102        this.Bptr = Bptr;
103        this.Cptr = Cptr;
104
105    }
106
107    /**
108     * Checks to see if the entity is correct.
109     */
110    @Override
111    public void check() { }
112
113    /**
114     * Read the Parameter Data from the String read in by the superclass.
115     *
116     * @param in input file
117     * @throws java.io.IOException
118     */
119    @Override
120    public void read(RandomAccessFile in) throws IOException {
121
122        if (Constants.DEBUG) {
123            System.out.println("Entity142.read() called");
124        }
125
126        super.read(in);
127        String s = getPDString();
128
129        if (Constants.DEBUG) {
130            System.out.println("PD String = \"" + s + "\"");
131        }
132
133        Crtn = getInt(s);   //  Indicates the way the curve on the surface has been created
134        Sptr = getInt(s);   //  Pointer to the DE of the surface on which the curve lies
135        Bptr = getInt(s);   //  Pointer to the DE of the entity that contains the definition of the curve Bcrv in the parametric space (u, v) of the surface Ssrf
136        Cptr = getInt(s);   //  Pointer to the DE of the curve Ccrv
137        Pref = getInt(s);   //  Indicates preferred representation in the sending system
138
139        super.read_additional();
140    }
141
142    /**
143     * Method used to apply IGES meta-data to GeomSS elements. This implementation stores
144     * in the user data, in addition to the header info, all the parameter information for
145     * this entity type prefixed by "IGES_142_".
146     */
147    @Override
148    protected void applyMetaData(GeomElement element) {
149        super.applyMetaData(element);
150        element.putUserData("IGES_142_CRTN", Crtn);
151        element.putUserData("IGES_142_PREF", Pref);
152    }
153
154    /**
155     * The GeomSS geometry element is created from the IGES parameters when this method is
156     * called.
157     */
158    @Override
159    void createGeometry() throws IOException {
160        Part part = getPart();
161        Parameter<Length> tol = Parameter.valueOf(Constants.Grain, Constants.unit);
162        curves = GeomList.newInstance();
163
164        //  Get the surface the curve is on.
165        Surface Ssrf;
166        Entity entity = part.getEntity(Sptr);
167        if (entity instanceof GeomSSEntity) {
168            GeomSSEntity geomEntity = (GeomSSEntity)entity;
169            geomEntity.setUsedInList(true); //  Indicate that entity is used by this association.
170            Ssrf = (Surface)geomEntity.getGeomElement(GTransform.IDENTITY);
171            if (Ssrf == null)
172                return;
173        } else
174            return;
175
176        //  Extract the original parametric bounds of the surface.
177        double sStart = (Double)Ssrf.getUserData("IGES_U0");
178        double tStart = (Double)Ssrf.getUserData("IGES_V0");
179        double sEnd = (Double)Ssrf.getUserData("IGES_U1");
180        double tEnd = (Double)Ssrf.getUserData("IGES_V1");
181
182        //  Get the parametric space definition curve.
183        GeomList<NurbsCurve> Bcrvs = GeomList.newInstance();
184        NurbsCurve Bcc;
185        entity = part.getEntity(Bptr);
186        if (entity instanceof GeomSSEntity) {
187            GeomSSEntity geomEntity = (GeomSSEntity)entity;
188            geomEntity.setUsedInList(true); //  Indicate that the entity is used by this association.
189            Curve crv = (Curve)geomEntity.getGeomElement(GTransform.IDENTITY);
190            if (crv == null)
191                return;
192
193            //  Convert the B curve to NURBS
194            Bcc = crv.toNurbs(tol);
195
196            //  Change B units to METER to be consistant with the requirements of SubrangeCurve.
197            Bcc = changeCurveUnits(Bcc, SI.METER);
198
199            //  Scale the B curve to the correct surface parametric range.
200            Bcc = scaleParCurve(sStart, sEnd, tStart, tEnd, Bcc);
201
202            //  Convert B from 3D to 2D by dropping the Z dimension.
203            Bcc = Bcc.toDimension(2);
204
205            //  Store the original B curve.
206            Bcc.putUserData("IGES_142_B", crv);
207
208            if (entity instanceof Entity102_CompositeCurve) {
209                //  We have a composite B curve.  We want the segments.
210
211                //  Extract the segments of the composite curve.
212                GeomList<GeomElement> segs = (GeomList)crv.getUserData("IGES_102_CCSegs");
213                curves.putUserData("IGES_142_BSegs", segs);
214
215                //  Convert the segments to NurbsCurves
216                GeomList<NurbsCurve> crvSegs = convert2NurbsSegs(segs, tol);
217
218                //  Convert all the segments to properly scaled subrange parameter curves.
219                for (NurbsCurve seg : crvSegs) {
220                    //  Change Bcrv units to METER to be consistant with the requirements of SubrangeCurve.
221                    NurbsCurve nseg = changeCurveUnits(seg, SI.METER);
222
223                    //  Scale the B curve to the correct surface parametric range.
224                    nseg = scaleParCurve(sStart, sEnd, tStart, tEnd, nseg);
225
226                    //  Convert B from 3D to 2D by dropping the Z dimension.
227                    nseg = nseg.toDimension(2);
228
229                    nseg.putUserData("IGES_142_BSeg", seg);
230
231                    //  Save the segment in the Bcrvs list.
232                    Bcrvs.add(nseg);
233                }
234
235            } else {
236                //  Just a single curve segment.
237                Bcrvs.add(Bcc);
238                Bcc = null;
239            }
240
241        } else
242            return;
243
244        //  Get the model space curve coincident with the curve on the surface.
245        Curve Ccrv = null;
246        entity = part.getEntity(Cptr);
247        if (entity instanceof GeomSSEntity) {
248            GeomSSEntity geomEntity = (GeomSSEntity)entity;
249            geomEntity.setUsedInList(true); //  Indicate that the entity is used by this association.
250            Ccrv = (Curve)geomEntity.getGeomElement(GTransform.IDENTITY);
251        }
252
253        //  Create the curves.
254        for (NurbsCurve B : Bcrvs) {
255            SubrangeCurve curve = SubrangeCurve.newInstance(Ssrf, B);
256            if (Ccrv != null)
257                curve.putUserData("IGES_142_C", Ccrv);
258            curves.add(curve);
259        }
260        if (Ccrv != null)
261            curves.putUserData("IGES_142_C", Ccrv);
262        if (Bcc != null)
263            //  This will only be added if B was originally a composite curve.
264            curves.putUserData("IGES_142_Bcc", Bcc);
265    }
266
267    /**
268     * Converts all the non-degenerate Curve elements in the specified geometry list into
269     * NurbsCurves.
270     *
271     * @param crvs The list of geometry elements to be converted.
272     * @param tol  The tolerance for the conversion to NURBS curves.
273     * @return A list of non-degenerate NURBS curve versions of each element in "crvs".
274     */
275    private static GeomList<NurbsCurve> convert2NurbsSegs(GeomList<GeomElement> crvs, Parameter<Length> tol) {
276        GeomList<NurbsCurve> nurbsSegs = GeomList.newInstance();
277        for (GeomElement elem : crvs) {
278            if (elem instanceof Curve) {
279                NurbsCurve nurbs = ((Curve)elem).toNurbs(tol);
280                nurbsSegs.add(nurbs);
281            } else if (elem instanceof GeomPoint) {
282                NurbsCurve nurbs = CurveFactory.createPoint(1, (GeomPoint)elem);
283                nurbsSegs.add(nurbs);
284            }
285        }
286        return nurbsSegs;
287    }
288
289    /**
290     * Return a new curve with the same numerical values as the 1st curve, but with the
291     * units changed to those specified.
292     *
293     * @param crv  The curve to have the units changed.
294     * @param unit The new unit for the output curve.
295     * @return A curve with the same numerical values as the "crv", but with the units
296     *         changed to "unit".
297     */
298    private static NurbsCurve changeCurveUnits(NurbsCurve crv, Unit<Length> unit) {
299        StackContext.enter();
300        try {
301
302            List<ControlPoint> oldCPList = crv.getControlPoints();
303            FastTable<ControlPoint> newCPList = FastTable.newInstance();
304            for (ControlPoint cp : oldCPList) {
305                Point p = cp.getPoint();
306                double w = cp.getWeight();
307                Point pnew = Point.valueOf(p.getValue(0), p.getValue(1), 0, unit);
308                newCPList.add(ControlPoint.valueOf(pnew, w));
309            }
310            crv = BasicNurbsCurve.newInstance(newCPList, crv.getKnotVector());
311            return StackContext.outerCopy((BasicNurbsCurve)crv);
312
313        } finally {
314            StackContext.exit();
315        }
316    }
317
318    /**
319     * Scales an input parametric curve who's values range from sStart,tStart to sEnd,tEnd
320     * to a new curve who's values range from 0,0 to 1,1.
321     *
322     * @param sStart The parametric S starting position on the parent surface.
323     * @param sEnd   The parametric T starting position on the parent surface.
324     * @param tStart The parametric S ending position on the parent surface.
325     * @param tEnd   The parametric T ending position on the parent surface.
326     * @param crv    The curve, in the range, sStart,tStart to sEnd,tEnd to be scaled.
327     * @return A new curve that is scaled off the input curve that is in the range 0,0 to
328     *         1,1.
329     */
330    private static NurbsCurve scaleParCurve(double sStart, double sEnd, double tStart, double tEnd, NurbsCurve crv) {
331        double mS = 1. / (sEnd - sStart);
332        double mT = 1. / (tEnd - tStart);
333        GTransform Tt = GTransform.newTranslation(-sStart, -tStart, 0); //  Translate sStart,tStart to 0,0.
334        GTransform Ts = GTransform.valueOf(mS, mT, 1, 1);               //  Scale S & T to the right range.
335        GTransform T = Ts.times(Tt);
336        crv = crv.getTransformed(T).copyToReal();
337
338        //  Are there small round-off errors causing the parameters to be out of bounds?
339        final double tol = 1e-4;
340        double min = crv.getLimitPoint(0, false, tol/10).getValue(0);
341        double max = crv.getLimitPoint(0, true, tol/10).getValue(0);
342        if (min < -tol || max > 1 + tol)
343            throw new IllegalArgumentException("Parametric curve out of range in Entity142: u0 = " + min + ", u1 = " + max);
344        sStart = 0;
345        sEnd = 1;
346        if (min < 0)
347            sStart = min;
348        if (max > 1)
349            sEnd = max;
350        mS = 1. / (sEnd - sStart);
351
352        min = crv.getLimitPoint(1, false, tol/10).getValue(1);
353        max = crv.getLimitPoint(1, true, tol/10).getValue(1);
354        if (min < -tol || max > 1 + tol)
355            throw new IllegalArgumentException("Parametric curve out of range in Entity142: v0 = " + min + ", v1 = " + max);
356        tStart = 0;
357        tEnd = 1;
358        if (min < 0)
359            tStart = min;
360        if (max > 1)
361            tEnd = max;
362        mT = 1. / (tEnd - tStart);
363        if (mS != 1 || mT != 1) {
364            Tt = GTransform.newTranslation(-sStart, -tStart, 0);
365            Ts = GTransform.valueOf(mS, mT, 1, 1);
366            T = Ts.times(Tt);
367            crv = crv.getTransformed(T).copyToReal();
368        }
369
370        return crv;
371    }
372
373    /**
374     * Return a reference to the Transformable GeomElement contained in this IGES Entity.
375     *
376     * @return A reference to the Transformable GeomElement contained in this IGES Entity.
377     */
378    @Override
379    protected Transformable getGeomElement() {
380        if (curves.size() == 1)
381            return curves.get(0);
382        return curves;
383    }
384
385    /**
386     * Returns <code>true</code> if the Entity can be written to an exchange file.
387     *
388     * @return true
389     */
390    @Override
391    public boolean canWrite() {
392        return true;
393    }
394
395    /**
396     * Write this entity object's parameter data to the specified PrintWriter.
397     *
398     * @param writer The PrintWriter to write the parameter data for this entity to.
399     * @param PDnum  The starting Parameter Data row index number.
400     * @return The Parameter Data row index number for the next row.
401     * @throws java.io.IOException
402     */
403    @Override
404    public int write(PrintWriter writer, int PDnum) throws IOException {
405        //  Build up the parameter data string.
406        StringBuilder buffer = new StringBuilder();
407        buffer.append(142);     buffer.append(Constants.Delim);
408        buffer.append(Crtn);    buffer.append(Constants.Delim);
409        buffer.append(Sptr);    buffer.append(Constants.Delim);
410        buffer.append(Bptr);    buffer.append(Constants.Delim);
411        buffer.append(Cptr);    buffer.append(Constants.Delim);
412        buffer.append(Pref);    buffer.append(Constants.Term);
413
414        //  Write it out.
415        int oldPDnum = PDnum;
416        PDnum = Constants.writeSection(writer, PDnum, Constants.makeSequenceNumber(getDENum()),
417                'P', buffer);
418
419        //  Store the PD line number and line count in the directory entry.
420        getDirectoryEntry().setPDNumber(oldPDnum, PDnum - oldPDnum);
421
422        return PDnum;
423    }
424
425    /**
426     * Returns a short String describing this Entity object's type.
427     *
428     * @return A short String describing this Entity object's type.
429     */
430    @Override
431    public String getTypeString() {
432        return "Entity142 - Curve on Parametric Surface";
433    }
434
435    /**
436     * Dump to String.
437     *
438     * @return String containing the resulting text.
439     */
440    @Override
441    public String toString() {
442        StringBuilder outStr = new StringBuilder(super.toString());
443        outStr.append("\n");
444
445        outStr.append("Crtn  = ");  outStr.append(Crtn);    outStr.append("\n");
446        outStr.append("Sptr  = ");  outStr.append(Sptr);    outStr.append("\n");
447        outStr.append("Bptr  = ");  outStr.append(Sptr);    outStr.append("\n");
448        outStr.append("Cptr  = ");  outStr.append(Sptr);    outStr.append("\n");
449        outStr.append("Pref  = ");  outStr.append(Sptr);    outStr.append("\n");
450
451        return outStr.toString();
452    }
453}