diff options
Diffstat (limited to 'src/javax/media/j3d/Text3DRetained.java')
-rw-r--r-- | src/javax/media/j3d/Text3DRetained.java | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/src/javax/media/j3d/Text3DRetained.java b/src/javax/media/j3d/Text3DRetained.java new file mode 100644 index 0000000..ed21941 --- /dev/null +++ b/src/javax/media/j3d/Text3DRetained.java @@ -0,0 +1,999 @@ +/* + * 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; i<numTotal; i++, k++) { + width = glyphVecs[k].getLogicalBounds().getWidth(); + bounds = glyphVecs[k].getVisualBounds(); + // 'switch' could be outside loop with little hacking, + width += charSpacing; + height = bounds.getHeight(); + + switch (this.path) { + case Text3D.PATH_RIGHT: + u.x += (width); + if (u.y < (height + location.y)) { + u.y = location.y + height; + } + break; + case Text3D.PATH_LEFT: + l.x -= (width); + if (u.y < ( height + location.y)) { + u.y = location.y + height; + } + break; + case Text3D.PATH_UP: + u.y += height; + if (u.x < (bounds.getWidth() + location.x)) { + u.x = location.x + bounds.getWidth(); + } + break; + case Text3D.PATH_DOWN: + l.y -= height; + if (u.x < (bounds.getWidth() + location.x)) { + u.x = location.x + bounds.getWidth(); + } + break; + } + } + + // Handle string alignment. ALIGN_FIRST is handled by default + if (alignment != Text3D.ALIGN_FIRST) { + double cx = (u.x - l.x); + double cy = (u.y - l.y); + + if (alignment == Text3D.ALIGN_CENTER) { + cx *= .5; + cy *= .5; + } + switch (path) { + case Text3D.PATH_RIGHT: + l.x -= cx; + u.x -= cx; + break; + case Text3D.PATH_LEFT: + l.x += cx; + u.x += cx; + break; + case Text3D.PATH_UP: + l.y -= cy; + u.y -= cy; + break; + case Text3D.PATH_DOWN: + l.y += cy; + u.y += cy; + break; + + } + } + } + + l.z = 0.0f; + if ((font3D == null) || (font3D.fontExtrusion == null)) { + u.z = l.z; + } else { + u.z = l.z + font3D.fontExtrusion.length; + } + } + + @Override + void update() {} + + + /** + * Returns the Font3D objects used by this Text3D NodeComponent object. + * + * @return the Font3D object of this Text3D node - null if no Font3D + * has been associated with this node. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + final Font3D getFont3D() { + return this.font3D; + } + + /** + * Sets the Font3D object used by this Text3D NodeComponent object. + * + * @param font3d the Font3D object to associate with this Text3D node. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + final void setFont3D(Font3D font3d) { + geomLock.getLock(); + this.font3D = font3d; + updateCharacterData(); + geomLock.unLock(); + sendDataChangedMessage(); + } + + /** + * Copies the character string used in the construction of the + * Text3D node into the supplied parameter. + * + * @return a copy of the String object in this Text3D node. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + final String getString() { + return this.string; + } + + /** + * Copies the character string from the supplied parameter into Tex3D + * node. + * + * @param string the String object to recieve the Text3D node's string. + * + * @exception CapabilityNotSetException if appropriate capability is + * not set and this object is part of live or compiled scene graph + */ + final void setString(String string) { + geomLock.getLock(); + this.string = string; + if (string == null) { + numChars = 0; + } else { + numChars = string.length(); + } + updateCharacterData(); + geomLock.unLock(); + sendDataChangedMessage(); + } + + /** + * Copies the node's <code>position</code> field into the supplied + * parameter. The <code>position</code> 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 <code>position</code> field to the supplied + * parameter. The <code>position</code> 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 <code>alignment</code> is used to specify how + * glyphs in the string are placed in relation to the + * <code>position</code> field. Valid values for this field + * are: + * <UL> + * <LI> ALIGN_CENTER - the center of the string is placed on the + * <code>position</code> point. + * <LI> ALIGN_FIRST - the first character of the string is placed on + * the <code>position</code> point. + * <LI> ALIGN_LAST - the last character of the string is placed on the + * <code>position</code> point. + * </UL> + * The default value of this field is <code>ALIGN_FIRST</code>. + * + * @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 <code>alignment</code> is used to specify how + * glyphs in the string are placed in relation to the + * <code>position</code> field. Valid values for this field + * are: + * <UL> + * <LI> ALIGN_CENTER - the center of the string is placed on the + * <code>position</code> point. + * <LI> ALIGN_FIRST - the first character of the string is placed on + * the <code>position</code> point. + * <LI> ALIGN_LAST - the last character of the string is placed on the + * <code>position</code> point. + * </UL> + * The default value of this field is <code>ALIGN_FIRST</code>. + * + * @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 <code>path</code> 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: + * <UL> + * <LI> PATH_LEFT: - succeeding glyphs are placed to the left of the + * current glyph. + * <LI> PATH_RIGHT: - succeeding glyphs are placed to the right of the + * current glyph. + * <LI> PATH_UP: - succeeding glyphs are placed above the current glyph. + * <LI> PATH_DOWN: - succeeding glyphs are placed below the current glyph. + * </UL> + * The default value of this field is <code>PATH_RIGHT</code>. + * + * @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 <code>path</code> 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: + * <UL> + * <LI> PATH_LEFT - succeeding glyphs are placed to the left of the + * current glyph. + * <LI> PATH_RIGHT - succeeding glyphs are placed to the right of the + * current glyph. + * <LI> PATH_UP - succeeding glyphs are placed above the current glyph. + * <LI> PATH_DOWN - succeeding glyphs are placed below the current glyph. + * </UL> + * The default value of this field is <code>PATH_RIGHT</code>. + * + * @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<numMessages; i++) { + m[i] = new J3dMessage(); + m[i].type = J3dMessage.TEXT3D_DATA_CHANGED; + m[i].threads = targetThreads; + ArrayList<Shape3DRetained> shapeList = userLists.get(i); + newGeometryAtomList.clear(); + oldGeometryAtomList.clear(); + + for (j=0; j<shapeList.size(); j++) { + Shape3DRetained s = shapeList.get(j); + if (s.boundsAutoCompute) { + // update combine bounds of mirrorShape3Ds. So we need to + // use its bounds and not localBounds. + // bounds is actually a reference to + // mirrorShape3D.source.localBounds. + // XXXX : Should only need to update distinct localBounds. + s.getCombineBounds((BoundingBox)s.bounds); + } + + gSize = s.geometryList.size(); + + GeometryAtom oldGA = Shape3DRetained.getGeomAtom(s); + GeometryAtom newGA = new GeometryAtom(); + + int geometryCnt = 0; + for(k = 0; k<gSize; k++) { + GeometryRetained geomRetained = s.geometryList.get(k); + if(geomRetained != null) { + Text3DRetained tempT3d = (Text3DRetained)geomRetained; + geometryCnt += tempT3d.numChars; + } + else { + // Slightly wasteful, but not quite worth to optimize yet. + geometryCnt++; + } + } + + newGA.geometryArray = new GeometryRetained[geometryCnt]; + newGA.lastLocalTransformArray = new Transform3D[geometryCnt]; + // Reset geometryCnt; + geometryCnt = 0; + + newGA.locale = s.locale; + newGA.visible = s.visible; + newGA.source = s; + int gaCnt=0; + GeometryRetained geometry = null; + for(; gaCnt<gSize; gaCnt++) { + geometry = s.geometryList.get(gaCnt); + if(geometry != null) { + newGA.geoType = geometry.geoType; + newGA.alphaEditable = s.isAlphaEditable(geometry); + break; + } + } + + for(; gaCnt<gSize; gaCnt++) { + geometry = s.geometryList.get(gaCnt); + if(geometry == null) { + newGA.geometryArray[gaCnt] = null; + } + else { + Text3DRetained t = (Text3DRetained)geometry; + GeometryRetained geo; + for (k=0; k<t.numChars; k++, geometryCnt++) { + geo = t.geometryList[k]; + if (geo != null) { + newGA.geometryArray[geometryCnt] = geo; + newGA.lastLocalTransformArray[geometryCnt] = + t.charTransforms[k]; + + } else { + newGA.geometryArray[geometryCnt] = null; + newGA.lastLocalTransformArray[geometryCnt] = null; + } + + } + + } + } + + oldGeometryAtomList.add(oldGA); + newGeometryAtomList.add(newGA); + Shape3DRetained.setGeomAtom(s, newGA); + } + + Object[] oldGAArray = oldGeometryAtomList.toArray(); + Object[] newGAArray = newGeometryAtomList.toArray(); + ArrayList uniqueList = getUniqueSource(shapeList); + int numSrc = uniqueList.size(); + int numMS3D; + Shape3DRetained ms, src; + + for (j=0; j<numSrc; j++) { + CachedTargets[] newCtArr = null; + src = (Shape3DRetained)uniqueList.get(j); + numMS3D = src.mirrorShape3D.size(); + + TargetsInterface ti = ((GroupRetained)src. + parent).getClosestTargetsInterface( + TargetsInterface.TRANSFORM_TARGETS); + + if (ti != null) { + CachedTargets ct; + newCtArr = new CachedTargets[numMS3D]; + + for (k=0; k<numMS3D; k++) { + ms = src.mirrorShape3D.get(k); + + GeometryAtom ga = + Shape3DRetained.getGeomAtom(ms); + for(kk=0; kk<newGAArray.length; kk++) { + if(ga == newGAArray[kk]) { + break; + } + } + + if(kk==newGAArray.length) { + System.err.println("Text3DRetained : Problem !!! Can't find matching geomAtom"); + } + + ct = ti.getCachedTargets(TargetsInterface. + TRANSFORM_TARGETS, k, -1); + if (ct != null) { + newCtArr[k] = new CachedTargets(); + newCtArr[k].copy(ct); + newCtArr[k].replace((NnuId)oldGAArray[kk], + (NnuId)newGAArray[kk], + Targets.GEO_TARGETS); + } else { + newCtArr[k] = null; + } + + } + + ti.resetCachedTargets( + TargetsInterface.TRANSFORM_TARGETS, newCtArr, -1); + + tiArrList.add(ti); + newCtArrArrList.add(newCtArr); + + } + + } + + m[i].args[0] = oldGAArray; + m[i].args[1] = newGAArray; + m[i].universe = universeList.get(i); + + if(tiArrList.size() > 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<numMessages; i++) { + m[i] = new J3dMessage(); + m[i].type = J3dMessage.TEXT3D_TRANSFORM_CHANGED; + m[i].threads = targetThreads; + ArrayList<Shape3DRetained> 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<s.geometryList.size(); sCnt++) { + geomR = s.geometryList.get(sCnt); + if(geomR == this) { + break; + } + } + + if(sCnt < s.geometryList.size()) + gaList.add(Shape3DRetained.getGeomAtom(s)); + + } + m[i].args[0] = gaList.toArray(); + m[i].args[1] = charTransforms; + m[i].universe = universeList.get(i); + } + VirtualUniverse.mc.processMessage(m); + } + } + } + } + + /** + * Update internal reprsentation of tranform matrices and geometry. + * This method will be called whenever string or font3D change. + */ + final void updateCharacterData() { + char c[] = new char[1]; + + if (geometryList.length != numChars) { + geometryList = new GeometryArrayRetained[numChars]; + glyphVecs = new GlyphVector[numChars]; + } + + if (font3D != null) { + for (int i=0; i<numChars; i++) { + c[0] = string.charAt(i); + glyphVecs[i] = font3D.font.createGlyphVector(font3D.frc, c); + geometryList[i] = font3D.triangulateGlyphs(glyphVecs[i], c[0]); + } + } + + updateTransformData(); + } + + /** + * Update per character transform based on Text3D location, + * per character size and path. + * + * WARNING: Caller of this method must make sure SceneGraph is live, + * else exceptions may be thrown. + */ + final void updateTransformData(){ + int i, k=0, numTotal=0; + double width = 0, height = 0; + Vector3f location = new Vector3f(this.position); + Rectangle2D bounds; + + //Reset bounds data + lower.set(location); + upper.set(location); + + charTransforms = new Transform3D[numChars]; + for (i=0; i<numChars; i++) { + charTransforms[i] = new Transform3D(); + } + + if (numChars != 0) { + charTransforms[0].set(location); + + // 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(); + upper.x += bounds.getWidth(); + upper.y += bounds.getHeight(); + } + + for (i=1; i<numTotal; i++, k++) { + width = glyphVecs[k].getLogicalBounds().getWidth(); + bounds = glyphVecs[k].getVisualBounds(); + // 'switch' could be outside loop with little hacking, + width += charSpacing; + height = bounds.getHeight(); + + switch (this.path) { + case Text3D.PATH_RIGHT: + location.x += width; + upper.x += (width); + if (upper.y < (height + location.y)) { + upper.y = location.y + height; + } + break; + case Text3D.PATH_LEFT: + location.x -= width; + lower.x -= (width); + if (upper.y < ( height + location.y)) { + upper.y = location.y + height; + } + break; + case Text3D.PATH_UP: + location.y += height; + upper.y += height; + if (upper.x < (bounds.getWidth() + location.x)) { + upper.x = location.x + bounds.getWidth(); + } + break; + case Text3D.PATH_DOWN: + location.y -= height; + lower.y -= height; + if (upper.x < (bounds.getWidth() + location.x)) { + upper.x = location.x + bounds.getWidth(); + } + break; + } + if (i < numChars) { + charTransforms[i].set(location); + } + } + + // Handle string alignment. ALIGN_FIRST is handled by default + if (alignment != Text3D.ALIGN_FIRST) { + double cx = (upper.x - lower.x); + double cy = (upper.y - lower.y); + + if (alignment == Text3D.ALIGN_CENTER) { + cx *= .5; + cy *= .5; + } + switch (path) { + case Text3D.PATH_RIGHT: + for (i=0;i < numChars;i++) { + charTransforms[i].mat[3] -= cx; + } + lower.x -= cx; + upper.x -= cx; + break; + case Text3D.PATH_LEFT: + for (i=0;i < numChars;i++) { + charTransforms[i].mat[3] += cx; + } + lower.x += cx; + upper.x += cx; + break; + + case Text3D.PATH_UP: + for (i=0;i < numChars;i++) { + charTransforms[i].mat[7] -=cy; + } + lower.y -= cy; + upper.y -= cy; + break; + case Text3D.PATH_DOWN: + for (i=0;i < numChars;i++) { + charTransforms[i].mat[7] +=cy; + } + lower.y += cy; + upper.y += cy; + break; + + } + } + } + + lower.z = 0.0f; + if ((font3D == null) || (font3D.fontExtrusion == null)) { + upper.z = lower.z; + } else { + upper.z = lower.z + font3D.fontExtrusion.length; + } + + // update geoBounds + getBoundingBox(geoBounds); + } + + + /** + * This method is called when the SceneGraph becomes live. All characters + * used by this.string are tesselated in this method, to avoid wait during + * traversal and rendering. + */ + @Override + void setLive(boolean inBackgroundGroup, int refCount) { + // Tesselate all character data and update character transforms + updateCharacterData(); + super.doSetLive(inBackgroundGroup, refCount); + super.markAsLive(); + } + + // TODO -- Need to rethink. Might have to consider charTransform[] in returns pickInfo. + @Override + boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, + GeometryRetained geom, int geomIndex) { + Transform3D tempT3D = new Transform3D(); + GeometryArrayRetained geo = null; + int sIndex = -1; + PickShape newPS; + double minDist = Double.MAX_VALUE; + double distance =0.0; + Point3d closestIPnt = new Point3d(); + + for (int i=0; i < numChars; i++) { + geo= geometryList[i]; + if (geo != null) { + tempT3D.invert(charTransforms[i]); + newPS = pickShape.transform(tempT3D); + if (geo.intersect(newPS, pickInfo, flags, iPnt, geom, geomIndex)) { + if (flags == 0) { + return true; + } + distance = newPS.distance(iPnt); + if (distance < minDist) { + sIndex = i; + minDist = distance; + closestIPnt.set(iPnt); + } + } + } + } + + if (sIndex >= 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<size; i++) { + src = ((Shape3DRetained)shapeList.get(i)).sourceNode; + index = uniqueList.indexOf(src); + if (index == -1) { + uniqueList.add(src); + } + } + return uniqueList; + } +} + |