001/** 002 * CSTCurveTrans -- A GeomTransform that has a CSTCurve for a child. 003 * 004 * Copyright (C) 2015, by Joseph A. Huwaldt. All rights reserved. 005 * 006 * This library is free software; you can redistribute it and/or modify it under the terms 007 * of the GNU Lesser General Public License as published by the Free Software Foundation; 008 * either version 2 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 012 * PARTICULAR PURPOSE. See the GNU Library General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License along with 015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - 016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html 017 */ 018package geomss.geom.cst; 019 020import geomss.geom.*; 021import geomss.geom.nurbs.NurbsCurve; 022import jahuwaldt.js.param.Parameter; 023import java.text.MessageFormat; 024import java.util.List; 025import java.util.Objects; 026import static java.util.Objects.requireNonNull; 027import javax.measure.converter.ConversionException; 028import javax.measure.quantity.Dimensionless; 029import javax.measure.quantity.Length; 030import javax.measure.unit.Unit; 031import javax.swing.event.ChangeListener; 032import javolution.context.ObjectFactory; 033import javolution.context.StackContext; 034import javolution.lang.Immutable; 035import javolution.xml.XMLFormat; 036import javolution.xml.stream.XMLStreamException; 037 038/** 039 * A {@link GeomTransform} object that refers to a {@link CSTCurve} object and masquerades 040 * as a CSTCurve object itself. 041 * 042 * <p> Modified by: Joseph A. Huwaldt </p> 043 * 044 * @author Joseph A. Huwaldt, Date: October 7, 2015 045 * @version November 28, 2015 046 */ 047@SuppressWarnings({"serial", "CloneableImplementsClone"}) 048public final class CSTCurveTrans extends CSTCurve implements GeomTransform<CSTCurve> { 049 050 /** 051 * The transformation represented by this transformation element. 052 */ 053 private GTransform _TM; 054 055 /** 056 * The object that is the child of this transform. 057 */ 058 private CSTCurve _child; 059 060 /** 061 * Reference to a change listener for this object's child. 062 */ 063 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 064 065 /** 066 * Returns a 3D {@link CSTCurveTrans} instance holding the specified {@link CSTCurve} 067 * and {@link GTransform}. 068 * 069 * @param child The CSTCurve that is the child of this transform element (may not 070 * be <code>null</code>). 071 * @param transform The transform held by this transform element (may not be 072 * <code>null</code>). 073 * @return the transform element having the specified values. 074 */ 075 public static CSTCurveTrans newInstance(CSTCurve child, GTransform transform) { 076 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 077 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 078 079 if (child.getPhyDimension() != 3) 080 throw new DimensionException( 081 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "CST curve", child.getPhyDimension())); 082 083 CSTCurveTrans obj = FACTORY.object(); 084 obj._TM = transform; 085 obj._child = child; 086 087 // Listen for changes to the child object and pass them on. 088 if (!(child instanceof Immutable)) 089 child.addChangeListener(obj._childChangeListener); 090 091 return obj; 092 } 093 094 /** 095 * Returns the transformation represented by this transformation element. 096 * 097 * @return The transformation represented by this element. 098 */ 099 @Override 100 public GTransform getTransform() { 101 return _TM; 102 } 103 104 /** 105 * Returns the total transformation represented by an entire chain of GeomTransform 106 * objects below this one. 107 * 108 * @return The total transformation represented by the entire chain of transform 109 * objects. 110 */ 111 @Override 112 public GTransform getTotalTransform() { 113 return GeomUtil.getTotalTransform(this); 114 } 115 116 /** 117 * Sets the transformation represented by this transformation element. 118 * 119 * @param transform The transform to set this transform element to (may not be 120 * <code>null</code>). 121 */ 122 @Override 123 public void setTransform(GTransform transform) { 124 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 125 _TM = transform; 126 fireChangeEvent(); 127 } 128 129 /** 130 * Returns the child object transformed by this transform element. 131 * 132 * @return The child object transformed by this element. 133 */ 134 @Override 135 public CSTCurve getChild() { 136 return _child; 137 } 138 139 /** 140 * Return the class function associated with this CST curve. 141 * 142 * @return The class function associated with this CST curve. 143 */ 144 @Override 145 public CSTClassFunction getCFunction() { 146 return _child.getCFunction(); 147 } 148 149 /** 150 * Return the shape function associated with this CST curve. 151 * 152 * @return The shape function associated with this CST curve. 153 */ 154 @Override 155 public CSTShapeFunction getSFunction() { 156 return _child.getSFunction(); 157 } 158 159 /** 160 * Return the offset of the trailing edge of the curve from the chord line. 161 * 162 * @return The offset of the trailing edge of the curve from the chord line. 163 */ 164 @Override 165 public Parameter<Length> getOffsetTE() { 166 // It is possible that the transform scales the offset length. 167 StackContext.enter(); 168 try { 169 Parameter<Length> dy = _child.getOffsetTE(); 170 GeomVector<Length> vec = _child.getYhat().times(dy).to(dy.getUnit()); 171 vec = vec.getTransformed(_TM); 172 return StackContext.outerCopy(vec.mag()); 173 } finally { 174 StackContext.exit(); 175 } 176 } 177 178 /** 179 * Return the chord length of the curve. This defines the scale or size of the curve. 180 * 181 * @return The chord length of the curve. 182 */ 183 @Override 184 public Parameter<Length> getChord() { 185 // It is possible that the transform scales the chord length. 186 StackContext.enter(); 187 try { 188 Parameter<Length> chord = _child.getChord(); 189 GeomVector<Length> vec = _child.getXhat().times(chord).to(chord.getUnit()); 190 vec = vec.getTransformed(_TM); 191 return StackContext.outerCopy(vec.mag()); 192 } finally { 193 StackContext.exit(); 194 } 195 } 196 197 /** 198 * Return the origin of the leading edge of this curve. This defines the s=0 location 199 * of the curve in space. 200 * 201 * @return The origin of the leading edge of this curve. 202 */ 203 @Override 204 public Point getOrigin() { 205 return _TM.transform(_child.getOrigin()); 206 } 207 208 /** 209 * Return the chord-wise direction of this curve (the curve X-direction). 210 * 211 * @return The chord-wise direction of this curve. 212 */ 213 @Override 214 public Vector<Dimensionless> getXhat() { 215 return _child.getXhat().getTransformed(_TM).immutable(); 216 } 217 218 /** 219 * Return the in-plane direction of this curve perpendicular to the chord-wise 220 * direction (the curve Y-direction). 221 * 222 * @return The in-plane direction of this curve perpendicular to Xhat. 223 */ 224 @Override 225 public Vector<Dimensionless> getYhat() { 226 return _child.getYhat().getTransformed(_TM).immutable(); 227 } 228 229 /** 230 * Return a copy of the child object transformed by this transformation. 231 * 232 * @return A copy of this object with any transformations or subranges removed 233 * (applied). 234 */ 235 @Override 236 public BasicCSTCurve copyToReal() { 237 StackContext.enter(); 238 try { 239 // Get the transformed piece-parts. 240 CSTClassFunction Cf = getCFunction(); 241 CSTShapeFunction Sf = getSFunction(); 242 Parameter<Length> DZTE = getOffsetTE(); 243 Parameter<Length> chord = getChord(); 244 Point origin = getOrigin(); 245 Vector<Dimensionless> xhat = getXhat(); 246 Vector<Dimensionless> yhat = getYhat(); 247 248 // Create and return a new CST curve. 249 BasicCSTCurve crv = BasicCSTCurve.newInstance(Cf, Sf, DZTE, origin, chord, xhat, yhat); 250 return StackContext.outerCopy(crv); 251 } finally { 252 StackContext.exit(); 253 } 254 } 255 256 /** 257 * Return an immutable version of this CST curve. 258 * 259 * @return an immutable version of this curve. 260 */ 261 @Override 262 public BasicCSTCurve immutable() { 263 return copyToReal(); 264 } 265 266 /** 267 * Return <code>true</code> if this curve contains valid and finite numerical 268 * components. A value of <code>false</code> will be returned if any of the coordinate 269 * values are NaN or Inf. 270 * 271 * @return true if this element contains valid and finite numerical values. 272 */ 273 @Override 274 public boolean isValid() { 275 return _child.isValid(); 276 } 277 278 /** 279 * Returns the number of physical dimensions of the geometry element. 280 */ 281 @Override 282 public int getPhyDimension() { 283 return _child.getPhyDimension(); 284 } 285 286 /** 287 * Calculate a point on the curve for the given parametric distance along the curve, 288 * <code>p(s)</code>. 289 * 290 * @param s parametric distance to calculate a point for (0.0 to 1.0 inclusive). 291 * @return the calculated point 292 */ 293 @Override 294 public Point getRealPoint(double s) { 295 return _TM.transform(_child.getRealPoint(s)); 296 } 297 298 /** 299 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with 300 * respect to parametric distance on the curve for the given parametric distance along 301 * the curve, <code>d^{grade}p(s)/d^{grade}s</code>. 302 * <p> 303 * Example:<br> 304 * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br> 305 * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc. 306 * </p> 307 * 308 * @param s Parametric distance to calculate derivatives for (0.0 to 1.0 309 * inclusive). 310 * @param grade The maximum grade to calculate the derivatives for (1=1st derivative, 311 * 2=2nd derivative, etc) 312 * @return A list of derivatives up to the specified grade of the curve at the 313 * specified parametric position. 314 * @throws IllegalArgumentException if the grade is < 0. 315 */ 316 @Override 317 public List<Vector<Length>> getSDerivatives(double s, int grade) { 318 BasicCSTCurve trans = copyToReal(); 319 List<Vector<Length>> ders = trans.getSDerivatives(s, grade); 320 BasicCSTCurve.recycle(trans); 321 return ders; 322 } 323 324 /** 325 * Return the coordinate point representing the minimum bounding box corner of this 326 * geometry element (e.g.: min X, min Y, min Z). 327 * 328 * @return The minimum bounding box coordinate for this geometry element. 329 * @throws IndexOutOfBoundsException if this list contains no elements. 330 */ 331 @Override 332 public Point getBoundsMin() { 333 BasicCSTCurve trans = copyToReal(); 334 Point minPoint = trans.getBoundsMin(); 335 BasicCSTCurve.recycle(trans); 336 return minPoint; 337 } 338 339 /** 340 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 341 * X, max Y, max Z). 342 * 343 * @return The maximum bounding box coordinate for this geometry element. 344 * @throws IndexOutOfBoundsException if this list contains no elements. 345 */ 346 @Override 347 public Point getBoundsMax() { 348 BasicCSTCurve trans = copyToReal(); 349 Point maxPoint = trans.getBoundsMax(); 350 BasicCSTCurve.recycle(trans); 351 return maxPoint; 352 } 353 354 /** 355 * Return a new curve that is identical to this one, but with the parameterization 356 * reversed. This does not change the origin or axis directions, but only the curve 357 * parameterization; what was s=0 will become s=1, etc 358 * 359 * @return A new curve identical to this one, but with the parameterization reversed. 360 */ 361 @Override 362 public CSTCurveTrans reverse() { 363 CSTCurveTrans crv = CSTCurveTrans.newInstance(_child.reverse(), _TM); 364 return copyState(crv); 365 } 366 367 /** 368 * Split this curve at the specified parametric position returning a list containing 369 * two curves (a lower curve with smaller parametric positions than "s" and an upper 370 * curve with larger parametric positions). The origin, chord length, and trailing 371 * edge offset from chord-line will be identical for the the returned segments and 372 * this curve. The only change is in the parameterization. 373 * 374 * @param s The parametric position where this curve should be split (must not be 0 or 375 * 1!). 376 * @return A list containing two curves: 0 == the lower curve, 1 == the upper curve. 377 */ 378 @Override 379 public GeomList<CSTCurve> splitAt(double s) { 380 381 // Split the parametric position curve. 382 GeomList<CSTCurve> pCrvs = _child.splitAt(s); 383 384 // Create the output list. 385 GeomList<CSTCurve> output = GeomList.newInstance(); 386 387 // Create the lower curve. 388 output.add(CSTCurveTrans.newInstance(pCrvs.getFirst(), _TM)); 389 output.add(CSTCurveTrans.newInstance(pCrvs.getLast(), _TM)); 390 391 GeomList.recycle(pCrvs); 392 393 return output; 394 } 395 396 /** 397 * Returns the unit in which the geometry of this curve is stated. 398 */ 399 @Override 400 public Unit<Length> getUnit() { 401 return _child.getUnit(); 402 } 403 404 /** 405 * Returns the equivalent to this curve but stated in the specified unit. 406 * 407 * @param unit the length unit of the curve to be returned. May not be null. 408 * @return an equivalent to this curve but stated in the specified unit. 409 * @throws ConversionException if the the input unit is not a length unit. 410 */ 411 @Override 412 public CSTCurveTrans to(Unit<Length> unit) throws ConversionException { 413 if (unit.equals(getUnit())) 414 return this; 415 416 return CSTCurveTrans.newInstance(_child.to(unit), _TM); 417 } 418 419 /** 420 * Return the equivalent of this curve converted to the specified number of physical 421 * dimensions. This implementation will throw an exception if the specified dimension 422 * is anything other than 3. 423 * 424 * @param newDim The dimension of the curve to return. MUST equal 3. 425 * @return The equivalent of this curve converted to the new dimensions. 426 * @throws IllegalArgumentException if the new dimension is anything other than 3. 427 */ 428 @Override 429 public CSTCurveTrans toDimension(int newDim) { 430 if (newDim == 3) 431 return this; 432 433 throw new IllegalArgumentException( 434 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 435 } 436 437 /** 438 * Return a NURBS curve representation of this curve to within the specified 439 * tolerance. 440 * 441 * @param tol The greatest possible difference between this curve and the NURBS 442 * representation returned. 443 * @return A NURBS curve that represents this curve to within the specified tolerance. 444 */ 445 @Override 446 public NurbsCurve toNurbs(Parameter<Length> tol) { 447 BasicCSTCurve crv = copyToReal(); 448 NurbsCurve ncrv = crv.toNurbs(tol); 449 BasicCSTCurve.recycle(crv); 450 return ncrv; 451 } 452 453 /** 454 * Returns a copy of this CSTCurveTrans instance 455 * {@link javolution.context.AllocatorContext allocated} by the calling thread 456 * (possibly on the stack). 457 * 458 * @return an identical and independent copy of this point. 459 */ 460 @Override 461 public CSTCurveTrans copy() { 462 return copyOf(this); 463 } 464 465 /** 466 * Compares this NurbsCurveTrans against the specified object for strict equality. 467 * 468 * @param obj the object to compare with. 469 * @return <code>true</code> if this transform is identical to that transform; 470 * <code>false</code> otherwise. 471 */ 472 @Override 473 public boolean equals(Object obj) { 474 if (this == obj) 475 return true; 476 if ((obj == null) || (obj.getClass() != this.getClass())) 477 return false; 478 479 CSTCurveTrans that = (CSTCurveTrans)obj; 480 return this._TM.equals(that._TM) 481 && this._child.equals(that._child) 482 && super.equals(obj); 483 } 484 485 /** 486 * Returns the hash code for this parameter. 487 * 488 * @return the hash code value. 489 */ 490 @Override 491 public int hashCode() { 492 return 31*super.hashCode() + Objects.hash(_TM, _child); 493 } 494 495 /** 496 * Recycles a <code>CSTCurveTrans</code> instance immediately (on the stack when 497 * executing in a StackContext). 498 * 499 * @param instance The instance to be recycled. 500 */ 501 public static void recycle(CSTCurveTrans instance) { 502 FACTORY.recycle(instance); 503 } 504 505 /** 506 * Holds the default XML representation for this object. 507 */ 508 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 509 protected static final XMLFormat<CSTCurveTrans> XML = new XMLFormat<CSTCurveTrans>(CSTCurveTrans.class) { 510 511 @Override 512 public CSTCurveTrans newInstance(Class<CSTCurveTrans> cls, XMLFormat.InputElement xml) throws XMLStreamException { 513 return FACTORY.object(); 514 } 515 516 @Override 517 public void read(XMLFormat.InputElement xml, CSTCurveTrans obj) throws XMLStreamException { 518 CSTCurve.XML.read(xml, obj); // Call parent read. 519 520 obj._TM = xml.getNext(); 521 CSTCurve child = xml.getNext(); 522 obj._child = child; 523 524 // Listen for changes to the child object and pass them on. 525 if (!(child instanceof Immutable)) 526 child.addChangeListener(obj._childChangeListener); 527 } 528 529 @Override 530 public void write(CSTCurveTrans obj, XMLFormat.OutputElement xml) throws XMLStreamException { 531 CSTCurve.XML.write(obj, xml); // Call parent write. 532 533 xml.add(obj._TM); 534 xml.add(obj._child); 535 536 } 537 }; 538 539 ////////////////////// 540 // Factory Creation // 541 ////////////////////// 542 private static final ObjectFactory<CSTCurveTrans> FACTORY = new ObjectFactory<CSTCurveTrans>() { 543 @Override 544 protected CSTCurveTrans create() { 545 return new CSTCurveTrans(); 546 } 547 548 @Override 549 protected void cleanup(CSTCurveTrans obj) { 550 obj.reset(); 551 obj._TM = null; 552 if (Objects.nonNull(obj._childChangeListener)) 553 obj._child.removeChangeListener(obj._childChangeListener); 554 obj._child = null; 555 } 556 }; 557 558 @SuppressWarnings("unchecked") 559 private static CSTCurveTrans copyOf(CSTCurveTrans original) { 560 CSTCurveTrans obj = FACTORY.object(); 561 obj._TM = original._TM.copy(); 562 obj._child = original._child.copy(); 563 original.copyState(obj); 564 if (!(obj._child instanceof Immutable)) 565 obj._child.addChangeListener(obj._childChangeListener); 566 return obj; 567 } 568 569 /** 570 * Do not allow the default constructor to be used. 571 */ 572 private CSTCurveTrans() { } 573 574}