001/** 002 * J3DPointString -- A Java3D node that represents a PointString 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.GeomPoint; 023import geomss.geom.PointString; 024import static java.util.Objects.requireNonNull; 025import javax.measure.unit.SI; 026import org.jogamp.java3d.*; 027import org.jogamp.vecmath.*; 028import javolution.context.StackContext; 029 030/** 031 * A Java 3D node that represents a PointString in a Java 3D scene graph. 032 * 033 * <p> Modified by: Joseph A. Huwaldt </p> 034 * 035 * @author Joseph A. Huwaldt, Date: April 13, 2009 036 * @version June 4, 2023 037 */ 038public class J3DPointString extends J3DGeomGroup<PointString> { 039 040 // The switch for main or mirrored geometry. 041 private Switch _symmSG; 042 043 // Store the Shape3D so it could potentially be used again in the future. 044 private Shape3D _oldLineShape; 045 private Shape3D _oldPointShape; 046 047 /** 048 * Construct a J3DPointString using the specified PointString as a reference. 049 * 050 * @param canvas The canvas that the geometry is being rendered into. 051 * @param geometry The GeomSS geometry to be turned into a Java3D node. 052 */ 053 public J3DPointString(GeomSSCanvas3D canvas, PointString geometry) { 054 super(requireNonNull(canvas), requireNonNull(geometry)); 055 } 056 057 /** 058 * Set the display of a mirrored copy of this geometry. This is called from 059 * "setDisplayed()" to turn on and off the display of mirrored geometry without 060 * changing the "mirrored state" of the object. This call does not affect the output 061 * of "isMirrored()". 062 */ 063 @Override 064 protected void internalSetMirrored(boolean mirrored) { 065 if (mirrored) 066 _symmSG.setWhichChild(Switch.CHILD_ALL); 067 else 068 _symmSG.setWhichChild(0); 069 } 070 071 /** 072 * Create a new Java 3D <code>Group</code> that contains the geometry contained in 073 * this object. This method is called from <code>createSceneGraph</code>. Sub-classes 074 * must over-ride this method to provide geometry specific implementations. 075 * 076 * @return New Java 3D Group that contains the geometry in this object. 077 * @see #createSceneGraph 078 */ 079 @Override 080 @SuppressWarnings("null") 081 protected Group createGeometry() { 082 083 J3DRenderingPrefs drawPrefs = getRenderingPrefs(); 084 Shape3D lineShape = null; 085 Shape3D pointShape = null; 086 087 // Try to recycle a previous rendering of this geometry element if at all possible. 088 J3DPointString oldGroup = (J3DPointString)getOldJ3DGeomGroup(); 089 if (oldGroup != null) { 090 J3DRenderingPrefs oldPrefs = oldGroup.getRenderingPrefs(); 091 if (oldPrefs.getDrawTolerance().equals(drawPrefs.getDrawTolerance())) { 092 //System.out.println("Re-using lineShape & pointShape"); 093 // Draw tolerance is unchanged, so re-use the existing Shape3D. 094 lineShape = (Shape3D)oldGroup._oldLineShape.cloneNode(true); 095 pointShape = (Shape3D)oldGroup._oldPointShape.cloneNode(true); 096 097 // Has the line color changed? 098 if (!oldPrefs.getLineColor().equals(drawPrefs.getLineColor())) { 099 //System.out.println("Changing line color"); 100 Color4f lineColor = drawPrefs.getLineColorJ3D(); 101 LineArray lineA = (LineArray)lineShape.getGeometry(); 102 int size = lineA.getVertexCount(); 103 for (int i = 0; i < size; ++i) { 104 lineA.setColor(i, lineColor); 105 } 106 } 107 108 // Has the point color changed? 109 if (!oldPrefs.getPointColor().equals(drawPrefs.getPointColor())) { 110 //System.out.println("Changing point color"); 111 Color4f pointColor = drawPrefs.getPointColorJ3D(); 112 PointArray pointA = (PointArray)pointShape.getGeometry(); 113 int size = pointA.getVertexCount(); 114 for (int i = 0; i < size; ++i) { 115 pointA.setColor(i, pointColor); 116 } 117 } 118 119 } 120 } 121 122 // If we couldn't recycle the lineShape & pointShape, then create new ones. 123 if (lineShape == null) { 124 StackContext.enter(); 125 try { 126 // Create a line and a point array. 127 PointString<?> thisString = this.getGeomElement(); 128 int size = thisString.size(); 129 int nVerts = 2 * (size - 1); 130 LineArray lineA = new LineArray(nVerts, LineArray.COORDINATES | LineArray.COLOR_4); 131 PointArray pointA = new PointArray(thisString.size(), PointArray.COORDINATES | PointArray.COLOR_4); 132 133 // Set the coordinates of the line. 134 Color4f pointColor = drawPrefs.getPointColorJ3D(); 135 Color4f lineColor = drawPrefs.getLineColorJ3D(); 136 Point3d oldPoint = null; 137 int lineIndex = 0; 138 for (int i = 0; i < size; ++i) { 139 GeomPoint point = thisString.get(i); 140 // Convert all geometry to meters. 141 int dims = point.getPhyDimension(); 142 double x = point.getValue(0, SI.METER); 143 double y = (dims > 1 ? point.getValue(1, SI.METER) : 0); 144 double z = (dims > 2 ? point.getValue(2, SI.METER) : 0); 145 Point3d point3d = new Point3d(x, y, z); 146 147 if (i != 0) { 148 lineA.setCoordinate(lineIndex, oldPoint); // Store start of line segment. 149 lineA.setColor(lineIndex++, lineColor); 150 lineA.setCoordinate(lineIndex, point3d); // Store end of line segment. 151 lineA.setColor(lineIndex++, lineColor); 152 } 153 pointA.setCoordinate(i, point3d); 154 pointA.setColor(i, pointColor); 155 156 oldPoint = point3d; 157 } 158 159 // Create a 3D shape from the line array 160 lineShape = new GeomShape3D(thisString, lineA); 161 pointShape = new GeomShape3D(thisString, pointA); 162 163 } finally { 164 StackContext.exit(); 165 } 166 } 167 _oldLineShape = (Shape3D)lineShape.cloneNode(true); // Save off for potential re-use in the future. 168 _oldPointShape = (Shape3D)pointShape.cloneNode(true); 169 170 // Define the appearance of the line array. 171 Appearance lineAppearance = new Appearance(); 172 LineAttributes lineAttrib = new LineAttributes(); 173 lineAttrib.setLineWidth(drawPrefs.getLineWidth()); 174 lineAppearance.setLineAttributes(lineAttrib); 175 lineShape.setAppearance(lineAppearance); 176 177 // Create a 3D shape from the point array 178 Appearance appearance = new Appearance(); 179 pointShape.setAppearance(appearance); 180 appearance.setPointAttributes(new PointAttributes(drawPrefs.getPointSize(), true)); 181 182 // Create a switch group for the main geometry (unmirrored) 183 // that contains each variation on the display properties of a PointString. 184 Switch renderTypeSG = new Switch(0); 185 renderTypeSG.setCapability(Switch.ALLOW_SWITCH_READ); 186 renderTypeSG.setCapability(Switch.ALLOW_SWITCH_WRITE); 187 188 // Add the geometry to the display switch. 189 renderTypeSG.addChild(lineShape); 190 renderTypeSG.addChild(pointShape); 191 192 // Add the basic unmirrored geometry to the symmetry switch. 193 _symmSG = new Switch(); 194 _symmSG.setCapability(Switch.ALLOW_SWITCH_READ); 195 _symmSG.setCapability(Switch.ALLOW_SWITCH_WRITE); 196 _symmSG.addChild(renderTypeSG); 197 198 // Clone the basic geometry to make the mirrored geometry. 199 Node mirrored = renderTypeSG.cloneTree(); 200 201 // Create a mirror across the XZ plane of symmetry transform. 202 Transform3D symmT = new Transform3D(); 203 symmT.setScale(new Vector3d(1, -1, 1)); 204 TransformGroup symmTG = new TransformGroup(symmT); 205 206 // Add the mirrored geometry to the symmetry transform group. 207 symmTG.addChild(mirrored); 208 209 // Add the mirrored geometry to the symmetry switch. 210 _symmSG.addChild(symmTG); 211 212 // By default, display only the main geometry (not the mirrored). 213 _symmSG.setWhichChild(0); 214 215 return _symmSG; 216 } 217 218 /** 219 * Set the render type used for this group. 220 */ 221 @Override 222 public void setRenderType(RenderType type) { 223 super.setRenderType(requireNonNull(type)); 224 225 // Get the main geometry's render switch. 226 Switch mainRenderSG = (Switch)_symmSG.getChild(0); 227 228 // Get the mirrored geometry's render switch. 229 Switch mirrRenderSG = (Switch)((Group)_symmSG.getChild(1)).getChild(0); 230 231 // Set switches based on render code. 232 switch (type) { 233 case SOLID_PLUS_WIREFRAME: 234 case SOLID: 235 case WIREFRAME: 236 case STRINGS: 237 mainRenderSG.setWhichChild(0); 238 mirrRenderSG.setWhichChild(0); 239 break; 240 241 case POINTS: 242 mainRenderSG.setWhichChild(1); 243 mirrRenderSG.setWhichChild(1); 244 break; 245 246 default: 247 break; 248 } 249 } 250 251 /** 252 * Creates a new instance of the node. This routine is called by 253 * <code>cloneTree</code> to duplicate the current node. 254 * 255 * @param forceDuplicate when set to <code>true</code>, causes the 256 * <code>duplicateOnCloneTree</code> flag to be ignored. When 257 * <code>false</code>, the value of each node's 258 * <code>duplicateOnCloneTree</code> variable determines whether 259 * NodeComponent data is duplicated or copied. 260 * @return A new instance of this Java3D node. 261 */ 262 @Override 263 public Node cloneNode(boolean forceDuplicate) { 264 J3DPointString node = new J3DPointString(this.getCanvas3D(), this.getGeomElement()); 265 node.duplicateNode(this, forceDuplicate); 266 return node; 267 } 268}