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