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