001/** 002 * ModelNoteTrans -- A GeomTransform that has a GenModelNote for a child. 003 * 004 * Copyright (C) 2014-2018, 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.awt.Font; 022import java.text.MessageFormat; 023import java.util.Objects; 024import static java.util.Objects.requireNonNull; 025import javax.measure.converter.ConversionException; 026import javax.measure.quantity.Dimensionless; 027import javax.measure.quantity.Length; 028import javax.measure.unit.Unit; 029import javax.swing.event.ChangeListener; 030import javolution.context.ObjectFactory; 031import javolution.lang.Immutable; 032import javolution.xml.XMLFormat; 033import javolution.xml.stream.XMLStreamException; 034 035/** 036 * A {@link GeomTransform} element that refers to a {@link GenModelNote} object and 037 * masquerades as a GenModelNote object itself. 038 * 039 * <p> Modified by: Joseph A. Huwaldt </p> 040 * 041 * @author Joseph A. Huwaldt, Date: February 10, 2014 042 * @version April 10, 2018 043 */ 044@SuppressWarnings({"serial", "CloneableImplementsClone"}) 045public final class ModelNoteTrans extends GenModelNote implements GeomTransform<GenModelNote> { 046 047 /** 048 * The transformation represented by this transformation element. 049 */ 050 private GTransform _TM; 051 052 /** 053 * The object that is the child of this transform. 054 */ 055 private GenModelNote _child; 056 057 /** 058 * Reference to a change listener for this object's child. 059 */ 060 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 061 062 /** 063 * Returns a 3D {@link ModelNoteTrans} instance holding the specified 064 * {@link GenModelNote} and {@link GTransform}. 065 * 066 * @param child The note that is the child of this transform element (may not be 067 * <code>null</code>). 068 * @param transform The transform held by this transform element (may not be 069 * <code>null</code>). 070 * @return the transform element having the specified values. 071 * @throws DimensionException if the input element is not 3D. 072 */ 073 public static ModelNoteTrans newInstance(GenModelNote child, GTransform transform) { 074 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 075 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 076 077 if (child.getPhyDimension() != 3) 078 throw new DimensionException(RESOURCES.getString("dimensionNot3"). 079 replace("<TYPE/>", "note").replace("<DIM/>", String.valueOf(child.getPhyDimension()))); 080 081 ModelNoteTrans obj = FACTORY.object(); 082 obj._TM = transform; 083 obj._child = child; 084 child.copyState(obj); 085 086 // Listen for changes to the child object and pass them on. 087 if (!(child instanceof Immutable)) 088 child.addChangeListener(obj._childChangeListener); 089 090 return obj; 091 } 092 093 /** 094 * Returns the transformation represented by this transformation element. 095 */ 096 @Override 097 public GTransform getTransform() { 098 return _TM; 099 } 100 101 /** 102 * Returns the total transformation represented by an entire chain of GeomTransform 103 * objects below this one. 104 */ 105 @Override 106 public GTransform getTotalTransform() { 107 return GeomUtil.getTotalTransform(this); 108 } 109 110 /** 111 * Sets the transformation represented by this transformation element. 112 * 113 * @param transform The transform to set this transform element to (may not be 114 * <code>null</code>). 115 */ 116 @Override 117 public void setTransform(GTransform transform) { 118 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 119 _TM = transform; 120 fireChangeEvent(); 121 } 122 123 /** 124 * Returns the child object transformed by this transform element. 125 */ 126 @Override 127 public GenModelNote getChild() { 128 return _child; 129 } 130 131 /** 132 * Return the text string associated with this note object. 133 * 134 * @return The text string associated with this note object. 135 */ 136 @Override 137 public String getNote() { 138 return _child.getNote(); 139 } 140 141 /** 142 * Return the location of this note in model space. 143 * 144 * @return The location of this note in model space. 145 */ 146 @Override 147 public Point getLocation() { 148 return _TM.transform(_child.getLocation()); 149 } 150 151 /** 152 * Return the vector indicating the horizontal axis direction for the text. 153 * 154 * @return The vector indicating the horizontal axis direction for the text. 155 */ 156 @Override 157 public GeomVector<Dimensionless> getXHat() { 158 return _child.getXHat().getTransformed(_TM); 159 } 160 161 /** 162 * Return the vector indicating the vertical axis direction (or ascent direction) for 163 * the text. 164 * 165 * @return The vector indicating the vertical axis direction (or ascent direction) for 166 * the text. 167 */ 168 @Override 169 public GeomVector<Dimensionless> getYHat() { 170 return _child.getYHat().getTransformed(_TM); 171 } 172 173 /** 174 * Return the height of the text box in model units. 175 * 176 * @return The height of the text box in model units. 177 */ 178 @Override 179 public Parameter<Length> getHeight() { 180 double zScale = _TM.getScaleVector().getValue(2); 181 return _child.getHeight().times(zScale); 182 } 183 184 /** 185 * Return the font used to display this note. 186 * 187 * @return The font used to display this note. 188 */ 189 @Override 190 public Font getFont() { 191 return _child.getFont(); 192 } 193 194 /** 195 * Return an immutable version of this note. 196 * 197 * @return An immutable version of this note. 198 */ 199 @Override 200 public ModelNote immutable() { 201 return copyToReal(); 202 } 203 204 /** 205 * Return a new note object identical to this one, but with the specified font. 206 * 207 * @param font The font for the copy of this note to use. May not be null. 208 * @return A new note object identical to this one, but with the specified font. 209 */ 210 @Override 211 public ModelNoteTrans changeFont(Font font) { 212 requireNonNull(font); 213 ModelNoteTrans note = ModelNoteTrans.newInstance(_child.changeFont(font), _TM); 214 copyState(note); 215 return note; 216 } 217 218 /** 219 * Return a new note object identical to this one, but with the specified location in 220 * model space. 221 * 222 * @param location The location for the copy of this note. May not be null. 223 * @return A new note object identical to this one, but with the specified location in 224 * model space. 225 */ 226 @Override 227 public ModelNoteTrans changeLocation(GeomPoint location) { 228 requireNonNull(location); 229 ModelNoteTrans note = ModelNoteTrans.newInstance(_child.changeLocation(location), _TM); 230 copyState(note); 231 return note; 232 } 233 234 /** 235 * Return a new note object identical to this one, but with the specified height in 236 * model space. 237 * 238 * @param height The height for the copy of this note. May not be null. 239 * @return A new note object identical to this one, but with the specified height in 240 * model space. 241 */ 242 @Override 243 public ModelNoteTrans changeHeight(Parameter<Length> height) { 244 requireNonNull(height); 245 ModelNoteTrans note = ModelNoteTrans.newInstance(_child.changeHeight(height), _TM); 246 copyState(note); 247 return note; 248 } 249 250 /** 251 * Return a copy of the child object transformed by this transformation. 252 * 253 * @return A copy of the child object transformed by this transformation. 254 */ 255 @Override 256 public ModelNote copyToReal() { 257 // Transform the geometry. 258 Point location = getLocation(); 259 Parameter<Length> height = getHeight(); 260 261 // Create a new note from the old one. 262 ModelNote note = _child.immutable().changeLocation(location).changeHeight(height); 263 copyState(note); 264 265 return note; 266 } 267 268 /** 269 * Returns the number of physical dimensions of the geometry element. This 270 * implementation always returns 3. 271 */ 272 @Override 273 public int getPhyDimension() { 274 return 3; 275 } 276 277 /** 278 * Return <code>true</code> if this Note contains valid and finite numerical 279 * components. A value of <code>false</code> will be returned if any of the coordinate 280 * values are NaN or Inf. 281 */ 282 @Override 283 public boolean isValid() { 284 return _child.isValid(); 285 } 286 287 /** 288 * Returns a copy of this ModelNoteTrans instance 289 * {@link javolution.context.AllocatorContext allocated} by the calling thread 290 * (possibly on the stack). 291 * 292 * @return an identical and independent copy of this note. 293 */ 294 @Override 295 public ModelNoteTrans copy() { 296 return copyOf(this); 297 } 298 299 /** 300 * Returns the unit in which the note location Point is stored. 301 */ 302 @Override 303 public final Unit<Length> getUnit() { 304 return _child.getUnit(); 305 } 306 307 /** 308 * Returns the equivalent to this note but with the location stated in the specified 309 * unit. 310 * 311 * @param unit the length unit of the note to be returned. May not be null. 312 * @return an equivalent of this note but with location stated in the specified unit. 313 * @throws ConversionException if the the input unit is not a length unit. 314 */ 315 @Override 316 public ModelNoteTrans to(Unit<Length> unit) throws ConversionException { 317 if (unit.equals(getUnit())) 318 return this; 319 320 ModelNoteTrans note = ModelNoteTrans.newInstance(_child.to(unit), _TM); 321 copyState(note); 322 return note; 323 } 324 325 /** 326 * Return the equivalent of this note converted to the specified number of physical 327 * dimensions. This implementation will throw an exception if the specified dimension 328 * is anything other than 3. 329 * 330 * @param newDim The dimension of the point to return. MUST equal 3. 331 * @return The equivalent of this note converted to the new dimensions. 332 * @throws IllegalArgumentException if the new dimension is anything other than 3. 333 */ 334 @Override 335 public ModelNoteTrans toDimension(int newDim) { 336 if (newDim == 3) 337 return this; 338 339 throw new IllegalArgumentException( 340 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 341 } 342 343 /** 344 * Compares this ModelNoteTrans against the specified object for strict equality (same 345 * values and same units). 346 * 347 * @param obj the object to compare with. 348 * @return <code>true</code> if this note is identical to that note; 349 * <code>false</code> otherwise. 350 */ 351 @Override 352 public boolean equals(Object obj) { 353 if (this == obj) 354 return true; 355 if ((obj == null) || (obj.getClass() != this.getClass())) 356 return false; 357 358 ModelNoteTrans that = (ModelNoteTrans)obj; 359 return this._TM.equals(that._TM) 360 && this._child.equals(that._child) 361 && super.equals(obj); 362 } 363 364 /** 365 * Returns the hash code for this parameter. 366 * 367 * @return the hash code value. 368 */ 369 @Override 370 public int hashCode() { 371 return 31*super.hashCode() + Objects.hash(_TM, _child); 372 } 373 374 /** 375 * Holds the default XML representation for this object. 376 */ 377 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 378 protected static final XMLFormat<ModelNoteTrans> XML = new XMLFormat<ModelNoteTrans>(ModelNoteTrans.class) { 379 380 @Override 381 public ModelNoteTrans newInstance(Class<ModelNoteTrans> cls, XMLFormat.InputElement xml) throws XMLStreamException { 382 return FACTORY.object(); 383 } 384 385 @Override 386 public void read(XMLFormat.InputElement xml, ModelNoteTrans obj) throws XMLStreamException { 387 GenModelNote.XML.read(xml, obj); // Call parent read. 388 389 obj._TM = xml.getNext(); 390 GenModelNote child = xml.getNext(); 391 obj._child = child; 392 393 // Listen for changes to the child object and pass them on. 394 if (!(child instanceof Immutable)) 395 child.addChangeListener(obj._childChangeListener); 396 397 } 398 399 @Override 400 public void write(ModelNoteTrans obj, XMLFormat.OutputElement xml) throws XMLStreamException { 401 GenModelNote.XML.write(obj, xml); // Call parent write. 402 403 xml.add(obj._TM); 404 xml.add(obj._child); 405 } 406 }; 407 408 /////////////////////// 409 // Factory creation. // 410 /////////////////////// 411 private ModelNoteTrans() { } 412 413 @SuppressWarnings("unchecked") 414 private static final ObjectFactory<ModelNoteTrans> FACTORY = new ObjectFactory<ModelNoteTrans>() { 415 @Override 416 protected ModelNoteTrans create() { 417 return new ModelNoteTrans(); 418 } 419 420 @Override 421 protected void cleanup(ModelNoteTrans obj) { 422 obj.reset(); 423 obj._TM = null; 424 if (!(obj._child instanceof Immutable)) 425 obj._child.removeChangeListener(obj._childChangeListener); 426 obj._child = null; 427 } 428 }; 429 430 @SuppressWarnings("unchecked") 431 private static ModelNoteTrans copyOf(ModelNoteTrans original) { 432 ModelNoteTrans obj = FACTORY.object(); 433 obj._TM = original._TM.copy(); 434 obj._child = original._child.copy(); 435 original.copyState(obj); 436 if (!(obj._child instanceof Immutable)) 437 obj._child.addChangeListener(obj._childChangeListener); 438 return obj; 439 } 440 441}