001/** 002 * PointComponent -- A list of PointArray objects. 003 * 004 * Copyright (C) 2009-2025, 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.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 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 java.text.MessageFormat; 021import java.util.Arrays; 022import java.util.Collection; 023import static java.util.Objects.requireNonNull; 024import javax.measure.converter.ConversionException; 025import javax.measure.quantity.Length; 026import javax.measure.unit.Unit; 027import javolution.context.ObjectFactory; 028import javolution.util.FastTable; 029import javolution.xml.XMLFormat; 030import javolution.xml.stream.XMLStreamException; 031 032/** 033 * A list that holds only {@link PointArray} objects. 034 * <p> 035 * WARNING: This list allows geometry to be stored in different units. If consistent units 036 * are required, then the user must specifically convert the list items. 037 * </p> 038 * 039 * <p> Modified by: Joseph A. Huwaldt </p> 040 * 041 * @author Joseph A. Huwaldt, Date: May 1, 2009 042 * @version February 17, 2025 043 */ 044@SuppressWarnings({"serial", "CloneableImplementsClone"}) 045public final class PointComponent extends AbstractPointGeomList<PointComponent, PointArray> { 046 047 private FastTable<PointArray> _list; 048 049 /** 050 * Return the list underlying this geometry list. 051 * 052 * @return The list underlying this geometry list. 053 */ 054 @Override 055 protected FastTable<PointArray> getList() { 056 return _list; 057 } 058 059 /** 060 * Returns a new, empty, preallocated or recycled <code>PointComponent</code> instance 061 * (on the stack when executing in a <code>StackContext</code>), that can store a list 062 * of {@link PointArray} objects. 063 * 064 * @return A new, empty PointComponent. 065 */ 066 public static PointComponent newInstance() { 067 PointComponent list = FACTORY.object(); 068 list._list = FastTable.newInstance(); 069 return list; 070 } 071 072 /** 073 * Returns a new, empty, preallocated or recycled <code>PointComponent</code> instance 074 * (on the stack when executing in a <code>StackContext</code>) with the specified 075 * name, that can store a list of {@link PointArray} objects. 076 * 077 * @param name The name to be assigned to this list (may be <code>null</code>). 078 * @return A new, empty PointComponent. 079 */ 080 public static PointComponent newInstance(String name) { 081 PointComponent list = PointComponent.newInstance(); 082 list.setName(name); 083 return list; 084 } 085 086 /** 087 * Return a PointComponent made up of the {@link PointArray} objects in the specified 088 * collection. 089 * 090 * @param name The name to be assigned to this list (may be <code>null</code>). 091 * @param elements A collection of PointArray elements. May not be null. 092 * @return A new PointComponent containing the elements in the specified collection. 093 */ 094 public static PointComponent valueOf(String name, Collection<? extends PointArray> elements) { 095 for (Object element : elements) { 096 requireNonNull(element, RESOURCES.getString("collectionElementsNullErr")); 097 if (!(element instanceof PointArray)) 098 throw new ClassCastException(MessageFormat.format( 099 RESOURCES.getString("listElementTypeErr"), "PointComponent", "PointArray")); 100 } 101 102 PointComponent list = PointComponent.newInstance(name); 103 list.addAll(elements); 104 105 return list; 106 } 107 108 /** 109 * Return a PointComponent made up of the {@link PointArray} objects in the specified 110 * array. 111 * 112 * @param name The name to be assigned to this list (may be <code>null</code>). 113 * @param elements A list of PointArray elements. May not be null. 114 * @return A new PointComponent containing the elements in the specified array. 115 */ 116 public static PointComponent valueOf(String name, PointArray... elements) { 117 requireNonNull(elements); 118 PointComponent list = PointComponent.newInstance(name); 119 list.addAll(Arrays.asList(elements)); 120 121 return list; 122 } 123 124 /** 125 * Return a PointComponent made up of the {@link PointArray} objects in the specified 126 * array. 127 * 128 * @param elements A list of PointArray elements. May not be null. 129 * @return A new PointComponent containing the elements in the specified array. 130 */ 131 public static PointComponent valueOf(PointArray... elements) { 132 return PointComponent.valueOf(null, elements); 133 } 134 135 /** 136 * Returns the range of elements in this list from the specified start and ending 137 * indexes. 138 * 139 * @param first index of the first element to return. 140 * @param last index of the last element to return. 141 * @return the list of elements in the given range from this list. 142 * @throws IndexOutOfBoundsException if the given index is out of range (index < 0 143 * || index ≥ size()) 144 */ 145 @Override 146 public PointComponent getRange(int first, int last) { 147 first = normalizeIndex(first); 148 last = normalizeIndex(last); 149 150 PointComponent list = PointComponent.newInstance(); 151 for (int i = first; i <= last; ++i) 152 list.add(get(i)); 153 return list; 154 } 155 156 /** 157 * Returns an new {@link PointString} with the elements in this list in reverse order. 158 * 159 * @return A new PointComponent with the elements in this list in reverse order. 160 */ 161 @Override 162 public PointComponent reverse() { 163 PointComponent list = PointComponent.newInstance(); 164 copyState(list); 165 int size = this.size(); 166 for (int i = size - 1; i >= 0; --i) { 167 list.add(get(i)); 168 } 169 return list; 170 } 171 172 /** 173 * Return the equivalent of this list converted to the specified number of physical 174 * dimensions. If the number of dimensions is greater than this element, then zeros 175 * are added to the additional dimensions. If the number of dimensions is less than 176 * this element, then the extra dimensions are simply dropped (truncated). If the new 177 * dimensions are the same as the dimension of this element, then this list is simply 178 * returned. 179 * 180 * @param newDim The dimension of the element to return. 181 * @return The equivalent of this list converted to the new dimensions. 182 */ 183 @Override 184 public PointComponent toDimension(int newDim) { 185 if (getPhyDimension() == newDim) 186 return this; 187 PointComponent newList = PointComponent.newInstance(); 188 copyState(newList); 189 int size = this.size(); 190 for (int i = 0; i < size; ++i) { 191 PointArray element = this.get(i); 192 newList.add(element.toDimension(newDim)); 193 } 194 return newList; 195 } 196 197 /** 198 * Returns the equivalent to this list but with <I>all</I> the elements stated in the 199 * specified unit. 200 * 201 * @param unit the length unit of the list to be returned. May not be null. 202 * @return an equivalent to this list but stated in the specified unit. 203 * @throws ConversionException if the the input unit is not a length unit. 204 */ 205 @Override 206 public PointComponent to(Unit<Length> unit) { 207 requireNonNull(unit); 208 PointComponent list = PointComponent.newInstance(); 209 copyState(list); 210 int size = this.size(); 211 for (int i = 0; i < size; ++i) { 212 PointArray e = this.get(i); 213 list.add(e.to(unit)); 214 } 215 return list; 216 } 217 218 /** 219 * Returns a copy of this <code>PointComponent</code> instance 220 * {@link javolution.context.AllocatorContext allocated} by the calling thread 221 * (possibly on the stack). 222 * 223 * @return an identical and independent copy of this object. 224 */ 225 @Override 226 public PointComponent copy() { 227 return copyOf(this); 228 } 229 230 /** 231 * Return a copy of this object with any transformations or subranges removed 232 * (applied). 233 * 234 * @return A copy of this list with any sub-element transformations or subranges 235 * removed. 236 */ 237 @Override 238 public PointComponent copyToReal() { 239 PointComponent newList = PointComponent.newInstance(); 240 copyState(newList); 241 int size = this.size(); 242 for (int i = 0; i < size; ++i) { 243 PointArray element = this.get(i); 244 newList.add(element.copyToReal()); 245 } 246 return newList; 247 } 248 249 /** 250 * Return the total number of quadrilateral panels in this component. 251 * 252 * @return the total number of panels in this component. 253 * @throws IndexOutOfBoundsException if the strings in any array in this component 254 * have different lengths. 255 */ 256 public int getNumberOfPanels() throws IndexOutOfBoundsException { 257 int sum = 0; 258 int size = this.size(); 259 for (int i = 0; i < size; ++i) { 260 PointArray e = this.get(i); 261 sum += e.getNumberOfPanels(); 262 } 263 return sum; 264 } 265 266 /** 267 * Returns transformed version of this element. The returned object implements 268 * {@link GeomTransform} and contains transformed versions of the contents of this 269 * list as children. 270 * 271 * @param transform The transform to apply to this geometry element. May not be null. 272 * @return A transformed version of this geometry element. 273 * @throws DimensionException if this element is not 3D. 274 */ 275 @Override 276 public PointComponent getTransformed(GTransform transform) { 277 requireNonNull(transform); 278 PointComponent list = PointComponent.newInstance(); 279 copyState(list); 280 int size = this.size(); 281 for (int i = 0; i < size; ++i) { 282 PointArray element = this.get(i); 283 list.add(element.getTransformed(transform)); 284 } 285 return list; 286 } 287 288 /** 289 * Replaces the {@link PointArray} at the specified position in this list with the 290 * specified element. Null elements are ignored. The input element must have the same 291 * physical dimensions as the other items in this list, or an exception is thrown. 292 * 293 * @param index The index of the element to replace (0 returns the 1st element, -1 294 * returns the last, -2 returns the 2nd from last, etc). 295 * @param element The element to be stored at the specified position. May not be null. 296 * @return The element previously at the specified position in this list. 297 * @throws java.lang.IndexOutOfBoundsException - if <code>index > size()</code> 298 * @throws DimensionException if the input element's dimensions are different from 299 * this list's dimensions. 300 */ 301 @Override 302 public PointArray set(int index, PointArray element) { 303 return super.set(index, requireNonNull(element)); 304 } 305 306 /** 307 * Inserts the specified {@link PointArray} at the specified position in this list. 308 * Shifts the element currently at that position (if any) and any subsequent elements 309 * to the right (adds one to their indices). Null values are ignored. The input 310 * value must have the same physical dimensions as the other items in this list, or 311 * an exception is thrown. 312 * <p> 313 * Note: If this method is used concurrent access must be synchronized (the list is 314 * not thread-safe). 315 * </p> 316 * 317 * @param index the index at which the specified element is to be inserted. (0 returns 318 * the 1st element, -1 returns the last, -2 returns the 2nd from last, 319 * etc). 320 * @param value the element to be inserted. May not be null. 321 * @throws IndexOutOfBoundsException if <code>index > size()</code> 322 * @throws DimensionException if the input value dimensions are different from 323 * this list's dimensions. 324 */ 325 @Override 326 public void add(int index, PointArray value) { 327 super.add(index, requireNonNull(value)); 328 } 329 330 /** 331 * Inserts all of the {@link PointArray} objects in the specified collection into this 332 * list at the specified position. Shifts the element currently at that position (if 333 * any) and any subsequent elements to the right (increases their indices). The new 334 * elements will appear in this list in the order that they are returned by the 335 * specified collection's iterator. The behavior of this operation is unspecified if 336 * the specified collection is modified while the operation is in progress. Note that 337 * this will occur if the specified collection is this list, and it's nonempty. The 338 * input elements must have the same physical dimensions as the other items in this 339 * list, or an exception is thrown. 340 * 341 * @param index index at which to insert first element from the specified collection. 342 * @param c Elements to be inserted into this collection. May not be null. 343 * @return <code>true</code> if this collection changed as a result of the call. 344 * @throws DimensionException if the input element's dimensions are different from 345 * this list's dimensions. 346 */ 347 @Override 348 public boolean addAll(int index, Collection<? extends PointArray> c) { 349 int thisSize = this.size(); 350 for (Object element : c) { 351 requireNonNull(element, RESOURCES.getString("collectionElementsNullErr")); 352 if (!(element instanceof PointArray)) 353 throw new ClassCastException(MessageFormat.format( 354 RESOURCES.getString("listElementTypeErr"), "PointComponent", "PointArray")); 355 if (thisSize != 0) { 356 if (((GeomElement)element).getPhyDimension() != this.getPhyDimension()) 357 throw new DimensionException(RESOURCES.getString("dimensionErr")); 358 } 359 } 360 return super.addAll(index, c); 361 } 362 363 /** 364 * Holds the default XML representation for this object. 365 */ 366 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 367 protected static final XMLFormat<PointComponent> XML = new XMLFormat<PointComponent>(PointComponent.class) { 368 369 @Override 370 public PointComponent newInstance(Class<PointComponent> cls, XMLFormat.InputElement xml) throws XMLStreamException { 371 PointComponent obj = PointComponent.newInstance(); 372 return obj; 373 } 374 375 @Override 376 public void read(XMLFormat.InputElement xml, PointComponent obj) throws XMLStreamException { 377 AbstractPointGeomList.XML.read(xml, obj); // Call parent read. 378 } 379 380 @Override 381 public void write(PointComponent obj, XMLFormat.OutputElement xml) throws XMLStreamException { 382 AbstractPointGeomList.XML.write(obj, xml); // Call parent write. 383 } 384 }; 385 386 ////////////////////// 387 // Factory Creation // 388 ////////////////////// 389 private static final ObjectFactory<PointComponent> FACTORY = new ObjectFactory<PointComponent>() { 390 @Override 391 protected PointComponent create() { 392 return new PointComponent(); 393 } 394 395 @Override 396 protected void cleanup(PointComponent obj) { 397 obj.reset(); 398 } 399 }; 400 401 /** 402 * Recycles a case instance immediately (on the stack when executing in a 403 * StackContext). 404 * 405 * @param instance The instance to be recycled. 406 */ 407 public static void recycle(PointComponent instance) { 408 FACTORY.recycle(instance); 409 } 410 411 /** 412 * Do not allow the default constructor to be used except by subclasses. 413 */ 414 protected PointComponent() { 415 } 416 417 private static PointComponent copyOf(PointComponent original) { 418 PointComponent o = PointComponent.newInstance(); 419 original.copyState(o); 420 int size = original.size(); 421 for (int i = 0; i < size; ++i) { 422 PointArray element = original.get(i); 423 o.add(element.copy()); 424 } 425 return o; 426 } 427}