/* * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; /** * Implements Text3D class. */ class Text3DRetained extends GeometryRetained { /** * Packaged scope variables needed for implementation */ Font3D font3D = null; String string = null; Point3f position = new Point3f(0.0f, 0.0f, 0.0f); int alignment = Text3D.ALIGN_FIRST, path = Text3D.PATH_RIGHT; float charSpacing = 0.0f; int numChars = 0; static final int targetThreads = (J3dThread.UPDATE_TRANSFORM | J3dThread.UPDATE_GEOMETRY | J3dThread.UPDATE_RENDER); /** * The temporary transforms for this Text3D */ Transform3D[] charTransforms = new Transform3D[0]; /** * A cached list of geometry arrays for the current settings */ GeometryArrayRetained[] geometryList = new GeometryArrayRetained[0]; GlyphVector[] glyphVecs = new GlyphVector[0]; /** * Bounding box data for this text string. */ Point3d lower = new Point3d(); Point3d upper = new Point3d(); /** * An Array list used for messages */ ArrayList newGeometryAtomList = new ArrayList(); ArrayList oldGeometryAtomList = new ArrayList(); /** * temporary model view matrix for immediate mode only */ Transform3D vpcToEc; Transform3D drawTransform; Text3DRetained(){ this.geoType = GEO_TYPE_TEXT3D; } @Override synchronized void computeBoundingBox() { Point3d l = new Point3d(); Point3d u = new Point3d(); Vector3f location = new Vector3f(this.position); int i, k=0, numTotal=0; double width = 0, height = 0; Rectangle2D bounds; //Reset bounds data l.set(location); u.set(location); if (numChars != 0) { // Set loop counters based on path type if (path == Text3D.PATH_RIGHT || path == Text3D.PATH_UP) { k = 0; numTotal = numChars + 1; } else if (path == Text3D.PATH_LEFT || path == Text3D.PATH_DOWN) { k = 1; numTotal = numChars; // Reset bounds to bounding box if first character bounds = glyphVecs[0].getVisualBounds(); u.x += bounds.getWidth(); u.y += bounds.getHeight(); } for (i=1; iposition field into the supplied * parameter. The position is used to determine the * initial placement of the Text3D string. The position, combined with * the path and alignment control how the text is displayed. * * @param position the point to position the text. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph * * @see #getAlignment * @see #getPath */ final void getPosition(Point3f position) { position.set(this.position); } /** * Sets the node's position field to the supplied * parameter. The position is used to determine the * initial placement of the Text3D string. The position, combined with * the path and alignment control how the text is displayed. * * @param position the point to position the text. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph * * @see #getAlignment * @see #getPath */ final void setPosition(Point3f position) { geomLock.getLock(); this.position.set(position); updateTransformData(); geomLock.unLock(); sendTransformChangedMessage(); } /** * Retrieves the text alignment policy for this Text3D NodeComponent * object. The alignment is used to specify how * glyphs in the string are placed in relation to the * position field. Valid values for this field * are: * * The default value of this field is ALIGN_FIRST. * * @return the current alingment policy for this node. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph * * @see #getPosition */ final int getAlignment() { return alignment; } /** * Sets the text alignment policy for this Text3D NodeComponent * object. The alignment is used to specify how * glyphs in the string are placed in relation to the * position field. Valid values for this field * are: * * The default value of this field is ALIGN_FIRST. * * @return the current alingment policy for this node. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph * * @see #getPosition */ final void setAlignment(int alignment) { geomLock.getLock(); this.alignment = alignment; updateTransformData(); geomLock.unLock(); sendTransformChangedMessage(); } /** * Retrieves the node's path field. This field * is used to specify how succeeding * glyphs in the string are placed in relation to the previous glyph. * Valid values for this field are: * * The default value of this field is PATH_RIGHT. * * @return the current alingment policy for this node. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph */ final int getPath() { return this.path; } /** * Sets the node's path field. This field * is used to specify how succeeding * glyphs in the string are placed in relation to the previous glyph. * Valid values for this field are: * * The default value of this field is PATH_RIGHT. * * @param path the value to set the path to. * * @return the current alingment policy for this node. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph */ final void setPath(int path) { this.path = path; updateTransformData(); sendTransformChangedMessage(); } /** * Retrieves the 3D bounding box that encloses this Text3D object. * * @param bounds the object to copy the bounding information to. * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph * * @see BoundingBox */ final void getBoundingBox(BoundingBox bounds) { synchronized (this) { bounds.setLower(lower); bounds.setUpper(upper); } } /** * Retrieves the character spacing used to construct the Text3D string. * This spacing is in addition to the regular spacing between glyphs as * defined in the Font object. 1.0 in this space is measured as the * width of the largest glyph in the 2D Font. The default value is * 0.0. * * @return the current character spacing value * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph */ final float getCharacterSpacing() { return charSpacing; } /** * Sets the character spacing used hwne constructing the Text3D string. * This spacing is in addition to the regular spacing between glyphs as * defined in the Font object. 1.0 in this space is measured as the * width of the largest glyph in the 2D Font. The default value is * 0.0. * * @param characterSpacing the new character spacing value * * @exception CapabilityNotSetException if appropriate capability is * not set and this object is part of live or compiled scene graph */ final void setCharacterSpacing(float characterSpacing) { geomLock.getLock(); this.charSpacing = characterSpacing; updateTransformData(); geomLock.unLock(); sendTransformChangedMessage(); } final void sendDataChangedMessage() { J3dMessage[] m; int i, j, k, kk, numMessages; int gSize; ArrayList gaList; GeometryAtom[] newGeometryAtoms; ArrayList tiArrList = new ArrayList(); ArrayList newCtArrArrList = new ArrayList(); synchronized(liveStateLock) { if (source.isLive()) { synchronized (universeList) { numMessages = universeList.size(); m = new J3dMessage[numMessages]; for (i=0; i shapeList = userLists.get(i); newGeometryAtomList.clear(); oldGeometryAtomList.clear(); for (j=0; j 0) { m[i].args[2] = tiArrList.toArray(); m[i].args[3] = newCtArrArrList.toArray(); } tiArrList.clear(); newCtArrArrList.clear(); } VirtualUniverse.mc.processMessage(m); } } } } final void sendTransformChangedMessage() { J3dMessage[] m; int i, j, numMessages, sCnt; ArrayList gaList = new ArrayList(); GeometryRetained geomR; synchronized(liveStateLock) { if (source.isLive()) { synchronized (universeList) { numMessages = universeList.size(); m = new J3dMessage[numMessages]; for (i=0; i shapeList = userLists.get(i); // gaList = new GeometryAtom[shapeList.size() * numChars]; for (j = 0; j < shapeList.size(); j++) { Shape3DRetained s = shapeList.get(j); // Find the right geometry. for(sCnt=0; sCnt= 0) { // We need to transform iPnt to the vworld to compute the actual distance. // In this method we'll transform iPnt by its char. offset. Shape3D will // do the localToVworld transform. iPnt.set(closestIPnt); charTransforms[sIndex].transform(iPnt); return true; } return false; } @Override boolean intersect(Point3d[] pnts) { Transform3D tempT3D = new Transform3D(); GeometryArrayRetained ga; boolean isIntersect = false; Point3d transPnts[] = new Point3d[pnts.length]; for (int j=pnts.length-1; j >= 0; j--) { transPnts[j] = new Point3d(); } for (int i=numChars-1; i >= 0; i--) { ga = geometryList[i]; if ( ga != null) { tempT3D.invert(charTransforms[i]); for (int j=pnts.length-1; j >= 0; j--) { tempT3D.transform(pnts[j], transPnts[j]); } if (ga.intersect(transPnts)) { isIntersect = true; break; } } } return isIntersect; } @Override boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) { GeometryArrayRetained ga; for (int i=numChars-1; i >=0; i--) { ga = geometryList[i]; if ((ga != null) && ga.intersect(thisToOtherVworld, geom)) { return true; } } return false; } @Override boolean intersect(Bounds targetBound) { GeometryArrayRetained ga; for (int i=numChars-1; i >=0; i--) { ga = geometryList[i]; if ((ga != null) && ga.intersect(targetBound)) { return true; } } return false; } void setModelViewMatrix(Transform3D vpcToEc, Transform3D drawTransform) { this.vpcToEc = vpcToEc; this.drawTransform = drawTransform; } @Override void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, boolean updateAlpha, float alpha, int screen, boolean ignoreVertexColors) { Transform3D trans = new Transform3D(); for (int i = 0; i < geometryList.length; i++) { trans.set(drawTransform); trans.mul(charTransforms[i]); cv.setModelViewMatrix(cv.ctx, vpcToEc.mat, trans); geometryList[i].execute(cv, ra, isNonUniformScale, updateAlpha, alpha, screen, ignoreVertexColors); } } @Override int getClassType() { return TEXT3D_TYPE; } ArrayList getUniqueSource(ArrayList shapeList) { ArrayList uniqueList = new ArrayList(); int size = shapeList.size(); Object src; int i, index; for (i=0; i