001/** 002 * NurbsCurveTrans -- A GeomTransform that has a NurbsCurve for a child. 003 * 004 * Copyright (C) 2009-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.requireNonNull; 025import javax.measure.converter.ConversionException; 026import javax.measure.quantity.Length; 027import javax.measure.unit.Unit; 028import javax.swing.event.ChangeListener; 029import javolution.context.ObjectFactory; 030import javolution.context.StackContext; 031import javolution.lang.Immutable; 032import javolution.util.FastTable; 033import javolution.xml.XMLFormat; 034import javolution.xml.stream.XMLStreamException; 035 036/** 037 * A {@link GeomTransform} object that refers to a {@link NurbsCurve} object and 038 * masquerades as a NurbsCurve object itself. 039 * 040 * <p> Modified by: Joseph A. Huwaldt </p> 041 * 042 * @author Joseph A. Huwaldt, Date: April 30, 2009 043 * @version November 28, 2015 044 */ 045@SuppressWarnings({"serial", "CloneableImplementsClone"}) 046public final class NurbsCurveTrans extends NurbsCurve implements GeomTransform<NurbsCurve> { 047 048 /** 049 * The transformation represented by this transformation element. 050 */ 051 private GTransform _TM; 052 053 /** 054 * The object that is the child of this transform. 055 */ 056 private NurbsCurve _child; 057 058 /** 059 * Reference to a change listener for this object's child. 060 */ 061 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 062 063 /** 064 * Returns a 3D {@link NurbsCurveTrans} instance holding the specified 065 * {@link NurbsCurve} and {@link GTransform}. 066 * 067 * @param child The NurbsCurve that is the child of this transform element (may not be 068 * <code>null</code>). 069 * @param transform The transform held by this transform element (may not be 070 * <code>null</code>). 071 * @return the transform element having the specified values. 072 */ 073 public static NurbsCurveTrans newInstance(NurbsCurve child, GTransform transform) { 074 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 075 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 076 077 if (child.getPhyDimension() != 3) 078 throw new DimensionException( 079 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "NURBS curve", child.getPhyDimension())); 080 081 NurbsCurveTrans obj = FACTORY.object(); 082 obj._TM = transform; 083 obj._child = child; 084 085 // Listen for changes to the child object and pass them on. 086 if (!(child instanceof Immutable)) 087 child.addChangeListener(obj._childChangeListener); 088 089 return obj; 090 } 091 092 /** 093 * Returns the transformation represented by this transformation element. 094 * 095 * @return The transformation represented by this element. 096 */ 097 @Override 098 public GTransform getTransform() { 099 return _TM; 100 } 101 102 /** 103 * Returns the total transformation represented by an entire chain of GeomTransform 104 * objects below this one. 105 * 106 * @return The total transformation represented by the entire chain of transform 107 * objects. 108 */ 109 @Override 110 public GTransform getTotalTransform() { 111 return GeomUtil.getTotalTransform(this); 112 } 113 114 /** 115 * Sets the transformation represented by this transformation element. 116 * 117 * @param transform The transform to set this transform element to (may not be 118 * <code>null</code>). 119 */ 120 @Override 121 public void setTransform(GTransform transform) { 122 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 123 _TM = transform; 124 fireChangeEvent(); 125 } 126 127 /** 128 * Returns the child object transformed by this transform element. 129 * 130 * @return The child object transformed by this element. 131 */ 132 @Override 133 public NurbsCurve getChild() { 134 return _child; 135 } 136 137 /** 138 * Return a copy of the child object transformed by this transformation. 139 * 140 * @return A copy of this object with any transformations or subranges removed 141 * (applied). 142 */ 143 @Override 144 public BasicNurbsCurve copyToReal() { 145 146 // Transform the child curve's control points. 147 FastTable<ControlPoint> transCPList = (FastTable)getControlPoints(); 148 149 // Create a new curve from the transformed control point list. 150 BasicNurbsCurve crv = BasicNurbsCurve.newInstance(transCPList, _child.getKnotVector()); 151 crv.setName(_child.getName()); 152 crv.putAllUserData(_child.getAllUserData()); 153 154 FastTable.recycle(transCPList); 155 return crv; 156 } 157 158 /** 159 * Return an immutable version of this NURBS curve. 160 * 161 * @return an immutable version of this curve. 162 */ 163 @Override 164 public BasicNurbsCurve immutable() { 165 return copyToReal(); 166 } 167 168 /** 169 * Returns the number of child-elements that make up this geometry element. This 170 * implementation returns the number of control points in the child NURBS curve. 171 */ 172 @Override 173 public int size() { 174 return _child.size(); 175 } 176 177 /** 178 * Returns the number of physical dimensions of the geometry element. 179 */ 180 @Override 181 public int getPhyDimension() { 182 return _child.getPhyDimension(); 183 } 184 185 /** 186 * Return a list of transformed control points for this curve. 187 * 188 * @return the ordered control points 189 */ 190 @Override 191 public List<ControlPoint> getControlPoints() { 192 193 // Loop over all the control points in the child curve and transform each in turn. 194 FastTable<ControlPoint> transCPList = FastTable.newInstance(); 195 196 StackContext.enter(); 197 try { 198 FastTable<ControlPoint> cPoly = (FastTable<ControlPoint>)_child.getControlPoints(); 199 int size = cPoly.size(); 200 for (int i = 0; i < size; ++i) { 201 ControlPoint cp = cPoly.get(i); 202 203 // Transform the control point's geomssic point by this transformation. 204 Point pnt = cp.getPoint().getTransformed(_TM).copyToReal(); 205 206 // Create a transformed control point. 207 ControlPoint newCP = ControlPoint.valueOf(pnt, cp.getWeight()); 208 transCPList.add(StackContext.outerCopy(newCP)); 209 } 210 211 } finally { 212 StackContext.exit(); 213 } 214 215 return transCPList; 216 } 217 218 /** 219 * Return the knot vector of this curve. 220 * 221 * @return The knot vector. 222 */ 223 @Override 224 public KnotVector getKnotVector() { 225 return _child.getKnotVector(); 226 } 227 228 /** 229 * Return the degree of the NURBS curve. 230 * 231 * @return degree of curve 232 */ 233 @Override 234 public int getDegree() { 235 return _child.getDegree(); 236 } 237 238 /** 239 * Calculate a point on the curve for the given parametric distance along the curve, 240 * <code>p(s)</code>. 241 * 242 * @param s parametric distance to calculate a point for (0.0 to 1.0 inclusive). 243 * @return the calculated point 244 */ 245 @Override 246 public Point getRealPoint(double s) { 247 return _TM.transform(_child.getRealPoint(s)); 248 } 249 250 /** 251 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with 252 * respect to parametric distance on the curve for the given parametric distance along 253 * the curve, <code>d^{grade}p(s)/d^{grade}s</code>. 254 * <p> 255 * Example:<br> 256 * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br> 257 * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc. 258 * </p> 259 * 260 * @param s Parametric distance to calculate derivatives for (0.0 to 1.0 inclusive). 261 * @param grade The maximum grade to calculate the derivatives for (1=1st derivative, 262 * 2=2nd derivative, etc) 263 * @return A list of derivatives up to the specified grade of the curve at the 264 * specified parametric position. 265 * @throws IllegalArgumentException if the grade is < 0. 266 */ 267 @Override 268 public List<Vector<Length>> getSDerivatives(double s, int grade) { 269 BasicNurbsCurve trans = copyToReal(); 270 List<Vector<Length>> ders = trans.getSDerivatives(s, grade); 271 BasicNurbsCurve.recycle(trans); 272 return ders; 273 } 274 275 /** 276 * Return the coordinate point representing the minimum bounding box corner of this 277 * geometry element (e.g.: min X, min Y, min Z). 278 * 279 * @return The minimum bounding box coordinate for this geometry element. 280 * @throws IndexOutOfBoundsException if this list contains no elements. 281 */ 282 @Override 283 public Point getBoundsMin() { 284 BasicNurbsCurve trans = copyToReal(); 285 Point minPoint = trans.getBoundsMin(); 286 BasicNurbsCurve.recycle(trans); 287 return minPoint; 288 } 289 290 /** 291 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 292 * X, max Y, max Z). 293 * 294 * @return The maximum bounding box coordinate for this geometry element. 295 * @throws IndexOutOfBoundsException if this list contains no elements. 296 */ 297 @Override 298 public Point getBoundsMax() { 299 BasicNurbsCurve trans = copyToReal(); 300 Point maxPoint = trans.getBoundsMax(); 301 BasicNurbsCurve.recycle(trans); 302 return maxPoint; 303 } 304 305 /** 306 * Returns the unit in which the control points in this curve are stated. 307 */ 308 @Override 309 public Unit<Length> getUnit() { 310 return _child.getUnit(); 311 } 312 313 /** 314 * Returns the equivalent to this curve but stated in the specified unit. 315 * 316 * @param unit The length unit of the curve to be returned. May not be null. 317 * @return An equivalent to this curve but stated in the specified unit. 318 * @throws ConversionException if the the input unit is not a length unit. 319 */ 320 @Override 321 public NurbsCurveTrans to(Unit<Length> unit) throws ConversionException { 322 if (unit.equals(getUnit())) 323 return this; 324 325 return NurbsCurveTrans.newInstance(_child.to(unit), _TM); 326 } 327 328 /** 329 * Return the equivalent of this curve converted to the specified number of physical 330 * dimensions. This implementation will throw an exception if the specified dimension 331 * is anything other than 3. 332 * 333 * @param newDim The dimension of the curve to return. MUST equal 3. 334 * @return The equivalent of this curve converted to the new dimensions. 335 * @throws IllegalArgumentException if the new dimension is anything other than 3. 336 */ 337 @Override 338 public NurbsCurveTrans toDimension(int newDim) { 339 if (newDim == 3) 340 return this; 341 342 throw new IllegalArgumentException( 343 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 344 } 345 346 /** 347 * Returns a copy of this NurbsCurveTrans instance 348 * {@link javolution.context.AllocatorContext allocated} by the calling thread 349 * (possibly on the stack). 350 * 351 * @return an identical and independent copy of this point. 352 */ 353 @Override 354 public NurbsCurveTrans copy() { 355 return copyOf(this); 356 } 357 358 /** 359 * Compares this NurbsCurveTrans against the specified object for strict equality. 360 * 361 * @param obj the object to compare with. 362 * @return <code>true</code> if this transform is identical to that transform; 363 * <code>false</code> otherwise. 364 */ 365 @Override 366 public boolean equals(Object obj) { 367 if (this == obj) 368 return true; 369 if ((obj == null) || (obj.getClass() != this.getClass())) 370 return false; 371 372 NurbsCurveTrans that = (NurbsCurveTrans)obj; 373 return this._TM.equals(that._TM) 374 && this._child.equals(that._child) 375 && super.equals(obj); 376 } 377 378 /** 379 * Returns the hash code for this parameter. 380 * 381 * @return the hash code value. 382 */ 383 @Override 384 public int hashCode() { 385 return 31*super.hashCode() + Objects.hash(_TM, _child); 386 } 387 388 /** 389 * Holds the default XML representation for this object. 390 */ 391 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 392 protected static final XMLFormat<NurbsCurveTrans> XML = new XMLFormat<NurbsCurveTrans>(NurbsCurveTrans.class) { 393 394 @Override 395 public NurbsCurveTrans newInstance(Class<NurbsCurveTrans> cls, InputElement xml) throws XMLStreamException { 396 return FACTORY.object(); 397 } 398 399 @Override 400 public void read(InputElement xml, NurbsCurveTrans obj) throws XMLStreamException { 401 NurbsCurve.XML.read(xml, obj); // Call parent read. 402 403 obj._TM = xml.getNext(); 404 NurbsCurve child = xml.getNext(); 405 obj._child = child; 406 407 // Listen for changes to the child object and pass them on. 408 if (!(child instanceof Immutable)) 409 child.addChangeListener(obj._childChangeListener); 410 } 411 412 @Override 413 public void write(NurbsCurveTrans obj, OutputElement xml) throws XMLStreamException { 414 NurbsCurve.XML.write(obj, xml); // Call parent write. 415 416 xml.add(obj._TM); 417 xml.add(obj._child); 418 419 } 420 }; 421 422 ////////////////////// 423 // Factory Creation // 424 ////////////////////// 425 private static final ObjectFactory<NurbsCurveTrans> FACTORY = new ObjectFactory<NurbsCurveTrans>() { 426 @Override 427 protected NurbsCurveTrans create() { 428 return new NurbsCurveTrans(); 429 } 430 431 @Override 432 protected void cleanup(NurbsCurveTrans obj) { 433 obj.reset(); 434 obj._TM = null; 435 if (Objects.nonNull(obj._childChangeListener)) 436 obj._child.removeChangeListener(obj._childChangeListener); 437 obj._child = null; 438 } 439 }; 440 441 /** 442 * Recycles a <code>NurbsCurveTrans</code> instance immediately (on the stack when 443 * executing in a StackContext). 444 * 445 * @param instance The instance to be recycled. 446 */ 447 public static void recycle(NurbsCurveTrans instance) { 448 FACTORY.recycle(instance); 449 } 450 451 @SuppressWarnings("unchecked") 452 private static NurbsCurveTrans copyOf(NurbsCurveTrans original) { 453 NurbsCurveTrans obj = FACTORY.object(); 454 obj._TM = original._TM.copy(); 455 obj._child = original._child.copy(); 456 original.copyState(obj); 457 if (!(obj._child instanceof Immutable)) 458 obj._child.addChangeListener(obj._childChangeListener); 459 return obj; 460 } 461 462 /** 463 * Do not allow the default constructor to be used. 464 */ 465 private NurbsCurveTrans() { } 466 467}