001/* 002* LineSeg -- A concrete line segment in nD space. 003* 004* Copyright (C) 2012-2015, 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 java.util.List; 026import java.util.Objects; 027import javax.measure.converter.ConversionException; 028import javax.measure.quantity.Dimensionless; 029import javax.measure.quantity.Length; 030import javax.measure.unit.Unit; 031import javolution.context.ObjectFactory; 032import javolution.lang.ValueType; 033import javolution.util.FastTable; 034import javolution.xml.XMLFormat; 035import javolution.xml.stream.XMLStreamException; 036 037 038/** 039* A concrete line segment in n-dimensional space. 040* 041* <p> Modified by: Joseph A. Huwaldt </p> 042* 043* @author Joseph A. Huwaldt Date: January 14, 2012 044* @version November 27, 2015 045*/ 046@SuppressWarnings({"serial", "CloneableImplementsClone"}) 047public final class LineSeg extends LineSegment implements ValueType { 048 049 050 /** 051 * The ends of the line segment. 052 */ 053 private Point _p0, _p1; 054 055 /** 056 * The length of the line segment. 057 */ 058 private Parameter<Length> _length; 059 060 /** 061 * Unit direction vector for the line segment. 062 */ 063 private Vector<Dimensionless> _u; 064 065 /** 066 * Dimensional vector for the line segment. 067 */ 068 private Vector<Length> _Ds; 069 070 071 072 /** 073 * Returns a <code>LineSeg</code> instance with the specified end points. 074 * The units of the curve will be the units of the start point. 075 * 076 * @param p0 The start (beginning) of the line segment. May not be null. 077 * @param p1 The end of the line segment. May not be null. 078 * @return A <code>LineSeg</code> instance that represents a line between the input points. 079 */ 080 public static LineSeg valueOf(Point p0, Point p1) { 081 082 // Check the dimensionality. 083 int numDims = Math.max(p0.getPhyDimension(), p1.getPhyDimension()); 084 085 LineSeg L = newInstance(); 086 L._p0 = p0.immutable().toDimension(numDims); 087 L._p1 = p1.immutable().to(p0.getUnit()).toDimension(numDims); 088 089 computeLineData(L); 090 091 return L; 092 } 093 094 /** 095 * Returns a <code>LineSeg</code> instance with the specified start point and 096 * direction/length vector. The units of the curve will be the units of the start 097 * point. 098 * 099 * @param p0 The start (beginning) of the line segment. May not be null. 100 * @param Ldir The vector defining the direction and length of the line segment. May 101 * not be null. 102 * @return A line segment from the input point in the direction and length of the 103 * input vector. 104 */ 105 public static LineSeg valueOf(Point p0, GeomVector<Length> Ldir) { 106 107 // Check the dimensionality. 108 int numDims = Math.max(p0.getPhyDimension(), Ldir.getPhyDimension()); 109 110 LineSeg L = newInstance(); 111 L._p0 = p0.immutable().toDimension(numDims); 112 L._p1 = L._p0.plus(Point.valueOf(Ldir)); 113 114 computeLineData(L); 115 116 return L; 117 } 118 119 /** 120 * Returns a <code>LineSeg</code> that represents a straight line between the end 121 * points of the input curve. WARNING: No check is made that the input curve is truly 122 * a straight line. The end points of the input curve are simply used to define a 123 * straight line segment. 124 * 125 * @param lineCrv A curve from which the end points will be used to define a line 126 * segment. May not be null. 127 * @return The line segment between the end points of the input curve. 128 * @see Curve#isLine 129 */ 130 public static LineSeg valueOf(Curve lineCrv) { 131 132 LineSeg L = newInstance(); 133 L._p0 = lineCrv.getRealPoint(0); 134 L._p1 = lineCrv.getRealPoint(1); 135 136 computeLineData(L); 137 138 return L; 139 } 140 141 /** 142 * Compute values defined by the end points of the line segment. 143 * It is assumed that the maximum dimension and units have 144 * been defined for p0 and p1 before calling this method. 145 */ 146 private static void computeLineData(LineSeg L) { 147 Point p0 = L._p0; 148 Point p1 = L._p1; 149 150 L._length = p0.distance(p1); 151 L._Ds = p1.minus(p0).toGeomVector(); 152 L._u = L._Ds.toUnitVector(); 153 } 154 155 /** 156 * Recycles a <code>LineSeg</code> instance immediately (on the stack when 157 * executing in a <code>StackContext</code>). 158 * 159 * @param instance The instance to recycle immediately. 160 */ 161 public static void recycle(LineSeg instance) { 162 FACTORY.recycle(instance); 163 } 164 165 166 /** 167 * Return the starting point of the line segment. 168 * 169 * @return The starting point for this line segment. 170 */ 171 @Override 172 public Point getStart() { 173 return _p0; 174 } 175 176 /** 177 * Return the end point of the line segment. 178 * 179 * @return The end point of this line segment. 180 */ 181 @Override 182 public Point getEnd() { 183 return _p1; 184 } 185 186 /** 187 * Get a unit direction vector for the line segment. 188 * 189 * @return A unit vector indicating the direction of this line segment. 190 */ 191 @Override 192 public Vector<Dimensionless> getUnitVector() { 193 return _u.copy(); 194 } 195 196 /** 197 * Return the dimensional derivative vector for this line segment. 198 * The length of this vector is the length of the line segment, 199 * the origin is at the start point and the end of the vector is the line end. 200 * 201 * @return The dimensional derivative vector for this line segment. 202 */ 203 @Override 204 public GeomVector<Length> getDerivativeVector() { 205 Vector<Length> D = _Ds.copy(); 206 D.setOrigin(_p0); 207 return D; 208 } 209 210 /** 211 * Calculate a point on the curve for the given parametric distance 212 * along the curve. 213 * 214 * @param s parametric distance to calculate a point for (0.0 to 1.0 inclusive). 215 * @return the calculated point 216 */ 217 @Override 218 public Point getRealPoint(double s) { 219 validateParameter(s); 220 221 if (parNearStart(s, TOL_S)) 222 return _p0; 223 else if (parNearEnd(s, TOL_S)) 224 return _p1; 225 226 Point p = _p0.plus(Point.valueOf(_Ds.times(s))); 227 228 return p; 229 } 230 231 /** 232 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with respect 233 * to parametric distance on the curve for the given parametric distance along the curve, 234 * <code>d^{grade}p(s)/d^{grade}s</code>. 235 * <p> 236 * Example:<br> 237 * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br> 238 * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc. 239 * </p> 240 * 241 * @param s Parametric distance to calculate derivatives for (0.0 to 1.0 inclusive). 242 * @param grade The maximum grade to calculate the derivatives for (1=1st derivative, 2=2nd derivative, etc) 243 * @return A list of derivatives up to the specified grade of the curve at the specified parametric position. 244 * @throws IllegalArgumentException if the grade is < 0. 245 */ 246 @Override 247 public List<Vector<Length>> getSDerivatives(double s, int grade) { 248 validateParameter(s); 249 if (grade < 0) 250 throw new IllegalArgumentException(RESOURCES.getString("gradeLTZeroErr")); 251 252 // Create a list to hold the output. 253 List<Vector<Length>> output = FastTable.newInstance(); 254 255 // Calculate the point. 256 Point p = getRealPoint(s); 257 Vector<Length> Pv = p.toGeomVector(); 258 output.add(Pv); 259 260 // Calculate each requested derivative. 261 int der = 1; 262 for (int i=1; i <= grade; ++i, ++der) { 263 264 if (i == 1) { 265 // Copy the derivative vector. 266 Vector<Length> Dv = _Ds.copy(); 267 Dv.setOrigin(p); 268 output.add(Dv); 269 270 } else { 271 // High derivatives are all zero. 272 Vector<Length> Dv = Vector.newInstance(getPhyDimension(), getUnit()); 273 Dv.setOrigin(p); 274 output.add(Dv); 275 } 276 } 277 278 return output; 279 } 280 281 /** 282 * Return the total arc length of this curve. 283 * 284 * @param eps The desired fractional accuracy on the arc-length. For this 285 * curve type, this parameter is ignored and the exact arc 286 * length is always returned. 287 * @return The total arc length of the curve. 288 */ 289 @Override 290 public Parameter<Length> getArcLength(double eps) { 291 return _length; 292 } 293 294 /** 295 * Return a new curve that is identical to this one, but with the 296 * parameterization reversed. 297 * 298 * @return A new curve that is identical to this one, but with the 299 * parameterization reversed. 300 */ 301 @Override 302 public LineSegment reverse() { 303 304 // Create the reversed curve. 305 LineSeg crv = LineSeg.valueOf(_p1, _p0); 306 307 return copyState(crv); // Copy over the super-class state for this object to the new one. 308 } 309 310 /** 311 * Return the coordinate point representing the 312 * minimum bounding box corner of this geometry element (e.g.: min X, min Y, min Z). 313 * 314 * @return The minimum bounding box coordinate for this geometry element. 315 */ 316 @Override 317 public Point getBoundsMin() { 318 return _p0.min(_p1); 319 } 320 321 /** 322 * Return the coordinate point representing the 323 * maximum bounding box corner (e.g.: max X, max Y, max Z). 324 * 325 * @return The maximum bounding box coordinate for this geometry element. 326 */ 327 @Override 328 public Point getBoundsMax() { 329 return _p0.max(_p1); 330 } 331 332 /** 333 * Return a copy of this curve converted to the specified number 334 * of physical dimensions. If the number of dimensions is greater than 335 * this element, then zeros are added to the additional dimensions. 336 * If the number of dimensions is less than this element, then 337 * the extra dimensions are simply dropped (truncated). If 338 * the new dimensions are the same as the dimension of this element, 339 * then this element is simply returned. 340 * 341 * @param newDim The dimension of the curve to return. 342 * @return This curve converted to the new dimensions. 343 */ 344 @Override 345 public LineSeg toDimension(int newDim) { 346 if (getPhyDimension() == newDim) return this; 347 348 LineSeg crv = LineSeg.valueOf(_p0.toDimension(newDim), _p1.toDimension(newDim)); 349 350 return copyState(crv); // Copy over the super-class state for this object to the new one. 351 } 352 353 /** 354 * Returns the equivalent to this element but stated in the 355 * specified unit. 356 * 357 * @param unit the length unit of the element to be returned. May not be null. 358 * @return an equivalent to this element but stated in the specified unit. 359 * @throws ConversionException if the the input unit is not a length unit. 360 */ 361 @Override 362 public LineSeg to(Unit<Length> unit) throws ConversionException { 363 if (unit.equals(getUnit())) 364 return this; 365 LineSeg crv = LineSeg.valueOf(_p0.to(unit), _p1.to(unit)); 366 return copyState(crv); // Copy over the super-class state for this object to the new one. 367 } 368 369 /** 370 * Returns a copy of this LineSeg instance 371 * {@link javolution.context.AllocatorContext allocated} 372 * by the calling thread (possibly on the stack). 373 * 374 * @return an identical and independent copy of this line segment. 375 */ 376 @Override 377 public LineSeg copy() { 378 return copyOf(this); 379 } 380 381 /** 382 * Return a copy of this object with any transformations or subranges 383 * removed (applied). 384 * 385 * @return A copy of this object with any transformations or subranges 386 * removed (applied). 387 */ 388 @Override 389 public LineSeg copyToReal() { 390 return copy(); 391 } 392 393 /** 394 * Compares this LineSeg against the specified object for strict 395 * equality. 396 * 397 * @param obj the object to compare with. 398 * @return <code>true</code> if this LineSeg is identical to that 399 * LineSeg; <code>false</code> otherwise. 400 */ 401 @Override 402 public boolean equals(Object obj) { 403 if (this == obj) 404 return true; 405 if ((obj == null) || (obj.getClass() != this.getClass())) 406 return false; 407 408 LineSeg that = (LineSeg)obj; 409 return this._p0.equals(that._p0) 410 && this._p1.equals(that._p1) 411 && super.equals(obj); 412 } 413 414 /** 415 * Returns the hash code for this parameter. 416 * 417 * @return the hash code value. 418 */ 419 @Override 420 public int hashCode() { 421 return 31*super.hashCode() + Objects.hash(_p0, _p1); 422 } 423 424 /** 425 * Holds the default XML representation for this object. 426 */ 427 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 428 protected static final XMLFormat<LineSeg> XML = new XMLFormat<LineSeg>(LineSeg.class) { 429 430 @Override 431 public LineSeg newInstance(Class<LineSeg> cls, InputElement xml) throws XMLStreamException { 432 return FACTORY.object(); 433 } 434 435 @Override 436 public void read(InputElement xml, LineSeg obj) throws XMLStreamException { 437 LineSegment.XML.read(xml, obj); // Call parent read. 438 439 obj._p0 = xml.getNext(); 440 obj._p1 = xml.getNext(); 441 442 computeLineData(obj); 443 } 444 445 @Override 446 public void write(LineSeg obj, OutputElement xml) throws XMLStreamException { 447 LineSegment.XML.write(obj, xml); // Call parent write. 448 449 xml.add(obj._p0); 450 xml.add(obj._p1); 451 452 } 453 }; 454 455 456 /////////////////////// 457 // Factory creation. // 458 /////////////////////// 459 460 private LineSeg() {} 461 462 private static final ObjectFactory<LineSeg> FACTORY = new ObjectFactory<LineSeg>() { 463 @Override 464 protected LineSeg create() { 465 return new LineSeg(); 466 } 467 @Override 468 protected void cleanup(LineSeg obj) { 469 obj.reset(); 470 obj._p0 = null; 471 obj._p1 = null; 472 } 473 }; 474 475 private static LineSeg copyOf(LineSeg original) { 476 LineSeg o = FACTORY.object(); 477 o._p0 = original._p0.copy(); 478 o._p1 = original._p1.copy(); 479 computeLineData(o); 480 original.copyState(o); 481 return o; 482 } 483 484 private static LineSeg newInstance() { 485 return FACTORY.object(); 486 } 487 488} 489 490