001/** 002 * J3DGenModelNote -- A Java3D node that represents a GenModelNote in a J3D scene graph. 003 * 004 * Copyright (C) 2014-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 org.jogamp.java3d.utils.geometry.Text2D; 022import geomss.app.GeomSSCanvas3D; 023import geomss.geom.GenModelNote; 024import geomss.geom.GeomPoint; 025import geomss.geom.GeomVector; 026import jahuwaldt.j3d.JColor3f; 027import java.awt.Canvas; 028import java.awt.Font; 029import java.awt.FontMetrics; 030import java.awt.SystemColor; 031import static java.util.Objects.requireNonNull; 032import javax.measure.quantity.Dimensionless; 033import javax.measure.unit.SI; 034import org.jogamp.java3d.*; 035import org.jogamp.vecmath.*; 036 037/** 038 * A Java 3D node that represents a GenModelNote in a Java 3D scene graph. 039 * 040 * <p> Modified by: Joseph A. Huwaldt </p> 041 * 042 * @author Joseph A. Huwaldt, Date: February 10, 2014 043 * @version June 4, 2023 044 */ 045public class J3DGenModelNote extends J3DGeomGroup<GenModelNote> { 046 047 private static final int X = 0, Y = 1, Z = 2; 048 049 // The switch for main or mirrored geometry. 050 private Switch _symmSG; 051 052 /** 053 * Construct a J3DGenModelNote using the specified GenModelNote as a reference. 054 * 055 * @param canvas The canvas that the geometry is being rendered into. 056 * @param geometry The GeomSS geometry to be turned into a Java3D node. 057 */ 058 public J3DGenModelNote(GeomSSCanvas3D canvas, GenModelNote geometry) { 059 super(requireNonNull(canvas), requireNonNull(geometry)); 060 } 061 062 /** 063 * Set the display of a mirrored copy of this geometry. This is called from 064 * "setDisplayed()" to turn on and off the display of mirrored geometry without 065 * changing the "mirrored state" of the object. This call does not affect the output 066 * of "isMirrored()". 067 * 068 * @param mirrored Flag indicating if the mirrored geometry should be displayed or 069 * not. 070 * @see #setMirrored(boolean) 071 * @see #isMirrored() 072 */ 073 @Override 074 protected void internalSetMirrored(boolean mirrored) { 075 if (mirrored) 076 _symmSG.setWhichChild(Switch.CHILD_ALL); 077 else 078 _symmSG.setWhichChild(0); 079 } 080 081 /** 082 * Create a new Java 3D <code>Group</code> that contains the geometry contained in 083 * this object. This method is called from <code>createSceneGraph</code>. 084 * 085 * @return New Java 3D Group that contains the geometry in this object. 086 * @see #createSceneGraph 087 */ 088 @Override 089 protected Group createGeometry() { 090 //J3DRenderingPrefs drawPrefs = getRenderingPrefs(); 091 092 // Create a Text2D representation of the note. 093 GenModelNote thisNote = this.getGeomElement(); 094 Font font = thisNote.getFont(); 095 font = font.deriveFont(font.getSize() * 4f); // Scale up the font a bit to make it sharper in the rendering. 096 JColor3f textColor = new JColor3f(SystemColor.textText); 097 Text2D t2d = new Text2D(thisNote.getNote(), textColor, font.getName(), font.getSize(), font.getStyle()); 098 099 // Set the text scale factor to get the box height correct. 100 FontMetrics fm = new Canvas().getFontMetrics(font); 101 int fontAscent = fm.getMaxAscent(); 102 int fontDescent = fm.getMaxDescent(); 103 int fontHeight = fontAscent + fontDescent; 104 105 // Need to make height a power of 2 because of Java3d texture 106 // size restrictions 107 int pow = 1; 108 for (int i = 1; i < 32; ++i) { 109 pow *= 2; 110 if (fontHeight <= pow) 111 break; 112 } 113 fontHeight = Math.max(fontHeight, pow); 114 115 double modelHeight = thisNote.getHeight().getValue(SI.METER); // Convert all geometry to meters. 116 double scale = modelHeight / fontHeight; 117 t2d.setRectangleScaleFactor((float)scale); 118 119 // Get the location of the note. 120 GeomPoint location = thisNote.getLocation().to(SI.METER); // Convert all geometry to meters. 121 int dims = location.getPhyDimension(); 122 double x = location.getValue(X); 123 double y = location.getValue(Y); 124 double z = (dims > 2 ? location.getValue(Z) : 0); 125 Vector3d location3d = new Vector3d(x, y, z); 126 127 // Get the orientation of the note. 128 Matrix3d rotT = new Matrix3d(); 129 GeomVector<Dimensionless> xhat = thisNote.getXHat(); 130 x = xhat.getValue(X); 131 y = xhat.getValue(Y); 132 z = (dims > 2 ? xhat.getValue(Z) : 0); 133 rotT.setColumn(X, x, y, z); 134 GeomVector<Dimensionless> yhat = thisNote.getYHat(); 135 x = yhat.getValue(X); 136 y = yhat.getValue(Y); 137 z = (dims > 2 ? yhat.getValue(Z) : 0); 138 rotT.setColumn(Y, x, y, z); 139 GeomVector<Dimensionless> zhat = thisNote.getNormal(); 140 x = zhat.getValue(X); 141 y = zhat.getValue(Y); 142 z = (dims > 2 ? zhat.getValue(Z) : 0); 143 rotT.setColumn(Z, x, y, z); 144 145 // Offset the text so that the text box origin is at the text string baseline. 146 Vector3d offset = new Vector3d(0, -fontDescent * scale, 0); 147 rotT.transform(offset); 148 location3d.add(offset); 149 150 // Update the appearance of the Text2D shape. 151 Appearance app = t2d.getAppearance(); 152 PolygonAttributes pa = app.getPolygonAttributes(); 153 if (pa == null) 154 pa = new PolygonAttributes(); 155 pa.setCullFace(PolygonAttributes.CULL_NONE); 156 if (app.getPolygonAttributes() == null) 157 app.setPolygonAttributes(pa); 158 159 // Create a transform group that contains the Text2D object oriented and 160 // positioned correctly. 161 Transform3D T = new Transform3D(); 162 T.setRotation(rotT); 163 T.setTranslation(location3d); 164 TransformGroup TG = new TransformGroup(T); 165 TG.addChild(t2d); 166 167 // Add the basic unmirrored geometry to the switch. 168 _symmSG = new Switch(); 169 _symmSG.setCapability(Switch.ALLOW_SWITCH_READ); 170 _symmSG.setCapability(Switch.ALLOW_SWITCH_WRITE); 171 _symmSG.addChild(TG); 172 173 // Clone the basic geometry to make the mirrored geometry. 174 Node mirrored = TG.cloneTree(); 175 176 // Create a mirror across the XZ plane of symmetry transform. 177 Transform3D symmT = new Transform3D(); 178 symmT.setScale(new Vector3d(1, -1, 1)); 179 TransformGroup symmTG = new TransformGroup(symmT); 180 181 // Add the mirrored geometry to the symmetry transform group. 182 symmTG.addChild(mirrored); 183 184 // Add the mirrored geometry to the switch. 185 _symmSG.addChild(symmTG); 186 187 // By default, display only the main geometry (not the mirrored). 188 _symmSG.setWhichChild(0); 189 190 return _symmSG; 191 } 192 193 /** 194 * Creates a new instance of the node. This routine is called by 195 * <code>cloneTree</code> to duplicate the current node. 196 * 197 * @param forceDuplicate when set to <code>true</code>, causes the 198 * <code>duplicateOnCloneTree</code> flag to be ignored. When 199 * <code>false</code>, the value of each node's 200 * <code>duplicateOnCloneTree</code> variable determines whether 201 * NodeComponent data is duplicated or copied. 202 * @return A new instance of this Java3D node. 203 */ 204 @Override 205 public Node cloneNode(boolean forceDuplicate) { 206 J3DGenModelNote node = new J3DGenModelNote(this.getCanvas3D(), this.getGeomElement()); 207 node.duplicateNode(this, forceDuplicate); 208 return node; 209 } 210 211}