001/** 002 * NurbsSurfaceTrans -- A GeomTransform that has a NurbsSurface for a child. 003 * 004 * Copyright (C) 2010-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.nurbs; 019 020import geomss.geom.*; 021import java.text.MessageFormat; 022import java.util.List; 023import java.util.Objects; 024import static java.util.Objects.nonNull; 025import static java.util.Objects.requireNonNull; 026import javax.measure.converter.ConversionException; 027import javax.measure.quantity.Length; 028import javax.measure.unit.Unit; 029import javax.swing.event.ChangeEvent; 030import javax.swing.event.ChangeListener; 031import javolution.context.ObjectFactory; 032import javolution.context.StackContext; 033import javolution.lang.Immutable; 034import javolution.util.FastTable; 035import javolution.xml.XMLFormat; 036import javolution.xml.stream.XMLStreamException; 037 038/** 039 * A {@link GeomTransform} object that refers to a {@link NurbsSurface} object and 040 * masquerades as a NurbsSurface object itself. 041 * 042 * <p> Modified by: Joseph A. Huwaldt </p> 043 * 044 * @author Joseph A. Huwaldt, Date: June 14, 2010 045 * @version November 28, 2015 046 */ 047@SuppressWarnings({"serial", "CloneableImplementsClone"}) 048public final class NurbsSurfaceTrans extends NurbsSurface implements GeomTransform<NurbsSurface> { 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 NurbsSurface _child; 059 060 /** 061 * Reference to a change listener for this object's child. 062 */ 063 private ChangeListener _childChangeListener = new MyChangeListener(this); 064 065 // The following supports an optimization that caches the "copyToReal" version of the child 066 // in order to speed up many calculations. 067 private BasicNurbsSurface _realChild; 068 069 /** 070 * Returns a 3D {@link NurbsSurfaceTrans} instance holding the specified 071 * {@link NurbsSurface} and {@link GTransform}. 072 * 073 * @param child The NurbsSurface that is the child of this transform element (may 074 * not be <code>null</code>). 075 * @param transform The transform held by this transform element (may not be 076 * <code>null</code>). 077 * @return the transform element having the specified values. 078 */ 079 public static NurbsSurfaceTrans newInstance(NurbsSurface child, GTransform transform) { 080 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 081 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 082 083 if (child.getPhyDimension() != 3) 084 throw new DimensionException( 085 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "NURBS surface", child.getPhyDimension())); 086 087 NurbsSurfaceTrans obj = FACTORY.object(); 088 obj._TM = transform; 089 obj._child = child; 090 obj._realChild = obj.privateCopyToReal(); 091 092 // Listen for changes to the child object and pass them on. 093 if (!(child instanceof Immutable)) 094 child.addChangeListener(obj._childChangeListener); 095 096 return obj; 097 } 098 099 // A change listener that recreates the realChild geometry as well as 100 // passing the child's change event on to any listeners. 101 private static class MyChangeListener extends ForwardingChangeListener { 102 103 private final NurbsSurfaceTrans thisTrans; 104 105 public MyChangeListener(NurbsSurfaceTrans trans) { 106 super(trans); 107 this.thisTrans = trans; 108 } 109 110 @Override 111 public void stateChanged(ChangeEvent e) { 112 // Re-create the internal real copy of the child geometry. 113 thisTrans.privateCopyToReal(); 114 super.stateChanged(e); 115 } 116 } 117 118 /** 119 * Returns the transformation represented by this transformation element. 120 */ 121 @Override 122 public GTransform getTransform() { 123 return _TM; 124 } 125 126 /** 127 * Returns the total transformation represented by an entire chain of GeomTransform 128 * objects below this one. 129 */ 130 @Override 131 public GTransform getTotalTransform() { 132 return GeomUtil.getTotalTransform(this); 133 } 134 135 /** 136 * Sets the transformation represented by this transformation element. 137 * 138 * @param transform The transform to set this transform element to (may not be 139 * <code>null</code>). 140 */ 141 @Override 142 public void setTransform(GTransform transform) { 143 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 144 145 if (!transform.equals(_TM)) { 146 // Create a new "real" child surface. 147 _TM = transform; 148 _realChild = privateCopyToReal(); 149 fireChangeEvent(); 150 } 151 152 } 153 154 /** 155 * Returns the child object transformed by this transform element. 156 */ 157 @Override 158 public NurbsSurface getChild() { 159 return _child; 160 } 161 162 /** 163 * Return a copy of the child object transformed by this transformation. 164 * 165 * @return A copy of the child object transformed by this transformation. 166 */ 167 @Override 168 public NurbsSurface copyToReal() { 169 return _realChild; 170 } 171 172 /** 173 * Returns the number of child-elements that make up this geometry element. This 174 * implementation returns the number of control points in the child NURBS surface. 175 */ 176 @Override 177 public int size() { 178 return _child.size(); 179 } 180 181 /** 182 * Returns the number of physical dimensions of the geometry element. 183 */ 184 @Override 185 public int getPhyDimension() { 186 return _child.getPhyDimension(); 187 } 188 189 /** 190 * Return a matrix or network of control points for this surface. 191 * 192 * @return the ordered control points 193 */ 194 @Override 195 public ControlPointNet getControlPoints() { 196 return _realChild.getControlPoints(); 197 } 198 199 /** 200 * Return a copy of the child object transformed by this transformation. 201 */ 202 private BasicNurbsSurface privateCopyToReal() { 203 StackContext.enter(); 204 try { 205 // Transform the child surface's control points. 206 ControlPointNet transCPNet = privateGetControlPoints(); 207 208 // Create a new surface from the transformed control point list. 209 BasicNurbsSurface srf = BasicNurbsSurface.newInstance(transCPNet, 210 _child.getSKnotVector().copy(), _child.getTKnotVector().copy()); 211 srf.setName(_child.getName()); 212 srf.putAllUserData(_child.getAllUserData()); 213 214 return StackContext.outerCopy(srf); 215 216 } finally { 217 StackContext.exit(); 218 } 219 } 220 221 /** 222 * Return a matrix or network of control points for this surface. 223 * 224 * @return the ordered control points 225 */ 226 private ControlPointNet privateGetControlPoints() { 227 228 // Loop over all the control points in the child surface and transform each in turn. 229 ControlPointNet cpNet = _child.getControlPoints(); 230 231 FastTable<FastTable<ControlPoint>> transCPNet = FastTable.newInstance(); 232 int ncol = cpNet.getNumberOfColumns(); 233 for (int i = 0; i < ncol; ++i) { 234 List<ControlPoint> tbl = cpNet.getColumn(i); 235 FastTable<ControlPoint> transCPList = FastTable.newInstance(); 236 transCPNet.add(transCPList); 237 238 int nrow = tbl.size(); 239 for (int j = 0; j < nrow; ++j) { 240 ControlPoint cp = tbl.get(j); 241 242 // Transform the control point's geometric point by this transformation. 243 Point pnt = _TM.transform(cp.getPoint()).copyToReal(); 244 245 // Create a transformed control point. 246 ControlPoint newCP = ControlPoint.valueOf(pnt, cp.getWeight()); 247 transCPList.add(newCP); 248 } 249 } 250 251 // Create a new control point net from the transformed control point list. 252 ControlPointNet cpNet2 = ControlPointNet.valueOf(transCPNet); 253 254 return cpNet2; 255 } 256 257 /** 258 * Return the control point matrix size in the s-direction (down a column of control 259 * points). 260 * 261 * @return The control point matrix size in the s-direction. 262 */ 263 @Override 264 public int getNumberOfRows() { 265 return _child.getNumberOfRows(); 266 } 267 268 /** 269 * Return the control point matrix size in the t-direction (across the columns of 270 * control points). 271 * 272 * @return The control point matrix size in the t-direction. 273 */ 274 @Override 275 public int getNumberOfColumns() { 276 return _child.getNumberOfColumns(); 277 } 278 279 /** 280 * Return the s-direction knot vector of this surface. 281 * 282 * @return The s-knot vector. 283 */ 284 @Override 285 public KnotVector getSKnotVector() { 286 return _child.getSKnotVector(); 287 } 288 289 /** 290 * Return the t-direction knot vector of this surface. 291 * 292 * @return The t-knot vector. 293 */ 294 @Override 295 public KnotVector getTKnotVector() { 296 return _child.getTKnotVector(); 297 } 298 299 /** 300 * Return the s-degree of the NURBS surface. 301 * 302 * @return s-degree of surface 303 */ 304 @Override 305 public int getSDegree() { 306 return _child.getSDegree(); 307 } 308 309 /** 310 * Return the t-degree of the NURBS surface. 311 * 312 * @return t-degree of surface 313 */ 314 @Override 315 public int getTDegree() { 316 return _child.getTDegree(); 317 } 318 319 /** 320 * Calculate a point on the surface for the given parametric position on the surface. 321 * 322 * @param s 1st parametric dimension distance to get the point for (0.0 to 1.0 323 * inclusive). 324 * @param t 2nd parametric dimension distance to get the point for (0.0 to 1.0 325 * inclusive). 326 * @return The calculated point on the surface at the specified parameter values. 327 * @throws IllegalArgumentException if there is any problem with the parameter values. 328 */ 329 @Override 330 public Point getRealPoint(double s, double t) { 331 return _realChild.getRealPoint(s, t); 332 } 333 334 /** 335 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with 336 * respect to parametric s-position on the surface for the given parametric position 337 * on the surface, <code>d^{grade}p(s,t)/d^{grade}s</code>. 338 * <p> 339 * Example:<br> 340 * 1st derivative (grade = 1), this returns <code>[p(s,t), dp(s,t)/ds]</code>;<br> 341 * 2nd derivative (grade = 2), this returns <code>[p(s,t), dp(s,t)/ds, d^2p(s,t)/d^2s]</code>; etc. 342 * </p> 343 * 344 * @param s 1st parametric dimension distance to calculate derivative for (0.0 to 345 * 1.0 inclusive). 346 * @param t 2nd parametric dimension distance to calculate derivative for (0.0 to 347 * 1.0 inclusive). 348 * @param grade The maximum grade to calculate the u-derivatives for (1=1st 349 * derivative, 2=2nd derivative, etc) 350 * @return A list of s-derivatives up to the specified grade of the surface at the 351 * specified parametric position. 352 * @throws IllegalArgumentException if the grade is < 0 or the parameter values are 353 * invalid. 354 */ 355 @Override 356 public List<Vector<Length>> getSDerivatives(double s, double t, int grade, boolean scaled) { 357 return _realChild.getSDerivatives(s, t, grade, scaled); 358 } 359 360 /** 361 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with 362 * respect to parametric t-position on the surface for the given parametric position 363 * on the surface, <code>d^{grade}p(s,t)/d^{grade}t</code>. 364 * <p> 365 * Example:<br> 366 * 1st derivative (grade = 1), this returns <code>[p(s,t), dp(s,t)/dt]</code>;<br> 367 * 2nd derivative (grade = 2), this returns <code>[p(s,t), dp(s,t)/dt, d^2p(s,t)/d^2t]</code>; etc. 368 * </p> 369 * 370 * @param s 1st parametric dimension distance to calculate derivative for (0.0 to 371 * 1.0 inclusive). 372 * @param t 2nd parametric dimension distance to calculate derivative for (0.0 to 373 * 1.0 inclusive). 374 * @param grade The maximum grade to calculate the v-derivatives for (1=1st 375 * derivative, 2=2nd derivative, etc) 376 * @return A list of t-derivatives up to the specified grade of the surface at the 377 * specified parametric position. 378 * @throws IllegalArgumentException if the grade is < 0 or the parameter values are 379 * invalid. 380 */ 381 @Override 382 public List<Vector<Length>> getTDerivatives(double s, double t, int grade, boolean scaled) { 383 return _realChild.getTDerivatives(s, t, grade, scaled); 384 } 385 386 /** 387 * Calculate the twist vector (d^2P/(ds*dt) = d(dP/ds)/dt) for this surface at the 388 * specified position on this surface. 389 * 390 * @param s 1st parametric dimension distance to calculate twist vector for (0.0 to 391 * 1.0 inclusive). 392 * @param t 2nd parametric dimension distance to calculate twist vector for (0.0 to 393 * 1.0 inclusive). 394 * @return The twist vector of this surface at the specified parametric position. 395 * @throws IllegalArgumentException if the parameter values are invalid. 396 */ 397 @Override 398 public Vector<Length> getTwistVector(double s, double t) { 399 return _realChild.getTwistVector(s, t); 400 } 401 402 /** 403 * Return the coordinate point representing the minimum bounding box corner of this 404 * geometry element (e.g.: min X, min Y, min Z). 405 * 406 * @return The minimum bounding box coordinate for this geometry element. 407 * @throws IndexOutOfBoundsException if this list contains no elements. 408 */ 409 @Override 410 public Point getBoundsMin() { 411 Point minPoint = _realChild.getBoundsMin(); 412 return minPoint; 413 } 414 415 /** 416 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 417 * X, max Y, max Z). 418 * 419 * @return The maximum bounding box coordinate for this geometry element. 420 * @throws IndexOutOfBoundsException if this list contains no elements. 421 */ 422 @Override 423 public Point getBoundsMax() { 424 Point maxPoint = _realChild.getBoundsMax(); 425 return maxPoint; 426 } 427 428 /** 429 * Returns transformed version of this element. The returned object implements 430 * {@link GeomTransform} and contains this element as a child. 431 */ 432 @Override 433 public NurbsSurfaceTrans getTransformed(GTransform transform) { 434 return NurbsSurfaceTrans.newInstance(this, requireNonNull(transform)); 435 } 436 437 /** 438 * Returns the unit in which the control points in this curve are stated. 439 */ 440 @Override 441 public Unit<Length> getUnit() { 442 return _child.getUnit(); 443 } 444 445 /** 446 * Returns the equivalent to this surface but stated in the specified unit. 447 * 448 * @param unit the length unit of the surface to be returned. May not be null. 449 * @return an equivalent to this surface but stated in the specified unit. 450 * @throws ConversionException if the the input unit is not a length unit. 451 */ 452 @Override 453 public NurbsSurfaceTrans to(Unit<Length> unit) throws ConversionException { 454 if (unit.equals(getUnit())) 455 return this; 456 457 return NurbsSurfaceTrans.newInstance(_child.to(unit), _TM); 458 } 459 460 /** 461 * Return the equivalent of this surface converted to the specified number of physical 462 * dimensions. This implementation will throw an exception if the specified dimension 463 * is anything other than 3. 464 * 465 * @param newDim The dimension of the surface to return. MUST equal 3. 466 * @return The equivalent of this surface converted to the new dimensions. 467 * @throws IllegalArgumentException if the new dimension is anything other than 3. 468 */ 469 @Override 470 public NurbsSurfaceTrans toDimension(int newDim) { 471 if (newDim == 3) 472 return this; 473 474 throw new IllegalArgumentException( 475 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 476 } 477 478 /** 479 * Returns a copy of this NurbsSurfaceTrans instance 480 * {@link javolution.context.AllocatorContext allocated} by the calling thread 481 * (possibly on the stack). 482 * 483 * @return an identical and independent copy of this point. 484 */ 485 @Override 486 public NurbsSurfaceTrans copy() { 487 return NurbsSurfaceTrans.copyOf(this); 488 } 489 490 /** 491 * Compares this NurbsSurfaceTrans against the specified object for strict equality. 492 * 493 * @param obj the object to compare with. 494 * @return <code>true</code> if this transform is identical to that transform; 495 * <code>false</code> otherwise. 496 */ 497 @Override 498 public boolean equals(Object obj) { 499 if (this == obj) 500 return true; 501 if ((obj == null) || (obj.getClass() != this.getClass())) 502 return false; 503 504 NurbsSurfaceTrans that = (NurbsSurfaceTrans)obj; 505 return this._TM.equals(that._TM) 506 && this._child.equals(that._child) 507 && super.equals(obj); 508 } 509 510 /** 511 * Returns the hash code for this parameter. 512 * 513 * @return the hash code value. 514 */ 515 @Override 516 public int hashCode() { 517 return 31*super.hashCode() + Objects.hash(_TM, _child); 518 } 519 520 /** 521 * Holds the default XML representation. For example: 522 */ 523 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 524 protected static final XMLFormat<NurbsSurfaceTrans> XML = new XMLFormat<NurbsSurfaceTrans>(NurbsSurfaceTrans.class) { 525 526 @Override 527 public NurbsSurfaceTrans newInstance(Class<NurbsSurfaceTrans> cls, InputElement xml) throws XMLStreamException { 528 return FACTORY.object(); 529 } 530 531 @Override 532 public void read(InputElement xml, NurbsSurfaceTrans obj) throws XMLStreamException { 533 NurbsSurface.XML.read(xml, obj); // Call parent read. 534 535 obj._TM = xml.getNext(); 536 NurbsSurface child = xml.getNext(); 537 obj._child = child; 538 obj._realChild = obj.privateCopyToReal(); 539 540 // Listen for changes to the child object and pass them on. 541 if (!(child instanceof Immutable)) 542 child.addChangeListener(obj._childChangeListener); 543 } 544 545 @Override 546 public void write(NurbsSurfaceTrans obj, OutputElement xml) throws XMLStreamException { 547 NurbsSurface.XML.write(obj, xml); // Call parent write. 548 549 xml.add(obj._TM); 550 xml.add(obj._child); 551 552 } 553 }; 554 555 ////////////////////// 556 // Factory Creation // 557 ////////////////////// 558 private static final ObjectFactory<NurbsSurfaceTrans> FACTORY = new ObjectFactory<NurbsSurfaceTrans>() { 559 @Override 560 protected NurbsSurfaceTrans create() { 561 return new NurbsSurfaceTrans(); 562 } 563 564 @Override 565 protected void cleanup(NurbsSurfaceTrans obj) { 566 obj.reset(); 567 obj._TM = null; 568 if (nonNull(obj._childChangeListener)) 569 obj._child.removeChangeListener(obj._childChangeListener); 570 obj._child = null; 571 obj._realChild = null; 572 } 573 }; 574 575 /** 576 * Recycles a <code>NurbsSurfaceTrans</code> instance immediately (on the stack when 577 * executing in a StackContext). 578 * 579 * @param instance The instance to be recycled. 580 */ 581 public static void recycle(NurbsSurfaceTrans instance) { 582 FACTORY.recycle(instance); 583 } 584 585 @SuppressWarnings("unchecked") 586 private static NurbsSurfaceTrans copyOf(NurbsSurfaceTrans original) { 587 NurbsSurfaceTrans obj = FACTORY.object(); 588 obj._TM = original._TM.copy(); 589 obj._child = original._child.copy(); 590 obj._realChild = original._realChild.copy(); 591 original.copyState(obj); 592 if (!(obj._child instanceof Immutable)) 593 obj._child.addChangeListener(obj._childChangeListener); 594 return obj; 595 } 596 597 /** 598 * Do not allow the default constructor to be used except by subclasses. 599 */ 600 private NurbsSurfaceTrans() { } 601 602}