001/** 002 * VectorTrans -- A GeomTransform that has an GeomVector 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.Parameter; 021import jahuwaldt.js.param.ParameterVector; 022import java.text.MessageFormat; 023import java.util.Objects; 024import static java.util.Objects.requireNonNull; 025import javax.measure.converter.ConversionException; 026import javax.measure.quantity.Dimensionless; 027import javax.measure.quantity.Quantity; 028import javax.measure.unit.Unit; 029import javax.swing.event.ChangeListener; 030import javolution.context.ObjectFactory; 031import javolution.context.StackContext; 032import javolution.lang.Immutable; 033import javolution.util.FastTable; 034import javolution.xml.XMLFormat; 035import javolution.xml.stream.XMLStreamException; 036import org.jscience.mathematics.number.Float64; 037import org.jscience.mathematics.vector.Float64Vector; 038 039/** 040 * A {@link GeomTransform} element that refers to a {@link GeomVector} object and 041 * masquerades as a GeomVector object itself. 042 * 043 * <p> Modified by: Joseph A. Huwaldt </p> 044 * 045 * @author Joseph A. Huwaldt, Date: June 13, 2009 046 * @version April 10, 2018 047 * 048 * @param <Q> The Quantity (unit type) of the elements of this vector. 049 */ 050@SuppressWarnings({"serial", "CloneableImplementsClone"}) 051public final class VectorTrans<Q extends Quantity> extends GeomVector<Q> implements GeomTransform<GeomVector> { 052 053 /** 054 * The transformation represented by this transformation element. 055 */ 056 private GTransform _TM; 057 058 /** 059 * Just the rotational and scale portion of the transformation. 060 */ 061 private GTransform _TM_RotScale; 062 063 /** 064 * The object that is the child of this transform. 065 */ 066 private GeomVector<Q> _child; 067 068 /** 069 * Reference to a change listener for this object's child. 070 */ 071 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 072 073 /** 074 * Returns a 3D {@link VectorTrans} instance holding the specified {@link GeomVector} 075 * and {@link GTransform}. 076 * 077 * @param <Q> The Quantity (unit type) of this vector. 078 * @param child The vector that is the child of this transform element (may not be 079 * <code>null</code>). 080 * @param transform The transform held by this transform element (may not be 081 * <code>null</code>). 082 * @return the transform element having the specified values. 083 * @throws DimensionException if the input element is not 3D. 084 */ 085 public static <Q extends Quantity> VectorTrans<Q> newInstance(GeomVector<Q> child, GTransform transform) { 086 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 087 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 088 089 if (child.getPhyDimension() != 3) 090 throw new DimensionException( 091 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "vector", child.getPhyDimension())); 092 093 VectorTrans<Q> obj = FACTORY.object(); 094 obj._TM = transform; 095 obj._TM_RotScale = GTransform.valueOf(transform.getRotationScale()); 096 obj._child = child; 097 child.copyState(obj); 098 099 // Listen for changes to the child object and pass them on. 100 if (!(child instanceof Immutable)) 101 child.addChangeListener(obj._childChangeListener); 102 103 return obj; 104 } 105 106 /** 107 * Returns the transformation represented by this transformation element. 108 */ 109 @Override 110 public GTransform getTransform() { 111 return _TM; 112 } 113 114 /** 115 * Returns the total transformation represented by an entire chain of GeomTransform 116 * objects below this one. 117 */ 118 @Override 119 public GTransform getTotalTransform() { 120 return GeomUtil.getTotalTransform(this); 121 } 122 123 /** 124 * Sets the transformation represented by this transformation element. 125 * 126 * @param transform The transform to set this transform element to (may not be 127 * <code>null</code>). 128 */ 129 @Override 130 public void setTransform(GTransform transform) { 131 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 132 _TM = transform; 133 _TM_RotScale = GTransform.valueOf(transform.getRotationScale()); 134 fireChangeEvent(); 135 } 136 137 /** 138 * Returns the child object transformed by this transform element. 139 */ 140 @Override 141 public GeomVector<Q> 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 Vector<Q> copyToReal() { 152 Unit<Q> unit = (Unit<Q>)_child.getUnit(); 153 Float64Vector dirV; 154 StackContext.enter(); 155 try { 156 157 // The direction of the vector is transformed by rotation and scaling only, not by translation! 158 Point pt2 = _TM_RotScale.transform(Point.valueOf(_child)); 159 160 FastTable<Float64> values = FastTable.newInstance(); 161 int dim = pt2.getPhyDimension(); 162 for (int i = 0; i < dim; ++i) { 163 values.add(Float64.valueOf(pt2.getValue(i))); 164 } 165 dirV = Float64Vector.valueOf(values); 166 dirV = StackContext.outerCopy(dirV); 167 168 } finally { 169 StackContext.exit(); 170 } 171 172 Vector<Q> V = Vector.valueOf(dirV, unit); 173 this.copyState(V); 174 175 // The origin is transformed by rotation, scale, AND translation. 176 V.setOrigin(_TM.transform(_child.getOrigin())); 177 178 return V; 179 } 180 181 /** 182 * Recycles a <code>VectorTrans</code> instance immediately (on the stack when 183 * executing in a <code>StackContext</code>). 184 * 185 * @param instance The instance to be recycled. 186 */ 187 public static void recycle(VectorTrans instance) { 188 FACTORY.recycle(instance); 189 } 190 191 /** 192 * Returns the number of physical dimensions of the geometry element. This 193 * implementation always returns 3. 194 */ 195 @Override 196 public int getPhyDimension() { 197 return 3; 198 } 199 200 /** 201 * Returns the value of the Parameter in this vector as a <code>double</code>, stated 202 * in this vector's {@link #getUnit unit}. 203 * 204 * @param i the dimension index. 205 * @return the value of the Parameter at <code>i</code>. 206 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 207 */ 208 @Override 209 public double getValue(int i) { 210 Vector pTrans = copyToReal(); 211 double value = pTrans.getValue(i); 212 Vector.recycle(pTrans); 213 return value; 214 } 215 216 /** 217 * Returns the value of the Parameter in this vector as a <code>double</code>, stated 218 * in the specified units. 219 * 220 * @param i the dimension index. 221 * @param unit the unit to return the value in. May not be null. 222 * @return the value of the Parameter at <code>i</code> in the specified unit. 223 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 224 */ 225 @Override 226 public double getValue(int i, Unit<Q> unit) { 227 requireNonNull(unit); 228 Vector pTrans = copyToReal(); 229 double value = pTrans.getValue(i, unit); 230 Vector.recycle(pTrans); 231 return value; 232 } 233 234 /** 235 * Set the origin point for this vector. The origin is used as a reference for drawing 236 * the vector and is <i>not</i> a part of the state of this vector and is not used in 237 * any calculations with this vector. 238 * 239 * @param origin The new origin point for the vector. May not be null. 240 */ 241 @Override 242 public void setOrigin(Point origin) { 243 requireNonNull(origin, MessageFormat.format(RESOURCES.getString("paramNullErr"), "origin")); 244 _child.setOrigin(_TM.transpose().transform(origin)); 245 } 246 247 /** 248 * Return the origin point for this vector. If no origin has been explicitly set, then 249 * the coordinate system origin is returned. 250 * 251 * @return The origin point for this vector. 252 */ 253 @Override 254 public Point getOrigin() { 255 return _TM.transform(_child.getOrigin()); 256 } 257 258 /** 259 * Returns the {@link #norm}, magnitude, or length value of the vector from the origin 260 * to this point (square root of the dot product of the origin-to-this-point vector 261 * and itself). 262 * 263 * @return <code>this.norm().doubleValue()</code>. 264 */ 265 @Override 266 public double normValue() { 267 Vector pTrans = copyToReal(); 268 double normV = pTrans.normValue(); 269 Vector.recycle(pTrans); 270 return normV; 271 } 272 273 /** 274 * Returns the negation of this vector. 275 * 276 * @return <code>-this</code>. 277 */ 278 @Override 279 public Vector<Q> opposite() { 280 Vector<Q> pTrans = copyToReal(); 281 Vector<Q> V = pTrans.opposite(); 282 Vector.recycle(pTrans); 283 return V; 284 } 285 286 /** 287 * Returns the sum of this vector with the one specified. The unit of the output 288 * vector will be the units of this vector. 289 * 290 * @param that the vector to be added. May not be null. 291 * @return <code>this + that</code>. 292 * @throws DimensionException if vector dimensions are different. 293 */ 294 @Override 295 public Vector<Q> plus(GeomVector<Q> that) { 296 requireNonNull(that); 297 Vector<Q> pTrans = copyToReal(); 298 Vector<Q> V = pTrans.plus(that); 299 Vector.recycle(pTrans); 300 return V; 301 } 302 303 /** 304 * Returns the sum of this vector with the parameter specified. The input parameter is 305 * added to each component of this vector. The unit of the output vector will be the 306 * units of this vector. 307 * 308 * @param that the parameter to be added to each element of this vector. May not be null. 309 * @return <code>this + that</code>. 310 */ 311 @Override 312 public Vector<Q> plus(Parameter<Q> that) { 313 requireNonNull(that); 314 Vector<Q> pTrans = copyToReal(); 315 Vector<Q> V = pTrans.plus(that); 316 Vector.recycle(pTrans); 317 return V; 318 } 319 320 /** 321 * Returns the difference between this vector and the one specified. The unit of the 322 * output vector will be the units of this vector. 323 * 324 * @param that the vector to be subtracted from this vector. May not be null. 325 * @return <code>this - that</code>. 326 * @throws DimensionException if vector dimensions are different. 327 */ 328 @Override 329 public Vector<Q> minus(GeomVector<Q> that) { 330 requireNonNull(that); 331 Vector<Q> pTrans = copyToReal(); 332 Vector<Q> V = pTrans.minus(that); 333 Vector.recycle(pTrans); 334 return V; 335 } 336 337 /** 338 * Subtracts the supplied Parameter from each element of this vector and returns the 339 * result. The unit of the output vector will be the units of this vector. 340 * 341 * @param that the Parameter to be subtracted from each element of this vector. May 342 * not be null. 343 * @return <code>this - that</code>. 344 */ 345 @Override 346 public Vector<Q> minus(Parameter<Q> that) { 347 requireNonNull(that); 348 Vector<Q> pTrans = copyToReal(); 349 Vector<Q> V = pTrans.minus(that); 350 Vector.recycle(pTrans); 351 return V; 352 } 353 354 /** 355 * Returns the product of this vector with the specified coefficient (dimensionless). 356 * 357 * @param k the coefficient multiplier. 358 * @return <code>this · k</code> 359 */ 360 @Override 361 public Vector<Q> times(double k) { 362 Vector<Q> pTrans = copyToReal(); 363 Vector<Q> V = pTrans.times(k); 364 Vector.recycle(pTrans); 365 return V; 366 } 367 368 /** 369 * Returns the product of this vector with the specified coefficient. 370 * 371 * @param k the coefficient multiplier. May not be null. 372 * @return <code>this · k</code> 373 */ 374 @Override 375 public Vector<? extends Quantity> times(Parameter<?> k) { 376 requireNonNull(k); 377 Vector pTrans = copyToReal(); 378 Vector V = pTrans.times(k); 379 Vector.recycle(pTrans); 380 return V; 381 } 382 383 /** 384 * Returns the dot product (scalar product) of this vector with the one specified. 385 * 386 * @param that the vector multiplier. May not be null. 387 * @return <code>this · that</code> 388 * @throws DimensionException if <code>this.dimension() != that.dimension()</code> 389 * @see <a href="http://en.wikipedia.org/wiki/Dot_product"> 390 * Wikipedia: Dot Product</a> 391 */ 392 @Override 393 public Parameter<? extends Quantity> times(GeomVector<?> that) { 394 requireNonNull(that); 395 Vector pTrans = copyToReal(); 396 Parameter P = pTrans.times(that); 397 Vector.recycle(pTrans); 398 return P; 399 } 400 401 /** 402 * Returns the element-by-element product of this vector with the one specified. 403 * 404 * @param that the vector multiplier. May not be null. 405 * @return <code>this .* that</code> 406 * @throws DimensionException if <code>this.dimension() != that.dimension()</code> 407 */ 408 @Override 409 public Vector<? extends Quantity> timesEBE(GeomVector<?> that) { 410 requireNonNull(that); 411 Vector<Q> pTrans = copyToReal(); 412 Vector V = pTrans.timesEBE(that); 413 Vector.recycle(pTrans); 414 return V; 415 } 416 417 /** 418 * Returns the cross product of two vectors. 419 * 420 * @param that the vector multiplier. May not be null. 421 * @return <code>this x that</code> 422 * @throws DimensionException if 423 * <code>(that.getDimension() != this.getDimension())</code> 424 * @see <a href="http://en.wikipedia.org/wiki/Cross_product"> 425 * Wikipedia: Cross Product</a> 426 */ 427 @Override 428 public Vector<? extends Quantity> cross(GeomVector<?> that) { 429 requireNonNull(that); 430 Vector<Q> pTrans = copyToReal(); 431 Vector V = pTrans.cross(that); 432 Vector.recycle(pTrans); 433 return V; 434 } 435 436 /** 437 * Returns this vector with each element divided by the specified divisor 438 * (dimensionless). 439 * 440 * @param divisor the divisor. 441 * @return <code>this / divisor</code>. 442 */ 443 @Override 444 public Vector<Q> divide(double divisor) { 445 Vector<Q> pTrans = copyToReal(); 446 Vector<Q> V = pTrans.divide(divisor); 447 Vector.recycle(pTrans); 448 return V; 449 } 450 451 /** 452 * Returns this vector with each element divided by the specified divisor. 453 * 454 * @param that the divisor. May not be null. 455 * @return <code>this / that</code>. 456 */ 457 @Override 458 public Vector<? extends Quantity> divide(Parameter<?> that) { 459 requireNonNull(that); 460 Vector<Q> pTrans = copyToReal(); 461 Vector V = pTrans.divide(that); 462 Vector.recycle(pTrans); 463 return V; 464 } 465 466 /** 467 * Return an immutable version of this vector. 468 * 469 * @return An immutable version of this vector. 470 */ 471 @Override 472 public Vector<Q> immutable() { 473 return copyToReal(); 474 } 475 476 /** 477 * Returns this vector converted to a unit vector by dividing all the vector's 478 * elements by the length ({@link #norm}) of this vector. 479 * 480 * @return This vector converted to a unit vector. 481 */ 482 @Override 483 public Vector<Dimensionless> toUnitVector() { 484 Vector<Q> pTrans = copyToReal(); 485 Vector<Dimensionless> V = pTrans.toUnitVector(); 486 Vector.recycle(pTrans); 487 return V; 488 } 489 490 /** 491 * Returns the unit in which the {@link #getValue values} in this vector are stated 492 * in. 493 */ 494 @Override 495 public Unit getUnit() { 496 return _child.getUnit(); 497 } 498 499 /** 500 * Returns the equivalent to this vector but stated in the specified unit. 501 * 502 * @param unit the unit of the vector to be returned. May not be null. 503 * @return an equivalent to this vector but stated in the specified unit. 504 * @throws ConversionException if the the input unit is not compatible with this unit. 505 */ 506 @Override 507 public GeomVector to(Unit unit) throws ConversionException { 508 if (unit.equals(getUnit())) 509 return this; 510 511 GeomVector newV = _child.to(unit); 512 VectorTrans result = VectorTrans.newInstance(newV, _TM); 513 this.copyState(result); 514 515 return result; 516 } 517 518 /** 519 * Returns a <code>ParameterVector</code> representation of this vector. 520 * 521 * @return A ParameterVector that is equivalent to this vector. 522 */ 523 @Override 524 public ParameterVector<Q> toParameterVector() { 525 Vector<Q> pTrans = copyToReal(); 526 ParameterVector<Q> V = pTrans.toParameterVector(); 527 Vector.recycle(pTrans); 528 return V; 529 } 530 531 /** 532 * Returns a <code>Float64Vector</code> containing the elements of this vector stated 533 * in the current units. 534 * 535 * @return A Float64Vector that contains the elements of this vector in the current 536 * units. 537 */ 538 @Override 539 public Float64Vector toFloat64Vector() { 540 Vector<Q> pTrans = copyToReal(); 541 Float64Vector V = pTrans.toFloat64Vector(); 542 Vector.recycle(pTrans); 543 return V; 544 } 545 546 /** 547 * Returns transformed version of this element. The returned object implements 548 * {@link GeomTransform} and contains this element as a child. 549 * 550 * @param transform The transform to apply to this vector. May not be null. 551 */ 552 @Override 553 public VectorTrans<Q> getTransformed(GTransform transform) { 554 return VectorTrans.newInstance(this, requireNonNull(transform)); 555 } 556 557 /** 558 * Return the equivalent of this vector converted to the specified number of physical 559 * dimensions. This implementation will throw an exception if the specified dimension 560 * is anything other than 3. 561 * 562 * @param newDim The dimension of the vector to return. MUST equal 3. 563 * @return The equivalent of this vector converted to the new dimensions. 564 * @throws IllegalArgumentException if the new dimension is anything other than 3. 565 */ 566 @Override 567 public VectorTrans<Q> toDimension(int newDim) { 568 if (newDim == 3) 569 return this; 570 571 throw new IllegalArgumentException( 572 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 573 } 574 575 /** 576 * Compares this VectorTrans against the specified object for strict equality (same 577 * values and same units). 578 * 579 * @param obj the object to compare with. 580 * @return <code>true</code> if this point is identical to that point; 581 * <code>false</code> otherwise. 582 */ 583 @Override 584 public boolean equals(Object obj) { 585 if (this == obj) 586 return true; 587 if ((obj == null) || (obj.getClass() != this.getClass())) 588 return false; 589 590 VectorTrans that = (VectorTrans)obj; 591 return this._TM.equals(that._TM) 592 && this._child.equals(that._child) 593 && super.equals(obj); 594 } 595 596 /** 597 * Returns the hash code for this parameter. 598 * 599 * @return the hash code value. 600 */ 601 @Override 602 public int hashCode() { 603 return 31*super.hashCode() + Objects.hash(_TM, _child); 604 } 605 606 /** 607 * Returns a copy of this VectorTrans instance 608 * {@link javolution.context.AllocatorContext allocated} by the calling thread 609 * (possibly on the stack). 610 * 611 * @return an identical and independent copy of this vector. 612 */ 613 @Override 614 public VectorTrans<Q> copy() { 615 return copyOf(this); 616 } 617 618 /** 619 * Holds the default XML representation for this object. 620 */ 621 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 622 protected static final XMLFormat<VectorTrans> XML = new XMLFormat<VectorTrans>(VectorTrans.class) { 623 624 @Override 625 public VectorTrans newInstance(Class<VectorTrans> cls, InputElement xml) throws XMLStreamException { 626 return FACTORY.object(); 627 } 628 629 @Override 630 public void read(InputElement xml, VectorTrans obj) throws XMLStreamException { 631 GeomVector.XML.read(xml, obj); // Call parent read. 632 633 obj._TM = xml.getNext(); 634 obj._TM_RotScale = GTransform.valueOf(obj._TM.getRotationScale()); 635 GeomVector child = xml.getNext(); 636 obj._child = child; 637 638 // Listen for changes to the child object and pass them on. 639 if (!(child instanceof Immutable)) 640 child.addChangeListener(obj._childChangeListener); 641 } 642 643 @Override 644 public void write(VectorTrans obj, OutputElement xml) throws XMLStreamException { 645 GeomVector.XML.write(obj, xml); // Call parent write. 646 647 xml.add(obj._TM); 648 xml.add(obj._child); 649 650 } 651 }; 652 653 /////////////////////// 654 // Factory creation. // 655 /////////////////////// 656 private VectorTrans() { } 657 658 @SuppressWarnings("unchecked") 659 private static final ObjectFactory<VectorTrans> FACTORY = new ObjectFactory<VectorTrans>() { 660 @Override 661 protected VectorTrans create() { 662 return new VectorTrans(); 663 } 664 665 @Override 666 protected void cleanup(VectorTrans obj) { 667 obj.reset(); 668 if (!(obj._child instanceof Immutable)) 669 obj._child.removeChangeListener(obj._childChangeListener); 670 } 671 }; 672 673 @SuppressWarnings("unchecked") 674 private static VectorTrans copyOf(VectorTrans original) { 675 VectorTrans obj = FACTORY.object(); 676 obj._TM = original._TM.copy(); 677 obj._TM_RotScale = original._TM_RotScale.copy(); 678 obj._child = original._child.copy(); 679 original.copyState(obj); 680 if (!(obj._child instanceof Immutable)) 681 obj._child.addChangeListener(obj._childChangeListener); 682 return obj; 683 } 684 685}