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