001/* 002 * ControlPointNet -- A network or matrix of control points for a NURBS surface in nD space. 003 * 004 * Copyright (C) 2010-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.nurbs; 023 024import geomss.geom.Point; 025import java.text.MessageFormat; 026import java.util.Iterator; 027import java.util.List; 028import static java.util.Objects.requireNonNull; 029import java.util.ResourceBundle; 030import javax.measure.converter.ConversionException; 031import javax.measure.quantity.Length; 032import javax.measure.unit.Unit; 033import javolution.context.ObjectFactory; 034import javolution.context.StackContext; 035import javolution.lang.ValueType; 036import javolution.text.Text; 037import javolution.text.TextBuilder; 038import javolution.util.FastTable; 039import javolution.xml.XMLFormat; 040import javolution.xml.XMLSerializable; 041import javolution.xml.stream.XMLStreamException; 042 043/** 044 * A network or matrix of control points for a NURBS surface in n-dimensional space. 045 * 046 * <p> Modified by: Joseph A. Huwaldt </p> 047 * 048 * @author Joseph A. Huwaldt, Date: June 15, 2010 049 * @version November 28, 2015 050 */ 051@SuppressWarnings("serial") 052public class ControlPointNet implements Iterable<List<ControlPoint>>, Cloneable, XMLSerializable, ValueType { 053 054 /** 055 * The resource bundle for this package. 056 */ 057 private static final ResourceBundle RESOURCES = geomss.geom.AbstractGeomElement.RESOURCES; 058 059 // A list of lists of ControlPoint objects. 060 private FastTable<FastTable<ControlPoint>> _matrix; 061 062 /** 063 * Returns a {@link ControlPointNet} instance made up of the control points in the 064 * specified java matrix. 065 * 066 * @param cps Matrix of control points: cps[t][s]. s-parameter runs down a column of 067 * points and the t-parameter runs across the columns of points. May not be 068 * null. 069 * @return A ControlPointNet object using the data in the specified matrix. 070 */ 071 public static ControlPointNet valueOf(ControlPoint[][] cps) { 072 requireNonNull(cps, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cps")); 073 int nt = cps.length; 074 if (nt < 1) 075 throw new IllegalArgumentException( 076 MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps")); 077 int ns = cps[0].length; 078 if (ns < 1) 079 throw new IllegalArgumentException( 080 MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps[0]")); 081 for (int t = 1; t < nt; ++t) { 082 if (cps[t].length != ns) 083 throw new IllegalArgumentException( 084 MessageFormat.format(RESOURCES.getString("irregMatrixErr"), "cps")); 085 } 086 087 Unit<Length> refUnit = cps[0][0].getUnit(); 088 ControlPointNet P = FACTORY.object(); 089 P._matrix = FastTable.newInstance(); 090 for (int t = 0; t < nt; ++t) { 091 FastTable<ControlPoint> tbl = FastTable.newInstance(); 092 for (int s = 0; s < ns; ++s) { 093 tbl.add(cps[t][s].to(refUnit)); 094 } 095 P._matrix.add(tbl); 096 } 097 098 return P; 099 } 100 101 /** 102 * Returns a {@link ControlPointNet} instance made up of the control points in the 103 * specified list of lists. 104 * 105 * @param cps List of lists (matrix) of control points: cps.get(t).get(s). s-parameter 106 * runs down a column of points and the t-parameter runs across the columns 107 * of points. May not be null. 108 * @return A ControlPointNet object using the data in the specified matrix. 109 */ 110 public static ControlPointNet valueOf(List<? extends List<ControlPoint>> cps) { 111 requireNonNull(cps, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cps")); 112 int nt = cps.size(); 113 if (nt < 1) 114 throw new IllegalArgumentException( 115 MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps")); 116 int ns = cps.get(0).size(); 117 if (ns < 1) 118 throw new IllegalArgumentException( 119 MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps.get(0)")); 120 for (int t = 1; t < nt; ++t) { 121 if (cps.get(t).size() != ns) 122 throw new IllegalArgumentException( 123 MessageFormat.format(RESOURCES.getString("irregMatrixErr"), "cps")); 124 } 125 126 Unit<Length> refUnit = cps.get(0).get(0).getUnit(); 127 ControlPointNet P = FACTORY.object(); 128 P._matrix = FastTable.newInstance(); 129 for (int t = 0; t < nt; ++t) { 130 FastTable<ControlPoint> tbl = FastTable.newInstance(); 131 int size = cps.get(t).size(); 132 for (int i = 0; i < size; ++i) { 133 ControlPoint cp = cps.get(t).get(i); 134 tbl.add(cp.to(refUnit)); 135 } 136 P._matrix.add(tbl); 137 } 138 139 return P; 140 } 141 142 /** 143 * Returns a {@link ControlPointNet} instance made up of the control points contained 144 * in the specified network. 145 * 146 * @param cpNet An existing control point net. The control points from this network 147 * are used to make this one. May not be null. 148 * @return A ControlPointNet object using the data in the specified network. 149 */ 150 public static ControlPointNet valueOf(ControlPointNet cpNet) { 151 requireNonNull(cpNet, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cpNet")); 152 ControlPointNet P = FACTORY.object(); 153 P._matrix = FastTable.newInstance(); 154 int ncols = cpNet.getNumberOfColumns(); 155 for (int i = 0; i < ncols; ++i) { 156 List<ControlPoint> column = cpNet.getColumn(i); 157 FastTable<ControlPoint> tbl = FastTable.newInstance(); 158 tbl.addAll(column); 159 P._matrix.add(tbl); 160 } 161 return P; 162 } 163 164 /** 165 * Returns the total number of control points in this matrix of control points. 166 * 167 * @return The total number of control points in this network. 168 */ 169 public int size() { 170 return _matrix.size() * _matrix.get(0).size(); 171 } 172 173 /** 174 * Return the control point matrix size in the s-direction (down a column of control 175 * points). 176 * 177 * @return The number of rows in this control point network. 178 */ 179 public int getNumberOfRows() { 180 return _matrix.get(0).size(); 181 } 182 183 /** 184 * Return the control point matrix size in the t-direction (across the columns of 185 * control points). 186 * 187 * @return The number of columns in this control point network. 188 */ 189 public int getNumberOfColumns() { 190 return _matrix.size(); 191 } 192 193 /** 194 * Returns the ControlPoint at the specified s,t position in this matrix. 195 * 196 * @param s the index in the s-direction (down a column of points). 197 * @param t the index in the t-direction (across the columns of points). 198 * @return the control point at the s,t position in this matrix. 199 * @throws IndexOutOfBoundsException <code>(s < 0) || (s ≥ 200 * getNumberOfRows()) || t < 0 || (t ≥ getNumberOfColumns()) )</code> 201 */ 202 public ControlPoint get(int s, int t) { 203 return _matrix.get(t).get(s); 204 } 205 206 /** 207 * Returns a list of ControlPoint objects that represent a single row in this network 208 * of control points. 209 * 210 * @param sIndex The index for the row of control points to return. 211 * @return The specified row of control points. 212 * @throws IndexOutOfBoundsException <code>sIndex < 0 || (sIndex ≥ 213 * getNumberOfRows()) )</code> 214 */ 215 public List<ControlPoint> getRow(int sIndex) { 216 FastTable<ControlPoint> list = FastTable.newInstance(); 217 int ncol = _matrix.size(); 218 for (int i = 0; i < ncol; ++i) { 219 List<ControlPoint> column = _matrix.get(i); 220 list.add(column.get(sIndex)); 221 } 222 return list; 223 } 224 225 /** 226 * Returns a list of ControlPoint objects that represent a single column in this 227 * network of control points. 228 * 229 * @param tIndex The index for the column of control points to return. 230 * @return The specified column of control points. 231 * @throws IndexOutOfBoundsException <code>tIndex < 0 || (tIndex ≥ 232 * getNumberOfColumns()) )</code> 233 */ 234 public List<ControlPoint> getColumn(int tIndex) { 235 FastTable<ControlPoint> list = FastTable.newInstance(); 236 list.addAll(_matrix.get(tIndex)); 237 return list; 238 } 239 240 /** 241 * Return the coordinate point representing the minimum bounding box corner (e.g.: min 242 * X, min Y, min Z) of this matrix of ControlPoint objects. 243 * 244 * @return The minimum bounding box coordinate for this geometry element. 245 */ 246 public Point getBoundsMin() { 247 StackContext.enter(); 248 try { 249 Point minPoint = get(0, 0).getBoundsMin(); 250 251 int ncol = _matrix.size(); 252 for (int i = 0; i < ncol; ++i) { 253 List<ControlPoint> tbl = _matrix.get(i); 254 int nrow = tbl.size(); 255 for (int j = 0; j < nrow; ++j) { 256 ControlPoint cp = tbl.get(j); 257 minPoint = minPoint.min(cp.getBoundsMin()); 258 } 259 } 260 return StackContext.outerCopy(minPoint); 261 262 } finally { 263 StackContext.exit(); 264 } 265 } 266 267 /** 268 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 269 * X, max Y, max Z) of this matrix of ControlPoint objects. 270 * 271 * @return The maximum bounding box coordinate for this geometry element. 272 */ 273 public Point getBoundsMax() { 274 StackContext.enter(); 275 try { 276 Point maxPoint = get(0, 0).getBoundsMax(); 277 278 int ncol = _matrix.size(); 279 for (int i = 0; i < ncol; ++i) { 280 List<ControlPoint> tbl = _matrix.get(i); 281 int nrow = tbl.size(); 282 for (int j = 0; j < nrow; ++j) { 283 ControlPoint cp = tbl.get(j); 284 maxPoint = maxPoint.max(cp.getBoundsMax()); 285 } 286 } 287 return StackContext.outerCopy(maxPoint); 288 289 } finally { 290 StackContext.exit(); 291 } 292 } 293 294 /** 295 * Return a new control point network that is the transpose of this network (the rows 296 * & columns are swapped). 297 * 298 * @return A new ControlPointNet identical to this one but with the rows and columns 299 * transposed. 300 */ 301 public ControlPointNet transpose() { 302 //StackContext.enter(); 303 try { 304 FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance(); 305 int numCols = getNumberOfColumns(); 306 int numRows = getNumberOfRows(); 307 for (int i = 0; i < numRows; ++i) { 308 FastTable<ControlPoint> col = FastTable.newInstance(); 309 nMat.add(col); 310 for (int j = 0; j < numCols; ++j) { 311 col.add(get(i, j)); 312 } 313 } 314 315 ControlPointNet nCpNet = ControlPointNet.valueOf(nMat); 316 return nCpNet; //StackContext.outerCopy(nCpNet); 317 318 } finally { 319 //StackContext.exit(); 320 } 321 } 322 323 /** 324 * Return a new control point network that is identical to this one but with the rows 325 * in reverse order. 326 * 327 * @return A new ControlPointNet identical to this one but with the row order 328 * reversed. 329 */ 330 public ControlPointNet reverseRows() { 331 StackContext.enter(); 332 try { 333 FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance(); 334 int numCols = getNumberOfColumns(); 335 int numRows = getNumberOfRows(); 336 for (int i = 0; i < numCols; ++i) { 337 FastTable<ControlPoint> col = FastTable.newInstance(); 338 nMat.add(col); 339 for (int j = numRows - 1; j >= 0; --j) { 340 col.add(_matrix.get(i).get(j)); 341 } 342 } 343 344 ControlPointNet nCpNet = ControlPointNet.valueOf(nMat); 345 return StackContext.outerCopy(nCpNet); 346 347 } finally { 348 StackContext.exit(); 349 } 350 } 351 352 /** 353 * Return a new control point network that is identical to this one but with the 354 * columns in reverse order. 355 * 356 * @return A new ControlPointNet identical to this one but with the column order 357 * reversed. 358 */ 359 public ControlPointNet reverseColumns() { 360 StackContext.enter(); 361 try { 362 FastTable<List<ControlPoint>> nMat = FastTable.newInstance(); 363 int numCols = getNumberOfColumns(); 364 for (int i = numCols - 1; i >= 0; --i) { 365 nMat.add(_matrix.get(i)); 366 } 367 368 ControlPointNet nCpNet = ControlPointNet.valueOf(nMat); 369 return StackContext.outerCopy(nCpNet); 370 371 } finally { 372 StackContext.exit(); 373 } 374 } 375 376 /** 377 * Returns the unit in which the control points in this network are stated. 378 * 379 * @return The unit in which this control point network is stated. 380 */ 381 public Unit<Length> getUnit() { 382 return get(0, 0).getUnit(); 383 } 384 385 /** 386 * Returns the equivalent to this control point network but stated in the specified 387 * unit. 388 * 389 * @param unit The length unit of the control point to be returned. May not be null. 390 * @return An equivalent to this control point network but stated in the specified 391 * unit. 392 * @throws ConversionException if the the input unit is not a length unit. 393 */ 394 public ControlPointNet to(Unit<Length> unit) throws ConversionException { 395 requireNonNull(unit); 396 397 StackContext.enter(); 398 try { 399 FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance(); 400 int ncol = _matrix.size(); 401 for (int i = 0; i < ncol; ++i) { 402 List<ControlPoint> lst = _matrix.get(i); 403 FastTable<ControlPoint> nLst = FastTable.newInstance(); 404 int nrow = lst.size(); 405 for (int j = 0; j < nrow; ++j) { 406 ControlPoint cp = lst.get(j); 407 nLst.add(cp.to(unit)); 408 } 409 nMat.add(nLst); 410 } 411 412 ControlPointNet nCpNet = ControlPointNet.valueOf(nMat); 413 return StackContext.outerCopy(nCpNet); 414 415 } finally { 416 StackContext.exit(); 417 } 418 } 419 420 /** 421 * Return the equivalent of this control point network converted to the specified 422 * number of physical dimensions. If the number of dimensions is greater than this 423 * element, then zeros are added to the additional dimensions. If the number of 424 * dimensions is less than this element, then the extra dimensions are simply dropped 425 * (truncated). If the new dimensions are the same as the dimension of this element, 426 * then this element is simply returned. 427 * 428 * @param newDim The dimension of the surface to return. 429 * @return The equivalent of this control point network converted to the new 430 * dimensions. 431 */ 432 public ControlPointNet toDimension(int newDim) { 433 434 StackContext.enter(); 435 try { 436 FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance(); 437 int ncol = _matrix.size(); 438 for (int i = 0; i < ncol; ++i) { 439 List<ControlPoint> lst = _matrix.get(i); 440 FastTable<ControlPoint> nLst = FastTable.newInstance(); 441 int nrow = lst.size(); 442 for (int j = 0; j < nrow; ++j) { 443 ControlPoint cp = lst.get(j); 444 ControlPoint nCP = ControlPoint.valueOf(cp.getPoint().toDimension(newDim), cp.getWeight()); 445 nLst.add(nCP); 446 } 447 nMat.add(nLst); 448 } 449 450 ControlPointNet nCpNet = ControlPointNet.valueOf(nMat); 451 return StackContext.outerCopy(nCpNet); 452 453 } finally { 454 StackContext.exit(); 455 } 456 } 457 458 /** 459 * Returns an iterator over the lists of ControlPoint objects in this network. The 460 * iterator returns lists containing the columns of control points. 461 */ 462 @Override 463 public Iterator<List<ControlPoint>> iterator() { 464 return (Iterator)_matrix.iterator(); 465 } 466 467 /** 468 * Return <code>true</code> if this ControlPointNet contains valid and finite 469 * numerical components. A value of <code>false</code> will be returned if any of the 470 * control point values are NaN or Inf. 471 * 472 * @return true if this ControlPointNet contains valid and finite numerical 473 * components. 474 */ 475 public boolean isValid() { 476 int ncol = _matrix.size(); 477 for (int i = 0; i < ncol; ++i) { 478 List<ControlPoint> row = _matrix.get(i); 479 int nrow = row.size(); 480 for (int j = 0; j < nrow; ++j) { 481 ControlPoint cp = row.get(j); 482 if (!cp.isValid()) 483 return false; 484 } 485 } 486 return true; 487 } 488 489 /** 490 * Returns a copy of this ControlPointNet instance 491 * {@link javolution.context.AllocatorContext allocated} by the calling thread 492 * (possibly on the stack). 493 * 494 * @return an identical and independent copy of this point. 495 */ 496 @Override 497 public ControlPointNet copy() { 498 return copyOf(this); 499 } 500 501 /** 502 * Returns a copy of this ControlPointNet instance 503 * {@link javolution.context.AllocatorContext allocated} by the calling thread 504 * (possibly on the stack). 505 * 506 * @return an identical and independent copy of this point. 507 * @throws java.lang.CloneNotSupportedException Never thrown. 508 */ 509 @Override 510 @SuppressWarnings("CloneDoesntCallSuperClone") 511 public Object clone() throws CloneNotSupportedException { 512 return copy(); 513 } 514 515 /** 516 * Compares this ControlPointNet against the specified object for strict equality 517 * (same sized lists of the equal ControlPoints). 518 * 519 * @param obj the object to compare with. 520 * @return <code>true</code> if this point is identical to that point; 521 * <code>false</code> otherwise. 522 */ 523 @Override 524 public boolean equals(Object obj) { 525 if (this == obj) 526 return true; 527 if ((obj == null) || (obj.getClass() != this.getClass())) 528 return false; 529 530 ControlPointNet that = (ControlPointNet)obj; 531 return this._matrix.equals(that._matrix); 532 } 533 534 /** 535 * Returns the hash code for this parameter. 536 * 537 * @return the hash code value. 538 */ 539 @Override 540 public int hashCode() { 541 int hash = 7; 542 543 int var_code = _matrix.hashCode(); 544 hash = hash * 31 + var_code; 545 546 return hash; 547 } 548 549 /** 550 * Returns the text representation of this control point matrix that consists of the 551 * the control points listed out. 552 * 553 * @return the text representation of this geometry element. 554 */ 555 public Text toText() { 556 TextBuilder tmp = TextBuilder.newInstance(); 557 tmp.append('{'); 558 tmp.append(_matrix.toText()); 559 tmp.append('}'); 560 Text txt = tmp.toText(); 561 TextBuilder.recycle(tmp); 562 return txt; 563 } 564 565 /** 566 * Returns the String representation of this control point matrix that consists of the 567 * control points listed out. 568 * 569 * @return the text representation of this geometry element. 570 */ 571 @Override 572 public String toString() { 573 return toText().toString(); 574 } 575 576 /** 577 * Holds the default XML representation for this object. 578 */ 579 protected static final XMLFormat<ControlPointNet> XML = new XMLFormat<ControlPointNet>(ControlPointNet.class) { 580 @Override 581 public ControlPointNet newInstance(Class<ControlPointNet> cls, InputElement xml) throws XMLStreamException { 582 ControlPointNet cpNet = FACTORY.object(); 583 cpNet._matrix = FastTable.newInstance(); 584 return cpNet; 585 } 586 587 @Override 588 public void read(InputElement xml, ControlPointNet obj) throws XMLStreamException { 589 590 FastTable<FastTable<ControlPoint>> matrix = FastTable.newInstance(); 591 while (xml.hasNext()) { 592 matrix.add(xml.get("Column", FastTable.class)); 593 } 594 obj._matrix.addAll(matrix); 595 596 } 597 598 @Override 599 public void write(ControlPointNet obj, OutputElement xml) throws XMLStreamException { 600 601 for (FastTable<ControlPoint> column : obj._matrix) { 602 xml.add(column, "Column", FastTable.class); 603 } 604 605 } 606 }; 607 608 /////////////////////// 609 // Factory creation. // 610 /////////////////////// 611 protected ControlPointNet() { 612 } 613 614 @SuppressWarnings("unchecked") 615 private static final ObjectFactory<ControlPointNet> FACTORY = new ObjectFactory<ControlPointNet>() { 616 @Override 617 protected ControlPointNet create() { 618 return new ControlPointNet(); 619 } 620 621 @Override 622 protected void cleanup(ControlPointNet obj) { 623 int ncol = obj._matrix.size(); 624 for (int i = 0; i < ncol; ++i) { 625 FastTable<ControlPoint> col = obj._matrix.get(i); 626 col.reset(); 627 } 628 obj._matrix.reset(); 629 obj._matrix = null; 630 } 631 }; 632 633 @SuppressWarnings("unchecked") 634 private static ControlPointNet copyOf(ControlPointNet original) { 635 ControlPointNet o = FACTORY.object(); 636 o._matrix = FastTable.newInstance(); 637 int ncol = original._matrix.size(); 638 for (int i = 0; i < ncol; ++i) { 639 List<ControlPoint> tbl = original._matrix.get(i); 640 FastTable<ControlPoint> tblCopy = FastTable.newInstance(); 641 int nrow = tbl.size(); 642 for (int j = 0; j < nrow; ++j) { 643 ControlPoint cp = tbl.get(j); 644 tblCopy.add(cp.copy()); 645 } 646 o._matrix.add(tblCopy); 647 } 648 return o; 649 } 650 651}