001/** 002 * GeomPointTrans -- A GeomTransform that has a GeomPoint for a child. 003 * 004 * Copyright (C) 2009-2018, 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.1 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 Lesser 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; 019 020import jahuwaldt.js.param.*; 021import java.text.MessageFormat; 022import java.util.Objects; 023import static java.util.Objects.requireNonNull; 024import javax.measure.converter.ConversionException; 025import javax.measure.quantity.Angle; 026import javax.measure.quantity.Length; 027import javax.measure.unit.SI; 028import javax.measure.unit.Unit; 029import javax.swing.event.ChangeListener; 030import javolution.context.ObjectFactory; 031import javolution.lang.Immutable; 032import javolution.xml.XMLFormat; 033import javolution.xml.stream.XMLStreamException; 034import org.jscience.mathematics.vector.Float64Vector; 035 036/** 037 * A {@link GeomTransform} element that refers to a {@link GeomPoint} object and 038 * masquerades as a GeomPoint object itself. 039 * 040 * <p> Modified by: Joseph A. Huwaldt </p> 041 * 042 * @author Joseph A. Huwaldt, Date: April 30, 2009 043 * @version April 10, 2018 044 */ 045@SuppressWarnings({"serial", "CloneableImplementsClone"}) 046public final class GeomPointTrans extends GeomPoint implements GeomTransform<GeomPoint> { 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 GeomPoint _child; 057 058 // The child point with the transformation applied to it (an optimization). 059 private Point _pTrans; 060 061 /** 062 * Reference to a change listener for this object's child. 063 */ 064 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 065 066 /** 067 * Returns a 3D {@link GeomPointTrans} instance holding the specified 068 * {@link GeomPoint} and {@link GTransform}. 069 * 070 * @param child The point that is the child of this transform element (may not be 071 * <code>null</code>). 072 * @param transform The transform held by this transform element (may not be 073 * <code>null</code>). 074 * @return the transform element having the specified values. 075 * @throws DimensionException if the input element is not 3D. 076 */ 077 public static GeomPointTrans newInstance(GeomPoint child, GTransform transform) { 078 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 079 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 080 081 if (child.getPhyDimension() != 3) 082 throw new DimensionException( 083 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "point", child.getPhyDimension())); 084 085 GeomPointTrans obj = FACTORY.object(); 086 obj._TM = transform; 087 obj._child = child; 088 obj._pTrans = transform.transform(child); 089 child.copyState(obj._pTrans); 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 represented by this transformation element. 103 */ 104 @Override 105 public GTransform getTransform() { 106 return _TM; 107 } 108 109 /** 110 * Returns the total transformation represented by an entire chain of GeomTransform 111 * objects below this one. 112 * 113 * @return The total transformation represented by an entire chain of GeomTransform 114 * objects below this one. 115 */ 116 @Override 117 public GTransform getTotalTransform() { 118 return GeomUtil.getTotalTransform(this); 119 } 120 121 /** 122 * Sets the transformation represented by this transformation element. 123 * 124 * @param transform The transform to set this transform element to (may not be 125 * <code>null</code>). 126 */ 127 @Override 128 public void setTransform(GTransform transform) { 129 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 130 _TM = transform; 131 _pTrans = transform.transform(_child); 132 _pTrans.setName(_child.getName()); 133 _pTrans.putAllUserData(_child.getAllUserData()); 134 fireChangeEvent(); 135 } 136 137 /** 138 * Returns the child object transformed by this transform element. 139 * 140 * @return The child object transformed by this transform element. 141 */ 142 @Override 143 public GeomPoint getChild() { 144 return _child; 145 } 146 147 /** 148 * Return a copy of the child object transformed by this transformation. 149 * 150 * @return A copy of the child object transformed by this transformation. 151 */ 152 @Override 153 public Point copyToReal() { 154 return _pTrans.copy(); 155 } 156 157 /** 158 * Recycles a <code>GeomPointTrans</code> instance immediately (on the stack when 159 * executing in a <code>StackContext</code>). 160 * 161 * @param instance The instance to be recycled. 162 */ 163 public static void recycle(GeomPointTrans instance) { 164 FACTORY.recycle(instance); 165 } 166 167 /** 168 * Return an immutable version of this point. 169 * 170 * @return An immutable version of this plane. 171 */ 172 @Override 173 public Point immutable() { 174 return copyToReal(); 175 } 176 177 /** 178 * Returns the number of physical dimensions of the geometry element. This 179 * implementation always returns 3. 180 */ 181 @Override 182 public int getPhyDimension() { 183 return 3; 184 } 185 186 /** 187 * Returns the value of the Parameter in this point as a <code>double</code>, stated 188 * in this point's {@link #getUnit unit}. 189 * 190 * @param i the dimension index. 191 * @return the value of the Parameter at <code>i</code>. 192 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ dimension())</code> 193 */ 194 @Override 195 public double getValue(int i) { 196 return _pTrans.getValue(i); 197 } 198 199 /** 200 * Returns the value of a coordinate in this point as a <code>double</code>, stated in 201 * the specified unit. 202 * 203 * @param i the dimension index. 204 * @param unit the unit to return the value in. May not be null. 205 * @return the value of the Parameter at <code>i</code> in the specified unit. 206 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ dimension())</code> 207 */ 208 @Override 209 public double getValue(int i, Unit<Length> unit) { 210 return _pTrans.getValue(i, requireNonNull(unit)); 211 } 212 213 /** 214 * Returns the square of the Euclidean norm, magnitude, or length value of the vector 215 * from the origin to this point (the dot product of the origin-to-this-point vector 216 * and itself). This is slightly faster than calling <code>normValue</code> if the 217 * squared value is all that is needed. 218 * 219 * @return <code>this.normSq().getValue()</code>. 220 * @see #normValue() 221 */ 222 @Override 223 public double normSqValue() { 224 return _pTrans.normSqValue(); 225 } 226 227 /** 228 * Returns the sum of this point with the one specified. The unit of the output point 229 * will be the units of this point. 230 * 231 * @param that the point to be added. May not be null. 232 * @return <code>this + that</code>. 233 * @throws DimensionException if point dimensions are different. 234 */ 235 @Override 236 public Point plus(GeomPoint that) { 237 return _pTrans.plus(that); 238 } 239 240 /** 241 * Adds the specified parameter to each component of this point. The unit of the 242 * output point will be the units of this point. 243 * 244 * @param that the parameter to be added to each component of this point. May not be 245 * null. 246 * @return <code>this + that</code>. 247 */ 248 @Override 249 public Point plus(Parameter<Length> that) { 250 return _pTrans.plus(that); 251 } 252 253 /** 254 * Returns the difference between this point and the one specified. The unit of the 255 * output point will be the units of this point. 256 * 257 * @param that the point to be subtracted from this point. May not be null. 258 * @return <code>this - that</code>. 259 * @throws DimensionException if point dimensions are different. 260 */ 261 @Override 262 public Point minus(GeomPoint that) { 263 return _pTrans.minus(that); 264 } 265 266 /** 267 * Subtracts the specified parameter from each component of this point. The unit of 268 * the output point will be the units of this point. 269 * 270 * @param that the parameter to be subtracted from each component of this point. May 271 * not be null. 272 * @return <code>this - that</code>. 273 */ 274 @Override 275 public Point minus(Parameter<Length> that) { 276 return _pTrans.minus(that); 277 } 278 279 /** 280 * Returns the negation of this point (all the values of each dimension negated). 281 * 282 * @return <code>-this</code> 283 */ 284 @Override 285 public Point opposite() { 286 return _pTrans.opposite(); 287 } 288 289 /** 290 * Returns the product of this point with the specified coefficient. 291 * 292 * @param k the coefficient multiplier. 293 * @return <code>this ยท k</code> 294 */ 295 @Override 296 public Point times(double k) { 297 return _pTrans.times(k); 298 } 299 300 /** 301 * Return <code>true</code> if this point contains valid and finite numerical 302 * components. A value of <code>false</code> will be returned if any of the coordinate 303 * values are NaN or Inf. 304 */ 305 @Override 306 public boolean isValid() { 307 return _pTrans.isValid(); 308 } 309 310 /** 311 * Returns a copy of this GeomPointTrans instance 312 * {@link javolution.context.AllocatorContext allocated} by the calling thread 313 * (possibly on the stack). 314 * 315 * @return an identical and independent copy of this point. 316 */ 317 @Override 318 public GeomPointTrans copy() { 319 return copyOf(this); 320 } 321 322 /** 323 * Returns the unit in which the {@link #getValue values} in this point are stated in. 324 */ 325 @Override 326 public Unit<Length> getUnit() { 327 return _child.getUnit(); 328 } 329 330 /** 331 * Returns the equivalent of this GeomPointTrans object that has a child point in the 332 * specified units. 333 * <p> 334 * WARNING: If the unit changes, then the returned transform element DOES NOT refer 335 * back to the original point (the link with the original point is broken). 336 * </p> 337 * 338 * @param unit the length unit of the point to be returned. May not be null. 339 * @return an equivalent to this point but stated in the specified unit. 340 * @throws ConversionException if the the input unit is not a length unit. 341 */ 342 @Override 343 public GeomPointTrans to(Unit<Length> unit) throws ConversionException { 344 if (unit.equals(getUnit())) 345 return this; 346 347 GeomPoint newP = _child.to(unit); 348 GeomPointTrans result = GeomPointTrans.newInstance(newP, _TM); 349 350 return result; 351 } 352 353 /** 354 * Returns a Vector3D representation of this transformed point if possible. 355 * 356 * @return A Vector3D that is equivalent to this transformed point 357 * @throws DimensionException if this point has any number of dimensions other than 3. 358 */ 359 @Override 360 public Vector3D<Length> toVector3D() { 361 return _pTrans.toVector3D(); 362 } 363 364 /** 365 * Returns the values stored in this transformed point, stated in this point's 366 * {@link #getUnit unit}, as a Float64Vector. 367 * 368 * @return A Float64Vector containing the values stored in this point in the current 369 * units. 370 */ 371 @Override 372 public Float64Vector toFloat64Vector() { 373 return _pTrans.toFloat64Vector(); 374 } 375 376 /** 377 * Return the equivalent of this point converted to the specified number of physical 378 * dimensions. This implementation will throw an exception if the specified dimension 379 * is anything other than 3. 380 * 381 * @param newDim The dimension of the point to return. MUST equal 3. 382 * @return The equivalent of this point converted to the new dimensions. 383 * @throws IllegalArgumentException if the new dimension is anything other than 3. 384 */ 385 @Override 386 public GeomPointTrans toDimension(int newDim) { 387 if (newDim == 3) 388 return this; 389 390 throw new IllegalArgumentException( 391 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 392 } 393 394 /** 395 * Compares this GeomPointTrans against the specified object for strict equality (same 396 * values and same units). 397 * 398 * @param obj the object to compare with. 399 * @return <code>true</code> if this point is identical to that point; 400 * <code>false</code> otherwise. 401 */ 402 @Override 403 public boolean equals(Object obj) { 404 if (this == obj) 405 return true; 406 if ((obj == null) || (obj.getClass() != this.getClass())) 407 return false; 408 409 GeomPointTrans that = (GeomPointTrans)obj; 410 return this._TM.equals(that._TM) 411 && this._child.equals(that._child) 412 && super.equals(obj); 413 } 414 415 /** 416 * Returns the hash code for this parameter. 417 * 418 * @return the hash code value. 419 */ 420 @Override 421 public int hashCode() { 422 return 31*super.hashCode() + Objects.hash(_TM, _child); 423 } 424 425 /** 426 * Holds the default XML representation for this object. 427 */ 428 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 429 protected static final XMLFormat<GeomPointTrans> XML = new XMLFormat<GeomPointTrans>(GeomPointTrans.class) { 430 431 @Override 432 public GeomPointTrans newInstance(Class<GeomPointTrans> cls, InputElement xml) throws XMLStreamException { 433 return FACTORY.object(); 434 } 435 436 @Override 437 public void read(InputElement xml, GeomPointTrans obj) throws XMLStreamException { 438 GeomPoint.XML.read(xml, obj); // Call parent read. 439 440 obj._TM = xml.getNext(); 441 GeomPoint child = xml.getNext(); 442 obj._child = child; 443 444 // Listen for changes to the child object and pass them on. 445 if (!(child instanceof Immutable)) 446 child.addChangeListener(obj._childChangeListener); 447 448 obj._pTrans = obj._TM.transform(obj._child); 449 obj._pTrans.setName(obj._child.getName()); 450 obj._pTrans.putAllUserData(obj._child.getAllUserData()); 451 } 452 453 @Override 454 public void write(GeomPointTrans obj, OutputElement xml) throws XMLStreamException { 455 GeomPoint.XML.write(obj, xml); // Call parent write. 456 457 xml.add(obj._TM); 458 xml.add(obj._child); 459 } 460 }; 461 462 /////////////////////// 463 // Factory creation. // 464 /////////////////////// 465 private GeomPointTrans() { } 466 467 @SuppressWarnings("unchecked") 468 private static final ObjectFactory<GeomPointTrans> FACTORY = new ObjectFactory<GeomPointTrans>() { 469 @Override 470 protected GeomPointTrans create() { 471 return new GeomPointTrans(); 472 } 473 474 @Override 475 protected void cleanup(GeomPointTrans obj) { 476 obj.reset(); 477 obj._TM = null; 478 if (!(obj._child instanceof Immutable)) 479 obj._child.removeChangeListener(obj._childChangeListener); 480 obj._child = null; 481 obj._pTrans = null; 482 } 483 }; 484 485 @SuppressWarnings("unchecked") 486 private static GeomPointTrans copyOf(GeomPointTrans original) { 487 GeomPointTrans obj = FACTORY.object(); 488 obj._TM = original._TM.copy(); 489 obj._child = original._child.copy(); 490 obj._pTrans = original._pTrans.copy(); 491 original.copyState(obj); 492 if (!(obj._child instanceof Immutable)) 493 obj._child.addChangeListener(obj._childChangeListener); 494 return obj; 495 } 496 497 /** 498 * Tests the methods in this class. 499 * 500 * @param args Command-line arguments (not used). 501 */ 502 public static void main(String args[]) { 503 System.out.println("Testing GeomPointTrans: test = result [correct result]"); 504 505 Parameter<Angle> psi = Parameter.valueOf(60, javax.measure.unit.NonSI.DEGREE_ANGLE); 506 Parameter<Angle> theta = Parameter.ZERO_ANGLE; 507 Parameter<Angle> phi = Parameter.valueOf(20, javax.measure.unit.NonSI.DEGREE_ANGLE); 508 DCMatrix dcm = DCMatrix.getEulerTM(psi, theta, phi); 509 GTransform T1 = GTransform.valueOf(dcm); 510 T1 = T1.applyScale(2.5); 511 Point p1 = Point.valueOf(1, 1, 1, SI.METER); 512 513 GeomPointTrans p1T = GeomPointTrans.newInstance(p1, T1); 514 515 System.out.println("\npsi = " + psi + ", theta = " + theta + ", phi = " + phi); 516 System.out.println("\np1 = " + p1); 517 System.out.println(" T1 = \n" + T1); 518 System.out.println(" p1T = " + p1T + " [-0.0439988715583743 m, 2.9121541062864 m, 3.20428191027894 m]"); 519 System.out.println(" p1T.norm() = " + p1T.norm() + " [4.33012701892219 m]"); 520 521 GTransform T2 = GTransform.newTranslation(Vector.valueOf(SI.METER, 2, -1, -3)); 522 GeomPointTrans p2T = GeomPointTrans.newInstance(p1T, T2); 523 System.out.println("\ntranslate p1T by: 2, -1, -3 m"); 524 System.out.println(" p2T = " + p2T + " [1.95600112844163 m, 1.9121541062864 m, 0.20428191027894 m]"); 525 System.out.println(" p2T.norm() = " + p2T.norm() + " [2.74299195031995 m]"); 526 527 // Write out XML data. 528 try { 529 System.out.println(); 530 531 // Creates some useful aliases for class names. 532 javolution.xml.XMLBinding binding = new GeomXMLBinding(); 533 534 javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out); 535 writer.setIndentation(" "); 536 writer.setBinding(binding); 537 writer.write(p1T, "GeomPointTrans", GeomPointTrans.class); 538 writer.flush(); 539 540 System.out.println(); 541 } catch (Exception e) { 542 e.printStackTrace(); 543 } 544 545 } 546 547}