001/** 002 * J3DGeomGroup -- A Java3D node that represents a GeomElement in a J3D scene graph. 003 * 004 * Copyright (C) 2009-2023 Joseph A. Huwaldt. 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or modify it under the terms 008 * of the GNU Lesser General Public License as published by the Free Software Foundation; 009 * either version 2.1 of the License, or (at your option) any later version. 010 * 011 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 013 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License along with 016 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - 017 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html 018 */ 019package geomss.j3d; 020 021import geomss.app.GeomSSCanvas3D; 022import geomss.geom.GeomElement; 023import jahuwaldt.js.param.Parameter; 024import java.awt.Color; 025import static java.util.Objects.requireNonNull; 026import java.util.ResourceBundle; 027import javax.measure.quantity.Length; 028import org.jogamp.java3d.BranchGroup; 029import org.jogamp.java3d.Group; 030import org.jogamp.java3d.Switch; 031import javax.swing.event.ChangeEvent; 032import javax.swing.event.ChangeListener; 033import javolution.lang.Immutable; 034 035/** 036 * A Java 3D node that represents a GeomElement in a Java 3D scene graph. 037 * 038 * <p> Modified by: Joseph A. Huwaldt </p> 039 * 040 * @author Joseph A. Huwaldt, Date: April 13, 2009 041 * @version June 4, 2023 042 * 043 * @param <T> The type of GeomElement represented by this object. 044 */ 045public abstract class J3DGeomGroup<T extends GeomElement> extends BranchGroup { 046 047 /** 048 * The resource bundle for this class and it's descendants. 049 */ 050 static final ResourceBundle RB 051 = ResourceBundle.getBundle("geomss.j3d.J3DResources", java.util.Locale.getDefault()); 052 053 /** 054 * The key used to store this instance in the user data of the supplied GeomElement 055 * object. 056 */ 057 public static final String USERDATA_KEY = "J3DGeomGroup"; 058 059 // Store the globally active rendering preferences. 060 private static J3DRenderingPrefs _defDrawPrefs = new J3DRenderingPrefs(); 061 062 // Store the rendering preferences used to render this object. 063 private final J3DRenderingPrefs _drawPrefs; 064 065 // Reference to the geometry for this geometry group. 066 private T _geometry; 067 068 // The display group containing the geometry. 069 private Switch _dispG; 070 071 // Indicates of the mirrored version of this geometry is displayed. 072 private boolean _mirrored = false; 073 074 // The RenderType used for this geometry. 075 private RenderType _renderType = RenderType.SOLID_PLUS_WIREFRAME; 076 077 // Store the old group so it can potentially be used again in the future. 078 private final J3DGeomGroup<T> _oldJ3DGroup; 079 080 // A reference to the Canvas3D that this geometry list is being rendered into. 081 private final GeomSSCanvas3D _canvas; 082 083 /** 084 * Construct a J3DGeomGroup using the specified GeomElement as a reference. 085 * 086 * @param canvas The canvas that the geometry is being rendered into. 087 * @param geometry The GeomSS geometry to be turned into a Java3D node. 088 */ 089 @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) 090 public J3DGeomGroup(GeomSSCanvas3D canvas, T geometry) { 091 _geometry = requireNonNull(geometry); 092 _canvas = requireNonNull(canvas); 093 _drawPrefs = _defDrawPrefs; 094 095 if (!(geometry instanceof Immutable)) { 096 // Add a change listener to the geometry and remove this group from the geometry 097 // if the geometry changes. 098 _geometry.addChangeListener(new ChangeListener() { 099 @Override 100 public void stateChanged(ChangeEvent e) { 101 _geometry.removeUserData(USERDATA_KEY); 102 _geometry.removeChangeListener(this); 103 } 104 }); 105 } 106 107 this.setCapability(BranchGroup.ALLOW_DETACH); 108 this.setCapability(Group.ALLOW_CHILDREN_READ); 109 110 // Get any existing J3DGeomGroups in this geometry. 111 _oldJ3DGroup = (J3DGeomGroup)geometry.getUserData(USERDATA_KEY); 112 113 // Create the scene graph for this group. 114 createSceneGraph(); 115 116 // Make sure this group has the same properties as the old one (if it exists). 117 if (_oldJ3DGroup != null) { 118 this.setMirrored(_oldJ3DGroup.isMirrored()); 119 this.setDisplayed(_oldJ3DGroup.isDisplayed()); 120 this.setRenderType(_oldJ3DGroup.getRenderType()); 121 } 122 123 // Store a reference to this group in the geometry element's user data 124 // (replacing any old data). 125 geometry.putUserData(USERDATA_KEY, this); 126 } 127 128 /** 129 * @return The canvas that this geometry is being rendered into. 130 */ 131 protected GeomSSCanvas3D getCanvas3D() { 132 return _canvas; 133 } 134 135 /** 136 * @return the J3DGeomGroup that was used to render this geometry element previously 137 * (if any). If this geometry has not been rendered previously, 138 * <code>null</code> will be returned. 139 */ 140 protected J3DGeomGroup<T> getOldJ3DGeomGroup() { 141 return _oldJ3DGroup; 142 } 143 144 /** 145 * @return the {@link GeomElement} that this group represents in the scene graph. 146 */ 147 public T getGeomElement() { 148 return _geometry; 149 } 150 151 /** 152 * @return the geometry group containing the scene graph for this GeomGroup. This 153 * returns the same object that was created by "createSceneGraph()". 154 * @see #createSceneGraph() 155 */ 156 protected Group getSceneGraph() { 157 return (Group)_dispG.getChild(0); 158 } 159 160 /** 161 * Create a 3D scene group to represent the geometry. 162 * 163 * @see #getSceneGraph() 164 */ 165 private void createSceneGraph() { 166 167 // Create a switch group so that sub-elements can be turned on or off. 168 _dispG = new Switch(Switch.CHILD_ALL); 169 _dispG.setCapability(Switch.ALLOW_SWITCH_READ); 170 _dispG.setCapability(Switch.ALLOW_SWITCH_WRITE); 171 172 // Create the geometry itself. 173 Group geomG = createGeometry(); 174 _dispG.addChild(requireNonNull(geomG)); 175 176 // Add the root to this group. 177 this.addChild(_dispG); 178 } 179 180 /** 181 * Returns the display setting of this geometry group. 182 * 183 * @return true if the geometry is being displayed and false if it is not displayed. 184 */ 185 public boolean isDisplayed() { 186 return _dispG.getWhichChild() != Switch.CHILD_NONE; 187 } 188 189 /** 190 * Sets the display of this geometry group to either displayed (true) or not displayed 191 * (false). Subclasses that override this method should call "super.setDisplayed()" to 192 * maintain proper state and object display. 193 * 194 * @param visible Flag indicating if the geometry is displayed or not. 195 */ 196 public void setDisplayed(boolean visible) { 197 if (visible) { 198 _dispG.setWhichChild(Switch.CHILD_ALL); 199 if (_mirrored) 200 internalSetMirrored(true); 201 } else { 202 _dispG.setWhichChild(Switch.CHILD_NONE); 203 if (_mirrored) 204 internalSetMirrored(false); 205 } 206 } 207 208 /** 209 * Set the display of a copy of this geometry mirrored across the XZ plane to either 210 * DISPLAYED (true) or NOT_DISPLAYED (false). Subclasses that override this method 211 * should call "super.setMirrored(mirrored)" to maintain proper state information. 212 * 213 * @param mirrored Flag indicating if the mirrored geometry should be displayed or 214 * not. 215 * @see #isMirrored() 216 * @see #internalSetMirrored(boolean) 217 */ 218 public void setMirrored(boolean mirrored) { 219 _mirrored = mirrored; 220 internalSetMirrored(mirrored); 221 } 222 223 /** 224 * Returns a flag indicating if the geometry display is currently mirrored about the 225 * XZ plane of symmetry or not. 226 * 227 * @return true if mirroring across the XZ plane is being DISPLAYED and false if it is 228 * NOT_DISPLAYED. 229 * @see #setMirrored(boolean) 230 */ 231 public boolean isMirrored() { 232 return _mirrored; 233 } 234 235 /** 236 * Set the render type used for this group. Sub-classes must provide specific 237 * implementations that depend on the implementation of the geometry and must call 238 * "super.setRenderType()" to properly maintain state. 239 * 240 * @param type Value indicating the way that some objects should be rendered. 241 * @see #getRenderType() 242 */ 243 public void setRenderType(RenderType type) { 244 _renderType = requireNonNull(type); 245 } 246 247 /** 248 * Return the render type used for this group. Sub-classes must provide specific 249 * implementations that depend on the the geometry being rendered. 250 * 251 * @return The render type used for this Group. 252 * @see #setRenderType(geomss.j3d.RenderType) 253 */ 254 public RenderType getRenderType() { 255 return _renderType; 256 } 257 258 /** 259 * @return the currently active rendering preferences that will be used to render 260 * future objects. 261 */ 262 public static J3DRenderingPrefs getDefaultRenderingPrefs() { 263 return _defDrawPrefs; 264 } 265 266 /** 267 * Set the currently active rendering preferences that will be used to render future 268 * objects. 269 * 270 * @param prefs The rendering preferences to make current. 271 */ 272 public static void setDefaultRenderingPrefs(J3DRenderingPrefs prefs) { 273 _defDrawPrefs = requireNonNull(prefs); 274 } 275 276 /** 277 * @return the rendering preferences that were used, in the past, to render this 278 * object. 279 */ 280 public J3DRenderingPrefs getRenderingPrefs() { 281 return _drawPrefs; 282 } 283 284 /** 285 * Set the color to use when rendering points. This is a convenience method for 286 * <code>J3DGeomGroup.setDefaultRenderingPrefs(getDefaultRenderingPrefs().changePointColor(color))</code>. 287 * 288 * @param color The Color to use for rending Point objects in the future. 289 */ 290 public static void setPointColor(Color color) { 291 _defDrawPrefs = _defDrawPrefs.changePointColor(requireNonNull(color)); 292 } 293 294 /** 295 * Set the color to use when rendering curves and lines. This is a convenience method 296 * for 297 * <code>J3DGeomGroup.setDefaultRenderingPrefs(getDefaultRenderingPrefs().changeLineColor(color))</code>. 298 * 299 * @param color The Color to use for rending lines and curves objects in the future. 300 */ 301 public static void setLineColor(Color color) { 302 _defDrawPrefs = _defDrawPrefs.changeLineColor(requireNonNull(color)); 303 } 304 305 /** 306 * Set the size that Point objects are rendered in pixels. This is a convenience 307 * method for 308 * <code>J3DGeomGroup.setDefaultRenderingPrefs(getDefaultRenderingPrefs().changePointSize(pixels))</code>. 309 * 310 * @param pixels The size, in pixels, to use when rendering points in the future. 311 * @throws IllegalArgumentException if the point size provided is < 1 pixel. 312 */ 313 public static void setPointSize(int pixels) { 314 _defDrawPrefs = _defDrawPrefs.changePointSize(pixels); 315 } 316 317 /** 318 * Set the width that line/curve objects are rendered in pixels. This is a convenience 319 * method for 320 * <code>J3DGeomGroup.setDefaultRenderingPrefs(getDefaultRenderingPrefs().changeLineWidth(pixels))</code>. 321 * 322 * @param pixels The width, in pixels, to use when rendering lines/curves in the 323 * future. 324 * @throws IllegalArgumentException if the line size provided is < 1 pixel. 325 */ 326 public static void setLineWidth(int pixels) { 327 _defDrawPrefs = _defDrawPrefs.changeLineWidth(pixels); 328 } 329 330 /** 331 * Set the tolerance used when drawing parametric objects such as curves and surfaces. 332 * This tolerance is used when determining how to subdivide parametric objects for 333 * rendering. If the input value is <code>null</code> or equal to <code>0</code>, it 334 * will be silently ignored. This is a convenience method for 335 * <code>J3DGeomGroup.setDefaultRenderingPrefs(getDefaultRenderingPrefs().changeDrawTolerance(tol))</code>. 336 * 337 * @param tol The geometric tolerance to use when rendering parametric objects. 338 */ 339 public static void setDrawTolerance(Parameter<Length> tol) { 340 _defDrawPrefs = _defDrawPrefs.changeDrawTolerance(requireNonNull(tol)); 341 } 342 343 /** 344 * Set the display of a mirrored copy of this geometry. This is called from 345 * "setDisplayed()" to turn on and off the display of mirrored geometry without 346 * changing the "mirrored state" of the object. This call should not affect the output 347 * of "isMirrored()". 348 * <p> 349 * Subclasses should override this to turn on or off a mirrored geometry in response 350 * to the main geometry being set to displayed or not-displayed. The default 351 * implementation does nothing. 352 * </p> 353 * 354 * @param mirrored Flag indicating if the mirrored geometry should be displayed or 355 * not. 356 * @see #setMirrored(boolean) 357 * @see #isMirrored() 358 */ 359 protected void internalSetMirrored(boolean mirrored) { 360 } 361 362 /** 363 * Create a new Java 3D <code>Group</code> that contains the geometry contained in 364 * this object. This method is called from <code>createSceneGraph</code>. Sub-classes 365 * must over-ride this method to provide geometry specific implementations. 366 * 367 * @return New Java 3D Group that contains the geometry in this object. 368 * @see #createSceneGraph 369 */ 370 protected abstract Group createGeometry(); 371 372}