001/** 002 * MutablePoint -- Holds the changeable floating point coordinates of a point in nD space. 003 * 004 * Copyright (C) 2013-2025, 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.Coordinate3D; 021import jahuwaldt.js.param.Parameter; 022import java.text.MessageFormat; 023import java.util.List; 024import java.util.Objects; 025import static java.util.Objects.requireNonNull; 026import javax.measure.converter.ConversionException; 027import javax.measure.quantity.Length; 028import javax.measure.quantity.Quantity; 029import javax.measure.unit.SI; 030import javax.measure.unit.Unit; 031import javolution.context.ObjectFactory; 032import javolution.util.FastTable; 033import javolution.xml.XMLFormat; 034import javolution.xml.stream.XMLStreamException; 035import org.jscience.mathematics.number.Float64; 036import org.jscience.mathematics.vector.Float64Vector; 037 038/** 039 * A mutable container that holds changeable coordinates of a point in n-dimensional 040 * space. 041 * 042 * <p> Modified by: Joseph A. Huwaldt </p> 043 * 044 * @author Joseph A. Huwaldt, Date: December 11, 1999 045 * @version February 17, 2025 046 */ 047@SuppressWarnings({"serial", "CloneableImplementsClone"}) 048public final class MutablePoint extends GeomPoint { 049 050 // Use an immutable Point as a backing for the MutablePoint. 051 private Point _point; 052 053 /** 054 * Returns a {@link MutablePoint} instance of the specified dimension with zero meters 055 * for each coordinate value. 056 * 057 * @param dim the physical dimension of the point to create. 058 * @return the point having the specified dimension and zero meters for values. 059 */ 060 public static MutablePoint newInstance(int dim) { 061 MutablePoint V = FACTORY.object(); 062 V._point = Point.newInstance(dim); 063 return V; 064 } 065 066 /** 067 * Returns a {@link Point} instance of the specified dimension and units with zero for 068 * each coordinate value. 069 * 070 * @param dim the physical dimension of the point to create. 071 * @param unit The unit for the point to create. May not be null. 072 * @return the point having the specified dimension & units and zero for values. 073 */ 074 public static MutablePoint newInstance(int dim, Unit<Length> unit) { 075 MutablePoint V = FACTORY.object(); 076 V._point = Point.newInstance(dim, requireNonNull(unit)); 077 return V; 078 } 079 080 /** 081 * Returns a {@link MutablePoint} instance holding the specified <code>double</code> 082 * value or values stated in meters. 083 * 084 * @param x the coordinate values stated in meters. May not be null. 085 * @return the point having the specified value. 086 */ 087 public static MutablePoint valueOf(double... x) { 088 return valueOf(SI.METER, x); 089 } 090 091 /** 092 * Returns a 1D {@link MutablePoint} instance holding the specified 093 * <code>double</code> values stated in the specified units. 094 * 095 * @param x the x value stated in the specified unit. 096 * @param unit the unit in which the coordinates are stated. May not be null. 097 * @return the point having the specified value. 098 */ 099 public static MutablePoint valueOf(double x, Unit<Length> unit) { 100 MutablePoint V = FACTORY.object(); 101 V._point = Point.valueOf(x, requireNonNull(unit)); 102 return V; 103 } 104 105 /** 106 * Returns a 2D {@link MutablePoint} instance holding the specified 107 * <code>double</code> value stated in the specified units. 108 * 109 * @param x the x value stated in the specified unit. 110 * @param y the y value stated in the specified unit. 111 * @param unit the unit in which the coordinates are stated. May not be null. 112 * @return the point having the specified values. 113 */ 114 public static MutablePoint valueOf(double x, double y, Unit<Length> unit) { 115 MutablePoint V = FACTORY.object(); 116 V._point = Point.valueOf(x, y, requireNonNull(unit)); 117 return V; 118 } 119 120 /** 121 * Returns a 3D {@link MutablePoint} instance holding the specified 122 * <code>double</code> values stated in the specified units. 123 * 124 * @param x the x value stated in the specified unit. 125 * @param y the y value stated in the specified unit. 126 * @param z the z value stated in the specified unit. 127 * @param unit the unit in which the coordinates are stated. May not be null. 128 * @return the point having the specified values. 129 */ 130 public static MutablePoint valueOf(double x, double y, double z, Unit<Length> unit) { 131 MutablePoint V = FACTORY.object(); 132 V._point = Point.valueOf(x, y, z, requireNonNull(unit)); 133 return V; 134 } 135 136 /** 137 * Returns a {@link MutablePoint} instance holding the specified <code>double</code> 138 * values stated in the specified units. 139 * 140 * @param unit the length unit in which the coordinates are stated. May not be null. 141 * @param values the list of values stated in the specified unit. May not be null. 142 * @return the point having the specified values. 143 */ 144 public static MutablePoint valueOf(Unit<Length> unit, double... values) { 145 MutablePoint V = FACTORY.object(); 146 V._point = Point.valueOf(requireNonNull(unit), requireNonNull(values)); 147 return V; 148 } 149 150 /** 151 * Returns a {@link MutablePoint} instance holding the specified 152 * <code>Parameter</code> values. All the values are converted to the same units as 153 * the 1st value. 154 * 155 * @param values The list of values to be stored. May not be null. 156 * @return the point having the specified values in the units of the 1st value. 157 */ 158 public static MutablePoint valueOf(Parameter<Length>... values) { 159 MutablePoint V = FACTORY.object(); 160 V._point = Point.valueOf(requireNonNull(values)); 161 return V; 162 } 163 164 /** 165 * Returns a {@link MutablePoint} instance holding the specified 166 * <code>Parameter</code> values. All the values are converted to the same units as 167 * the 1st value. 168 * 169 * @param values The list of values to be stored. May not be null. 170 * @return the point having the specified values in the units of the 1st value. 171 */ 172 public static MutablePoint valueOf(List<Parameter<Length>> values) { 173 MutablePoint V = FACTORY.object(); 174 V._point = Point.valueOf(requireNonNull(values)); 175 return V; 176 } 177 178 /** 179 * Returns a {@link MutablePoint} instance holding the specified 180 * <code>@link jahuwaldt.js.param.Coordinate3D Coordinate3D</code> values. 181 * 182 * @param coord The {@link jahuwaldt.js.param.Coordinate3D Coordinate3D} to be stored. 183 * May not be null. 184 * @return the point having the specified values. 185 */ 186 public static MutablePoint valueOf(Coordinate3D<Length> coord) { 187 MutablePoint P = FACTORY.object(); 188 P._point = Point.valueOf(requireNonNull(coord)); 189 return P; 190 } 191 192 /** 193 * Returns a {@link MutablePoint} instance containing the specified vector of Float64 194 * values stated in the specified units. 195 * 196 * @param vector the vector of Float64 values stated in the specified unit. May not be null. 197 * @param unit the unit in which the values are stated. May not be null. 198 * @return the point having the specified values. 199 */ 200 public static MutablePoint valueOf(org.jscience.mathematics.vector.Vector<Float64> vector, Unit<Length> unit) { 201 MutablePoint V = FACTORY.object(); 202 V._point = Point.valueOf(requireNonNull(vector), requireNonNull(unit)); 203 return V; 204 } 205 206 /** 207 * Returns a {@link MutablePoint} instance containing the specified vector of 208 * Parameter values with length units. All the values are converted to the same units 209 * as the 1st value. 210 * 211 * @param <Q> The Quantity (unit type) of this point. 212 * @param vector the vector of Parameter values stated in length or dimensionless 213 * units. If in dimensionless units, the values are interpreted as being 214 * in meters. May not be null. 215 * @return the point having the specified values. 216 * @throws ConversionException if the input vector is not in length (or Dimensionless) 217 * units. 218 */ 219 public static <Q extends Quantity> MutablePoint valueOf(org.jscience.mathematics.vector.Vector<Parameter<Q>> vector) { 220 MutablePoint P = FACTORY.object(); 221 P._point = Point.valueOf(requireNonNull(vector)); 222 return P; 223 } 224 225 /** 226 * Returns a {@link MutablePoint} instance that represents the direction elements of 227 * the specified GeomVector given in length or dimensionless units. If the user wishes 228 * for the point to represent the end or tip of the vehicle they should use the 229 * following: 230 * <code>MutablePoint p = MutablePoint.valueOf(vector).plus(vector.getOrigin());</code> 231 * 232 * @param vector the GeomVector stated in length or dimensionless units. If in 233 * dimensionless units, the values are interpreted as being in meters. 234 * May not be null. 235 * @return the point having the specified values of the vector end point or tip. 236 * @throws ConversionException if the input vector is not in length (or Dimensionless) 237 * units. 238 */ 239 public static MutablePoint valueOf(GeomVector<?> vector) { 240 MutablePoint P = FACTORY.object(); 241 P._point = Point.valueOf(requireNonNull(vector)); 242 return P; 243 } 244 245 /** 246 * Returns a {@link MutablePoint} instance containing the specified GeomPoint's data. 247 * 248 * @param point the GeomPoint to be copied into a new Point. May not be null. 249 * @return the point having the specified values. 250 */ 251 public static MutablePoint valueOf(GeomPoint point) { 252 MutablePoint P = FACTORY.object(); 253 P._point = point.immutable(); 254 return P; 255 } 256 257 /** 258 * Recycles a <code>MutablePoint</code> instance immediately (on the stack when 259 * executing in a <code>StackContext</code>). 260 * 261 * @param instance The instance to be recycled. 262 */ 263 public static void recycle(MutablePoint instance) { 264 FACTORY.recycle(instance); 265 } 266 267 /** 268 * Returns the number of physical dimensions of the geometry element. 269 * 270 * @return The number of physical dimensions of the geometry element. 271 */ 272 @Override 273 public int getPhyDimension() { 274 return _point.getPhyDimension(); 275 } 276 277 /** 278 * Set the value of elements of this point to the elements of the specified point. If 279 * the input GeomPoint is of type "Point", then the data stored in this MutablePoint 280 * is set to the data stored in "p" without creating any new object instances (copy by 281 * value). 282 * 283 * @param p The new point to make this point equal to. May not be null. 284 * @throws DimensionException <code>(p.getPhyDimension() != getPhyDimension())</code> 285 */ 286 public void set(GeomPoint p) { 287 if (p.getPhyDimension() != getPhyDimension()) 288 throw new DimensionException(RESOURCES.getString("consistantPointDim")); 289 _point = p.immutable(); 290 fireChangeEvent(); // Notify change listeners. 291 } 292 293 /** 294 * Set the value of elements of this point to the elements of the specified vector of 295 * Parameter objects. The origin of the vector is ignored. 296 * 297 * @param vector The new vector to make this point equal to in length or dimensionless 298 * units. If in dimensionless units, the values are interpreted as being 299 * in the same units as this point. May not be null. 300 * @throws DimensionException <code>(vector.getDimension() != getPhyDimension())</code> 301 * @throws ConversionException if the input vector is not in length (or Dimensionless) 302 * units. 303 */ 304 public void set(GeomVector<?> vector) { 305 if (vector.getPhyDimension() != getPhyDimension()) 306 throw new DimensionException(RESOURCES.getString("consistantPointDim")); 307 _point = Point.valueOf(vector); 308 } 309 310 /** 311 * Set the value of a point dimension to the specified Parameter. 312 * 313 * @param i the dimension index. 314 * @param value The new value of the parameter to set at <code>i</code>. May not be 315 * null. 316 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 317 */ 318 public void set(int i, Parameter<Length> value) { 319 requireNonNull(value); 320 321 // Get a list of values in this point. 322 FastTable<Parameter<Length>> values = FastTable.newInstance(); 323 int size = _point.getPhyDimension(); 324 for (int j = 0; j < size; ++j) { 325 values.add(_point.get(j)); 326 } 327 328 // Change the dimension indicated. 329 values.set(i, value); 330 _point = Point.valueOf(values); 331 FastTable.recycle(values); 332 333 fireChangeEvent(); // Notify change listeners. 334 } 335 336 /** 337 * Set the value of a point dimension to the specified double in the current point 338 * units. 339 * 340 * @param i the dimension index. 341 * @param value The new value of the parameter to set at <code>i</code> in the current 342 * units of this point. 343 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 344 */ 345 public void setValue(int i, double value) { 346 // Create a Parameter object. 347 Parameter<Length> param = Parameter.valueOf(value, getUnit()); 348 349 // Set the specified dimension to the new parameter object. 350 set(i, param); 351 } 352 353 /** 354 * Returns the value of a coordinate in this point as a <code>double</code>, stated in 355 * this point's {@link #getUnit unit}. 356 * 357 * @param i the dimension index. 358 * @return the value of the Parameter at <code>i</code>. 359 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 360 */ 361 @Override 362 public double getValue(int i) { 363 return _point.getValue(i); 364 } 365 366 /** 367 * Returns the value of a coordinate in this point as a <code>double</code>, stated in 368 * the specified unit. 369 * 370 * @param i the dimension index. 371 * @param unit the unit to return the value in. May not be null. 372 * @return the value of the Parameter at <code>i</code> in the specified unit. 373 * @throws IndexOutOfBoundsException <code>(i < 0) || (i ≥ getPhyDimension())</code> 374 */ 375 @Override 376 public double getValue(int i, Unit<Length> unit) { 377 return _point.getValue(i, unit); 378 } 379 380 /** 381 * Returns the square of the Euclidean norm, magnitude, or length value of the vector 382 * from the origin to this point (the dot product of the origin-to-this-point vector 383 * and itself). This is slightly faster than calling <code>normValue</code> if the 384 * squared value is all that is needed. 385 * 386 * @return <code>this.normSq().getValue()</code>. 387 * @see #normValue() 388 */ 389 @Override 390 public double normSqValue() { 391 return _point.normSqValue(); 392 } 393 394 /** 395 * Returns the sum of this point with the one specified. The unit of the output point 396 * will be the units of this point. 397 * 398 * @param that the point to be added. May not be null. 399 * @return <code>this + that</code>. 400 * @throws DimensionException if point dimensions are different. 401 */ 402 @Override 403 public Point plus(GeomPoint that) { 404 return _point.plus(that); 405 } 406 407 /** 408 * Adds the specified parameter to each component of this point. The unit of the 409 * output point will be the units of this point. 410 * 411 * @param that the parameter to be added to each component of this point. May not be 412 * null. 413 * @return <code>this + that</code>. 414 */ 415 @Override 416 public Point plus(Parameter<Length> that) { 417 return _point.plus(that); 418 } 419 420 /** 421 * Returns the difference between this point and the one specified. The unit of the 422 * output point will be the units of this point. 423 * 424 * @param that the point to be subtracted from this point. May not be null. 425 * @return <code>this - that</code>. 426 * @throws DimensionException if point dimensions are different. 427 */ 428 @Override 429 public Point minus(GeomPoint that) { 430 return _point.minus(that); 431 } 432 433 /** 434 * Subtracts the specified parameter from each component of this point. The unit of 435 * the output point will be the units of this point. 436 * 437 * @param that the parameter to be subtracted from each component of this point. May 438 * not be null. 439 * @return <code>this - that</code>. 440 */ 441 @Override 442 public Point minus(Parameter<Length> that) { 443 return _point.minus(that); 444 } 445 446 /** 447 * Returns the negation of this point (all the values of each dimension negated). 448 * 449 * @return <code>-this</code> 450 */ 451 @Override 452 public Point opposite() { 453 return _point.opposite(); 454 } 455 456 /** 457 * Returns the product of this point with the specified coefficient. 458 * 459 * @param k the coefficient multiplier. 460 * @return <code>this ยท k</code> 461 */ 462 @Override 463 public Point times(double k) { 464 return _point.times(k); 465 } 466 467 /** 468 * Returns a copy of this MutablePoint instance 469 * {@link javolution.context.AllocatorContext allocated} by the calling thread 470 * (possibly on the stack). 471 * 472 * @return an identical and independent copy of this point. 473 */ 474 @Override 475 public MutablePoint copy() { 476 return copyOf(this); 477 } 478 479 /** 480 * Return a copy of this object with any transformations or subranges removed 481 * (applied). 482 * 483 * @return A copy of this object with any transformations or subranges removed. 484 */ 485 @Override 486 public Point copyToReal() { 487 return Point.valueOf(_point); 488 } 489 490 /** 491 * Returns the unit in which the {@link #getValue values} in this point are stated in. 492 * 493 * @return The unit in which the {@link #getValue values} in this point are stated in. 494 */ 495 @Override 496 public final Unit<Length> getUnit() { 497 return _point.getUnit(); 498 } 499 500 /** 501 * Return an immutable version of this point. 502 * 503 * @return An immutable version of this point. 504 */ 505 @Override 506 public Point immutable() { 507 return _point; 508 } 509 510 /** 511 * Returns the equivalent to this point but stated in the specified unit. 512 * 513 * @param unit the length unit of the point to be returned. May not be null. 514 * @return an equivalent of this point but stated in the specified unit. 515 * @throws ConversionException if the the input unit is not a length unit. 516 */ 517 @Override 518 public MutablePoint to(Unit<Length> unit) throws ConversionException { 519 if (unit.equals(getUnit())) 520 return this; 521 MutablePoint P = FACTORY.object(); 522 P._point = _point.to(unit); 523 return P; 524 } 525 526 /** 527 * Return the equivalent of this point converted to the specified number of physical 528 * dimensions. If the number of dimensions is greater than this element, then zeros 529 * are added to the additional dimensions. If the number of dimensions is less than 530 * this element, then the extra dimensions are simply dropped (truncated). If the new 531 * dimensions are the same as the dimension of this element, then this element is 532 * simply returned. 533 * 534 * @param newDim The dimension of the point to return. 535 * @return A copy of this point converted to the new dimensions. 536 */ 537 @Override 538 public MutablePoint toDimension(int newDim) { 539 if (newDim < 1) 540 throw new IllegalArgumentException(RESOURCES.getString("pointDimGT0")); 541 int thisDim = this.getPhyDimension(); 542 if (newDim == thisDim) 543 return this; 544 MutablePoint P = FACTORY.object(); 545 P._point = _point.toDimension(newDim); 546 return P; 547 } 548 549 /** 550 * Returns the values stored in this point, stated in this point's 551 * {@link #getUnit unit}, as a Float64Vector. 552 * 553 * @return A Float64Vector containing the coordinate values for this point. 554 */ 555 @Override 556 public final Float64Vector toFloat64Vector() { 557 return _point.toFloat64Vector(); 558 } 559 560 /** 561 * Compares this Point against the specified object for strict equality (same values 562 * and same units). 563 * 564 * @param obj the object to compare with. 565 * @return <code>true</code> if this point is identical to that point; 566 * <code>false</code> otherwise. 567 */ 568 @Override 569 public boolean equals(Object obj) { 570 if (this == obj) 571 return true; 572 if ((obj == null) || (obj.getClass() != this.getClass())) 573 return false; 574 575 MutablePoint that = (MutablePoint)obj; 576 return this._point.equals(that._point) 577 && super.equals(obj); 578 } 579 580 /** 581 * Returns the hash code for this parameter. 582 * 583 * @return the hash code value. 584 */ 585 @Override 586 public int hashCode() { 587 return 31*super.hashCode() + Objects.hash(_point); 588 } 589 590 /** 591 * Holds the default XML representation for this object. 592 */ 593 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 594 protected static final XMLFormat<MutablePoint> XML = new XMLFormat<MutablePoint>(MutablePoint.class) { 595 596 @Override 597 public MutablePoint newInstance(Class<MutablePoint> cls, InputElement xml) throws XMLStreamException { 598 return FACTORY.object(); 599 } 600 601 @Override 602 public void read(InputElement xml, MutablePoint obj) throws XMLStreamException { 603 Unit unit = Unit.valueOf(xml.getAttribute("unit")); 604 if (!Length.UNIT.isCompatible(unit)) 605 throw new XMLStreamException( 606 MessageFormat.format(RESOURCES.getString("incompatibleUnits"), 607 "MutablePoint", "length")); 608 609 GeomPoint.XML.read(xml, obj); // Call parent read. 610 611 FastTable<Float64> valueList = FastTable.newInstance(); 612 while (xml.hasNext()) { 613 Float64 value = xml.getNext(); 614 valueList.add(value); 615 } 616 obj._point = Point.valueOf(Float64Vector.valueOf(valueList), unit); 617 618 } 619 620 @Override 621 public void write(MutablePoint obj, OutputElement xml) throws XMLStreamException { 622 xml.setAttribute("unit", obj.getUnit().toString()); 623 624 GeomPoint.XML.write(obj, xml); // Call parent write. 625 626 int size = obj._point.getPhyDimension(); 627 for (int i = 0; i < size; ++i) 628 xml.add(Float64.valueOf(obj._point.getValue(i))); 629 630 } 631 }; 632 633 /////////////////////// 634 // Factory creation. // 635 /////////////////////// 636 private MutablePoint() { } 637 638 @SuppressWarnings("unchecked") 639 private static final ObjectFactory<MutablePoint> FACTORY = new ObjectFactory<MutablePoint>() { 640 @Override 641 protected MutablePoint create() { 642 return new MutablePoint(); 643 } 644 645 @Override 646 protected void cleanup(MutablePoint obj) { 647 obj.reset(); 648 } 649 }; 650 651 @SuppressWarnings("unchecked") 652 private static MutablePoint copyOf(MutablePoint original) { 653 MutablePoint P = FACTORY.object(); 654 P._point = original._point.copy(); 655 original.copyState(P); 656 return P; 657 } 658 659}