001/** 002 * TriangleList -- A concrete list of GeomTriangle objects. 003 * 004 * Copyright (C) 2015-2017, by 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 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 Library 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 static geomss.geom.AbstractGeomElement.RESOURCES; 021import jahuwaldt.js.param.Parameter; 022import java.text.MessageFormat; 023import java.util.Collection; 024import java.util.List; 025import static java.util.Objects.nonNull; 026import static java.util.Objects.requireNonNull; 027import javax.measure.converter.ConversionException; 028import javax.measure.quantity.Area; 029import javax.measure.quantity.Dimensionless; 030import javax.measure.quantity.Length; 031import javax.measure.unit.Unit; 032import javolution.context.ObjectFactory; 033import javolution.context.StackContext; 034import javolution.util.FastMap; 035import javolution.util.FastTable; 036import javolution.xml.XMLFormat; 037import javolution.xml.stream.XMLStreamException; 038import org.jscience.mathematics.number.Integer64; 039 040/** 041 * A concrete list of {@link GeomTriangle} objects. This list is similar 042 * to a GeomList, but has methods specifically designed for working with large 043 * sets of triangles. All the triangles in a list must have the same physical dimensions. 044 * <p> 045 * WARNING: This list allows geometry to be stored in different units. If consistent units 046 * are required, then the user must specifically convert the list items. 047 * </p> 048 * 049 * <p> Modified by: Joseph A. Huwaldt </p> 050 * 051 * @author Joseph A. Huwaldt, Date: August 28, 2015 052 * @version January 31, 2017 053 * 054 * @param <E> The type of triangle stored in this list. 055 */ 056@SuppressWarnings({"serial", "CloneableImplementsClone"}) 057public final class TriangleList<E extends GeomTriangle> extends AbstractGeomList<TriangleList,E> { 058 059 private FastTable<E> _list; 060 061 /** 062 * Return the list underlying this geometry list. 063 * 064 * @return The list underlying this geometry list. 065 */ 066 @Override 067 protected FastTable<E> getList() { 068 return _list; 069 } 070 071 /** 072 * Returns a new, empty, preallocated or recycled <code>TriangleList</code> 073 * instance (on the stack when executing in a <code>StackContext</code>) 074 * that can store a list of {@link GeomTriangle} objects. 075 * 076 * @return A new, empty TriangleList. 077 */ 078 public static TriangleList newInstance() { 079 TriangleList list = FACTORY.object(); 080 list._list = FastTable.newInstance(); 081 return list; 082 } 083 084 /** 085 * Returns a new, empty, preallocated or recycled <code>TriangleList</code> 086 * instance (on the stack when executing in a <code>StackContext</code>) 087 * with the specified name, that can store a list of {@link GeomTriangle} 088 * objects. 089 * 090 * @param name The name to be assigned to this list (may be <code>null</code>). 091 * @return A new, empty TriangleList with the specified name. 092 */ 093 public static TriangleList newInstance(String name) { 094 TriangleList list = TriangleList.newInstance(); 095 list.setName(name); 096 return list; 097 } 098 099 /** 100 * Return a TriangleList containing the {@link GeomTriangle} objects in the 101 * specified collection. 102 * 103 * @param <E> The type of triangle contained in the returned list of triangles. 104 * @param name The name to be assigned to this list (may be 105 * <code>null</code>). 106 * @param elements A collection of triangle elements. May not be null. 107 * @return A new TriangleList containing the elements in the specified collection. 108 */ 109 public static <E extends GeomTriangle> TriangleList<E> valueOf(String name, Collection<E> elements) { 110 for (Object element : elements) { 111 requireNonNull(element, RESOURCES.getString("collectionElementsNullErr")); 112 if (!(element instanceof GeomTriangle)) 113 throw new ClassCastException(MessageFormat.format( 114 RESOURCES.getString("listElementTypeErr"), "TriangleList", "GeomTriangle")); 115 } 116 117 TriangleList<E> list = TriangleList.newInstance(name); 118 list.addAll(elements); 119 120 return list; 121 } 122 123 /** 124 * Return a TriangleList containing the {@link GeomTriangle} objects in the 125 * specified list. 126 * 127 * @param <E> The type of triangle contained in the returned list of triangles. 128 * @param name The name to be assigned to this list (may be 129 * <code>null</code>). 130 * @param elements A list of triangle elements. May not be null. 131 * @return A new TriangleList containing the elements in the specified list. 132 */ 133 public static <E extends GeomTriangle> TriangleList<E> valueOf(String name, E... elements) { 134 requireNonNull(elements); 135 TriangleList<E> list = TriangleList.newInstance(name); 136 list.addAll(elements); 137 138 return list; 139 } 140 141 /** 142 * Return a TriangleList containing the {@link GeomTriangle} objects in the 143 * specified array. 144 * 145 * @param <E> The type of triangle contained in the returned list of triangles. 146 * @param elements An array of triangle elements. May not be null. 147 * @return A new TriangleList containing the elements in the specified array. 148 */ 149 public static <E extends GeomTriangle> TriangleList<E> valueOf(E... elements) { 150 return TriangleList.valueOf(null, elements); 151 } 152 153 /** 154 * Return a TriangleList containing the triangles 155 * 156 * @param vertices A list of all the vertices in the list of triangles. May not be null. 157 * @param indexes An array of indexes into the vertices list. Each set of 3 consecutive 158 * indexes into the vertices list defines a triangle. May not be null. 159 * @return A new TriangleList containing the elements in the specified collection. 160 */ 161 public static TriangleList<Triangle> valueOf(List<? extends GeomPoint> vertices, int[] indexes) { 162 requireNonNull(vertices); 163 requireNonNull(indexes); 164 165 TriangleList<Triangle> list = TriangleList.newInstance(); 166 int numTris = indexes.length/3; 167 int pos = 0; 168 for (int i=0; i < numTris; ++i) { 169 GeomPoint p1 = vertices.get(indexes[pos++]); 170 GeomPoint p2 = vertices.get(indexes[pos++]); 171 GeomPoint p3 = vertices.get(indexes[pos++]); 172 Triangle tri = Triangle.valueOf(p1, p2, p3); 173 list.add(tri); 174 } 175 176 return list; 177 } 178 179 /** 180 * Return a TriangleList containing a simple triangulation of the input 181 * array of quadrilateral panels. The input quads are triangulated by 182 * dividing each quad into two triangles. Any subranges or transforms on the 183 * input points is lost by this triangulation. 184 * 185 * @param quads An array of quadrilateral panels (defined by a topologically 186 * rectangular array of points). May not be null. 187 * @return A new TriangleList containing a triangulation of the input quads. 188 */ 189 public static TriangleList<Triangle> triangulateQuads(PointArray<? extends GeomPoint> quads) { 190 requireNonNull(quads); 191 192 // Create an empty triangle list. 193 TriangleList<Triangle> list = TriangleList.newInstance(); 194 195 // Loop over all the panels in the array and turn each into two triangles. 196 int numRows = quads.size(); 197 int numCols = quads.get(0).size(); 198 199 // Loop over the strings. 200 for (int row = 1; row < numRows; ++row) { 201 PointString<?> str1 = quads.get(row - 1); 202 PointString<?> str2 = quads.get(row); 203 204 // Loop over points in strings. 205 for (int col = 1; col < numCols; ++col) { 206 // Get quad points. 207 Point p1 = str1.get(col - 1).immutable(); 208 Point p2 = str1.get(col).immutable(); 209 Point p3 = str2.get(col).immutable(); 210 Point p4 = str2.get(col - 1).immutable(); 211 212 // Form the 1st triangle: p1,p2,p4 213 Triangle tri = Triangle.valueOf(p1,p2,p4); 214 list.add(tri); 215 216 // Form the 2nd triangle: p4,p2,p3 217 tri = Triangle.valueOf(p4,p2,p3); 218 list.add(tri); 219 } 220 } 221 222 return list; 223 } 224 225 /** 226 * Returns the number of physical dimensions of this geometry element. This 227 * implementation always returns the physical dimension of the underlying 228 * {@link Triangle} objects or 0 if this list has no Triangle objects in it. 229 * 230 * @return The number of physical dimensions of this geometry element. 231 */ 232 @Override 233 public int getPhyDimension() { 234 if (isEmpty()) 235 return 0; 236 return get(0).getPhyDimension(); 237 } 238 239 /** 240 * Returns the range of elements in this list from the specified start and 241 * ending indexes. 242 * 243 * @param first index of the first element to return (0 returns the 1st 244 * element, -1 returns the last, etc). 245 * @param last index of the last element to return (0 returns the 1st 246 * element, -1 returns the last, etc). 247 * @return the list of elements in the given range from this list. 248 * @throws IndexOutOfBoundsException if the given index is out of range: 249 * <code>index >= size()</code> 250 */ 251 @Override 252 public TriangleList<E> getRange(int first, int last) { 253 first = normalizeIndex(first); 254 last = normalizeIndex(last); 255 256 TriangleList list = TriangleList.newInstance(); 257 for (int i=first; i <= last; ++i) 258 list.add(get(i)); 259 return list; 260 } 261 262 /** 263 * Replaces the {@link GeomTriangle} at the specified position in this list with the 264 * specified element. Null elements are ignored. The input element must have the same 265 * physical dimensions as the other items in this list, or an exception is thrown. 266 * 267 * @param index The index of the element to replace (0 returns the 1st element, -1 268 * returns the last, -2 returns the 2nd from last, etc). 269 * @param element The element to be stored at the specified position. 270 * <code>null</code> elements are ignored. 271 * @return The element previously at the specified position in this list. May not be 272 * null. 273 * @throws java.lang.IndexOutOfBoundsException - if <code>index > size()</code> 274 * @throws DimensionException if the input element's dimensions are different from 275 * this list's dimensions. 276 */ 277 @Override 278 public E set(int index, E element) { 279 requireNonNull(element); 280 if (size() > 0 && element.getPhyDimension() != this.getPhyDimension()) 281 throw new DimensionException(RESOURCES.getString("dimensionErr")); 282 return super.set(index, element); 283 } 284 285 /** 286 * Inserts the specified {@link GeomTriangle} at the specified position in this list. 287 * Shifts the element currently at that position (if any) and any subsequent elements 288 * to the right (adds one to their indices). Null values are ignored. The input 289 * element must have the same physical dimensions as the other items in this list, or 290 * an exception is thrown. 291 * <p> 292 * Note: If this method is used concurrent access must be synchronized (the list is 293 * not thread-safe). 294 * </p> 295 * 296 * @param index the index at which the specified element is to be inserted. (0 returns 297 * the 1st element, -1 returns the last, -2 returns the 2nd from last, 298 * etc). 299 * @param value The element to be inserted. May not be null. 300 * @throws IndexOutOfBoundsException if <code>index > size()</code> 301 * @throws DimensionException if the input element's dimensions are different from 302 * this list's dimensions. 303 */ 304 @Override 305 public void add(int index, E value) { 306 requireNonNull(value); 307 if (size() > 0 && value.getPhyDimension() != this.getPhyDimension()) 308 throw new DimensionException(RESOURCES.getString("dimensionErr")); 309 super.add(index, value); 310 } 311 312 /** 313 * Inserts all of the {@link GeomTriangle} objects in the specified collection into 314 * this list at the specified position. Shifts the element currently at that position 315 * (if any) and any subsequent elements to the right (increases their indices). The 316 * new elements will appear in this list in the order that they are returned by the 317 * specified collection's iterator. The behavior of this operation is unspecified if 318 * the specified collection is modified while the operation is in progress. Note that 319 * this will occur if the specified collection is this list, and it's nonempty. The 320 * input elements must have the same physical dimensions as the other items in this 321 * list, or an exception is thrown. 322 * 323 * @param index index at which to insert first element from the specified collection. 324 * @param c Elements to be inserted into this collection. May not be null. 325 * @return <code>true</code> if this collection changed as a result of the call. 326 * @throws DimensionException if the input element's dimensions are different from 327 * this list's dimensions. 328 */ 329 @Override 330 public boolean addAll(int index, Collection<? extends E> c) { 331 int thisSize = this.size(); 332 for (Object element : c) { 333 requireNonNull(element, RESOURCES.getString("collectionElementsNullErr")); 334 if (!(element instanceof GeomTriangle)) 335 throw new ClassCastException(MessageFormat.format( 336 RESOURCES.getString("listElementTypeErr"), "TriangleList", "GeomTriangle")); 337 if (thisSize != 0) { 338 if (((GeomElement)element).getPhyDimension() != this.getPhyDimension()) 339 throw new DimensionException(RESOURCES.getString("dimensionErr")); 340 } 341 } 342 return super.addAll(index,c); 343 } 344 345 /** 346 * Returns an new {@link TriangleList} with the elements in this list in reverse 347 * order. 348 * 349 * @return A new TriangleList with the elements in this list in reverse order. 350 */ 351 @Override 352 public TriangleList<E> reverse() { 353 TriangleList newList = TriangleList.newInstance(); 354 copyState(newList); 355 int size = this.size(); 356 for (int i=size-1; i >= 0; --i) { 357 newList.add(get(i)); 358 } 359 return newList; 360 } 361 362 /** 363 * Return a new triangle list that is identical to this one, but with any degenerate 364 * triangles removed. 365 * 366 * @param tol The tolerance for determining if a triangle is degenerate. A value of 367 * <code>null</code> would indicate that exactly zero area is required to 368 * be degenerate. 369 * @return A new triangle list that is identical to this one, but with any degenerate 370 * triangles removed. 371 */ 372 public TriangleList<E> removeDegenerate(Parameter<Length> tol) { 373 TriangleList<E> newList = TriangleList.newInstance(); 374 copyState(newList); 375 int size = size(); 376 for (int i=0; i < size; ++i) { 377 E tri = get(i); 378 if (!tri.isDegenerate(tol)) 379 newList.add(tri); 380 } 381 return newList; 382 } 383 384 /** 385 * Return the total surface area of this list of triangles. This is the sum of the 386 * area of all the triangles in this list. 387 * 388 * @return The surface area of all the triangles in this list. 389 */ 390 public Parameter<Area> getArea() { 391 StackContext.enter(); 392 try { 393 394 Parameter<Area> area = Parameter.ZERO_AREA.to(getUnit().pow(2).asType(Area.class)); 395 for (E tri : this) 396 area = area.plus(tri.getArea()); 397 398 return StackContext.outerCopy(area); 399 400 } finally { 401 StackContext.exit(); 402 } 403 } 404 405 /** 406 * Return a copy of this list converted to the specified number of physical 407 * dimensions. If the number of dimensions is greater than this element, 408 * then zeros are added to the additional dimensions. If the number of 409 * dimensions is less than this element, then the extra dimensions are 410 * simply dropped (truncated). If the new dimensions are the same as the 411 * dimension of this element, then this list is simply returned. 412 * 413 * @param newDim The dimension of the element to return. 414 * @return A copy of this list converted to the new dimensions. 415 */ 416 @Override 417 public TriangleList<E> toDimension(int newDim) { 418 TriangleList newList = TriangleList.newInstance(); 419 copyState(newList); 420 int size = this.size(); 421 for (int i=0; i < size; ++i) { 422 E element = this.get(i); 423 newList.add(element.toDimension(newDim)); 424 } 425 return newList; 426 } 427 428 /** 429 * Returns the equivalent to this list but with <I>all</I> the elements stated in the 430 * specified unit. 431 * 432 * @param unit the length unit of the list to be returned. May not be null. 433 * @return an equivalent to this list but stated in the specified unit. 434 * @throws ConversionException if the the input unit is not a length unit. 435 */ 436 @Override 437 public TriangleList<E> to(Unit<Length> unit) { 438 requireNonNull(unit); 439 TriangleList newList = TriangleList.newInstance(); 440 copyState(newList); 441 int size = this.size(); 442 for (int i = 0; i < size; ++i) { 443 E element = this.get(i); 444 newList.add(element.to(unit)); 445 } 446 return newList; 447 } 448 449 /** 450 * Returns a copy of this <code>TriangleList</code> instance 451 * {@link javolution.context.AllocatorContext allocated} by the calling 452 * thread (possibly on the stack). 453 * 454 * @return an identical and independent copy of this object. 455 */ 456 @Override 457 public TriangleList<E> copy() { 458 return copyOf(this); 459 } 460 461 /** 462 * Return a copy of this object with any transformations or subranges 463 * removed (applied). 464 * 465 * @return A copy of this list with any transformations or subranges removed. 466 */ 467 @Override 468 public TriangleList<Triangle> copyToReal() { 469 TriangleList newList = TriangleList.newInstance(); 470 copyState(newList); 471 int size = this.size(); 472 for (int i=0; i < size; ++i) { 473 E element = this.get(i); 474 newList.add(element.copyToReal()); 475 } 476 return newList; 477 } 478 479 /** 480 * Returns transformed version of this element. The returned object 481 * implements {@link GeomTransform} and contains transformed versions of the 482 * contents of this list as children. Any list elements that are not 483 * transformable will simply be added to the output list without 484 * transformation. 485 * 486 * @param transform The transform to apply to this geometry element. May not be null. 487 * @return A transformed version of this geometry element. 488 * @throws DimensionException if this element is not 3D. 489 */ 490 @Override 491 public TriangleList<TriangleTrans> getTransformed(GTransform transform) { 492 requireNonNull(transform); 493 TriangleList list = TriangleList.newInstance(); 494 copyState(list); // Copy over the super-class state for this object to the new one. 495 int size = this.size(); 496 for (int i=0; i < size; ++i) { 497 E element = this.get(i); 498 if (element instanceof Transformable) 499 list.add(((Transformable)element).getTransformed(transform)); 500 else 501 list.add(element); 502 } 503 return list; 504 } 505 506 /** 507 * Convert this list of Triangle objects into a TriangleVertData structure with a 508 * non-repeating (unique) list of vertices, non-degenerate triangles defined by 509 * indices into the list of vertices, areas of all the triangles, and normals for all 510 * the triangles. This is handy for converting between this list of triangles and 511 * various triangulated file formats. 512 * 513 * @param tol The position tolerance used to determine if two vertices are coincident. 514 * Pass <code>null</code> or 0 if all vertices are to be retained. 515 * @return A TriangleVertData structure with information about the triangles in this 516 * list. 517 */ 518 public TriangleVertData toVertData(Parameter<Length> tol) { 519 520 // Create lists to contain per-triangle data. 521 FastTable<Parameter<Area>> areas = FastTable.newInstance(); // The area of each triangle. 522 FastTable<GeomVector<Dimensionless>> normals = FastTable.newInstance(); // The normal vector of each triangle. 523 524 // Remove degenerate triangles. 525 TriangleList<E> triLst = this; 526 if (nonNull(tol) && !tol.isApproxZero()) 527 triLst = triLst.removeDegenerate(tol); 528 529 // Create an index into the vertex list for the vertices of each triangle. 530 int numTris = triLst.size(); 531 int[] triIndexes = new int[numTris * 3]; 532 533 // Identify all the unique vertices in the list of triangles. 534 PointString<GeomPoint> verts = uniqueVerts(triLst, triIndexes, tol); 535 536 // Fill in the lists of areas and normal vectors. 537 for (int i = 0; i < numTris; ++i) { 538 E tri = triLst.get(i); 539 areas.add(tri.getArea()); 540 normals.add(tri.getNormal()); 541 } 542 543 // Create the output data structure and fill it in. 544 TriangleVertData output = TriangleVertData.newInstance(); 545 output.vertices = verts; 546 output.numTris = numTris; 547 output.tris = triIndexes; 548 output.areas = areas; 549 output.normals = normals; 550 551 return output; 552 } 553 554 /** 555 * Return a map of unique vertices for each triangle in this list. Coincident vertices 556 * are determined using the supplied tolerance on vertex position. 557 * 558 * @param triLst A list of non-degenerate Triangle objects. 559 * @param triIndexes An array of indexes into "verts" for each triangle as consecutive 560 * sets of 3 indexes. 561 * @return A new vertex list with a unique set of vertices. The input triIndexes array 562 * is also modified to use indexes into the output list of vertices. 563 * @param tol The position tolerance used to determine if two vertices are 564 * coincident. 565 */ 566 private <E extends GeomTriangle> PointString<GeomPoint> uniqueVerts(TriangleList<E> triLst, 567 int[] triIndexes, Parameter<Length> tol) { 568 569 /* Algorithm is similar to the one for edges in "admesh": https://github.com/admesh/admesh 570 1. Each vertex is turned into a TriVert object. 571 2. Use a hash map to store the vertices. The hash code for TriVert is such that 572 any vertex with vertices within "tol" of each other should return the same 573 hash code. 574 3. Repeat for all triangles. If a vertex with the same hash code already exists 575 in the hash map, then a duplicate vertex has been found. 576 */ 577 FastMap<TriVert, TriVert> vertMap = FastMap.newInstance(); 578 PointString<GeomPoint> vertLst = PointString.newInstance(); 579 GeomPoint[] triVerts = new GeomPoint[3]; 580 581 // Loop over all the triangles in this list. 582 int vert_idx = 0; 583 int numTris = triLst.size(); 584 for (int triIdx = 0; triIdx < numTris; ++triIdx) { 585 GeomTriangle tri = triLst.get(triIdx); 586 if (!tri.isDegenerate(tol)) { 587 tri.getPoints(triVerts); 588 589 // Loop over the three vertices of the triangle. 590 for (int i = 0; i < 3; ++i) { 591 // Get the vertex point for this triangle. 592 GeomPoint vertex = triVerts[i]; 593 594 // Form a vertex object using this vertex point. 595 TriVert vThis = TriVert.newInstance(vert_idx, vertex, tol); 596 597 // Insert this vertex into the hash map and look for a duplicate. 598 boolean exists = vertMap.containsKey(vThis); 599 if (exists) { 600 // Found a duplicate vertex; get the original from the map. 601 TriVert vOther = vertMap.get(vThis); 602 603 // Index this triangles vertex to the original one. 604 triIndexes[triIdx * 3 + i] = vOther.vert_index; 605 606 // Recycle the redudant vertex. 607 TriVert.recycle(vThis); 608 609 } else { 610 // If the vertex isn't in the map yet, add it. 611 vertMap.put(vThis, vThis); 612 613 // Add the vertex to the output list as well (as position "vert_idx"). 614 vertLst.add(vertex); 615 616 // Index this triangle's vertex to the correct position in the list. 617 triIndexes[triIdx * 3 + i] = vert_idx; 618 619 // Prepare for next vertex. 620 ++vert_idx; 621 } 622 } 623 } 624 } 625 626 // Recycle the TriVert objects now that we are done with them. 627 for (FastMap.Entry<TriVert, TriVert> e = vertMap.head(), end = vertMap.tail(); (e = e.getNext()) != end;) { 628 TriVert v = e.getKey(); 629 TriVert.recycle(v); 630 } 631 FastMap.recycle(vertMap); 632 633 return vertLst; 634 } 635 636 /** 637 * Holds the default XML representation for this object. 638 */ 639 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 640 protected static final XMLFormat<TriangleList> XML = new XMLFormat<TriangleList>(TriangleList.class) { 641 642 @Override 643 public TriangleList newInstance(Class<TriangleList> cls, XMLFormat.InputElement xml) throws XMLStreamException { 644 TriangleList obj = TriangleList.newInstance(); 645 return obj; 646 } 647 648 @Override 649 public void read(XMLFormat.InputElement xml, TriangleList obj) throws XMLStreamException { 650 AbstractGeomList.XML.read(xml, obj); // Call parent read. 651 } 652 653 @Override 654 public void write(TriangleList obj, XMLFormat.OutputElement xml) throws XMLStreamException { 655 AbstractGeomList.XML.write(obj, xml); // Call parent write. 656 } 657 }; 658 659 ////////////////////// 660 // Factory Creation // 661 ////////////////////// 662 private static final ObjectFactory<TriangleList> FACTORY = new ObjectFactory<TriangleList>() { 663 @Override 664 protected TriangleList create() { 665 return new TriangleList(); 666 } 667 668 @Override 669 protected void cleanup(TriangleList obj) { 670 obj.reset(); 671 } 672 }; 673 674 /** 675 * Recycles a TriangleList instance immediately (on the stack when executing in a 676 * StackContext). 677 * 678 * @param instance The instance to be recycled. 679 */ 680 public static void recycle(TriangleList instance) { 681 FACTORY.recycle(instance); 682 } 683 684 /** 685 * Do not allow the default constructor to be used except by subclasses. 686 */ 687 protected TriangleList() {} 688 689 private static TriangleList copyOf(TriangleList<? extends GeomElement> original) { 690 TriangleList o = TriangleList.newInstance(); 691 original.copyState(o); 692 int size = original.size(); 693 for (int i=0; i < size; ++i) { 694 GeomElement element = original.get(i); 695 o.add(element.copy()); 696 } 697 return o; 698 } 699 700 /** 701 * An object that represents a vertex of a triangle. This is used to sort out a unique 702 * set of vertices. 703 */ 704 private static class TriVert { 705 706 private static final int ONE = 1 << 16; 707 708 public int vert_index; // The index to this vertex in the full vertex list. 709 public int hashCode; // The hash code for this vertex. 710 private FastTable<Integer64> vertFP; // The vertex coordinates in fixed point. 711 712 /** 713 * Return a new TriVert object using the supplied vertex index, vertex and 714 * tolerance for use in determining if this vertex is coincident with another. 715 * 716 * @param vert_index The index in the full vertex list of this vertex. 717 * @param vert The actual vertex point represented by this TriVert object. 718 * @param tol The tolerance for determining if two vertices are coincident. 719 * @return A new TriVert object. 720 */ 721 public static TriVert newInstance(int vert_index, GeomPoint vert, Parameter<Length> tol) { 722 TriVert o = FACTORY.object(); 723 o.vert_index = vert_index; 724 725 // Round off the input vertex coordinates and convert them to fixed-point integers. 726 double tolv = 0; 727 if (nonNull(tol) && !tol.isApproxZero()) 728 tolv = 2.0 * tol.doubleValue(vert.getUnit()); 729 730 o.vertFP = FastTable.newInstance(); 731 int numDims = vert.getPhyDimension(); 732 for (int i = 0; i < numDims; ++i) { 733 double value = vert.getValue(i); 734 value = approxValue(value, tolv); 735 int fp = toFix(value); 736 o.vertFP.add(Integer64.valueOf(fp)); 737 } 738 739 // Pre-compute the hash code. 740 o.hashCode = o.vertFP.hashCode(); 741 742 return o; 743 } 744 745 @Override 746 public boolean equals(Object obj) { 747 if (this == obj) 748 return true; 749 if ((obj == null) || (obj.getClass() != this.getClass())) 750 return false; 751 752 TriVert that = (TriVert)obj; 753 return this.vertFP.equals(that.vertFP); 754 } 755 756 @Override 757 public int hashCode() { 758 return hashCode; 759 } 760 761 // Convert a double to fixed-point integer. 762 public static int toFix(double val) { 763 return (int)(val * ONE); 764 } 765 766 // Round a float to the specified tolerance. 767 private static double approxValue(double value, double tolv) { 768 if (tolv > 0.0) 769 // Round to tolerance (approximately). 770 return tolv * Math.round(value / tolv); 771 return value; 772 } 773 774 public static void recycle(TriVert instance) { 775 FACTORY.recycle(instance); 776 } 777 778 private TriVert() { } 779 780 private static final ObjectFactory<TriVert> FACTORY = new ObjectFactory<TriVert>() { 781 @Override 782 protected TriVert create() { 783 return new TriVert(); 784 } 785 786 @Override 787 protected void cleanup(TriVert obj) { 788 FastTable.recycle(obj.vertFP); 789 obj.vertFP = null; 790 } 791 }; 792 } 793}