001/* 002 * LineSegTrans -- A GeomTransform that has a LineSegment for a child. 003 * 004 * Copyright (C) 2012-2025, by Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Library General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom; 023 024import geomss.geom.nurbs.NurbsCurve; 025import geomss.geom.nurbs.NurbsCurveTrans; 026import jahuwaldt.js.param.Parameter; 027import java.text.MessageFormat; 028import java.util.List; 029import java.util.Objects; 030import static java.util.Objects.requireNonNull; 031import javax.measure.converter.ConversionException; 032import javax.measure.quantity.Dimensionless; 033import javax.measure.quantity.Length; 034import javax.measure.unit.Unit; 035import javax.swing.event.ChangeListener; 036import javolution.context.ObjectFactory; 037import javolution.lang.Immutable; 038import javolution.xml.XMLFormat; 039import javolution.xml.stream.XMLStreamException; 040 041/** 042 * A {@link GeomTransform} object that refers to a {@link LineSegment} object 043 * and masquerades as a LineSegment object itself. 044 * 045 * <p>Modified by: Joseph A. Huwaldt</p> 046 * 047 * @author Joseph A. Huwaldt, Date: January 15, 2012 048 * @version February 17, 2025 049 */ 050@SuppressWarnings({"serial", "CloneableImplementsClone"}) 051public final class LineSegTrans extends LineSegment implements GeomTransform<LineSegment> { 052 053 /** 054 * The transformation represented by this transformation element. 055 */ 056 private GTransform _TM; 057 058 /** 059 * The object that is the child of this transform. 060 */ 061 private LineSegment _child; 062 063 /** 064 * Reference to a change listener for this object's child. 065 */ 066 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 067 068 /** 069 * Returns a 3D {@link LineSegTrans} instance holding the specified 070 * {@link LineSegment} and {@link GTransform}. 071 * 072 * @param child The LineSegment that is the child of this transform element 073 * (may not be <code>null</code>). 074 * @param transform The transform held by this transform element (may not be 075 * <code>null</code>). 076 * @return the transform element having the specified values. 077 */ 078 public static LineSegTrans newInstance(LineSegment child, GTransform transform) { 079 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 080 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 081 082 if (child.getPhyDimension() != 3) 083 throw new DimensionException( 084 MessageFormat.format(RESOURCES.getString("dimensionNot3"), 085 "line segment", String.valueOf(child.getPhyDimension()))); 086 087 LineSegTrans obj = FACTORY.object(); 088 obj._TM = transform; 089 obj._child = child; 090 child.copyState(obj); 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 /** 100 * Returns the transformation represented by this transformation element. 101 * 102 * @return The transformation from the child object represented by this 103 * element. 104 */ 105 @Override 106 public GTransform getTransform() { 107 return _TM; 108 } 109 110 /** 111 * Returns the total transformation represented by an entire chain of 112 * GeomTransform objects below this one. 113 * 114 * @return The total transformation represented by the entire chain of 115 * objects below this one. 116 */ 117 @Override 118 public GTransform getTotalTransform() { 119 return GeomUtil.getTotalTransform(this); 120 } 121 122 /** 123 * Sets the transformation represented by this transformation element. 124 * 125 * @param transform The transform to set this transform element to (may not 126 * be <code>null</code>). 127 */ 128 @Override 129 public void setTransform(GTransform transform) { 130 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 131 _TM = transform; 132 fireChangeEvent(); 133 } 134 135 /** 136 * Returns the child object transformed by this transform element. 137 * 138 * @return The child object transformed by this transform element. 139 */ 140 @Override 141 public LineSegment getChild() { 142 return _child; 143 } 144 145 /** 146 * Return a copy of the child object transformed by this transformation. 147 * 148 * @return A copy of the child object transformed by this transformation. 149 */ 150 @Override 151 public LineSegment copyToReal() { 152 Point p0 = getStart().copyToReal(); 153 Point p1 = getEnd().copyToReal(); 154 LineSeg L = LineSeg.valueOf(p0, p1); 155 return copyState(L); // Copy over the super-class state for this object to the new one. 156 } 157 158 /** 159 * Return the starting point of the line segment. 160 * 161 * @return The starting point of the line segment. 162 */ 163 @Override 164 public GeomPoint getStart() { 165 return _child.getStart().getTransformed(_TM); 166 } 167 168 /** 169 * Return the end point of the line segment. 170 * 171 * @return The end point of the lien segment. 172 */ 173 @Override 174 public GeomPoint getEnd() { 175 return _child.getEnd().getTransformed(_TM); 176 } 177 178 /** 179 * Return the dimensional derivative vector for this line segment. The 180 * length of this vector is the length of the line segment, the origin is at 181 * the start point and the end of the vector is the line end. 182 * 183 * @return The dimensional derivative vector for this line segment. 184 */ 185 @Override 186 public GeomVector<Length> getDerivativeVector() { 187 return _child.getDerivativeVector().getTransformed(_TM); 188 } 189 190 /** 191 * Get unit direction vector for the line segment. 192 * 193 * @return A unit vector indicating the direction of this line segment. 194 */ 195 @Override 196 public GeomVector<Dimensionless> getUnitVector() { 197 return _child.getUnitVector().getTransformed(_TM); 198 } 199 200 /** 201 * Returns the number of child-elements that make up this geometry element. 202 * This implementation returns the size in the child curve. 203 */ 204 @Override 205 public int size() { 206 return _child.size(); 207 } 208 209 /** 210 * Returns the number of physical dimensions of the geometry element. 211 */ 212 @Override 213 public int getPhyDimension() { 214 return _child.getPhyDimension(); 215 } 216 217 /** 218 * Calculate a point on the curve for the given parametric distance along 219 * the curve, <code>p(s)</code>. 220 * 221 * @param s parametric distance to calculate a point for (0.0 to 1.0 222 * inclusive). 223 * @return the calculated point 224 */ 225 @Override 226 public Point getRealPoint(double s) { 227 return _TM.transform(_child.getRealPoint(s)); 228 } 229 230 /** 231 * Calculate all the derivatives from <code>0</code> to <code>grade</code> 232 * with respect to parametric distance on the curve for the given parametric 233 * distance along the curve, <code>d^{grade}p(s)/d^{grade}s</code>. 234 * <p> 235 * Example:<br> 236 * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br> 237 * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc. 238 * </p> 239 * 240 * @param s Parametric distance to calculate derivatives for (0.0 to 1.0 241 * inclusive). 242 * @param grade The maximum grade to calculate the derivatives for (1=1st 243 * derivative, 2=2nd derivative, etc) 244 * @return A list of derivatives up to the specified grade of the curve at 245 * the specified parametric position. 246 * @throws IllegalArgumentException if the grade is < 0. 247 */ 248 @Override 249 public List<Vector<Length>> getSDerivatives(double s, int grade) { 250 LineSegment trans = copyToReal(); 251 return trans.getSDerivatives(s, grade); 252 } 253 254 /** 255 * Return a new curve that is identical to this one, but with the 256 * parameterization reversed. 257 * 258 * @return A new curve that is identical to this one, but with the 259 * parameterization reversed. 260 */ 261 @Override 262 public LineSegTrans reverse() { 263 return LineSegTrans.newInstance(_child.reverse(), _TM); 264 } 265 266 /** 267 * Return the coordinate point representing the minimum bounding box corner 268 * of this geometry element (e.g.: min X, min Y, min Z). 269 * 270 * @return The minimum bounding box coordinate for this geometry element. 271 * @throws IndexOutOfBoundsException if this list contains no elements. 272 */ 273 @Override 274 public Point getBoundsMin() { 275 LineSegment trans = copyToReal(); 276 Point minPoint = trans.getBoundsMin(); 277 return minPoint; 278 } 279 280 /** 281 * Return the coordinate point representing the maximum bounding box corner 282 * (e.g.: max X, max Y, max Z). 283 * 284 * @return The maximum bounding box coordinate for this geometry element. 285 * @throws IndexOutOfBoundsException if this list contains no elements. 286 */ 287 @Override 288 public Point getBoundsMax() { 289 LineSegment trans = copyToReal(); 290 Point maxPoint = trans.getBoundsMax(); 291 return maxPoint; 292 } 293 294 /** 295 * Returns transformed version of this element. The returned object 296 * implements {@link GeomTransform} and contains this element as a child. 297 * 298 * @param transform The transformation to apply to this geometry. May not be null. 299 * @return A new line segment that is identical to this one with the 300 * specified transformation applied. 301 * @throws DimensionException if this point is not 3D. 302 */ 303 @Override 304 public LineSegTrans getTransformed(GTransform transform) { 305 return LineSegTrans.newInstance(this, requireNonNull(transform)); 306 } 307 308 /** 309 * Returns the unit in which this curve is stated. 310 */ 311 @Override 312 public Unit<Length> getUnit() { 313 return _child.getUnit(); 314 } 315 316 /** 317 * Returns the equivalent to this curve but stated in the specified unit. 318 * 319 * @param unit the length unit of the curve to be returned. May not be null. 320 * @return an equivalent to this curve but stated in the specified unit. 321 * @throws ConversionException if the the input unit is not a length unit. 322 */ 323 @Override 324 public LineSegTrans to(Unit<Length> unit) throws ConversionException { 325 if (unit.equals(getUnit())) 326 return this; 327 328 return LineSegTrans.newInstance(_child.to(unit), _TM); 329 } 330 331 /** 332 * Return the equivalent of this line segment converted to the specified 333 * number of physical dimensions. This implementation will throw an 334 * exception if the specified dimension is anything other than 3. 335 * 336 * @param newDim The dimension of the line segment to return. MUST equal 3. 337 * @return The equivalent of this line segment converted to the new 338 * dimensions. 339 * @throws IllegalArgumentException if the new dimension is anything other 340 * than 3. 341 */ 342 @Override 343 public LineSegTrans toDimension(int newDim) { 344 if (newDim == 3) 345 return this; 346 347 throw new IllegalArgumentException( 348 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 349 } 350 351 /** 352 * Return a transformed NURBS curve representation of this line segment. An 353 * exact representation is returned and the tolerance parameter is ignored. 354 * 355 * @param tol Ignored. May pass <code>null</code>. 356 * @return A transformed NURBS curve that exactly represents this line 357 * segment. 358 */ 359 @Override 360 public NurbsCurve toNurbs(Parameter<Length> tol) { 361 NurbsCurve curve = NurbsCurveTrans.newInstance(_child.toNurbs(tol), _TM); 362 copyState(curve); // Copy over the super-class state for this curve to the new one. 363 return curve; 364 } 365 366 /** 367 * Returns a copy of this LineSegTrans instance allocated by the calling 368 * thread (possibly on the stack). 369 * 370 * @return an identical and independent copy of this point. 371 */ 372 @Override 373 public LineSegTrans copy() { 374 return LineSegTrans.copyOf(this); 375 } 376 377 /** 378 * Compares this LineSegTrans against the specified object for strict 379 * equality. 380 * 381 * @param obj the object to compare with. 382 * @return <code>true</code> if this transform is identical to that 383 * transform; <code>false</code> otherwise. 384 */ 385 @Override 386 public boolean equals(Object obj) { 387 if (this == obj) 388 return true; 389 if ((obj == null) || (obj.getClass() != this.getClass())) 390 return false; 391 392 LineSegTrans that = (LineSegTrans)obj; 393 return this._TM.equals(that._TM) 394 && this._child.equals(that._child) 395 && super.equals(obj); 396 } 397 398 /** 399 * Returns the hash code for this parameter. 400 * 401 * @return the hash code value. 402 */ 403 @Override 404 public int hashCode() { 405 return 31*super.hashCode() + Objects.hash(_TM, _child); 406 } 407 408 /** 409 * Holds the default XML representation for this object. 410 */ 411 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 412 protected static final XMLFormat<LineSegTrans> XML = new XMLFormat<LineSegTrans>(LineSegTrans.class) { 413 414 @Override 415 public LineSegTrans newInstance(Class<LineSegTrans> cls, InputElement xml) throws XMLStreamException { 416 return FACTORY.object(); 417 } 418 419 @Override 420 public void read(InputElement xml, LineSegTrans obj) throws XMLStreamException { 421 LineSegment.XML.read(xml, obj); // Call parent read. 422 423 obj._TM = xml.getNext(); 424 LineSegment child = xml.getNext(); 425 obj._child = xml.getNext(); 426 427 // Listen for changes to the child object and pass them on. 428 if (!(child instanceof Immutable)) 429 child.addChangeListener(obj._childChangeListener); 430 } 431 432 @Override 433 public void write(LineSegTrans obj, OutputElement xml) throws XMLStreamException { 434 LineSegment.XML.write(obj, xml); // Call parent write. 435 436 xml.add(obj._TM); 437 xml.add(obj._child); 438 439 } 440 }; 441 442 ////////////////////// 443 // Factory Creation // 444 ////////////////////// 445 private static final ObjectFactory<LineSegTrans> FACTORY = new ObjectFactory<LineSegTrans>() { 446 @Override 447 protected LineSegTrans create() { 448 return new LineSegTrans(); 449 } 450 451 @Override 452 protected void cleanup(LineSegTrans obj) { 453 obj.reset(); 454 obj._TM = null; 455 if (!(obj._child instanceof Immutable)) 456 obj._child.removeChangeListener(obj._childChangeListener); 457 obj._child = null; 458 } 459 }; 460 461 /** 462 * Recycles a <code>LineSegTrans</code> instance immediately (on the stack 463 * when executing in a StackContext). 464 * 465 * @param instance The instance to recycle immediately. 466 */ 467 public static void recycle(LineSegTrans instance) { 468 FACTORY.recycle(instance); 469 } 470 471 @SuppressWarnings("unchecked") 472 private static LineSegTrans copyOf(LineSegTrans original) { 473 LineSegTrans obj = FACTORY.object(); 474 obj._TM = original._TM.copy(); 475 obj._child = original._child.copy(); 476 original.copyState(obj); 477 if (!(obj._child instanceof Immutable)) 478 obj._child.addChangeListener(obj._childChangeListener); 479 return obj; 480 } 481 482 /** 483 * Do not allow the default constructor to be used except by subclasses. 484 */ 485 private LineSegTrans() {} 486 487}