001/*
002 *   Entity144_TrimmedSurface  -- An entity representing a Trimmed 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.CurveFactory;
025import geomss.geom.nurbs.CurveUtils;
026import geomss.geom.nurbs.NurbsCurve;
027import jahuwaldt.js.param.Parameter;
028import java.io.IOException;
029import java.io.PrintWriter;
030import java.io.RandomAccessFile;
031import javax.measure.quantity.Dimensionless;
032import javax.measure.quantity.Length;
033import javax.measure.unit.SI;
034
035/**
036 * <b><i>TRIMMED PARAMETRIC SURFACE ENTITY</i></b> - This entity represents a surface that
037 * is a subset of another surface bounded on the outside by a boundary curve and on the
038 * inside (holes) with a series of boundary curves.
039 *
040 * <p>
041 * This entity, when read from an IGES file, is converted to a SubrangeSurface surface if
042 * possible (if the number of boundary curve segments is exactly 4 or can be automatically
043 * converted into 4). This entity type can be written out to an IGES file. When reading in
044 * this entity, the IGES parameters are stored in the user data with the prefix
045 * "IGES_144_" followed by the parameter name.
046 * </p>
047 *
048 * <p> Modified by: Joseph A. Huwaldt </p>
049 * 
050 * @author Joseph A. Huwaldt, Date: April 10, 2013
051 * @version April 12, 2016
052 */
053public class Entity144_TrimmedSurface extends GeomSSEntity {
054
055    private static final double epsEP = 0.86603;    //  End-point parallel angle tolerance : cos(30 deg)
056    private int PTS;    //  Pointer to the DE of the surface entity that is to be trimmed
057    private int N1 = 1; //  0 = the outer boundary is the boundary of D, 1 = otherwise
058    private int N2 = 0; //  This number indicates the number of simple closed curves which
059                        //  constitute the inner boundary of the trimmed surface.
060                        //  In case no inner boundary is introduced, this is set equal to zero.
061    private int PTO;    //  Pointer to the DE of the Curve on a Parametric Surface Entity
062                        //  that constitutes the outer boundary of the trimmed surface or zero.
063
064    //  This implementation ignores all interior boundaries.
065    
066    private Surface surface;    //  The subrange surface this entity represents.
067
068    /**
069     * Default constructor.
070     *
071     * @param p  part to which this entity is contained
072     * @param de Directory Entry for this entity
073     */
074    public Entity144_TrimmedSurface(Part p, DirEntry de) {
075        super(p, de);
076
077        if (Constants.DEBUG) {
078            System.out.println("Entity144 constructor called");
079        }
080
081    }
082
083    /**
084     * Create this entity from the specified GeomSS geometry element.
085     *
086     * @param part  The Part to which this entity is contained.
087     * @param DEnum The line count from the start of the Directory Entry Section for this
088     *              entry (odd number).
089     * @param geom  The GeomSS GeomPoint geometry to return an Entity for.
090     * @param PTS   Pointer to the DE of the surface entity that is to be trimmed.
091     * @param PTO   Pointer to the DE of the Curve on a Parametric Surface Entity that
092     *              constitutes the outer boundary of the trimmed surface or zero.
093     */
094    public Entity144_TrimmedSurface(Part part, int DEnum, SubrangeSurface geom, int PTS, int PTO) {
095        super(part, new DirEntry(144, 0, DEnum, 0, geom.getName()));
096        this.PTS = PTS;
097        this.PTO = PTO;
098        this.surface = geom;
099    }
100
101    /**
102     * Checks to see if the entity is correct.
103     */
104    @Override
105    public void check() {
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
113     */
114    @Override
115    public void read(RandomAccessFile in) throws IOException {
116
117        if (Constants.DEBUG) {
118            System.out.println("Entity144.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        PTS = getInt(s);    //  Pointer to the DE of the surface entity that is to be trimmed
129        N1 = getInt(s);     //  0 = the outer boundary is the boundary of D, 1 = otherwise
130        N2 = getInt(s);     //  The number of interior boundary curves if any.
131        PTO = getInt(s);    //  Pointer to the DE of the Curve on a Parametric Surface Entity that constitutes the outer boundary of the trimmed surface or zero.
132
133        //  Read in, but ignore, all the inner boundary curve segment pointers.
134        for (int i = 0; i < N2; ++i) {
135            int PTI = getInt(s);
136        }
137
138        super.read_additional();
139    }
140
141    /**
142     * Method used to apply IGES meta-data to GeomSS elements. This implementation stores
143     * in the user data, in addition to the header info, all the parameter information for
144     * this entity type prefixed by "IGES_144_".
145     */
146    @Override
147    protected void applyMetaData(GeomElement element) {
148        super.applyMetaData(element);
149        element.putUserData("IGES_144_N1", N1);
150        element.putUserData("IGES_144_N2", N2);
151    }
152
153    /**
154     * The GeomSS geometry element is created from the IGES parameters when this method is
155     * called.
156     */
157    @Override
158    void createGeometry() throws IOException {
159        Part part = getPart();
160        Parameter<Length> tol = Parameter.valueOf(1e-4, SI.METER);  //  Parametric tolerance.
161
162        //  Get the surface to be trimmed.
163        Surface Ssrf;
164        Entity entity = part.getEntity(PTS);
165        GeomSSEntity srfEntity;
166        if (entity instanceof GeomSSEntity) {
167            srfEntity = (GeomSSEntity)entity;
168            Ssrf = (Surface)srfEntity.getGeomElement(GTransform.IDENTITY);
169            if (Ssrf == null)
170                return;
171        } else
172            return;
173
174        //  If the outer boundary of Ssrf is the boundary, we are done.
175        if (N1 == 0) {
176            surface = SubrangeSurface.newInstance(Ssrf, 0, 0, 1, 1);
177            srfEntity.setUsedInList(true);  //  Indicate that the entity is used by this association.
178            return;
179        }
180
181        //  Get the boundary curve.
182        entity = part.getEntity(PTO);
183        if (entity instanceof Entity142_CurveOnSurface) {
184            GeomSSEntity geomEntity = (GeomSSEntity)entity;
185            GeomElement tmp = geomEntity.getGeomElement(GTransform.IDENTITY);
186            if (tmp == null || !(tmp instanceof GeomList))
187                return;
188            GeomList<SubrangeCurve> Dcc = (GeomList)tmp;
189
190            //  We can only handle 4 sided trimmed surfaces in SubrangeSurface.
191            //  So, first try to deal with common situations where there are not
192            //  exactly 4 boundary curves.
193            if (Dcc.size() < 3)
194                //  Don't know what to deal with a 0, 1 or 2 segment boundary.
195                return;
196
197            else if (Dcc.size() == 3) {
198                //  Check to see if one edge is collapsed.  If all 3 curves connect
199                //  end to end, then add a degenerate curve to the list to make 4 sides.
200                GeomPoint p0 = Dcc.getFirst().getRealPoint(1);
201                GeomPoint pim1e = p0;
202                for (int i = 1; i < 3; ++i) {
203                    SubrangeCurve crv = Dcc.get(i);
204                    if (crv.getRealPoint(0).distance(pim1e).isGreaterThan(tol))
205                        //  Curves do not connect end-to-end (as they should).
206                        return;
207                    pim1e = crv.getRealPoint(1);
208                }
209                if (pim1e.distance(p0).isGreaterThan(tol))
210                    //  Last curve end point doesn't connect with 1st curve start point.
211                    return;
212
213                //  All the end points are within tolerance of each other, so add a degenerate curve
214                //  between the last and the 1st points.
215                GeomPoint ppnt = Dcc.getLast().getParPosition().getRealPoint(1);
216                Curve pcrv = CurveFactory.createPoint(1, ppnt);
217                SubrangeCurve crv = SubrangeCurve.newInstance(Ssrf, pcrv);
218                Dcc.add(crv);
219
220            } else if (Dcc.size() > 4) {
221                //  Maybe some of the segments can be joined to get down to 4 segments?
222                GeomList<SubrangeCurve> Dcc2 = combineSegments(Dcc, Ssrf);
223                if (Dcc2.size() != 4) {
224                    GeomList.recycle(Dcc2);
225                    return;
226                }
227
228                //  Swap out the old list of segments for the new one.
229                Dcc = Dcc2;
230            }
231
232            //  Pull out the boundary curves for the subrange surface.
233            Curve s0 = Dcc.get(0).getParPosition();
234            Curve t1 = Dcc.get(1).getParPosition();
235            Curve s1 = Dcc.get(2).getParPosition().reverse();
236            Curve t0 = Dcc.get(3).getParPosition().reverse();
237
238            //  Create the subrange surface.
239            surface = SubrangeSurface.newInstance(Ssrf, s0, t0, s1, t1, 1e-4);
240
241            geomEntity.setUsedInList(true); //  Indicate that the entity is used by this association.
242            srfEntity.setUsedInList(true);  //  Indicate that the entity is used by this association.
243        }
244
245    }
246
247    /**
248     * Return a reference to the Transformable GeomElement contained in this IGES Entity.
249     *
250     * @return A reference to the Transformable GeomElement contained in this IGES Entity.
251     */
252    @Override
253    protected Transformable getGeomElement() {
254        return surface;
255    }
256
257    /**
258     * Returns <code>true</code> if the Entity can be written to an exchange file.
259     *
260     * @return true
261     */
262    @Override
263    public boolean canWrite() {
264        return true;
265    }
266
267    /**
268     * Write this entity object's parameter data to the specified PrintWriter.
269     *
270     * @param writer The PrintWriter to write the parameter data for this entity to.
271     * @param PDnum  The starting Parameter Data row index number.
272     * @return The Parameter Data row index number for the next row.
273     * @throws java.io.IOException
274     */
275    @Override
276    public int write(PrintWriter writer, int PDnum) throws IOException {
277        //  Build up the parameter data string.
278        StringBuilder buffer = new StringBuilder();
279        buffer.append(144);     buffer.append(Constants.Delim);
280        buffer.append(PTS);     buffer.append(Constants.Delim);
281        buffer.append(N1);      buffer.append(Constants.Delim);
282        buffer.append(N2);      buffer.append(Constants.Delim);
283        buffer.append(PTO);     buffer.append(Constants.Term);
284
285        //  Write it out.
286        int oldPDnum = PDnum;
287        PDnum = Constants.writeSection(writer, PDnum, Constants.makeSequenceNumber(getDENum()),
288                'P', buffer);
289
290        //  Store the PD line number and line count in the directory entry.
291        getDirectoryEntry().setPDNumber(oldPDnum, PDnum - oldPDnum);
292
293        return PDnum;
294    }
295
296    /**
297     * Returns a short String describing this Entity object's type.
298     *
299     * @return A short String describing this Entity object's type.
300     */
301    @Override
302    public String getTypeString() {
303        return "Entity144 - Trimmed Parametric Surface";
304    }
305
306    /**
307     * Dump to String.
308     *
309     * @return String containing the resulting text.
310     */
311    @Override
312    public String toString() {
313        StringBuilder outStr = new StringBuilder(super.toString());
314        outStr.append("\n");
315
316        return outStr.toString();
317    }
318
319    /**
320     * Method that tries to combine the segments of a composite curve in such a way that
321     * they make up a 4 sided boundary for a SubrangeSurface entity.
322     */
323    private static GeomList<SubrangeCurve> combineSegments(GeomList<SubrangeCurve> Dcc, Surface Ssrf) throws IllegalArgumentException {
324        Parameter<Length> ptol = Parameter.valueOf(1e-6, SI.METER);
325
326        GeomList<SubrangeCurve> Dcc2 = GeomList.newInstance();
327        SubrangeCurve lastCrv = Dcc.getFirst();
328        NurbsCurve lastPcrv = lastCrv.getParPosition().toNurbs(ptol);
329
330        int size = Dcc.size();
331        for (int i = 1; i < size; ++i) {
332            SubrangeCurve crv = Dcc.get(i);
333            NurbsCurve pcrv = crv.getParPosition().toNurbs(ptol);
334
335            //  Check to see if the end-start vectors in parametric space for each curve
336            //  are approximately parallel.
337            GeomVector<Dimensionless> Tim1 = lastPcrv.getSDerivative(1, 1).toUnitVector();
338            GeomVector<Dimensionless> Ti = pcrv.getSDerivative(0, 1).toUnitVector();
339            double cosa = Ti.dot(Tim1).getValue();
340            if (cosa > epsEP) {
341                //  End vectors are parallel.
342
343                //  Join the two segments together
344                pcrv = CurveUtils.connectCurves(lastPcrv, pcrv);
345                crv = SubrangeCurve.newInstance(Ssrf, pcrv);
346
347            } else
348                //  End vectors are not parallel.
349                Dcc2.add(lastCrv);
350
351            //  Pepare for next loop.
352            lastCrv = crv;
353            lastPcrv = pcrv;
354        }
355        if (Dcc2.size() == 0)
356            return Dcc2;
357
358        //  See if the last curve connects with the 1st one.
359        SubrangeCurve crv = Dcc2.get(0);
360        NurbsCurve pcrv = crv.getParPosition().toNurbs(ptol);
361        GeomVector<Dimensionless> Tim1 = lastPcrv.getSDerivative(1, 1).toUnitVector();
362        GeomVector<Dimensionless> Ti = pcrv.getSDerivative(0, 1).toUnitVector();
363        double cosa = Ti.dot(Tim1).getValue();
364        if (cosa > epsEP) {
365            pcrv = CurveUtils.connectCurves(lastPcrv, pcrv);
366            crv = SubrangeCurve.newInstance(Ssrf, pcrv);
367            Dcc2.remove(0);
368            lastCrv = crv;
369        }
370        Dcc2.add(lastCrv);
371
372        return Dcc2;
373    }
374
375}