001/** 002 * GeomPlaneTrans -- A GeomTransform that has an GeomPlane for a child. 003 * 004 * Copyright (C) 2009-2016, 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 jahuwaldt.js.param.Parameter; 021import java.text.MessageFormat; 022import java.util.Objects; 023import static java.util.Objects.requireNonNull; 024import javax.measure.converter.ConversionException; 025import javax.measure.quantity.Dimensionless; 026import javax.measure.quantity.Length; 027import javax.measure.unit.Unit; 028import javax.swing.event.ChangeListener; 029import javolution.context.ObjectFactory; 030import javolution.xml.XMLFormat; 031import javolution.xml.stream.XMLStreamException; 032 033/** 034 * A {@link GeomTransform} element that refers to a {@link GeomPlane} object and 035 * masquerades as a GeomPlane object itself. 036 * 037 * <p> Modified by: Joseph A. Huwaldt </p> 038 * 039 * @author Joseph A. Huwaldt, Date: June 14, 2009 040 * @version September 13, 2016 041 */ 042@SuppressWarnings({"serial", "CloneableImplementsClone"}) 043public final class GeomPlaneTrans extends GeomPlane implements GeomTransform<GeomPlane> { 044 045 /** 046 * The transformation represented by this transformation element. 047 */ 048 private GTransform _TM; 049 050 /** 051 * The object that is the child of this transform. 052 */ 053 private GeomPlane _child; 054 055 /** 056 * Reference to a change listener for this object's child. 057 */ 058 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 059 060 /** 061 * Returns a 3D {@link GeomPlaneTrans} instance holding the specified 062 * {@link GeomPlane} and {@link GTransform}. 063 * 064 * @param child The plane that is the child of this transform element (may not be 065 * <code>null</code>). 066 * @param transform The transform held by this transform element (may not be 067 * <code>null</code>). 068 * @return the transform element having the specified values. 069 * @throws DimensionException if the input element is not 3D. 070 */ 071 public static GeomPlaneTrans newInstance(GeomPlane child, GTransform transform) { 072 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 073 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 074 075 if (child.getPhyDimension() != 3) 076 throw new DimensionException( 077 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "plane", child.getPhyDimension())); 078 079 GeomPlaneTrans obj = FACTORY.object(); 080 obj._TM = transform; 081 obj._child = child; 082 083 // Listen for changes to the child object and pass them on. 084 child.addChangeListener(obj._childChangeListener); 085 086 return obj; 087 } 088 089 /** 090 * Returns the transformation represented by this transformation element. 091 * 092 * @return The transformation represented by this transformation element. 093 */ 094 @Override 095 public GTransform getTransform() { 096 return _TM; 097 } 098 099 /** 100 * Returns the total transformation represented by an entire chain of GeomTransform 101 * objects below this one. 102 * 103 * @return The total transformation represented by an entire chain of GeomTransform 104 * objects below this one. 105 */ 106 @Override 107 public GTransform getTotalTransform() { 108 return GeomUtil.getTotalTransform(this); 109 } 110 111 /** 112 * Sets the transformation represented by this transformation element. 113 * 114 * @param transform The transform to set this transform element to (may not be 115 * <code>null</code>). 116 */ 117 @Override 118 public void setTransform(GTransform transform) { 119 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 120 _TM = transform; 121 fireChangeEvent(); 122 } 123 124 /** 125 * Returns the child object transformed by this transform element. 126 * 127 * @return The child object transformed by this transform element. 128 */ 129 @Override 130 public GeomPlane getChild() { 131 return _child; 132 } 133 134 /** 135 * Return a copy of the child object transformed by this transformation. 136 * 137 * @return A copy of the child object transformed by this transformation. 138 */ 139 @Override 140 public Plane copyToReal() { 141 GeomVector n = _child.getNormal(); 142 VectorTrans nt = n.getTransformed(_TM); 143 n = nt.copyToReal(); 144 145 Point refPoint = _TM.transform(_child.getRefPoint()).copyToReal(); 146 Plane P = Plane.valueOf(n, refPoint); 147 148 _child.copyState(P); 149 150 return P; 151 } 152 153 /** 154 * Return an immutable version of this plane. 155 * 156 * @return An immutable version of this plane. 157 */ 158 @Override 159 public Plane immutable() { 160 return copyToReal(); 161 } 162 163 /** 164 * Recycles a <code>GeomPlaneTrans</code> instance immediately (on the stack when 165 * executing in a <code>StackContext</code>). 166 * 167 * @param instance The instance to be recycled. 168 */ 169 public static void recycle(GeomPlaneTrans instance) { 170 requireNonNull(instance, MessageFormat.format(RESOURCES.getString("paramNullErr"), "instance")); 171 FACTORY.recycle(instance); 172 } 173 174 /** 175 * Returns the number of physical dimensions of the geometry element. This 176 * implementation always returns 3. 177 */ 178 @Override 179 public int getPhyDimension() { 180 return 3; 181 } 182 183 /** 184 * Return the normal vector for the plane. The normal vector is a unit vector that is 185 * perpendicular to the plane. 186 * 187 * @return The normal vector for the plane. 188 */ 189 @Override 190 public GeomVector<Dimensionless> getNormal() { 191 return _child.getNormal().getTransformed(_TM); 192 } 193 194 /** 195 * Return the constant term of the plane equation (e.g.: "D" for a 3D plane: 196 * <code>A*x + B*y + C*z = D</code>). 197 * 198 * @return The constant term of the plane equation for this plane. 199 */ 200 @Override 201 public Parameter<Length> getConstant() { 202 GeomPointTrans pt = getRefPoint(); 203 Vector<Length> v = Vector.valueOf(pt); 204 GeomVector<Dimensionless> n = getNormal(); 205 206 Parameter<Length> c = (Parameter<Length>)n.dot(v); 207 208 return c; 209 } 210 211 /** 212 * Return the reference point for this plane. The reference point is an arbitrary 213 * point that is contained in the plane and is used as a reference when drawing the 214 * plane. 215 * 216 * @return The reference point for this plane. 217 */ 218 @Override 219 public GeomPointTrans getRefPoint() { 220 return _child.getRefPoint().getTransformed(_TM); 221 } 222 223 /** 224 * Return the equivalent of this plane converted to the specified number of physical 225 * dimensions. This implementation will throw an exception if the specified dimension 226 * is anything other than 3. 227 * 228 * @param newDim The dimension of the plane to return. MUST equal 3. 229 * @return The equivalent of this plane converted to the new dimensions. 230 * @throws IllegalArgumentException if the new dimension is anything other than 3. 231 */ 232 @Override 233 public GeomPlaneTrans toDimension(int newDim) { 234 if (newDim == 3) 235 return this; 236 237 throw new IllegalArgumentException( 238 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 239 } 240 241 /** 242 * Returns the unit in which the geometry in this element are stated. 243 */ 244 @Override 245 public Unit<Length> getUnit() { 246 return _child.getUnit(); 247 } 248 249 /** 250 * Returns the equivalent to this element but stated in the specified unit. 251 * <p> 252 * WARNING: If the unit changes, then the returned transform element DOES NOT refer 253 * back to the original plane (the link with the original plane is broken). 254 * </p> 255 * 256 * @param unit the length unit of the element to be returned. May not be null. 257 * @return an equivalent to this element but stated in the specified unit. 258 * @throws ConversionException if the the input unit is not a length unit. 259 */ 260 @Override 261 public GeomPlane to(Unit<Length> unit) throws ConversionException { 262 if (unit.equals(getUnit())) 263 return this; 264 GeomPlaneTrans newPlane = GeomPlaneTrans.newInstance(_child.to(unit), _TM); 265 return newPlane; 266 } 267 268 /** 269 * Compares this GeomPlaneTrans against the specified object for strict equality (same 270 * values and same units). 271 * 272 * @param obj the object to compare with. 273 * @return <code>true</code> if this point is identical to that point; 274 * <code>false</code> otherwise. 275 */ 276 @Override 277 public boolean equals(Object obj) { 278 if (this == obj) 279 return true; 280 if ((obj == null) || (obj.getClass() != this.getClass())) 281 return false; 282 283 GeomPlaneTrans that = (GeomPlaneTrans)obj; 284 return this._TM.equals(that._TM) 285 && this._child.equals(that._child) 286 && super.equals(obj); 287 } 288 289 /** 290 * Returns the hash code for this object. 291 * 292 * @return the hash code value. 293 */ 294 @Override 295 public int hashCode() { 296 return 31*super.hashCode() + Objects.hash(_TM, _child); 297 } 298 299 /** 300 * Returns a copy of this GeomPlaneTrans instance 301 * {@link javolution.context.AllocatorContext allocated} by the calling thread 302 * (possibly on the stack). 303 * 304 * @return an identical and independent copy of this object. 305 */ 306 @Override 307 public GeomPlaneTrans copy() { 308 return copyOf(this); 309 } 310 311 /** 312 * Holds the default XML representation for this object. 313 */ 314 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 315 protected static final XMLFormat<GeomPlaneTrans> XML = new XMLFormat<GeomPlaneTrans>(GeomPlaneTrans.class) { 316 317 @Override 318 public GeomPlaneTrans newInstance(Class<GeomPlaneTrans> cls, InputElement xml) throws XMLStreamException { 319 return FACTORY.object(); 320 } 321 322 @Override 323 public void read(InputElement xml, GeomPlaneTrans obj) throws XMLStreamException { 324 GeomPlane.XML.read(xml, obj); // Call parent read. 325 326 obj._TM = xml.getNext(); 327 GeomPlane child = xml.getNext(); 328 obj._child = child; 329 330 // Listen for changes to the child object and pass them on. 331 child.addChangeListener(obj._childChangeListener); 332 } 333 334 @Override 335 public void write(GeomPlaneTrans obj, OutputElement xml) throws XMLStreamException { 336 GeomPlane.XML.write(obj, xml); // Call parent write. 337 338 xml.add(obj._TM); 339 xml.add(obj._child); 340 341 } 342 }; 343 344 /////////////////////// 345 // Factory creation. // 346 /////////////////////// 347 private GeomPlaneTrans() { } 348 349 @SuppressWarnings("unchecked") 350 private static final ObjectFactory<GeomPlaneTrans> FACTORY = new ObjectFactory<GeomPlaneTrans>() { 351 @Override 352 protected GeomPlaneTrans create() { 353 return new GeomPlaneTrans(); 354 } 355 356 @Override 357 protected void cleanup(GeomPlaneTrans obj) { 358 obj.reset(); 359 obj._TM = null; 360 obj._child.removeChangeListener(obj._childChangeListener); 361 obj._child = null; 362 } 363 }; 364 365 @SuppressWarnings("unchecked") 366 private static GeomPlaneTrans copyOf(GeomPlaneTrans original) { 367 GeomPlaneTrans obj = FACTORY.object(); 368 obj._TM = original._TM.copy(); 369 obj._child = original._child.copy(); 370 original.copyState(obj); 371 obj._child.addChangeListener(obj._childChangeListener); 372 return obj; 373 } 374 375}