001/** 002 * GeomVector -- Partial implementation of an n-D vector. 003 * 004 * Copyright (C) 2009-2016, 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 static java.util.Objects.nonNull; 023import static java.util.Objects.requireNonNull; 024import javax.measure.quantity.Angle; 025import javax.measure.quantity.Dimensionless; 026import javax.measure.quantity.Quantity; 027import javax.measure.unit.SI; 028import javax.measure.unit.Unit; 029import javolution.lang.MathLib; 030import javolution.text.Text; 031import javolution.text.TextBuilder; 032import org.jscience.mathematics.vector.Float64Vector; 033 034/** 035 * Partial implementation of an n-dimensional vector which indicates direction, but not 036 * position. 037 * 038 * <p> Modified by: Joseph A. Huwaldt </p> 039 * 040 * @author Joseph A. Huwaldt Date: June 13, 2009 041 * @version September 9, 2016 042 * 043 * @param <Q> The Quantity (unit type) associated with this vector's coordinate values. 044 */ 045@SuppressWarnings({"serial", "CloneableImplementsClone"}) 046public abstract class GeomVector<Q extends Quantity> extends AbstractGeomElement<GeomVector> 047 implements Transformable<GeomVector> { 048 049 /** 050 * Constant used to identify the X coordinate in a 3D vector. 051 */ 052 public static final int X = 0; 053 054 /** 055 * Constant used to identify the Y coordinate in a 3D vector. 056 */ 057 public static final int Y = 1; 058 059 /** 060 * Constant used to identify the Z coordinate in a 3D vector. 061 */ 062 public static final int Z = 2; 063 064 /** 065 * Returns the number of child-elements that make up this geometry element. This 066 * implementation always returns 0 as a vector is not made up of any other elements. 067 */ 068 @Override 069 public int size() { 070 return 0; 071 } 072 073 /** 074 * Returns the number of parametric dimensions of the geometry element. This 075 * implementation always returns 0 as a vector is not parametric. 076 */ 077 @Override 078 public int getParDimension() { 079 return 0; 080 } 081 082 /** 083 * Returns the value of a Parameter from this vector. 084 * 085 * @param i the dimension index. 086 * @return the value of the parameter at <code>i</code>. 087 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 088 */ 089 public Parameter<Q> get(int i) { 090 return Parameter.valueOf(getValue(i), (Unit)getUnit()); 091 } 092 093 /** 094 * Returns the value of the Parameter in this vector as a <code>double</code>, stated 095 * in this vector's {@link #getUnit unit}. 096 * 097 * @param i the dimension index. 098 * @return the value of the Parameter at <code>i</code>. 099 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 100 */ 101 public abstract double getValue(int i); 102 103 /** 104 * Returns the value of the Parameter in this vector as a <code>double</code>, stated 105 * in the specified units. 106 * 107 * @param i the dimension index. 108 * @param unit the unit to return the value in. May not be null. 109 * @return the value of the Parameter at <code>i</code> in the specified unit. 110 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 111 */ 112 public abstract double getValue(int i, Unit<Q> unit); 113 114 /** 115 * Set the origin point for this vector. The origin is used as a reference for drawing 116 * the vector and is <i>not</i> a part of the state of this vector and is not used in 117 * any calculations with this vector. 118 * 119 * @param o The origin point to assign to this vector. May not be null. 120 */ 121 public abstract void setOrigin(Point o); 122 123 /** 124 * Return the origin point for this vector. If no origin has been explicitly set, then 125 * the coordinate system origin in the default units is returned. 126 * 127 * @return The origin point associated with this vector. 128 */ 129 public abstract Point getOrigin(); 130 131 /** 132 * Returns the Euclidian norm, magnitude, or length of this vector (square root of the 133 * dot product of this vector and itself). 134 * 135 * @return <code>sqrt(this · this)</code>. 136 */ 137 public Parameter<Q> norm() { 138 return Parameter.valueOf(normValue(), (Unit)getUnit()); 139 } 140 141 /** 142 * Returns the {@link #norm}, magnitude, or length value of this vector (square root 143 * of the dot product of this vector and itself). 144 * 145 * @return <code>this.norm().doubleValue()</code>. 146 */ 147 public abstract double normValue(); 148 149 /** 150 * Returns the Euclidian norm, magnitude, or length of this vector (square root of the 151 * dot product of this vector and itself). 152 * 153 * @return <code>sqrt(this · this)</code>. 154 */ 155 public Parameter<Q> mag() { 156 return norm(); 157 } 158 159 /** 160 * Returns the {@link #norm}, magnitude, or length value of this vector. 161 * 162 * @return <code>this.norm().doubleValue()</code>. 163 */ 164 public double magValue() { 165 return normValue(); 166 } 167 168 /** 169 * Returns the negation of this vector. 170 * 171 * @return <code>-this</code>. 172 */ 173 public abstract Vector<Q> opposite(); 174 175 /** 176 * Returns the sum of this vector with the one specified. The unit of the output 177 * vector will be the units of this vector. 178 * 179 * @param that the vector to be added. May not be null. 180 * @return <code>this + that</code>. 181 * @throws DimensionException if vector dimensions are different. 182 */ 183 public abstract Vector<Q> plus(GeomVector<Q> that); 184 185 /** 186 * Returns the sum of this vector with the parameter specified. The input parameter is 187 * added to each component of this vector. The unit of the output vector will be the 188 * units of this vector. 189 * 190 * @param that the parameter to be added to each element of this vector. May not be 191 * null. 192 * @return <code>this + that</code>. 193 */ 194 public abstract Vector<Q> plus(Parameter<Q> that); 195 196 /** 197 * Returns the difference between this vector and the one specified. The unit of the 198 * output vector will be the units of this vector. 199 * 200 * @param that the vector to be subtracted from this vector. May not be null. 201 * @return <code>this - that</code>. 202 * @throws DimensionException if vector dimensions are different. 203 */ 204 public abstract Vector<Q> minus(GeomVector<Q> that); 205 206 /** 207 * Subtracts the supplied Parameter from each element of this vector and returns the 208 * result. The unit of the output vector will be the units of this vector. 209 * 210 * @param that the Parameter to be subtracted from each element of this vector. May 211 * not be null. 212 * @return <code>this - that</code>. 213 */ 214 public abstract Vector<Q> minus(Parameter<Q> that); 215 216 /** 217 * Returns the product of this vector with the specified coefficient (dimensionless). 218 * 219 * @param k the coefficient multiplier. 220 * @return <code>this · k</code> 221 */ 222 public abstract Vector<Q> times(double k); 223 224 /** 225 * Returns the product of this vector with the specified coefficient. 226 * 227 * @param k the coefficient multiplier. May not be null. 228 * @return <code>this · k</code> 229 */ 230 public abstract Vector<? extends Quantity> times(Parameter<?> k); 231 232 /** 233 * Returns the dot product (scalar product) of this vector with the one specified. 234 * 235 * @param that the vector multiplier. May not be null. 236 * @return <code>this · that</code> 237 * @throws DimensionException if <code>this.dimension() != that.dimension()</code> 238 * @see <a href="http://en.wikipedia.org/wiki/Dot_product"> 239 * Wikipedia: Dot Product</a> 240 */ 241 public abstract Parameter<? extends Quantity> times(GeomVector<?> that); 242 243 /** 244 * Returns the dot product (scalar product) of this vector with the one specified. 245 * 246 * @param that the vector multiplier. May not be null. 247 * @return <code>this · that</code> 248 * @throws DimensionException if <code>this.dimension() != that.dimension()</code> 249 * @see <a href="http://en.wikipedia.org/wiki/Dot_product"> 250 * Wikipedia: Dot Product</a> 251 */ 252 public Parameter<? extends Quantity> dot(GeomVector<?> that) { 253 return times(that); 254 } 255 256 /** 257 * Returns the element-by-element product of this vector with the one specified. 258 * 259 * @param that the vector multiplier. May not be null. 260 * @return <code>this .* that</code> 261 * @throws DimensionException if <code>this.dimension() != that.dimension()</code> 262 */ 263 public abstract Vector<? extends Quantity> timesEBE(GeomVector<?> that); 264 265 /** 266 * Returns the cross product of two vectors. 267 * 268 * @param that the vector multiplier. May not be null. 269 * @return <code>this x that</code> 270 * @throws DimensionException if 271 * <code>(that.getDimension() != this.getDimension())</code> 272 * @see <a href="http://en.wikipedia.org/wiki/Cross_product"> 273 * Wikipedia: Cross Product</a> 274 */ 275 public abstract Vector<? extends Quantity> cross(GeomVector<?> that); 276 277 /** 278 * Returns this vector with each element divided by the specified divisor 279 * (dimensionless). 280 * 281 * @param divisor the divisor. 282 * @return <code>this / divisor</code>. 283 */ 284 public abstract Vector<Q> divide(double divisor); 285 286 /** 287 * Returns this vector with each element divided by the specified divisor. 288 * 289 * @param that the divisor. May not be null. 290 * @return <code>this / that</code>. 291 */ 292 public abstract Vector<? extends Quantity> divide(Parameter<?> that); 293 294 /** 295 * Returns the angle between this vector and the specified vector. 296 * 297 * @param that the vector to which the angle will be determined. May not be null. 298 * @return <code>acos(this · that)/(norm(this)*norm(that))</code> 299 */ 300 public Parameter<Angle> angle(GeomVector<?> that) { 301 that = that.to(getUnit()); 302 double scalar = this.dot(that).getValue(); 303 double abs1 = this.normValue(); 304 double abs2 = that.normValue(); 305 306 double argument = 1.; 307 double dum = abs1 * abs2; 308 if (dum >= Parameter.SQRT_EPS) 309 argument = scalar / dum; 310 311 if (argument > 1.) 312 argument = 1.; 313 else if (argument < -1.) 314 argument = -1.; 315 316 return Parameter.valueOf(MathLib.acos(argument), SI.RADIAN); 317 } 318 319 /** 320 * Return an immutable version of this vector. 321 * 322 * @return An immutable version of this vector. 323 */ 324 public abstract Vector<Q> immutable(); 325 326 /** 327 * Return <code>true</code> if this GeomVector contains valid and finite numerical 328 * components. A value of <code>false</code> will be returned if any of the elements 329 * in this vector, including the origin, are invalid. 330 */ 331 @Override 332 public boolean isValid() { 333 // Check the origin point. 334 GeomPoint o = getOrigin(); 335 if (!o.isValid()) 336 return false; 337 338 // Check the vector elements. 339 int dim = getPhyDimension(); 340 for (int i = 0; i < dim; ++i) { 341 double value = getValue(i); 342 if (Double.isNaN(value) || Double.isInfinite(value)) 343 return false; 344 } 345 346 return true; 347 } 348 349 /** 350 * Returns this vector converted to a unit vector by dividing all the vector's 351 * elements by the length ({@link #norm}) of this vector. 352 * 353 * @return This vector converted to a unit vector by dividing all the vector's 354 * elements by the length of this vector. 355 */ 356 public abstract Vector<Dimensionless> toUnitVector(); 357 358 /** 359 * Return the coordinate point representing the minimum bounding box corner (e.g.: min 360 * X, min Y, min Z). 361 * 362 * @return The minimum bounding box coordinate for this geometry element. 363 */ 364 @Override 365 public Point getBoundsMin() { 366 Point o = getOrigin(); 367 Point tip = Point.valueOf(this).plus(o); 368 return o.min(tip); 369 } 370 371 /** 372 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 373 * X, max Y, max Z). 374 * 375 * @return The maximum bounding box coordinate for this geometry element. 376 */ 377 @Override 378 public Point getBoundsMax() { 379 Point o = getOrigin(); 380 Point tip = Point.valueOf(this).plus(o); 381 return o.max(tip); 382 } 383 384 /** 385 * Returns the most extreme point, either minimum or maximum, in the specified 386 * coordinate direction on this geometry element. This implementation simply returns 387 * <code>getBoundsMin</code> or <code>getBoundsMax</code> depending on the setting of 388 * the "max" flag. 389 * 390 * @param dim An index indicating the dimension to find the min/max point for (0=X, 391 * 1=Y, 2=Z, etc). 392 * @param max Set to <code>true</code> to return the maximum value, <code>false</code> 393 * to return the minimum. 394 * @param tol Fractional tolerance to refine the min/max point position to if 395 * necessary. 396 * @return The point found on this element that is the min or max in the specified 397 * coordinate direction. 398 * @see #getBoundsMin 399 * @see #getBoundsMax 400 */ 401 @Override 402 public Point getLimitPoint(int dim, boolean max, double tol) { 403 if (max) 404 return getBoundsMax(); 405 return getBoundsMin(); 406 } 407 408 /** 409 * Returns a <code>ParameterVector</code> representation of this vector. 410 * 411 * @return A ParameterVector that is equivalent to this vector. 412 */ 413 public abstract ParameterVector<Q> toParameterVector(); 414 415 /** 416 * Returns a <code>Float64Vector</code> containing the elements of this vector stated 417 * in the current units. 418 * 419 * @return A Float64Vector that contains the elements of this vector in the current 420 * units. 421 */ 422 public abstract Float64Vector toFloat64Vector(); 423 424 /** 425 * Returns the values stored in this vector as a Java array, stated in the 426 * current {@link #getUnit units}. 427 * 428 * @return A new array with the vector element values copied into it. 429 */ 430 public double[] toArray() { 431 return toArray(new double[this.getPhyDimension()]); 432 } 433 434 /** 435 * Returns the values stored in this vector, stated in the current 436 * {@link #getUnit units} stored in the input Java array. 437 * 438 * @param array An existing array that has at least as many elements as the physical 439 * dimension of this vector. 440 * @return A reference to the input array after the vector elements have been copied 441 * over to it. 442 * @see #getPhyDimension() 443 */ 444 public double[] toArray(double[] array) { 445 int numDims = this.getPhyDimension(); 446 for (int i = 0; i < numDims; ++i) 447 array[i] = this.getValue(i); 448 return array; 449 } 450 451 /** 452 * Returns transformed version of this element. The returned object implements 453 * {@link GeomTransform} and contains this element as a child. 454 * 455 * @param transform The transformation to apply to this geometry. May not be null. 456 * @return A new triangle that is identical to this one with the specified 457 * transformation applied. 458 * @throws DimensionException if this point is not 3D. 459 */ 460 @Override 461 public VectorTrans<Q> getTransformed(GTransform transform) { 462 return VectorTrans.newInstance(this, requireNonNull(transform)); 463 } 464 465 /** 466 * Return a copy of this vector converted to the specified number of physical 467 * dimensions. If the number of dimensions is greater than this element, then zeros 468 * are added to the additional dimensions. If the number of dimensions is less than 469 * this element, then the extra dimensions are simply dropped (truncated). If the new 470 * dimensions are the same as the dimension of this element, then this element is 471 * simply returned. 472 * 473 * @param newDim The dimension of the vector to return. 474 * @return A copy of this vector converted to the new dimensions. 475 */ 476 @Override 477 public abstract GeomVector<Q> toDimension(int newDim); 478 479 /** 480 * Returns the text representation of this geometry element that consists of the name 481 * followed by the vector axis values. For example: 482 * <pre> 483 * {v = {{10 ft, -3 ft, 4.56 ft},o={0 m, 0m, 0m}}} 484 * </pre> 485 * If there is no name, then the output looks like this: 486 * <pre> 487 * {{10 ft, -3 ft, 4.56 ft},o={0 m, 0 m, 0 m}}} 488 * </pre> 489 * 490 * @return the text representation of this geometry element. 491 */ 492 @Override 493 public Text toText() { 494 int dimension = this.getPhyDimension(); 495 TextBuilder tmp = TextBuilder.newInstance(); 496 tmp.append('{'); 497 String nameStr = getName(); 498 boolean hasName = nonNull(nameStr); 499 if (hasName) { 500 tmp.append(nameStr); 501 tmp.append(" = {{"); 502 } else 503 tmp.append("{"); 504 for (int i = 0; i < dimension; i++) { 505 tmp.append(get(i)); 506 if (i != dimension - 1) { 507 tmp.append(", "); 508 } 509 } 510 GeomPoint o = getOrigin(); 511 if (o == null) { 512 tmp.append("},o={null}"); 513 } else { 514 tmp.append("},o="); 515 tmp.append(o); 516 } 517 518 if (hasName) 519 tmp.append('}'); 520 tmp.append('}'); 521 Text txt = tmp.toText(); 522 TextBuilder.recycle(tmp); 523 return txt; 524 } 525 526 /** 527 * Returns a copy of this GeomVector instance 528 * {@link javolution.context.AllocatorContext allocated} by the calling thread 529 * (possibly on the stack). 530 * 531 * @return an identical and independent copy of this vector. 532 */ 533 @Override 534 public abstract GeomVector<Q> copy(); 535 536 /** 537 * Compares this vector against the specified vector for approximate equality 538 * (coordinate values approximately equal to this one to within the numerical roundoff 539 * tolerance). The origin point is ignored. 540 * 541 * @param obj The vector object to compare with. 542 * @return <code>true</code> if this vector is approximately identical to that vector; 543 * <code>false</code> otherwise. 544 */ 545 public boolean isApproxEqual(GeomVector obj) { 546 if (this == obj) 547 return true; 548 if (obj == null) 549 return false; 550 551 int numDims = getPhyDimension(); 552 if (obj.getPhyDimension() != numDims) 553 return false; 554 555 for (int i = 0; i < numDims; ++i) { 556 Parameter thisi = get(i); 557 Parameter thati = obj.get(i); 558 if (!thisi.isApproxEqual(thati)) 559 return false; 560 } 561 562 return true; 563 } 564 565 /** 566 * Compares this vector against the specified vector for approximate equality 567 * (coordinate values approximately equal to this one to within the numerical roundoff 568 * tolerance). The origin point is ignored. 569 * 570 * @param obj The vector object to compare with. 571 * @param tol The amount use to define approximate equality. If <code>null</code> then 572 * exact numerical equality is required. 573 * @return <code>true</code> if this vector is approximately identical to that vector; 574 * <code>false</code> otherwise. 575 */ 576 public boolean isApproxEqual(GeomVector obj, Parameter<Q> tol) { 577 if (this == obj) 578 return true; 579 if (obj == null) 580 return false; 581 582 int numDims = getPhyDimension(); 583 if (obj.getPhyDimension() != numDims) 584 return false; 585 586 for (int i = 0; i < numDims; ++i) { 587 Parameter thisi = get(i); 588 Parameter thati = obj.get(i); 589 if (!thisi.isApproxEqual(thati, tol)) 590 return false; 591 } 592 593 return true; 594 } 595 596}