001/* 002 * TriangleTrans -- A GeomTransform that has a Triangle for a child. 003 * 004 * Copyright (C) 2015-2018, by 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 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 * Library General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom; 023 024import jahuwaldt.js.param.Parameter; 025import java.text.MessageFormat; 026import java.util.Objects; 027import static java.util.Objects.requireNonNull; 028import javax.measure.converter.ConversionException; 029import javax.measure.quantity.Area; 030import javax.measure.quantity.Dimensionless; 031import javax.measure.quantity.Length; 032import javax.measure.unit.Unit; 033import javax.swing.event.ChangeListener; 034import javolution.context.ObjectFactory; 035import javolution.lang.Immutable; 036import javolution.xml.XMLFormat; 037import javolution.xml.stream.XMLStreamException; 038 039/** 040 * A {@link GeomTransform} object that refers to a {@link Triangle} object 041 * and masquerades as a Triangle object itself. 042 * 043 * <p> Modified by: Joseph A. Huwaldt</p> 044 * 045 * @author Joseph A. Huwaldt, Date: August 26, 2015 046 * @version April 10, 2018 047 */ 048@SuppressWarnings({"serial", "CloneableImplementsClone"}) 049public final class TriangleTrans extends GeomTriangle implements GeomTransform<GeomTriangle> { 050 051 /** 052 * The transformation represented by this transformation element. 053 */ 054 private GTransform _TM; 055 056 /** 057 * The object that is the child of this transform. 058 */ 059 private GeomTriangle _child; 060 061 /** 062 * Reference to a change listener for this object's child. 063 */ 064 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 065 066 /** 067 * Returns a 3D {@link TriangleTrans} instance holding the specified 068 * {@link GeomTriangle} and {@link GTransform}. 069 * 070 * @param child The GeomTriangle that is the child of this transform element (may 071 * not be <code>null</code>). 072 * @param transform The transform held by this transform element (may not be 073 * <code>null</code>). 074 * @return the transform element having the specified values. 075 */ 076 public static TriangleTrans newInstance(GeomTriangle child, GTransform transform) { 077 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 078 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 079 080 if (child.getPhyDimension() != 3) 081 throw new DimensionException( 082 MessageFormat.format(RESOURCES.getString("dimensionNot3"), 083 "triangle", String.valueOf(child.getPhyDimension()))); 084 085 TriangleTrans obj = FACTORY.object(); 086 obj._TM = transform; 087 obj._child = child; 088 child.copyState(obj); 089 090 // Listen for changes to the child object and pass them on. 091 if (!(child instanceof Immutable)) 092 child.addChangeListener(obj._childChangeListener); 093 094 return obj; 095 } 096 097 /** 098 * Returns the transformation represented by this transformation element. 099 * 100 * @return The transformation from the child object represented by this 101 * element. 102 */ 103 @Override 104 public GTransform getTransform() { 105 return _TM; 106 } 107 108 /** 109 * Returns the total transformation represented by an entire chain of 110 * GeomTransform objects below this one. 111 * 112 * @return The total transformation represented by the entire chain of 113 * objects below this one. 114 */ 115 @Override 116 public GTransform getTotalTransform() { 117 return GeomUtil.getTotalTransform(this); 118 } 119 120 /** 121 * Sets the transformation represented by this transformation element. 122 * 123 * @param transform The transform to set this transform element to (may not 124 * be <code>null</code>). 125 */ 126 @Override 127 public void setTransform(GTransform transform) { 128 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 129 _TM = transform; 130 fireChangeEvent(); 131 } 132 133 /** 134 * Returns the child object transformed by this transform element. 135 * 136 * @return The child object transformed by this transform element. 137 */ 138 @Override 139 public GeomTriangle getChild() { 140 return _child; 141 } 142 143 /** 144 * Return a copy of the child object transformed by this transformation. 145 * 146 * @return A copy of the child object transformed by this transformation. 147 */ 148 @Override 149 public Triangle copyToReal() { 150 Point p1 = getP1().copyToReal(); 151 Point p2 = getP2().copyToReal(); 152 Point p3 = getP3().copyToReal(); 153 Triangle T = Triangle.valueOf(p1, p2, p3); 154 return copyState(T); // Copy over the super-class state for this object to the new one. 155 } 156 157 /** 158 * Return the first vertex in this triangle. 159 * 160 * @return The first vertex in this triangle. 161 */ 162 @Override 163 public GeomPoint getP1() { 164 return _child.getP1().getTransformed(_TM); 165 } 166 167 /** 168 * Return the second vertex in this triangle. 169 * 170 * @return The second vertex in this triangle. 171 */ 172 @Override 173 public GeomPoint getP2() { 174 return _child.getP2().getTransformed(_TM); 175 } 176 177 /** 178 * Return the third and last vertex in this triangle. 179 * 180 * @return The third and last vertex in this triangle. 181 */ 182 @Override 183 public GeomPoint getP3() { 184 return _child.getP3().getTransformed(_TM); 185 } 186 187 /** 188 * Return the surface unit normal vector for this triangle. 189 * If the triangle is degenerate (zero area), then the 190 * normal vector will have zero length. 191 * 192 * @return The surface normal vector for this triangle. 193 */ 194 @Override 195 public VectorTrans<Dimensionless> getNormal() { 196 return _child.getNormal().getTransformed(_TM); 197 } 198 199 /** 200 * Return the surface area of one side of this triangle. 201 * The returned area is always positive, but can be zero. 202 * 203 * @return The surface area of one side of this triangle. 204 */ 205 @Override 206 public Parameter<Area> getArea() { 207 return _child.getArea(); 208 } 209 210 /** 211 * Returns the number of physical dimensions of the geometry element. 212 */ 213 @Override 214 public int getPhyDimension() { 215 return _child.getPhyDimension(); 216 } 217 218 /** 219 * Return a new triangle that is identical to this one, but with the order 220 * of the points (and the surface normal direction) reversed. 221 * 222 * @return A new Triangle that is identical to this one, but with the order 223 * of the points reversed. 224 */ 225 @Override 226 public TriangleTrans reverse() { 227 return TriangleTrans.newInstance(_child.reverse(), _TM); 228 } 229 230 /** 231 * Return the coordinate point representing the minimum bounding box corner 232 * of this geometry element (e.g.: min X, min Y, min Z). 233 * 234 * @return The minimum bounding box coordinate for this geometry element. 235 * @throws IndexOutOfBoundsException if this list contains no elements. 236 */ 237 @Override 238 public Point getBoundsMin() { 239 Triangle trans = copyToReal(); 240 Point minPoint = trans.getBoundsMin(); 241 return minPoint; 242 } 243 244 /** 245 * Return the coordinate point representing the maximum bounding box corner 246 * (e.g.: max X, max Y, max Z). 247 * 248 * @return The maximum bounding box coordinate for this geometry element. 249 * @throws IndexOutOfBoundsException if this list contains no elements. 250 */ 251 @Override 252 public Point getBoundsMax() { 253 Triangle trans = copyToReal(); 254 Point maxPoint = trans.getBoundsMax(); 255 return maxPoint; 256 } 257 258 /** 259 * Returns transformed version of this element. The returned object 260 * implements {@link GeomTransform} and contains this element as a child. 261 * 262 * @param transform The transformation to apply to this geometry. May not be null. 263 * @return A new line segment that is identical to this one with the 264 * specified transformation applied. 265 * @throws DimensionException if this point is not 3D. 266 */ 267 @Override 268 public TriangleTrans getTransformed(GTransform transform) { 269 return TriangleTrans.newInstance(this, requireNonNull(transform)); 270 } 271 272 /** 273 * Returns the unit in which this triangle is stated. 274 */ 275 @Override 276 public Unit<Length> getUnit() { 277 return _child.getUnit(); 278 } 279 280 /** 281 * Returns the equivalent to this element but stated in the specified unit. 282 * 283 * @param unit the length unit of the element to be returned. May not be null. 284 * @return an equivalent to this element but stated in the specified unit. 285 * @throws ConversionException if the the input unit is not a length unit. 286 */ 287 @Override 288 public TriangleTrans to(Unit<Length> unit) throws ConversionException { 289 if (unit.equals(getUnit())) 290 return this; 291 292 return TriangleTrans.newInstance(_child.to(unit), _TM); 293 } 294 295 /** 296 * Return a copy of this Triangle converted to the specified number of 297 * physical dimensions. If the number of dimensions is greater than this 298 * element, then zeros are added to the additional dimensions. If the number 299 * of dimensions is less than this element, then the extra dimensions are 300 * simply dropped (truncated). If the new dimensions are the same as the 301 * dimension of this element, then this element is simply returned. 302 * 303 * @param newDim The dimension of the Triangle to return. 304 * @return This Triangle converted to the new dimensions. 305 * @throws IllegalArgumentException if the new dimension is anything other 306 * than 3. 307 */ 308 @Override 309 public TriangleTrans toDimension(int newDim) { 310 if (newDim == 3) 311 return this; 312 313 throw new IllegalArgumentException( 314 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 315 } 316 317 /** 318 * Returns a copy of this TriangleTrans instance allocated by the calling 319 * thread (possibly on the stack). 320 * 321 * @return an identical and independent copy of this point. 322 */ 323 @Override 324 public TriangleTrans copy() { 325 return TriangleTrans.copyOf(this); 326 } 327 328 /** 329 * Compares this TriangleTrans against the specified object for strict 330 * equality. 331 * 332 * @param obj the object to compare with. 333 * @return <code>true</code> if this transform is identical to that 334 * transform; <code>false</code> otherwise. 335 */ 336 @Override 337 public boolean equals(Object obj) { 338 if (this == obj) 339 return true; 340 if ((obj == null) || (obj.getClass() != this.getClass())) 341 return false; 342 343 TriangleTrans that = (TriangleTrans)obj; 344 return this._TM.equals(that._TM) 345 && this._child.equals(that._child) 346 && super.equals(obj); 347 } 348 349 /** 350 * Returns the hash code for this parameter. 351 * 352 * @return the hash code value. 353 */ 354 @Override 355 public int hashCode() { 356 return 31*super.hashCode() + Objects.hash(_TM, _child); 357 } 358 359 /** 360 * Holds the default XML representation for this object. 361 */ 362 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 363 protected static final XMLFormat<TriangleTrans> XML = new XMLFormat<TriangleTrans>(TriangleTrans.class) { 364 365 @Override 366 public TriangleTrans newInstance(Class<TriangleTrans> cls, InputElement xml) throws XMLStreamException { 367 return FACTORY.object(); 368 } 369 370 @Override 371 public void read(InputElement xml, TriangleTrans obj) throws XMLStreamException { 372 GeomTriangle.XML.read(xml, obj); // Call parent read. 373 374 obj._TM = xml.getNext(); 375 GeomTriangle child = xml.getNext(); 376 obj._child = xml.getNext(); 377 378 // Listen for changes to the child object and pass them on. 379 if (!(child instanceof Immutable)) 380 child.addChangeListener(obj._childChangeListener); 381 } 382 383 @Override 384 public void write(TriangleTrans obj, OutputElement xml) throws XMLStreamException { 385 GeomTriangle.XML.write(obj, xml); // Call parent write. 386 387 xml.add(obj._TM); 388 xml.add(obj._child); 389 390 } 391 }; 392 393 ////////////////////// 394 // Factory Creation // 395 ////////////////////// 396 private static final ObjectFactory<TriangleTrans> FACTORY = new ObjectFactory<TriangleTrans>() { 397 @Override 398 protected TriangleTrans create() { 399 return new TriangleTrans(); 400 } 401 402 @Override 403 protected void cleanup(TriangleTrans obj) { 404 obj.reset(); 405 obj._TM = null; 406 if (!(obj._child instanceof Immutable)) 407 obj._child.removeChangeListener(obj._childChangeListener); 408 obj._child = null; 409 } 410 }; 411 412 /** 413 * Recycles a <code>TriangleTrans</code> instance immediately (on the stack 414 * when executing in a StackContext). 415 * 416 * @param instance The instance to recycle immediately. 417 */ 418 public static void recycle(TriangleTrans instance) { 419 FACTORY.recycle(instance); 420 } 421 422 @SuppressWarnings("unchecked") 423 private static TriangleTrans copyOf(TriangleTrans original) { 424 TriangleTrans obj = FACTORY.object(); 425 obj._TM = original._TM.copy(); 426 obj._child = original._child.copy(); 427 original.copyState(obj); 428 if (!(obj._child instanceof Immutable)) 429 obj._child.addChangeListener(obj._childChangeListener); 430 return obj; 431 } 432 433 /** 434 * Do not allow the default constructor to be used except by subclasses. 435 */ 436 private TriangleTrans() { } 437 438}