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}