001/** 002 * LinearComboCurve -- Represents a linear combination of two or more curves. 003 * 004 * Copyright (C) 2015-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 geomss.geom.nurbs.BasicNurbsCurve; 021import geomss.geom.nurbs.CurveFactory; 022import geomss.geom.nurbs.NurbsCurve; 023import jahuwaldt.js.param.Parameter; 024import java.text.MessageFormat; 025import java.util.*; 026import static java.util.Objects.nonNull; 027import static java.util.Objects.requireNonNull; 028import javax.measure.converter.ConversionException; 029import javax.measure.quantity.Length; 030import javax.measure.unit.Unit; 031import javax.swing.event.ChangeListener; 032import javolution.context.ObjectFactory; 033import javolution.context.StackContext; 034import javolution.lang.Immutable; 035import javolution.text.Text; 036import javolution.text.TextBuilder; 037import javolution.util.FastTable; 038import javolution.xml.XMLFormat; 039import javolution.xml.stream.XMLStreamException; 040 041/** 042 * Represents a linear combination made up of a list of {@link Curve} objects. A linear 043 * combination curve is formed by a weighted linear addition of a list of one or more 044 * curves. For example: <code>B = W1*Crv_1 + ... + Wn*Crv_n</code> where W1 through Wn are 045 * scalar weights. The linear addition is done independently of physical dimension (each 046 * dimension is added separately) and the weighted addition is done in parameter space: 047 * <code> B = W1*Crv_1(s) + ... + Wn*Crv_n(s)</code>. 048 *<p> 049 * Any number of curves may be added to a LinearComboCurve, but they all must have the 050 * same physical dimensions. 051 * </p> 052 * <p> Modified by: Joseph A. Huwaldt </p> 053 * 054 * @author Joseph A. Huwaldt, Date: September 8, 2015 055 * @version March 23, 2025 056 */ 057@SuppressWarnings({"serial", "CloneableImplementsClone"}) 058public class LinearComboCurve extends AbstractCurve<LinearComboCurve> implements LinearCombination<LinearComboCurve,Curve> { 059 060 // The list of curves being combined. 061 private List<Curve> _crvs; 062 063 // The list of weights applied to each curve. 064 private List<Double> _wts; 065 066 /** 067 * Reference to a change listener for this object's child curves. 068 */ 069 private final ChangeListener _childChangeListener = new ForwardingChangeListener(this); 070 071 072 /** 073 * Returns a new, empty, preallocated or recycled <code>LinearComboCurve</code> 074 * instance (on the stack when executing in a <code>StackContext</code>), that can 075 * store a list of {@link Curve} objects and associated weighting factors. The list is 076 * initially empty and therefore the linear combination is initially undefined. 077 * 078 * @return A new empty LinearComboCurve. 079 */ 080 public static LinearComboCurve newInstance() { 081 LinearComboCurve list = FACTORY.object(); 082 list._crvs = FastTable.newInstance(); 083 list._wts = FastTable.newInstance(); 084 return list; 085 } 086 087 /** 088 * Returns a new, empty, preallocated or recycled <code>LinearComboCurve</code> 089 * instance (on the stack when executing in a <code>StackContext</code>), that can 090 * store a list of {@link Curve} objects and associated weighting factors. The list is 091 * initially empty and therefore the linear combination is initially undefined. 092 * 093 * @param name The name to be assigned to this LinearComboCurve (may be null). 094 * @return A new empty LinearComboCurve. 095 */ 096 public static LinearComboCurve newInstance(String name) { 097 LinearComboCurve list = LinearComboCurve.newInstance(); 098 list.setName(name); 099 return list; 100 } 101 102 /** 103 * Return a LinearComboCurve made up of the {@link Curve} and weight objects in the 104 * specified collections. 105 * 106 * @param curves A collection of curves that define the LinearComboCurve. May not be 107 * null. 108 * @param weights A collection of weights (one for each curve). May not be null and 109 * must have the same size as "curves". 110 * @return A new LinearComboCurve made up of the curves and weights in the specified 111 * collections. 112 */ 113 public static LinearComboCurve valueOf(Collection<? extends Curve> curves, Collection<Double> weights) { 114 115 LinearComboCurve list = LinearComboCurve.newInstance(); 116 list.addAll(requireNonNull(curves), requireNonNull(weights)); 117 118 return list; 119 } 120 121 /** 122 * Return a LinearComboCurve made up of the {@link Curve} and weight objects in the 123 * specified collections. 124 * 125 * @param curves A collection of curves that define the LinearComboCurve. May not be 126 * null. 127 * @param weights An array of weights (one for each curve). May not be null and 128 * must have the same size as "curves". 129 * @return A new LinearComboCurve made up of the curves and weights in the specified 130 * collections. 131 */ 132 public static LinearComboCurve valueOf(Collection<? extends Curve> curves, double... weights) { 133 134 LinearComboCurve list = LinearComboCurve.newInstance(); 135 list.addAll(requireNonNull(curves), requireNonNull(weights)); 136 137 return list; 138 } 139 140 /** 141 * Return a LinearComboCurve made up of the {@link Curve} objects in the specified 142 * array each with a default weight of 1.0. 143 * 144 * @param curves An array of curves that define the LinearComboCurve. May not be null. 145 * @return A new LinearComboCurve made up of the curves in the specified 146 * array each with a default weight of 1.0. 147 */ 148 public static LinearComboCurve valueOf(Curve... curves) { 149 150 LinearComboCurve list = LinearComboCurve.newInstance(); 151 list.addAll(Arrays.asList(requireNonNull(curves))); 152 153 return list; 154 } 155 156 /** 157 * Validate that the curve is properly formed, otherwise throw an exception. 158 */ 159 private void validateCurve() { 160 if (size() < 1) 161 throw new IllegalArgumentException(MessageFormat.format( 162 RESOURCES.getString("incDefiningCrvCount"), "LoftedSurface", 2, size())); 163 } 164 165 /** 166 * Return the input index normalized into the range 0 ≤ index < size(). This 167 * allows negative indexing (-1 referring to the last element in the list), but does 168 * not allow wrap-around positive indexing. 169 */ 170 private int normalizeIndex(int index) { 171 int size = size(); 172 while (index < 0) 173 index += size; 174 return index; 175 } 176 177 /** 178 * Returns an unmodifiable list view of the curves in this list. Attempts to modify 179 * the returned collection result in an UnsupportedOperationException being thrown. 180 * 181 * @return the unmodifiable view over this list of curves. 182 */ 183 @Override 184 public List<Curve> unmodifiableList() { 185 return ((FastTable)_crvs).unmodifiable(); 186 } 187 188 /** 189 * Returns an unmodifiable list view of the weights in this list. Attempts to modify 190 * the returned collection result in an UnsupportedOperationException being thrown. 191 * 192 * @return the unmodifiable view over this list of weights. 193 */ 194 @Override 195 public List<Double> unmodifiableWeightList() { 196 return ((FastTable)_wts).unmodifiable(); 197 } 198 199 /** 200 * Returns <code>true</code> if this collection contains no elements. 201 */ 202 @Override 203 public boolean isEmpty() { 204 return _crvs.isEmpty(); 205 } 206 207 /** 208 * Returns <code>true</code> if this list actually contains any curves and 209 * <code>false</code> if this list is empty. 210 * 211 * @return true if this list actually contains geometry. 212 */ 213 @Override 214 public boolean containsGeometry() { 215 return !isEmpty(); 216 } 217 218 /** 219 * Returns the number of {@link Curve} objects that make up this linear combination 220 * curve. If the surface contains more than Integer.MAX_VALUE elements, returns 221 * Integer.MAX_VALUE. 222 * 223 * @return the number of elements in this list of curves. 224 */ 225 @Override 226 public int size() { 227 return _crvs.size(); 228 } 229 230 /** 231 * Returns the Curve at the specified position in this LinearComboCurve. 232 * 233 * @param index index of curve to return (0 returns the 1st element, -1 returns the 234 * last, -2 returns the 2nd from last, etc). 235 * @return the Curve at the specified position in this LinearComboCurve. 236 * @throws IndexOutOfBoundsException if the given index is out of range: 237 * <code>index > size()</code> 238 */ 239 @Override 240 public Curve get(int index) { 241 index = normalizeIndex(index); 242 return _crvs.get(index); 243 } 244 245 /** 246 * Returns the linear combination weight at the specified position in this 247 * LinearComboCurve. 248 * 249 * @param index index of weight to return (0 returns the 1st element, -1 returns the 250 * last, -2 returns the 2nd from last, etc). 251 * @return the linear combination weight at the specified position in this 252 * LinearComboCurve. 253 * @throws IndexOutOfBoundsException if the given index is out of range: 254 * <code>index > size()</code> 255 */ 256 @Override 257 public Double getWeight(int index) { 258 index = normalizeIndex(index); 259 return _wts.get(index); 260 } 261 262 /** 263 * Returns the first curve from this LinearComboCurve. 264 * 265 * @return the first curve in this list. 266 */ 267 @Override 268 public Curve getFirst() { 269 return get(0); 270 } 271 272 /** 273 * Returns the first linear combination weight from this LinearComboCurve. 274 * 275 * @return the first weight in this list. 276 */ 277 @Override 278 public Double getFirstWeight() { 279 return getWeight(0); 280 } 281 282 /** 283 * Returns the last curve from this LinearComboCurve list of curves. 284 * 285 * @return the last curve in this list. 286 */ 287 @Override 288 public Curve getLast() { 289 return get(size() - 1); 290 } 291 292 /** 293 * Returns the last linear combination weight from this LinearComboCurve. 294 * 295 * @return the last weight in this list. 296 */ 297 @Override 298 public Double getLastWeight() { 299 return getWeight(size() - 1); 300 } 301 302 /** 303 * Returns the range of Curves in this LinearComboCurve from the specified start and ending 304 * indexes as a new LinearComboCurve. 305 * 306 * @param first index of the first element to return (0 returns the 1st element, -1 307 * returns the last, etc). 308 * @param last index of the last element to return (0 returns the 1st element, -1 309 * returns the last, etc). 310 * @return A LinearComboCurve made up of the curves in the given range from this LinearComboCurve. 311 * @throws IndexOutOfBoundsException if the given index is out of range: 312 * <code>index ≥ size()</code> 313 */ 314 @Override 315 public LinearComboCurve getRange(int first, int last) { 316 first = normalizeIndex(first); 317 last = normalizeIndex(last); 318 319 LinearComboCurve list = LinearComboCurve.newInstance(); 320 for (int i = first; i <= last; ++i) 321 list.add(get(i),getWeight(i)); 322 323 return list; 324 } 325 326 /** 327 * Returns the range of linear combination weights in this LinearComboCurve from the 328 * specified start and ending indexes as a List of double values. 329 * 330 * @param first index of the first element to return (0 returns the 1st element, -1 331 * returns the last, etc). 332 * @param last index of the last element to return (0 returns the 1st element, -1 333 * returns the last, etc). 334 * @return A List made up of the weights in the given range from this 335 * LinearComboCurve. 336 * @throws IndexOutOfBoundsException if the given index is out of range: 337 * <code>index ≥ size()</code> 338 */ 339 @Override 340 public List<Double> getWeightRange(int first, int last) { 341 first = normalizeIndex(first); 342 last = normalizeIndex(last); 343 344 FastTable<Double> list = FastTable.newInstance(); 345 for (int i = first; i <= last; ++i) 346 list.add(getWeight(i)); 347 348 return list; 349 } 350 351 /** 352 * Returns the {@link Curve} with the specified name from this list. 353 * 354 * @param name The name of the curve we are looking for in the list. 355 * @return The curve matching the specified name. If the specified element name 356 * isn't found in the list, then <code>null</code> is returned. 357 */ 358 @Override 359 public Curve get(String name) { 360 361 Curve element = null; 362 int index = getIndexFromName(name); 363 if (index >= 0) 364 element = this.get(index); 365 366 return element; 367 } 368 369 /** 370 * Returns a view of the portion of this LinearComboCurve between fromIndex, 371 * inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the 372 * returned LinearComboCurve is empty.) The returned LinearComboCurve is backed by 373 * this LinearComboCurve, so changes in the returned LinearComboCurve are reflected in 374 * this LinearComboCurve, and vice-versa. 375 * 376 * This method eliminates the need for explicit range operations (of the sort that 377 * commonly exist for arrays). Any operation that expects a list can be used as a 378 * range operation by passing a subList view instead of a whole list. For example, the 379 * following idiom removes a range of values from a list: <code> 380 * list.subList(from, to).clear();</code> Similar idioms may be constructed for 381 * <code>indexOf</code> and <code>lastIndexOf</code>, and all of the algorithms in the 382 * <code>Collections</code> class can be applied to a subList. 383 * 384 * The semantics of the list returned by this method become undefined if the backing 385 * list (i.e., this list) is <i>structurally modified</i> in any way other than via 386 * the returned list (structural modifications are those that change the size of this 387 * list, or otherwise perturb it in such a fashion that iterations in progress may 388 * yield incorrect results). 389 * 390 * @param fromIndex low endpoint (inclusive) of the subList. 391 * @param toIndex high endpoint (exclusive) of the subList. 392 * @return a view of the specified range within this LinearComboCurve. 393 * @throws IndexOutOfBoundsException if the given index is out of range: 394 * <code>index > size()</code> 395 */ 396 @Override 397 public LinearComboCurve subList(int fromIndex, int toIndex) { 398 fromIndex = normalizeIndex(fromIndex); 399 toIndex = normalizeIndex(toIndex); 400 401 LinearComboCurve crv = FACTORY.object(); 402 crv._crvs = _crvs.subList(fromIndex, toIndex); 403 crv._wts = _wts.subList(fromIndex, toIndex); 404 405 return crv; 406 } 407 408 /** 409 * Returns an new {@link GeomList} with all the curves in this list. 410 * 411 * @return A new GeomList with all the curves in this list. 412 */ 413 @Override 414 public GeomList<Curve> getAll() { 415 GeomList<Curve> list = GeomList.newInstance(); 416 list.addAll(_crvs); 417 return list; 418 } 419 420 /** 421 * Return a new curve that is identical to this one, but with the 422 * parameterization reversed. 423 * 424 * @return A new curve that is identical to this one, but with the 425 * parameterization reversed. 426 */ 427 @Override 428 public LinearComboCurve reverse() { 429 LinearComboCurve list = LinearComboCurve.newInstance(); 430 copyState(list); 431 int size = this.size(); 432 for (int i=0; i < size; ++i) { 433 list.add(_crvs.get(i).reverse(), _wts.get(i)); 434 } 435 return list; 436 } 437 438 /** 439 * Returns the index in this list of the first occurrence of the specified 440 * {@link Curve}, or -1 if the list does not contain this curve. 441 * 442 * @param element The Curve to search for. 443 * @return the index in this List of the first occurrence of the specified curve, or 444 * -1 if the List does not contain this curve. 445 */ 446 @Override 447 public int indexOf(Object element) { 448 return _crvs.indexOf(element); 449 } 450 451 /** 452 * Returns the index in this list of the last occurrence of the specified 453 * {@link Curve}, or -1 if the list does not contain this curve. More formally, 454 * returns the highest index i such that (o==null ? get(i)==null : o.equals(get(i))), 455 * or -1 if there is no such index. 456 * 457 * @param element The Curve to search for. 458 * @return the index in this list of the last occurrence of the specified curve, or -1 459 * if the list does not contain this curve. 460 */ 461 @Override 462 public int lastIndexOf(Object element) { 463 return _crvs.lastIndexOf(element); 464 } 465 466 /** 467 * Returns true if this LinearComboCurve contains the specified {@link Curve}. More formally, 468 * returns true if and only if this LinearComboCurve's curves contains at least one element e such 469 * that (o==null ? e==null : o.equals(e)). 470 * 471 * @param o object to be checked for containment in this collection. 472 * @return <code>true</code> if this collection contains the specified element. 473 */ 474 @Override 475 public boolean contains(Object o) { 476 return _crvs.contains(o); 477 } 478 479 /** 480 * Returns true if this collection contains all of the {@link Curve} objects in the specified 481 * collection. 482 * 483 * @param c collection of curves to be checked for containment in this collection. 484 * @return <code>true</code> if this collection contains all of the curves in the 485 * specified collection. 486 */ 487 @Override 488 public boolean containsAll(Collection<?> c) { 489 return _crvs.containsAll(c); 490 } 491 492 /** 493 * Return the index to the 1st Curve in this list with the specified name. 494 * 495 * @param name The name of the Curve to find in this list 496 * @return The index to the named Curve or -1 if it is not found. 497 */ 498 @Override 499 public int getIndexFromName(String name) { 500 if (name == null) 501 return -1; 502 503 int result = -1; 504 int size = this.size(); 505 for (int i = 0; i < size; ++i) { 506 GeomElement element = this.get(i); 507 String eName = element.getName(); 508 if (name.equals(eName)) { 509 result = i; 510 break; 511 } 512 } 513 514 return result; 515 } 516 517 /** 518 * Inserts the specified {@link Curve} at the specified position in this list with a 519 * default weight of 1.0. Shifts the element currently at that position (if any) and 520 * any subsequent elements to the right (adds one to their indices). Null values are 521 * ignored. The input value must have the same physical dimensions as the other items 522 * in this list, or an exception is thrown. 523 * <p> 524 * Note: If this method is used concurrent access must be synchronized (the table is 525 * not thread-safe). 526 * </p> 527 * 528 * @param index the index at which the specified element is to be inserted. (0 returns 529 * the 1st element, -1 returns the last, -2 returns the 2nd from last, 530 * etc). 531 * @param value the Curve to be inserted with a default weight of 1.0. 532 * @throws IndexOutOfBoundsException if <code>index > size()</code> 533 * @throws DimensionException if the input value dimensions are different from this 534 * list's dimensions. 535 */ 536 @Override 537 public void add(int index, Curve value) { 538 add(index, value, 1.0); 539 } 540 541 /** 542 * Inserts the specified {@link Curve} at the specified position in this list. Shifts 543 * the element currently at that position (if any) and any subsequent elements to the 544 * right (adds one to their indices). Null values are ignored. The input curve must 545 * have the same physical dimensions as the other items in this list, or an exception 546 * is thrown. 547 * <p> 548 * Note: If this method is used concurrent access must be synchronized (the table is 549 * not thread-safe). 550 * </p> 551 * 552 * @param index The index at which the specified element is to be inserted. (0 553 * returns the 1st element, -1 returns the last, -2 returns the 2nd from 554 * last, etc). 555 * @param curve The Curve to be inserted. May not be null. 556 * @param weight The linear combination weight of the curve to be inserted. May not be 557 * null. 558 * @throws IndexOutOfBoundsException if <code>index > size()</code> 559 * @throws DimensionException if the input curve dimensions are different from this 560 * list's dimensions. 561 */ 562 @Override 563 public void add(int index, Curve curve, Double weight) { 564 requireNonNull(weight); 565 if (size() > 0 && curve.getPhyDimension() != getPhyDimension()) 566 throw new DimensionException(MessageFormat.format( 567 RESOURCES.getString("incCrvDimension"), "curve", getPhyDimension())); 568 index = normalizeIndex(index); 569 570 _wts.add(index, weight); 571 _crvs.add(index, curve); 572 if (!(curve instanceof Immutable)) 573 curve.addChangeListener(_childChangeListener); 574 575 fireChangeEvent(); // Notify change listeners. 576 } 577 578 /** 579 * Appends the specified {@link Curve} to the end of this LinearComboCurve with a 580 * default weight of 1.0. Null values are ignored. The input curve must have the same 581 * physical dimensions as the other items in this list, or an exception is thrown. 582 * <p> 583 * Note: If this method is used concurrent access must be synchronized (the table is 584 * not thread-safe). 585 * </p> 586 * 587 * @param curve The curve to be appended to this list with a default weight of 1.0. 588 * May not be null. 589 * @return true if this list changed as a result of this call. 590 * @throws DimensionException if the input element's dimensions are different from 591 * this list's dimensions. 592 * @throws DimensionException if the input curve dimensions are different from this 593 * list's dimensions. 594 */ 595 @Override 596 public boolean add(Curve curve) { 597 return add(curve, 1.0); 598 } 599 600 /** 601 * Appends the specified {@link Curve} to the end of this list of curves. Null values 602 * are ignored. The input curve must have the same physical dimensions as the other 603 * items in this list, or an exception is thrown. 604 * <p> 605 * Note: If this method is used concurrent access must be synchronized (the table is 606 * not thread-safe). 607 * </p> 608 * 609 * @param curve The curve to be appended to this list. May not be null. 610 * @param weight The linear combination weight of the curve to be appended. May not be 611 * null. 612 * @return true if this list changed as a result of this call. 613 * @throws DimensionException if the input element's dimensions are different from 614 * this list's dimensions. 615 * @throws DimensionException if the input curve dimensions are different from this 616 * list's dimensions. 617 */ 618 @Override 619 public boolean add(Curve curve, Double weight) { 620 add(size(), curve, weight); 621 return true; 622 } 623 624 /** 625 * Appends all of the elements in the specified list of arguments to this 626 * LinearComboCurve with a default weight of 1.0 assigned to each. The input curve 627 * must have the same physical dimensions as the other items in this list, or an 628 * exception is thrown. 629 * 630 * @param array the curves to be inserted into this collection with a default weight 631 * of 1.0 assigned to each. May not be null. 632 * @return <code>true</code> if this collection changed as a result of the call. 633 * @throws DimensionException if the input curve dimensions are different from this 634 * list's dimensions. 635 */ 636 @Override 637 public boolean add(Curve... array) { 638 return add(size(), array); 639 } 640 641 /** 642 * Inserts all of the {@link Curve} objects in the specified list of arguments into 643 * this LinearComboCurve at the specified position with a default weight of 1.0 644 * assigned to each. Shifts the element currently at that position (if any) and any 645 * subsequent elements to the right (increases their indices). The new elements will 646 * appear in this list in the order that they are appeared in the array. The input 647 * curve must have the same physical dimensions as the other items in this list, or an 648 * exception is thrown. 649 * 650 * @param index index at which to insert first element from the specified array. 651 * @param array the curves to be inserted into this collection with a default weight 652 * of 1.0 for each. May not be null. 653 * @return <code>true</code> if this collection changed as a result of the call. 654 * @throws DimensionException if the input curve dimensions are different from this 655 * list's dimensions. 656 */ 657 @Override 658 public boolean add(int index, Curve... array) { 659 return addAll(index, Arrays.asList(requireNonNull(array))); 660 } 661 662 /** 663 * Adds all of the curves in the specified collection to this LinearComboCurve with a 664 * default weight of 1.0 for each. The behavior of this operation is undefined if the 665 * specified collection is modified while the operation is in progress. This implies 666 * that the behavior of this call is undefined if the specified collection is this 667 * collection, and this collection is nonempty. The input curves must have the same 668 * physical dimensions as the other items in this list, or an exception is thrown. 669 * 670 * @param curves curves to be inserted into this list with a default of 1.0 for each. 671 * May not be null. 672 * @return <code>true</code> if this LinearComboCurve changed as a result of the call 673 * @throws DimensionException if the input curve dimensions are different from this 674 * list's dimensions. 675 */ 676 @Override 677 public boolean addAll(Collection<? extends Curve> curves) { 678 return addAll(size(), curves); 679 } 680 681 /** 682 * Inserts all of the curves in the specified collection of curves into this 683 * LinearComboCurve with default weights of 1.0 for each. Shifts the curve currently 684 * at that position (if any) and any subsequent curves to the right (increases their 685 * indices). The new curves will appear in this list in the order that they are 686 * returned by the specified collection's iterator. The behavior of this operation is 687 * unspecified if the specified collection is modified while the operation is in 688 * progress. Note that this will occur if the specified collection is this list, and 689 * it's nonempty. The input curves must have the same physical dimensions as the other 690 * items in this list, or an exception is thrown. 691 * 692 * @param index index at which to insert first curve from the specified collection. 693 * @param curves curves to be inserted into this list with default weights of 1.0 for 694 * each. May not be null. 695 * @return <code>true</code> if this LinearComboCurve changed as a result of the call 696 * @throws DimensionException if the input curve dimensions are different from this 697 * list's dimensions. 698 */ 699 @Override 700 public boolean addAll(int index, Collection<? extends Curve> curves) { 701 FastTable<Double> weights = FastTable.newInstance(); 702 Double ONE = 1.0; 703 704 int size = curves.size(); 705 for (int i=size-1; i >= 0; --i) 706 weights.add(ONE); 707 708 boolean output = addAll(index, curves, weights); 709 710 FastTable.recycle(weights); 711 return output; 712 } 713 714 /** 715 * Appends all of the curves in the specified collection to this LinearComboCurve. The 716 * behavior of this operation is undefined if the specified collection is modified 717 * while the operation is in progress. This implies that the behavior of this call is 718 * undefined if the specified collection is this collection, and this collection is 719 * nonempty. The input curves must have the same physical dimensions as the other 720 * items in this list, or an exception is thrown. 721 * 722 * @param curves the curves to be appended onto this list of curves. May not be null. 723 * @param weights the linear combination weights associated with all of the curves 724 * being appended. May not be null and must be the same size as 725 * "curves". 726 * @return <code>true</code> if this LinearComboCurve changed as a result of the call 727 * @throws DimensionException if the input curve dimensions are different from this 728 * list's dimensions. 729 */ 730 @Override 731 public boolean addAll(Collection<? extends Curve> curves, Collection<Double> weights) { 732 return addAll(size(), curves, weights); 733 } 734 735 /** 736 * Inserts all of the curves in the specified collection and their associated weights 737 * into this LinearComboCurve at the specified position. Shifts the curve currently at 738 * that position (if any) and any subsequent curves to the right (increases their 739 * indices). The new curves will appear in this list in the order that they are 740 * returned by the specified collection's iterator. The behavior of this operation is 741 * unspecified if the specified collection is modified while the operation is in 742 * progress. Note that this will occur if the specified collection is this list, and 743 * it's nonempty. The input curves must have the same physical dimensions as the other items 744 * in this list, or an exception is thrown. 745 * 746 * @param index index at which to insert first curve from the specified collection. 747 * @param curves the curves to be inserted into this linear combination. May not be null. 748 * @param weights the linear combination weights associated with each curve being 749 * inserted. May not be null and must be the same size as "curves". 750 * @return <code>true</code> if this LinearComboCurve changed as a result of the call 751 * @throws DimensionException if the input curve dimensions are different from this 752 * list's dimensions. 753 */ 754 @Override 755 public boolean addAll(int index, Collection<? extends Curve> curves, Collection<Double> weights) { 756 if (curves.size() != weights.size()) 757 throw new IllegalArgumentException(MessageFormat.format( 758 RESOURCES.getString("lstSameSizeErr"), "curves list", "weights list")); 759 if (size() > 0) { 760 int dim = getPhyDimension(); 761 for (Curve crv : curves) 762 if (crv.getPhyDimension() != dim) 763 throw new DimensionException(MessageFormat.format( 764 RESOURCES.getString("incCrvDimension"), "curves", getPhyDimension())); 765 } 766 for (Double wt : weights) 767 requireNonNull(wt); 768 769 index = normalizeIndex(index); 770 boolean changed = _crvs.addAll(index, curves); 771 if (changed) { 772 for (Curve crv : curves) { 773 if (!(crv instanceof Immutable)) 774 crv.addChangeListener(_childChangeListener); 775 } 776 fireChangeEvent(); // Notify change listeners. 777 } 778 _wts.addAll(index,weights); 779 return changed; 780 } 781 782 /** 783 * Appends all of the curves in the specified array to this LinearComboCurve with a 784 * default weight of 1.0 assigned to each. The behavior of this operation is undefined 785 * if the specified collection is modified while the operation is in progress. The 786 * input curves must have the same physical dimensions as the other items in this 787 * list, or an exception is thrown. 788 * 789 * @param arr curves to be appended onto this collection with a default weight of 1.0 790 * for each. May not be null. 791 * @return <code>true</code> if this collection changed as a result of the call. 792 * @throws DimensionException if the input curve dimensions are different from this 793 * list's dimensions. 794 */ 795 @Override 796 public boolean addAll(Curve[] arr) { 797 return addAll(size(), arr); 798 } 799 800 /** 801 * Inserts all of the {@link Curve} objects in the specified array into this 802 * LinearComboCurve at the specified position with a default weight of 1.0 assigned to 803 * each. Shifts the element currently at that position (if any) and any subsequent 804 * elements to the right (increases their indices). The new elements will appear in 805 * this list in the order that they are returned by the specified collection's 806 * iterator. The input curves must have the same physical dimensions as the other 807 * items in this list, or an exception is thrown. 808 * 809 * @param index index at which to insert first element from the specified array. 810 * @param arr the curves to be inserted into this collection with a default weight 811 * of 1.0 for each. May not be null. 812 * @return <code>true</code> if this collection changed as a result of the call. 813 * @throws DimensionException if the input curve dimensions are different from this 814 * list's dimensions. 815 */ 816 @Override 817 public boolean addAll(int index, Curve[] arr) { 818 return addAll(index, Arrays.asList(requireNonNull(arr))); 819 } 820 821 /** 822 * Replaces the Curve at the specified position in this list of curves with 823 * the specified Curve. The weight at that position is left unchanged. Null elements 824 * are ignored. The input curve must have the same physical dimensions as the other 825 * items in this list, or an exception is thrown. 826 * 827 * @param index The index of the curve to replace (0 returns the 1st curve, -1 returns 828 * the last, -2 returns the 2nd from last, etc). 829 * @param curve The curve to be stored at the specified position. The weight at that 830 * position is left unchanged. May not be null. 831 * @return The curve previously at the specified position in this list. 832 * @throws java.lang.IndexOutOfBoundsException - if <code>index > size()</code> 833 * @throws DimensionException if the input curve dimensions are different from this 834 * list's dimensions. 835 */ 836 @Override 837 public Curve set(int index, Curve curve) { 838 if (size() > 0 && curve.getPhyDimension() != getPhyDimension()) 839 throw new DimensionException(MessageFormat.format( 840 RESOURCES.getString("incCrvDimension"), "curve", getPhyDimension())); 841 index = normalizeIndex(index); 842 843 Curve old = _crvs.set(index, curve); 844 if (!(old instanceof Immutable)) 845 old.removeChangeListener(_childChangeListener); 846 if (!(curve instanceof Immutable)) 847 curve.addChangeListener(_childChangeListener); 848 849 fireChangeEvent(); // Notify change listeners. 850 851 return old; 852 } 853 854 /** 855 * Replaces the weight at the specified position in this LinearComboCurve with the 856 * specified weight. The curve at that position is left unchanged. Null elements are 857 * ignored. 858 * 859 * @param index The index of the weight to replace (0 returns the 1st element, -1 860 * returns the last, -2 returns the 2nd from last, etc). 861 * @param weight The weight to be stored at the specified position. The curve at that 862 * position is left unchanged. May not be null. 863 * @return The weight previously at the specified position in this list. 864 * @throws java.lang.IndexOutOfBoundsException - if <code>index > size()</code> 865 */ 866 @Override 867 public Double setWeight(int index, Double weight) { 868 index = normalizeIndex(index); 869 return _wts.set(index, requireNonNull(weight)); 870 } 871 872 /** 873 * Replaces the Curve and weight at the specified position in this 874 * LinearComboCurve with the specified curve and weight. Null elements are ignored. 875 * The input curve must have the same physical dimensions as the other items in this 876 * list, or an exception is thrown. 877 * 878 * @param index The index of the curve and weight to replace (0 returns the 1st 879 * element, -1 returns the last, -2 returns the 2nd from last, etc). 880 * @param curve The curve to be stored at the specified position. May not be null. 881 * @param weight The weight to be stored at the specified position. May not be null. 882 * @return The curve previously at the specified position in this list. The previous 883 * weight is lost. 884 * @throws java.lang.IndexOutOfBoundsException - if <code>index > size()</code> 885 * @throws DimensionException if the input curve dimensions are different from this 886 * list's dimensions. 887 */ 888 @Override 889 public Curve set(int index, Curve curve, Double weight) { 890 requireNonNull(curve); 891 requireNonNull(weight); 892 Curve old = set(index, curve); 893 setWeight(index, weight); 894 return old; 895 } 896 897 /** 898 * Removes from this list all the {@link Curve} objects that are contained in the 899 * specified collection. 900 * 901 * @param c Collection that defines which curves will be removed from this list. May 902 * not be null. 903 * @return <code>true</code> if this list changed as a result of the call. 904 */ 905 @Override 906 public boolean removeAll(Collection<?> c) { 907 requireNonNull(c); 908 boolean modified = false; 909 Iterator<?> it = iterator(); 910 Iterator<Double> itw = _wts.iterator(); 911 while (it.hasNext()) { 912 itw.next(); 913 if (c.contains(it.next())) { 914 it.remove(); 915 itw.remove(); 916 modified = true; 917 } 918 } 919 return modified; 920 } 921 922 /** 923 * Retains only the curves in this list that are contained in the specified 924 * collection. In other words, removes from this LinearComboCurve all the 925 * {@link Curve} objects that are not contained in the specified collection. 926 * 927 * @param c Collection that defines which curves this set will retain. May not be 928 * null. 929 * @return <code>true</code> if this list changed as a result of the call. 930 */ 931 @Override 932 public boolean retainAll(Collection<?> c) { 933 requireNonNull(c); 934 boolean modified = false; 935 Iterator<Curve> it = iterator(); 936 Iterator<Double> itw = _wts.iterator(); 937 while (it.hasNext()) { 938 itw.next(); 939 if (!c.contains(it.next())) { 940 it.remove(); 941 itw.remove(); 942 modified = true; 943 } 944 } 945 return modified; 946 } 947 948 /** 949 * Removes a single instance of the specified {@link Curve} (and its associated 950 * weight) from this collection, if it is present. More formally, removes an element e 951 * such that (o==null ? e==null : o.equals(e)), if this collection contains one or 952 * more such elements. Returns true if this collection contained the specified curve 953 * (or equivalently, if this collection changed as a result of the call). 954 * 955 * @param o Curve to be removed from this collection (along with its associated 956 * weight), if present. 957 * @return <code>true</code> if this collection changed as a result of the call 958 */ 959 @Override 960 public boolean remove(Object o) { 961 int index = _crvs.indexOf(o); 962 boolean changed = _crvs.remove(o); 963 if (changed) { 964 _wts.remove(index); 965 if (o instanceof AbstractGeomElement && !(o instanceof Immutable)) { 966 ((AbstractGeomElement)o).removeChangeListener(_childChangeListener); 967 } 968 fireChangeEvent(); 969 } 970 return changed; 971 } 972 973 /** 974 * Removes the curve (and its associated weight) at the specified position in this 975 * LinearComboCurve. Shifts any subsequent curves and weights to the left (subtracts 976 * one from their indices). Returns the curve that was removed from the list; the 977 * removed weight is lost. 978 * 979 * @param index the index of the curve and weight to remove. (0 returns the 1st 980 * element, -1 returns the last, -2 returns the 2nd from last, etc). 981 * @return the curve previously at the specified position. 982 */ 983 @Override 984 public Curve remove(int index) { 985 index = normalizeIndex(index); 986 _wts.remove(index); 987 Curve old = _crvs.remove(index); 988 if (!(old instanceof Immutable)) 989 old.removeChangeListener(_childChangeListener); 990 fireChangeEvent(); // Notify change listeners. 991 return old; 992 } 993 994 /** 995 * Removes the curve with the specified name from this list. Shifts any subsequent 996 * elements to the left (subtracts one from their indices). Returns the element that 997 * was removed from the list. 998 * 999 * @param name the name of the element to remove. 1000 * @return the element previously at the specified position. 1001 */ 1002 @Override 1003 public Curve remove(String name) { 1004 1005 Curve element = null; 1006 int index = getIndexFromName(name); 1007 if (index >= 0) 1008 element = this.remove(index); 1009 1010 return element; 1011 } 1012 1013 /** 1014 * Removes all of the curves from this linear combination. The linear combination will 1015 * be empty and undefined after this call returns. 1016 */ 1017 @Override 1018 public void clear() { 1019 int size = _crvs.size(); 1020 for (int i = 0; i < size; ++i) { 1021 Curve crv = _crvs.get(i); 1022 if (!(crv instanceof Immutable)) 1023 crv.removeChangeListener(_childChangeListener); 1024 } 1025 _crvs.clear(); 1026 _wts.clear(); 1027 fireChangeEvent(); // Notify change listeners. 1028 } 1029 1030 /** 1031 * Returns an iterator over the curves in this LinearComboCurve. 1032 * 1033 * @return an iterator over this list of curves. 1034 */ 1035 @Override 1036 public java.util.Iterator<Curve> iterator() { 1037 return _crvs.iterator(); 1038 } 1039 1040 /** 1041 * Returns a list iterator over the curves in this LinearComboCurve. 1042 * 1043 * @return an iterator over this list of curves. 1044 */ 1045 @Override 1046 public java.util.ListIterator<Curve> listIterator() { 1047 return _crvs.listIterator(); 1048 } 1049 1050 /** 1051 * Returns a Curve list iterator from the specified position. 1052 * 1053 * @param index the index of first Curve to be returned from the list iterator (by a 1054 * call to the next method). 1055 * @return a list iterator of the curves in this list starting at the specified 1056 * position in this list. 1057 */ 1058 @Override 1059 public java.util.ListIterator<Curve> listIterator(int index) { 1060 return _crvs.listIterator(index); 1061 } 1062 1063 /** 1064 * Returns an array containing all of the curves in this collection. 1065 */ 1066 @Override 1067 public Curve[] toArray() { 1068 return (Curve[])_crvs.toArray(); 1069 } 1070 1071 /** 1072 * Returns an array containing all of the curves in this collection. If the 1073 * collection fits in the specified array, it is returned therein. Otherwise, a new 1074 * array is allocated with the runtime type of the specified array and the size of 1075 * this collection. 1076 * 1077 * @param <T> The type of elements in this LinearComboCurve (Curve type). 1078 * @param a the array into which the elements of the collection are to be stored, if 1079 * it is big enough; otherwise, a new array of the same type is allocated 1080 * for this purpose. 1081 * @return an array containing the curves of this collection. 1082 */ 1083 @Override 1084 @SuppressWarnings("SuspiciousToArrayCall") 1085 public <T> T[] toArray(T[] a) { 1086 return _crvs.toArray(a); 1087 } 1088 1089 /** 1090 * Returns the number of physical dimensions of the geometry element. This 1091 * implementation always returns the physical dimension of the underlying Curve 1092 * objects or 0 if this list has no Curve objects in it. 1093 * 1094 * @return The number of physical dimensions of the geometry element. 1095 */ 1096 @Override 1097 public int getPhyDimension() { 1098 if (isEmpty()) 1099 return 0; 1100 return get(0).getPhyDimension(); 1101 } 1102 1103 /** 1104 * Calculate a point on the curve for the given parametric distance along the curve. 1105 * 1106 * @param s parametric distance to calculate a point for (0.0 to 1.0 inclusive). 1107 * @return the calculated point 1108 */ 1109 @Override 1110 public Point getRealPoint(double s) { 1111 validateCurve(); 1112 validateParameter(s); 1113 1114 StackContext.enter(); 1115 try { 1116 Point p = _crvs.get(0).getRealPoint(s).times(_wts.get(0)); 1117 int size = _crvs.size(); 1118 for (int i = 1; i < size; ++i) { 1119 Point crvPnt = _crvs.get(i).getRealPoint(s); 1120 double Wi = _wts.get(i); 1121 p = p.plus(crvPnt.times(Wi)); 1122 } 1123 1124 return StackContext.outerCopy(p); 1125 } finally { 1126 StackContext.exit(); 1127 } 1128 } 1129 1130 /** 1131 * Calculate all the derivatives from <code>0</code> to <code>grade</code> with respect 1132 * to parametric distance on the curve for the given parametric distance along the curve, 1133 * <code>d^{grade}p(s)/d^{grade}s</code>. 1134 * <p> 1135 * Example:<br> 1136 * 1st derivative (grade = 1), this returns <code>[p(s), dp(s)/ds]</code>;<br> 1137 * 2nd derivative (grade = 2), this returns <code>[p(s), dp(s)/ds, d^2p(s)/d^2s]</code>; etc. 1138 * </p> 1139 * 1140 * @param s Parametric distance to calculate derivatives for (0.0 to 1.0 inclusive). 1141 * @param grade The maximum grade to calculate the derivatives for (1=1st derivative, 2=2nd derivative, etc) 1142 * @return A list of derivatives up to the specified grade of the curve at the specified parametric position. 1143 * @throws IllegalArgumentException if the grade is < 0. 1144 */ 1145 @Override 1146 public List<Vector<Length>> getSDerivatives(double s, int grade) { 1147 validateCurve(); 1148 validateParameter(s); 1149 if (grade < 0) 1150 throw new IllegalArgumentException(RESOURCES.getString("gradeLTZeroErr")); 1151 1152 // Create a list to hold the output. 1153 List<Vector<Length>> output = _crvs.get(0).getSDerivatives(s, grade); 1154 double Wi = _wts.get(0); 1155 for (int g=0; g <= grade; ++g) { 1156 Vector<Length> v = output.get(g); 1157 v = v.times(Wi); 1158 output.set(g, v); 1159 } 1160 1161 int size = _crvs.size(); 1162 for (int i=1; i < size; ++i) { 1163 List<Vector<Length>> ders = _crvs.get(i).getSDerivatives(s, grade); 1164 Wi = _wts.get(i); 1165 for (int g = 0; g <= grade; ++g) { 1166 Vector<Length> v = ders.get(g); 1167 v = v.times(Wi); 1168 v = v.plus(output.get(g)); 1169 output.set(g, v); 1170 } 1171 } 1172 1173 // Set the origin of all the vectors to p(s). 1174 Point p = Point.valueOf(output.get(0)); 1175 for (int g=0; g <= grade; ++g) { 1176 output.get(g).setOrigin(p); 1177 } 1178 1179 return output; 1180 } 1181 1182 /** 1183 * Split this curve at the specified parametric position returning a list containing 1184 * two curves (a lower curve with smaller parametric positions than "s" and an upper 1185 * curve with larger parametric positions). 1186 * 1187 * @param s The parametric position where this curve should be split (must not be 0 or 1188 * 1!). 1189 * @return A list containing two curves: 0 == the lower curve, 1 == the upper curve. 1190 */ 1191 @Override 1192 public GeomList<LinearComboCurve> splitAt(double s) { 1193 validateCurve(); 1194 validateParameter(s); 1195 if (parNearEnds(s, TOL_S)) 1196 throw new IllegalArgumentException(MessageFormat.format( 1197 RESOURCES.getString("canNotSplitAtEnds"), "curve")); 1198 1199 // Create LinearComboCurve objects for each end. 1200 LinearComboCurve cl = LinearComboCurve.newInstance(); 1201 LinearComboCurve cu = LinearComboCurve.newInstance(); 1202 1203 // Split each child curve at the specified position. 1204 int size = size(); 1205 for (int i=0; i < size; ++i) { 1206 Curve crvi = _crvs.get(i); 1207 Double Wi = _wts.get(i); 1208 GeomList<Curve> splits = crvi.splitAt(s); 1209 cl.add(splits.get(0),Wi); 1210 cu.add(splits.get(1),Wi); 1211 } 1212 1213 // Create the output list. 1214 GeomList output = GeomList.newInstance(); 1215 output.add(cl, cu); 1216 1217 return output; 1218 } 1219 1220 /** 1221 * Return <code>true</code> if this LinearComboCurve contains valid and finite numerical 1222 * components. A value of <code>false</code> will be returned if any of the coordinate 1223 * values or weights are NaN or Inf. 1224 * 1225 * @return true if this line segment contains valid and finite data. 1226 */ 1227 @Override 1228 public boolean isValid() { 1229 int size = _crvs.size(); 1230 for (int i=size-1; i >= 0; --i) { 1231 Curve crv = _crvs.get(i); 1232 double w = _wts.get(i); 1233 if (!crv.isValid() || Double.isInfinite(w) || Double.isNaN(w)) 1234 return false; 1235 } 1236 return true; 1237 } 1238 1239 /** 1240 * Returns a transformed version of this element. The returned list of objects 1241 * implement {@link GeomTransform} and contains transformed versions of the contents 1242 * of this list as children. 1243 * 1244 * @param transform The transformation to apply to this geometry. May not be null. 1245 * @return A new LinearCombonCurve that is identical to this one with the specified 1246 * transformation applied to member curves. 1247 * @throws DimensionException if this surface is not 3D. 1248 */ 1249 @Override 1250 public LinearComboCurve getTransformed(GTransform transform) { 1251 requireNonNull(transform); 1252 LinearComboCurve list = LinearComboCurve.newInstance(); 1253 copyState(list); 1254 1255 int size = _crvs.size(); 1256 for (int i = 0; i < size; ++i) { 1257 Curve crvi = _crvs.get(i); 1258 Double Wi = _wts.get(i); 1259 list.add((Curve)crvi.getTransformed(transform), Wi); 1260 } 1261 return list; 1262 } 1263 1264 /** 1265 * Return the coordinate point representing the minimum bounding box corner of this 1266 * geometry element (e.g.: min X, min Y, min Z). 1267 * 1268 * @return The minimum bounding box coordinate for this geometry element. 1269 * @throws IndexOutOfBoundsException if this list contains no geometry. 1270 */ 1271 @Override 1272 public Point getBoundsMin() { 1273 if (isEmpty()) 1274 throw new IndexOutOfBoundsException(RESOURCES.getString("listNoGeometry")); 1275 1276 StackContext.enter(); 1277 try { 1278 Point minPoint = _crvs.get(0).getBoundsMin().times(_wts.get(0)); 1279 int size = _crvs.size(); 1280 for (int i = 1; i < size; ++i) { 1281 double Wi = _wts.get(i); 1282 GeomElement element = _crvs.get(i); 1283 minPoint = minPoint.plus(element.getBoundsMin().times(Wi)); 1284 } 1285 1286 return StackContext.outerCopy(minPoint); 1287 } finally { 1288 StackContext.exit(); 1289 } 1290 } 1291 1292 /** 1293 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 1294 * X, max Y, max Z). 1295 * 1296 * @return The maximum bounding box coordinate for this geometry element. 1297 * @throws IndexOutOfBoundsException if this list contains no elements. 1298 */ 1299 @Override 1300 public Point getBoundsMax() { 1301 if (isEmpty()) 1302 throw new IndexOutOfBoundsException(RESOURCES.getString("listNoGeometry")); 1303 1304 StackContext.enter(); 1305 try { 1306 Point maxPoint = _crvs.get(0).getBoundsMax().times(_wts.get(0)); 1307 int size = _crvs.size(); 1308 for (int i = 1; i < size; ++i) { 1309 double Wi = _wts.get(i); 1310 GeomElement element = _crvs.get(i); 1311 maxPoint = maxPoint.plus(element.getBoundsMax().times(Wi)); 1312 } 1313 1314 return StackContext.outerCopy(maxPoint); 1315 } finally { 1316 StackContext.exit(); 1317 } 1318 } 1319 1320 /** 1321 * Returns the unit in which the curves in this linear combination curve are stated. 1322 * 1323 * @return The unit in which the curves in this LinearComboCurve are stated or the 1324 * default unit if this surface has no member curves. 1325 */ 1326 @Override 1327 public Unit<Length> getUnit() { 1328 if (isEmpty()) 1329 return GeomUtil.getDefaultUnit(); 1330 1331 return _crvs.get(0).getUnit(); 1332 } 1333 1334 /** 1335 * Returns the equivalent to this LinearComboCurve but stated in the specified unit. 1336 * 1337 * @param unit the length unit of the curve to be returned. May not be null. 1338 * @return an equivalent to this LinearComboCurve but stated in the specified unit. 1339 * @throws ConversionException if the the input unit is not a length unit. 1340 */ 1341 @Override 1342 public LinearComboCurve to(Unit<Length> unit) throws ConversionException { 1343 if (unit.equals(getUnit())) 1344 return this; 1345 1346 LinearComboCurve crv = LinearComboCurve.newInstance(); 1347 copyState(crv); 1348 1349 // Convert the curves. 1350 int size = _crvs.size(); 1351 for (int i = 0; i < size; ++i) { 1352 Curve crvi = _crvs.get(i); 1353 Double Wi = _wts.get(i); 1354 crv.add(crvi.to(unit), Wi); 1355 } 1356 1357 return crv; 1358 } 1359 1360 /** 1361 * Return the equivalent of this LinearComboCurve converted to the specified number of physical 1362 * dimensions. If the number of dimensions is greater than this element, then zeros 1363 * are added to the additional dimensions. If the number of dimensions is less than 1364 * this element, then the extra dimensions are simply dropped (truncated). If the new 1365 * dimensions are the same as the dimension of this element, then this element is 1366 * simply returned. 1367 * 1368 * @param newDim The dimension of the curve to return. 1369 * @return This LinearComboCurve converted to the new dimensions. 1370 */ 1371 @Override 1372 public LinearComboCurve toDimension(int newDim) { 1373 if (getPhyDimension() == newDim) 1374 return this; 1375 1376 LinearComboCurve crv = LinearComboCurve.newInstance(); 1377 copyState(crv); 1378 1379 // Convert the curves. 1380 int size = _crvs.size(); 1381 for (int i = 0; i < size; ++i) { 1382 Curve crvi = _crvs.get(i); 1383 Double Wi = _wts.get(i); 1384 crv.add(crvi.toDimension(newDim), Wi); 1385 } 1386 1387 return crv; 1388 } 1389 1390 /** 1391 * Return a NURBS curve representation of this curve to within the specified 1392 * tolerance. 1393 * 1394 * @param tol The greatest possible difference between this curve and the NURBS 1395 * representation returned. May not be null. 1396 * @return A NURBS curve that represents this curve to within the specified tolerance. 1397 */ 1398 @Override 1399 public NurbsCurve toNurbs(Parameter<Length> tol) { 1400 StackContext.enter(); 1401 try { 1402 1403 // Grid some points onto the curve. 1404 PointString<SubrangePoint> str = this.gridToTolerance(requireNonNull(tol)); 1405 1406 // Fit a cubic NURBS curve to the points. 1407 int deg = 3; 1408 if (str.size() <= 3) 1409 deg = str.size() - 1; 1410 BasicNurbsCurve curve = CurveFactory.fitPoints(deg, str); 1411 copyState(curve); // Copy over the super-class state for this curve to the new one. 1412 1413 return StackContext.outerCopy(curve); 1414 1415 } finally { 1416 StackContext.exit(); 1417 } 1418 } 1419 1420 /** 1421 * Returns the text representation of this geometry element. 1422 * 1423 * @return the text representation of this geometry element. 1424 */ 1425 @Override 1426 public Text toText() { 1427 TextBuilder tmp = TextBuilder.newInstance(); 1428 String className = this.getClass().getName(); 1429 tmp.append(className.substring(className.lastIndexOf(".") + 1)); 1430 tmp.append(": {"); 1431 String nameStr = getName(); 1432 boolean hasName = nonNull(nameStr); 1433 if (hasName) { 1434 tmp.append(nameStr); 1435 tmp.append(" = {"); 1436 } 1437 tmp.append("\n"); 1438 int size = this.size(); 1439 for (int i = 0; i < size; ++i) { 1440 GeomElement e = this.get(i); 1441 tmp.append(e.toText()); 1442 if (i < size - 1) 1443 tmp.append(",\n"); 1444 else 1445 tmp.append("\n"); 1446 } 1447 if (hasName) 1448 tmp.append('}'); 1449 tmp.append("}"); 1450 Text txt = tmp.toText(); 1451 TextBuilder.recycle(tmp); 1452 return txt; 1453 } 1454 1455 /** 1456 * Compares the specified object with this list of <code>Curve</code> objects for 1457 * equality. Returns true if and only if both collections are of the same type and 1458 * both collections contain equal elements in the same order. 1459 * 1460 * @param obj the object to compare with. 1461 * @return <code>true</code> if this list is identical to that list; 1462 * <code>false</code> otherwise. 1463 */ 1464 @Override 1465 public boolean equals(Object obj) { 1466 if (this == obj) 1467 return true; 1468 if ((obj == null) || (obj.getClass() != this.getClass())) 1469 return false; 1470 1471 LinearComboCurve that = (LinearComboCurve)obj; 1472 return this._crvs.equals(that._crvs) 1473 && this._wts.equals(that._wts) 1474 && super.equals(obj); 1475 } 1476 1477 /** 1478 * Returns the hash code for this <code>LinearComboCurve</code>. 1479 * 1480 * @return the hash code value. 1481 */ 1482 @Override 1483 public int hashCode() { 1484 return 31*super.hashCode() + Objects.hash(_crvs, _wts); 1485 } 1486 1487 /** 1488 * Returns a copy of this <code>LinearComboCurve</code> instance 1489 * {@link javolution.context.AllocatorContext allocated} by the calling thread 1490 * (possibly on the stack). 1491 * 1492 * @return an identical and independent copy of this object. 1493 */ 1494 @Override 1495 public LinearComboCurve copy() { 1496 return copyOf(this); 1497 } 1498 1499 /** 1500 * Return a copy of this object with any transformations or subranges removed 1501 * (applied). 1502 * 1503 * @return A copy of this object with any transformations or subranges removed. 1504 */ 1505 @Override 1506 public LinearComboCurve copyToReal() { 1507 LinearComboCurve newCrv = LinearComboCurve.newInstance(); 1508 copyState(newCrv); 1509 1510 int size = this.size(); 1511 for (int i = 0; i < size; ++i) { 1512 Curve crvi = this._crvs.get(i); 1513 Double Wi = this._wts.get(i); 1514 Curve crv2i = crvi.copyToReal(); 1515 newCrv.add(crv2i,Wi); 1516 } 1517 1518 return newCrv; 1519 } 1520 1521 /** 1522 * Resets the internal state of this object to its default values. Subclasses that 1523 * override this method must call <code>super.reset();</code> to ensure that the state 1524 * is reset properly. 1525 */ 1526 @Override 1527 public void reset() { 1528 clear(); 1529 ((FastTable)_crvs).reset(); 1530 ((FastTable)_wts).reset(); 1531 super.reset(); 1532 } 1533 1534 /** 1535 * Holds the default XML representation for this object. 1536 */ 1537 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 1538 protected static final XMLFormat<LinearComboCurve> XML = new XMLFormat<LinearComboCurve>(LinearComboCurve.class) { 1539 1540 @Override 1541 public LinearComboCurve newInstance(Class<LinearComboCurve> cls, XMLFormat.InputElement xml) throws XMLStreamException { 1542 LinearComboCurve obj = FACTORY.object(); 1543 return obj; 1544 } 1545 1546 @Override 1547 public void read(XMLFormat.InputElement xml, LinearComboCurve obj) throws XMLStreamException { 1548 AbstractCurve.XML.read(xml, obj); // Call parent read. 1549 1550 FastTable<Curve> crvs = xml.get("Contents", FastTable.class); 1551 FastTable<Double> wts = xml.get("Weights", FastTable.class); 1552 obj._crvs = crvs; 1553 obj._wts = wts; 1554 } 1555 1556 @Override 1557 public void write(LinearComboCurve obj, XMLFormat.OutputElement xml) throws XMLStreamException { 1558 AbstractCurve.XML.write(obj, xml); // Call parent write. 1559 1560 xml.add((FastTable)obj._crvs, "Contents", FastTable.class); 1561 xml.add((FastTable)obj._wts, "Weights", FastTable.class); 1562 } 1563 }; 1564 1565 ////////////////////// 1566 // Factory Creation // 1567 ////////////////////// 1568 /** 1569 * Do not allow the default constructor to be used except by subclasses. 1570 */ 1571 protected LinearComboCurve() { 1572 } 1573 1574 private static final ObjectFactory<LinearComboCurve> FACTORY = new ObjectFactory<LinearComboCurve>() { 1575 @Override 1576 protected LinearComboCurve create() { 1577 return new LinearComboCurve(); 1578 } 1579 1580 @Override 1581 protected void cleanup(LinearComboCurve obj) { 1582 obj.reset(); 1583 obj._crvs = null; 1584 obj._wts = null; 1585 } 1586 }; 1587 1588 /** 1589 * Recycles a case instance immediately (on the stack when executing in a 1590 * StackContext). 1591 * 1592 * @param instance The instance to be recycled. 1593 */ 1594 public static void recycle(LinearComboCurve instance) { 1595 FACTORY.recycle(instance); 1596 } 1597 1598 @SuppressWarnings("unchecked") 1599 private static LinearComboCurve copyOf(LinearComboCurve original) { 1600 LinearComboCurve obj = LinearComboCurve.newInstance(); 1601 1602 int size = original.size(); 1603 for (int i = 0; i < size; ++i) { 1604 Curve crvi = original.get(i).copy(); 1605 Double Wi = original._wts.get(i); 1606 obj.add(crvi, Wi); 1607 } 1608 1609 original.copyState(obj); 1610 return obj; 1611 } 1612 1613}