001/* 002 * GeomPoint -- Holds the floating point coordinates of a point in nD space. 003 * 004 * Copyright (C) 2002-2018, 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.1 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 * Lesser 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 jahuwaldt.js.param.Parameter; 025import jahuwaldt.js.param.ParameterVector; 026import jahuwaldt.js.param.Vector3D; 027import jahuwaldt.util.XYPoint; 028import java.text.MessageFormat; 029import static java.util.Objects.isNull; 030import static java.util.Objects.nonNull; 031import static java.util.Objects.requireNonNull; 032import javax.measure.quantity.Area; 033import javax.measure.quantity.Dimensionless; 034import javax.measure.quantity.Length; 035import javax.measure.unit.Unit; 036import javolution.context.StackContext; 037import javolution.lang.MathLib; 038import javolution.text.Text; 039import javolution.text.TextBuilder; 040import javolution.util.FastTable; 041import org.jscience.mathematics.number.Float64; 042import org.jscience.mathematics.vector.Float64Vector; 043 044/** 045 * A container that holds the coordinates of a point in n-dimensional space. 046 * 047 * <p> * Modified by: Joseph A. Huwaldt </p> 048 * 049 * @author Joseph A. Huwaldt, Date: December 11, 1999 050 * @version February 5, 2018 051 */ 052@SuppressWarnings({"serial", "CloneableImplementsClone"}) 053public abstract class GeomPoint extends AbstractGeomElement<GeomPoint> 054 implements PointGeometry<GeomPoint>, Transformable<GeomPoint>, XYPoint { 055 056 /** 057 * Constant used to identify the X or 1st coordinate. 058 */ 059 public static final int X = 0; 060 061 /** 062 * Constant used to identify the Y or 2nd coordinate. 063 */ 064 public static final int Y = 1; 065 066 /** 067 * Constant used to identify the Z or 3rd coordinate. 068 */ 069 public static final int Z = 2; 070 071 /** 072 * Constant used to identify the W or 4th coordinate. 073 */ 074 public static final int W = 2; 075 076 /** 077 * Return an immutable version of this point. 078 * 079 * @return an immutable version of this point. 080 */ 081 public abstract Point immutable(); 082 083 /** 084 * Returns the number of child-elements that make up this geometry element. This 085 * implementation always returns 0 as a GeomPoint is not made up of any other 086 * elements. 087 */ 088 @Override 089 public int size() { 090 return 0; 091 } 092 093 /** 094 * Returns the number of parametric dimensions of the geometry element. This 095 * implementation always returns 0 as a GeomPoint is not parametric. 096 */ 097 @Override 098 public int getParDimension() { 099 return 0; 100 } 101 102 /** 103 * Returns the value of a Parameter from this point. 104 * 105 * @param i the dimension index. 106 * @return the value of the parameter at <code>i</code>. 107 * @throws IndexOutOfBoundsException 108 * <code>(i < 0) || (i ≥ getPhyDimension())</code> 109 */ 110 public Parameter<Length> get(int i) { 111 return Parameter.valueOf(getValue(i), getUnit()); 112 } 113 114 /** 115 * Returns the value of a coordinate in this point as a <code>double</code>, stated in 116 * this point's {@link #getUnit unit}. 117 * 118 * @param i the dimension index. 119 * @return the value of the Parameter at <code>i</code>. 120 * @throws IndexOutOfBoundsException 121 * <code>(i < 0) || (i ≥ getPhyDimension())</code> 122 */ 123 public abstract double getValue(int i); 124 125 /** 126 * Returns the value of a coordinate in this point as a <code>double</code>, stated in 127 * the specified unit. 128 * 129 * @param i the dimension index. 130 * @param unit the unit to return the value in. May not be null. 131 * @return the value of the Parameter at <code>i</code> in the specified unit. 132 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ dimension())</code> 133 */ 134 public abstract double getValue(int i, Unit<Length> unit); 135 136 /** 137 * Return the X-coordinate of this point as a <code>double</code>, stated in 138 * this point's {@link #getUnit unit}. 139 * 140 * @return the value of the X-coordinate in the current units. 141 */ 142 @Override 143 public double getX() { 144 return this.getValue(X); 145 } 146 147 /** 148 * Return the Y-coordinate of this point as a <code>double</code>, stated in 149 * this point's {@link #getUnit unit}. 150 * 151 * @return the value of the Y-coordinate in the current units. 152 */ 153 @Override 154 public double getY() { 155 return this.getValue(Y); 156 } 157 158 /** 159 * Return the Z-coordinate of this point as a <code>double</code>, stated in 160 * this point's {@link #getUnit unit}. 161 * 162 * @return the value of the Z-coordinate in the current units. 163 */ 164 public double getZ() { 165 return this.getValue(Z); 166 } 167 168 /** 169 * Returns the square of the Euclidean norm, magnitude, or length value of the vector 170 * from the origin to this point (the dot product of the origin-to-this-point vector 171 * and itself). This is slightly faster than calling <code>normValue</code> if the 172 * squared value is all that is needed. 173 * 174 * @return <code>this.normSq().getValue()</code>. 175 * @see #normValue() 176 */ 177 public abstract double normSqValue(); 178 179 /** 180 * Returns the square of the Euclidean norm, magnitude, or length of the vector from 181 * the origin to this point (the dot product of the origin-to-this-point vector and 182 * itself). This is slightly faster than calling <code>normValue</code> if the 183 * squared value is all that is needed. 184 * 185 * @return <code>this · this</code>. 186 * @see #norm() 187 */ 188 public Parameter<Area> normSq() { 189 return Parameter.valueOf(normSqValue(), getUnit().pow(2)).asType(Area.class); 190 } 191 192 /** 193 * Returns the {@link #norm}, magnitude, or length value of the vector from the origin 194 * to this point (square root of the dot product of the origin-to-this-point vector 195 * and itself). 196 * 197 * @return <code>this.norm().doubleValue()</code>. 198 * @see #normSqValue() 199 */ 200 public double normValue() { 201 return MathLib.sqrt(normSqValue()); 202 } 203 204 /** 205 * Returns the Euclidian norm, magnitude, or length of the vector from the origin to 206 * this point (square root of the dot product of the origin-to-this-point vector and 207 * itself). 208 * 209 * @return <code>sqrt(this · this)</code>. 210 * @see #normValue() 211 */ 212 public Parameter<Length> norm() { 213 return Parameter.valueOf(normValue(), getUnit()); 214 } 215 216 /** 217 * Returns the sum of this point with the one specified. The unit of the output point 218 * will be the units of this point. 219 * 220 * @param that the point to be added. May not be null. 221 * @return <code>this + that</code>. 222 * @throws DimensionException if point dimensions are different. 223 */ 224 public abstract Point plus(GeomPoint that); 225 226 /** 227 * Adds the specified parameter to each component of this point. The unit of the 228 * output point will be the units of this point. 229 * 230 * @param that the parameter to be added to each component of this point. May not be 231 * null. 232 * @return <code>this + that</code>. 233 */ 234 public abstract Point plus(Parameter<Length> that); 235 236 /** 237 * Returns the difference between this point and the one specified. The unit of the 238 * output point will be the units of this point. 239 * 240 * @param that the point to be subtracted from this point. May not be null. 241 * @return <code>this - that</code>. 242 * @throws DimensionException if point dimensions are different. 243 */ 244 public abstract Point minus(GeomPoint that); 245 246 /** 247 * Subtracts the specified parameter from each component of this point. The unit of 248 * the output point will be the units of this point. 249 * 250 * @param that the parameter to be subtracted from each component of this point. May 251 * not be null. 252 * @return <code>this - that</code>. 253 */ 254 public abstract Point minus(Parameter<Length> that); 255 256 /** 257 * Returns the negation of this point (all the values of each dimension negated). 258 * 259 * @return <code>-this</code> 260 */ 261 public abstract Point opposite(); 262 263 /** 264 * Returns the product of this point with the specified coefficient. 265 * 266 * @param k the coefficient multiplier. 267 * @return <code>this · k</code> 268 */ 269 public abstract Point times(double k); 270 271 /** 272 * Returns the product of this point with the specified dimensionless Parameter. 273 * 274 * @param k the dimensionless Parameter multiplier. May not be null. 275 * @return <code>this · k</code> 276 */ 277 public Point times(Parameter<Dimensionless> k) { 278 return times(k.getValue(Dimensionless.UNIT)); 279 } 280 281 /** 282 * Returns this point with each element divided by the specified divisor. 283 * 284 * @param divisor the divisor. 285 * @return <code>this / divisor</code>. 286 */ 287 public Point divide(double divisor) { 288 return times(1.0 / divisor); 289 } 290 291 /** 292 * Returns this point with each element divided by the specified dimensionless 293 * Parameter. 294 * 295 * @param divisor the dimensionless Parameter divisor. May not be null. 296 * @return <code>this / divisor</code>. 297 */ 298 public Point divide(Parameter<Dimensionless> divisor) { 299 return divide(divisor.getValue(Dimensionless.UNIT)); 300 } 301 302 /** 303 * Return the square of the Euclidian distance between this point and the one 304 * specified. This is slightly faster than calling <code>distance</code> if the 305 * squared value is all that you need. 306 * 307 * @param that The point to determine the distance squared from this point to. May not 308 * be null. 309 * @return the distance squared from this point to the one specified. 310 * @see #distance(geomss.geom.GeomPoint) 311 */ 312 public Parameter<Area> distanceSq(GeomPoint that) { 313 return Parameter.valueOf(distanceSqValue(that), getUnit().pow(2)).asType(Area.class); 314 } 315 316 /** 317 * Return the Euclidian distance squared between this point and the one specified as a 318 * <code>double</code>. This is slightly faster than calling <code>distance</code> if 319 * the squared value is all that you need. 320 * 321 * @param that The point to determine the distance squared from this point to. May not 322 * be null. 323 * @return the distance squared from this point to the one specified. 324 * @see #distanceValue(geomss.geom.GeomPoint) 325 */ 326 public double distanceSqValue(GeomPoint that) { 327 StackContext.enter(); 328 try { 329 GeomPoint dp = this.minus(requireNonNull(that)); 330 return dp.normSqValue(); 331 } finally { 332 StackContext.exit(); 333 } 334 } 335 336 /** 337 * Return the Euclidian distance between this point and the one specified. 338 * 339 * @param that The point to determine the distance from this point to. May not be null. 340 * @return the distance from this point to the one specified. 341 * @see #distanceSq(geomss.geom.GeomPoint) 342 */ 343 public Parameter<Length> distance(GeomPoint that) { 344 return Parameter.valueOf(distanceValue(that), getUnit()); 345 } 346 347 /** 348 * Return the Euclidian distance between this point and the one specified as a 349 * <code>double</code>. 350 * 351 * @param that The point to determine the distance from this point to. May not be null. 352 * @return the distance from this point to the one specified. 353 * @see #distanceSqValue(geomss.geom.GeomPoint) 354 */ 355 public double distanceValue(GeomPoint that) { 356 return MathLib.sqrt(distanceSqValue(that)); 357 } 358 359 /** 360 * Returns a point consisting of the minimum value in each dimension between this 361 * point and the input point. This is used to find points that bound a geometry. 362 * 363 * @param that The point being compared with this one for minimum values in each 364 * dimension. May not be null. 365 * @return A point consisting of the minimum value in each dimension between this 366 * point and the input point. 367 */ 368 public Point min(GeomPoint that) { 369 requireNonNull(that); 370 StackContext.enter(); 371 try { 372 Unit<Length> unit = getUnit(); 373 374 FastTable<Float64> valueList = FastTable.newInstance(); 375 int numDims = getPhyDimension(); 376 for (int i = 0; i < numDims; ++i) { 377 double value = MathLib.min(this.getValue(i), that.getValue(i, unit)); 378 valueList.add(Float64.valueOf(value)); 379 } 380 Float64Vector V = Float64Vector.valueOf(valueList); 381 382 Point P = Point.valueOf(V, unit); 383 return StackContext.outerCopy(P); 384 385 } finally { 386 StackContext.exit(); 387 } 388 } 389 390 /** 391 * Returns a point consisting of the maximum value in each dimension between this 392 * point and the input point. This is used to find points that bound a geometry. 393 * 394 * @param that The point being compared with this one for maximum values in each 395 * dimension. May not be null. 396 * @return A point consisting of the maximum value in each dimension between this 397 * point and the input point. 398 */ 399 public Point max(GeomPoint that) { 400 requireNonNull(that); 401 StackContext.enter(); 402 try { 403 Unit<Length> unit = getUnit(); 404 405 FastTable<Float64> valueList = FastTable.newInstance(); 406 int numDims = getPhyDimension(); 407 for (int i = 0; i < numDims; ++i) { 408 double value = MathLib.max(this.getValue(i), that.getValue(i, unit)); 409 valueList.add(Float64.valueOf(value)); 410 } 411 Float64Vector V = Float64Vector.valueOf(valueList); 412 413 Point P = Point.valueOf(V, getUnit()); 414 return StackContext.outerCopy(P); 415 416 } finally { 417 StackContext.exit(); 418 } 419 } 420 421 /** 422 * Return the coordinate point representing the minimum bounding box corner (e.g.: min 423 * X, min Y, min Z). 424 * 425 * @return The minimum bounding box coordinate for this geometry element. 426 */ 427 @Override 428 public Point getBoundsMin() { 429 return this.immutable(); 430 } 431 432 /** 433 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 434 * X, max Y, max Z). 435 * 436 * @return The maximum bounding box coordinate for this geometry element. 437 */ 438 @Override 439 public Point getBoundsMax() { 440 return this.immutable(); 441 } 442 443 /** 444 * Returns the most extreme point, either minimum or maximum, in the specified 445 * coordinate direction on this geometry element. This implementation always returns 446 * this point's coordinate. 447 * 448 * @param dim An index indicating the dimension to find the min/max point for (0=X, 449 * 1=Y, 2=Z, etc). 450 * @param max Set to <code>true</code> to return the maximum value, <code>false</code> 451 * to return the minimum. 452 * @param tol Fractional tolerance to refine the min/max point position to if 453 * necessary. 454 * @return The point found on this element that is the min or max in the specified 455 * coordinate direction. 456 * @see #getBoundsMin 457 * @see #getBoundsMax 458 */ 459 @Override 460 public GeomPoint getLimitPoint(int dim, boolean max, double tol) { 461 return this; 462 } 463 464 /** 465 * Return the total number of points in this geometry element. This implementation 466 * always returns 1. 467 */ 468 @Override 469 public int getNumberOfPoints() { 470 return 1; 471 } 472 473 /** 474 * Return <code>true</code> if this point contains valid and finite numerical 475 * components. A value of <code>false</code> will be returned if any of the coordinate 476 * values are NaN or Inf. 477 */ 478 @Override 479 public boolean isValid() { 480 int numDims = getPhyDimension(); 481 for (int i = 0; i < numDims; ++i) { 482 double value = getValue(i); 483 if (Double.isInfinite(value) || Double.isNaN(value)) 484 return false; 485 } 486 return true; 487 } 488 489 /** 490 * Return a copy of this object with any transformations or subranges removed 491 * (applied). 492 */ 493 @Override 494 public abstract Point copyToReal(); 495 496 /** 497 * Returns a copy of this {@link GeomPoint} instance 498 * {@link javolution.context.AllocatorContext allocated} by the calling thread 499 * (possibly on the stack). 500 * 501 * @return an identical and independent copy of this point. 502 */ 503 @Override 504 public abstract GeomPoint copy(); 505 506 /** 507 * Returns a Vector3D representation of this point if possible. 508 * 509 * @return A Vector3D that is equivalent to this point 510 * @throws DimensionException if this point has any number of dimensions other than 3. 511 */ 512 public Vector3D<Length> toVector3D() { 513 if (getPhyDimension() != 3) 514 throw new DimensionException( 515 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "point", getPhyDimension())); 516 return Vector3D.valueOf(this.toFloat64Vector(), this.getUnit()); 517 } 518 519 /** 520 * Returns a ParameterVector representation of this point. 521 * 522 * @return A ParameterVector that is equivalent to this point 523 */ 524 public ParameterVector<Length> toParameterVector() { 525 return ParameterVector.valueOf(this.toFloat64Vector(), this.getUnit()); 526 } 527 528 /** 529 * Returns a <code>GeomVector</code> representation of this point. 530 * 531 * @return A GeomVector that is equivalent to this point 532 */ 533 public Vector<Length> toGeomVector() { 534 return Vector.valueOf(this); 535 } 536 537 /** 538 * Returns the values stored in this point, stated in the current 539 * {@link #getUnit units}, as a Float64Vector. 540 * 541 * @return A Float64Vector containing the values stored in this point in the current 542 * units. 543 */ 544 public abstract Float64Vector toFloat64Vector(); 545 546 /** 547 * Returns the values stored in this point as a Java array, stated in the 548 * current {@link #getUnit units}. 549 * 550 * @return A new array with the point values copied into it. 551 */ 552 public double[] toArray() { 553 return toArray(new double[this.getPhyDimension()]); 554 } 555 556 /** 557 * Returns the values stored in this point, stated in the current 558 * {@link #getUnit units} stored in the input Java array. 559 * 560 * @param array An existing array that has at least as many elements as the physical 561 * dimension of this point. 562 * @return A reference to the input array after the point values have been copied over 563 * to it. 564 * @see #getPhyDimension() 565 */ 566 public double[] toArray(double[] array) { 567 int numDims = this.getPhyDimension(); 568 for (int i = 0; i < numDims; ++i) 569 array[i] = this.getValue(i); 570 return array; 571 } 572 573 /** 574 * Returns transformed version of this element. The returned object implements 575 * {@link GeomTransform} and contains this element as a child. 576 * 577 * @param transform The transformation to apply to this geometry. May not be null. 578 * @return A new triangle that is identical to this one with the specified 579 * transformation applied. 580 * @throws DimensionException if this point is not 3D. 581 */ 582 @Override 583 public GeomPointTrans getTransformed(GTransform transform) { 584 return GeomPointTrans.newInstance(this, requireNonNull(transform)); 585 } 586 587 /** 588 * Returns the text representation of this geometry element that consists of the name 589 * followed by the coordinate values. For example: 590 * <pre> 591 * {aPoint = {10 ft, -3 ft, 4.56 ft}} 592 * </pre> 593 * If there is no name, then the output looks like this: 594 * <pre> 595 * {10 ft, -3 ft, 4.56 ft} 596 * </pre> 597 * 598 * @return the text representation of this geometry element. 599 */ 600 @Override 601 public Text toText() { 602 final int dimension = this.getPhyDimension(); 603 TextBuilder tmp = TextBuilder.newInstance(); 604 tmp.append('{'); 605 String nameStr = getName(); 606 boolean hasName = nonNull(nameStr); 607 if (hasName) { 608 tmp.append(nameStr); 609 tmp.append(" = {"); 610 } 611 for (int i = 0; i < dimension; i++) { 612 tmp.append(get(i)); 613 if (i != dimension - 1) { 614 tmp.append(", "); 615 } 616 } 617 if (hasName) 618 tmp.append('}'); 619 tmp.append('}'); 620 Text txt = tmp.toText(); 621 TextBuilder.recycle(tmp); 622 return txt; 623 } 624 625 /** 626 * Compares this point against the specified point for approximate equality 627 * (coordinate values approximately equal to this one to within the numerical roundoff 628 * tolerance). 629 * 630 * @param obj The GeomPoint object to compare with. 631 * @return <code>true</code> if this point is approximately identical to that point; 632 * <code>false</code> otherwise. 633 */ 634 public boolean isApproxEqual(GeomPoint obj) { 635 if (this == obj) 636 return true; 637 if (obj == null) 638 return false; 639 640 int numDims = getPhyDimension(); 641 if (obj.getPhyDimension() != numDims) 642 return false; 643 644 for (int i = 0; i < numDims; ++i) { 645 Parameter<Length> thisi = get(i); 646 Parameter<Length> thati = obj.get(i); 647 if (!thisi.isApproxEqual(thati)) 648 return false; 649 } 650 651 return true; 652 } 653 654 /** 655 * Compares this point against the specified point for approximate equality 656 * (coordinate values approximately equal to this one to within the numerical roundoff 657 * tolerance). 658 * 659 * @param obj The GeomPoint object to compare with. 660 * @param tol The amount use to define approximate equality. If <code>null</code> is 661 * passed, exact numerical equality will be required. 662 * @return <code>true</code> if this point is approximately identical to that point; 663 * <code>false</code> otherwise. 664 */ 665 public boolean isApproxEqual(GeomPoint obj, Parameter<Length> tol) { 666 if (this == obj) 667 return true; 668 if (isNull(obj)) 669 return false; 670 671 int numDims = getPhyDimension(); 672 if (obj.getPhyDimension() != numDims) 673 return false; 674 675 for (int i = 0; i < numDims; ++i) { 676 Parameter<Length> thisi = get(i); 677 Parameter<Length> thati = obj.get(i); 678 if (!thisi.isApproxEqual(thati, tol)) 679 return false; 680 } 681 682 return true; 683 } 684 685}