001/* 002 * Entity126_BSplineCurve -- An entity representing a Rational B-Spline Curve. 003 * 004 * Copyright (C) 2010-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.Point; 025import geomss.geom.Transformable; 026import geomss.geom.nurbs.BasicNurbsCurve; 027import geomss.geom.nurbs.ControlPoint; 028import geomss.geom.nurbs.KnotVector; 029import geomss.geom.nurbs.NurbsCurve; 030import jahuwaldt.js.param.Parameter; 031import jahuwaldt.tools.math.MathTools; 032import java.io.IOException; 033import java.io.PrintWriter; 034import java.io.RandomAccessFile; 035import java.text.MessageFormat; 036import java.util.ArrayList; 037import java.util.List; 038import javax.measure.quantity.Length; 039 040/** 041 * <p> 042 * <b><i>RATIONAL B-SPLINE CURVE ENTITY</i></b> - This entity represents a Rational 043 * B-Spline Curve that may be isolated or used as a component of a Composite Curve Entity 044 * or a Subfigure Entity. This represents a parametric equation obtained by dividing two 045 * summations involving weights (which are real numbers), the control points, and B-Spline 046 * basis functions.</p> 047 * 048 * <p> 049 * This entity, when read from an IGES file, is converted to a NURBS curve. This entity 050 * type <b>can</b> be written out to an IGES file. When reading in this entity, all plane 051 * parameters are stored in the user data with the prefix "IGES_126_" followed by the 052 * parameter name. 053 * </p> 054 * 055 * <p> Modified by: Joseph A. Huwaldt </p> 056 * 057 * @author Joseph A. Huwaldt, Date: August 23, 2010 058 * @version February 22, 2025 059 */ 060public class Entity126_BSplineCurve extends GeomSSEntity { 061 062 private int prop1 = 0; // 0=non-planar, 1=planar 063 private int prop2 = 0; // 0=open, 1=closed 064 private int prop3 = 0; // 0=rational, 1=polynomial 065 private int prop4 = 0; // 0=non-periodic (clamped), 1=periodic (un-clamped). 066 private double[] knots = null; // Knot sequence 067 private double ustart = 0; // Starting parameter value. 068 private double uend = 1; // Ending parameter value. 069 private double xnorm = 0; // Unit normal (if curve is planar; flag1=1) 070 private double ynorm = 0; 071 private double znorm = 1; 072 073 private NurbsCurve curve; // The GeomSS curve this entity represents. 074 075 /** 076 * Default constructor. 077 * 078 * @param p part to which this entity is contained 079 * @param de Directory Entry for this entity 080 */ 081 public Entity126_BSplineCurve(Part p, DirEntry de) { 082 super(p, de); 083 084 if (Constants.DEBUG) { 085 System.out.println("Entity126 constructor called"); 086 } 087 088 } 089 090 /** 091 * Create this entity from the specified GeomSS geometry element. 092 * 093 * @param part The Part to which this entity is contained. 094 * @param DEnum The line count from the start of the Directory Entry Section for this 095 * entry (odd number). 096 * @param geom The GeomSS GeomPoint geometry to return an Entity for. 097 */ 098 public Entity126_BSplineCurve(Part part, int DEnum, NurbsCurve geom) { 099 super(part, new DirEntry(126, 0, DEnum, 0, geom.getName())); 100 curve = geom; 101 102 // Store off the Knot vector also. 103 KnotVector kv = curve.getKnotVector(); 104 int n = kv.length(); 105 knots = new double[n]; 106 for (int i = 0; i < n; ++i) 107 knots[i] = kv.getValue(i); 108 109 // Is the curve planar? 110 Parameter<Length> tol = Parameter.valueOf(Constants.Grain, Constants.unit); 111 if (curve.isPlanar(tol)) 112 prop1 = 1; 113 114 // Is the curve closed? 115 if (curve.getRealPoint(0).distance(curve.getRealPoint(1)).isLessThan(tol)) 116 prop2 = 1; 117 118 // Is the curve rational (prop3 = 0) or polynomial (prop3 = 1)? 119 List<ControlPoint> cps = curve.getControlPoints(); 120 n = cps.size(); 121 double w = cps.get(0).getWeight(); 122 prop3 = 1; 123 for (int i = 1; i < n; ++i) { 124 ControlPoint cp = cps.get(i); 125 if (Math.abs(cp.getWeight() - w) > MathTools.EPS) { 126 prop3 = 0; 127 break; 128 } 129 } 130 } 131 132 /** 133 * Checks to see if the entity is correct. The following restrictions are imposed: 134 * 135 * - The Label Display Pointer shall be 0 136 */ 137 @Override 138 public void check() { 139 DirEntry DE = getDirectoryEntry(); 140 141 // DE LblDsp shall be 0 142 if (DE.getLblDsp() != 0) { 143 String msg = MessageFormat.format(RESOURCES.getString("labelDisplay"), DE.getLblDsp()); 144 addErrorMessage(getWarningString(msg)); 145 } 146 147 } 148 149 /** 150 * Read the Parameter Data from the String read in by the superclass. 151 * 152 * @param in input file 153 * @throws java.io.IOException if the parameter data could not be read in. 154 */ 155 @Override 156 public void read(RandomAccessFile in) throws IOException { 157 158 if (Constants.DEBUG) { 159 System.out.println("Entity126.read() called"); 160 } 161 162 super.read(in); 163 String s = getPDString(); 164 165 if (Constants.DEBUG) { 166 System.out.println("PD String = \"" + s + "\""); 167 } 168 169 int K = getInt(s); // Number of control points. 170 int M = getInt(s); // Degree of basis functions. 171 prop1 = getInt(s); 172 prop2 = getInt(s); 173 prop3 = getInt(s); 174 prop4 = getInt(s); 175 176 int n = K + 1 + M + 1; // Number of knots. 177 knots = new double[n]; 178 for (int i = 0; i < n; ++i) { 179 double u = getReal(s); 180 knots[i] = u; 181 } 182 183 List<Double> weights = new ArrayList(); // List of weights for each control point. 184 for (int i = 0; i <= K; ++i) { 185 double w = getReal(s); 186 weights.add(w); 187 } 188 189 List<Point> cps = new ArrayList(); // List of control points. 190 for (int i = 0; i <= K; ++i) { 191 cps.add(getPoint3(s)); 192 } 193 194 ustart = getReal(s); 195 uend = getReal(s); 196 197 xnorm = getReal(s); 198 if (Math.abs(xnorm) < Constants.Grain) 199 xnorm = 0; 200 ynorm = getReal(s); 201 if (Math.abs(ynorm) < Constants.Grain) 202 ynorm = 0; 203 znorm = getReal(s); 204 if (Math.abs(znorm) < Constants.Grain) 205 znorm = 0; 206 207 // Make sure all the knots read in are in-range. 208 for (int i = 0; i < n; ++i) { 209 double kv = knots[i]; 210 if (kv < ustart) 211 knots[i] = ustart; 212 else if (kv > uend) 213 knots[i] = uend; 214 } 215 216 // Scale the knots into the range 0-1. 217 double[] scaledKnots = knots; 218 if (Math.abs(ustart) > MathTools.EPS || Math.abs(uend - 1.0) > MathTools.EPS) { 219 scaledKnots = new double[n]; 220 double m = 1. / (uend - ustart); 221 double b = -m * ustart; 222 for (int i = 0; i < n; ++i) { 223 double kv = knots[i]; 224 kv = scaleKnot(m, b, kv); 225 scaledKnots[i] = kv; 226 } 227 } 228 229 // Create the knot vector. 230 KnotVector kv = KnotVector.newInstance(M, scaledKnots); 231 232 // Create the control points. 233 List<ControlPoint> cpList = new ArrayList(); 234 n = cps.size(); 235 for (int i = 0; i < n; ++i) { 236 double weight = weights.get(i); 237 Point pnt = cps.get(i); 238 cpList.add(ControlPoint.valueOf(pnt, weight)); 239 } 240 241 // Create the curve. 242 curve = BasicNurbsCurve.newInstance(cpList, kv); 243 244 super.read_additional(); 245 } 246 247 /** 248 * The GeomSS geometry element is created from the IGES parameters when this method is 249 * called. 250 */ 251 @Override 252 void createGeometry() throws IOException { 253 // Everything done in "read()". 254 } 255 256 /** 257 * Scale the knot value in the the required range from 0.0 to 1.0. 258 */ 259 private double scaleKnot(double m, double b, double value) { 260 // Scale the knot value. 261 double kv = m * value + b; 262 263 // Watch for roundoff. 264 if (kv < 0.0) 265 kv = 0.0; 266 else if (kv > 1.0) 267 kv = 1.0; 268 269 return kv; 270 } 271 272 /** 273 * Method used to apply IGES meta-data to GeomSS elements. This implementation stores 274 * in the user data, in addition to the header info, all the parameter information for 275 * this entity type prefixed by "IGES_126_". 276 */ 277 @Override 278 protected void applyMetaData(GeomElement element) { 279 super.applyMetaData(element); 280 element.putUserData("IGES_126_PROP1", prop1); 281 element.putUserData("IGES_126_PROP2", prop2); 282 element.putUserData("IGES_126_PROP3", prop3); 283 element.putUserData("IGES_126_PROP4", prop4); 284 element.putUserData("IGES_U0", ustart); 285 element.putUserData("IGES_U1", uend); 286 element.putUserData("IGES_126_XNORM", xnorm); 287 element.putUserData("IGES_126_YNORM", ynorm); 288 element.putUserData("IGES_126_ZNORM", znorm); 289 } 290 291 /** 292 * Return a reference to the Transformable GeomElement contained in this IGES Entity. 293 * 294 * @return A reference to the Transformable GeomElement contained in this IGES Entity. 295 */ 296 @Override 297 protected Transformable getGeomElement() { 298 return curve; 299 } 300 301 /** 302 * Returns <code>true</code> if the Entity can be written to an exchange file. 303 * 304 * @return true 305 */ 306 @Override 307 public boolean canWrite() { 308 return true; 309 } 310 311 /** 312 * Write this entity's parameter data to the specified PrintWriter. 313 * 314 * @param writer The PrintWriter to write the parameter data for this entity to. 315 * @param PDnum The starting Parameter Data row index number. 316 * @return The Parameter Data row index number for the next row. 317 * @throws java.io.IOException if the parameter data could not be written out. 318 */ 319 @Override 320 public int write(PrintWriter writer, int PDnum) throws IOException { 321 322 // Build up the parameter data string. 323 NurbsCurve curve2 = curve.to(Constants.unit); 324 List<ControlPoint> cps = curve2.getControlPoints(); 325 int K = cps.size() - 1; 326 KnotVector kv = curve2.getKnotVector(); 327 int M = kv.getDegree(); 328 329 StringBuilder buffer = new StringBuilder(); 330 buffer.append(126); buffer.append(Constants.Delim); 331 buffer.append(K); buffer.append(Constants.Delim); 332 buffer.append(M); buffer.append(Constants.Delim); 333 buffer.append(prop1); buffer.append(Constants.Delim); 334 buffer.append(prop2); buffer.append(Constants.Delim); 335 buffer.append(prop3); buffer.append(Constants.Delim); 336 buffer.append(prop4); buffer.append(Constants.Delim); 337 338 int n = kv.length(); 339 for (int i = 0; i < n; ++i) { 340 double u = kv.getValue(i); 341 buffer.append(u); buffer.append(Constants.Delim); 342 } 343 344 for (int i = 0; i <= K; ++i) { 345 double w = cps.get(i).getWeight(); 346 buffer.append(w); buffer.append(Constants.Delim); 347 } 348 349 for (int i = 0; i <= K; ++i) { 350 Point cp = cps.get(i).getPoint(); 351 appendPoint3(buffer, cp); 352 } 353 354 buffer.append(ustart); buffer.append(Constants.Delim); 355 buffer.append(uend); buffer.append(Constants.Delim); 356 357 if (Math.abs(xnorm) < Constants.Grain) 358 xnorm = 0; 359 buffer.append(xnorm); buffer.append(Constants.Delim); 360 if (Math.abs(ynorm) < Constants.Grain) 361 ynorm = 0; 362 buffer.append(ynorm); buffer.append(Constants.Delim); 363 if (Math.abs(znorm) < Constants.Grain) 364 znorm = 0; 365 buffer.append(znorm); buffer.append(Constants.Term); 366 367 // Write it out. 368 int oldPDnum = PDnum; 369 PDnum = Constants.writeSection(writer, PDnum, Constants.makeSequenceNumber(getDENum()), 370 'P', buffer); 371 372 // Store the PD line number and line count in the directory entry. 373 getDirectoryEntry().setPDNumber(oldPDnum, PDnum - oldPDnum); 374 375 return PDnum; 376 } 377 378 /** 379 * Returns a short String describing this Entity object's type. 380 * 381 * @return A short String describing this Entity object's type. 382 */ 383 @Override 384 public String getTypeString() { 385 return "Entity126 - Rational B-Spline Curve"; 386 } 387 388 /** 389 * Dump to String. 390 * 391 * @return String containing the resulting text. 392 */ 393 @Override 394 public String toString() { 395 if (curve == null) 396 return super.toString(); 397 398 StringBuilder outStr = new StringBuilder(super.toString()); 399 outStr.append("\n"); 400 401 // Get the data we need. 402 List<ControlPoint> cps = curve.getControlPoints(); 403 int K = cps.size()-1; 404 KnotVector kv = curve.getKnotVector(); 405 int M = kv.getDegree(); 406 407 outStr.append("K = "); outStr.append(K); outStr.append("\n"); 408 outStr.append("M = "); outStr.append(M ); outStr.append("\n"); 409 outStr.append("prop1 = "); outStr.append(prop1); outStr.append("\n"); 410 outStr.append("prop2 = "); outStr.append(prop2); outStr.append("\n"); 411 outStr.append("prop3 = "); outStr.append(prop3); outStr.append("\n"); 412 outStr.append("prop4 = "); outStr.append(prop4); outStr.append("\n"); 413 414 int n = knots.length; 415 for (int i = 0; i < n; i++) { 416 outStr.append("T("); outStr.append(i); outStr.append("): "); 417 outStr.append(knots[i]); outStr.append("\n"); 418 } 419 outStr.append("\n"); 420 421 422 for (int i=0; i <= K; i++) { 423 double weight = cps.get(i).getWeight(); 424 outStr.append("W("); outStr.append(i); outStr.append("): "); 425 outStr.append(weight); outStr.append("\n"); 426 } 427 428 for (int i = 0; i <= K; i++) { 429 Point cp = cps.get(i).getPoint(); 430 outStr.append("X("); outStr.append(i); outStr.append("): "); 431 outStr.append(cp.get(0)); outStr.append("\n"); 432 outStr.append("Y("); outStr.append(i); outStr.append("): "); 433 outStr.append(cp.get(1)); outStr.append("\n"); 434 outStr.append("Z("); outStr.append(i); outStr.append("): "); 435 outStr.append(cp.get(2)); outStr.append("\n"); 436 } 437 438 outStr.append("ustart = "); outStr.append(ustart); outStr.append("\n"); 439 outStr.append("uend = "); outStr.append(uend); outStr.append("\n"); 440 441 if (prop1 == 1) { 442 outStr.append("xnorm = "); outStr.append(xnorm); outStr.append("\n"); 443 outStr.append("ynorm = "); outStr.append(ynorm); outStr.append("\n"); 444 outStr.append("znorm = "); outStr.append(znorm); outStr.append("\n"); 445 } 446 447 return outStr.toString(); 448 } 449 450}