diff options
Diffstat (limited to 'src/javax/media/j3d/SceneGraphPath.java')
-rw-r--r-- | src/javax/media/j3d/SceneGraphPath.java | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/src/javax/media/j3d/SceneGraphPath.java b/src/javax/media/j3d/SceneGraphPath.java new file mode 100644 index 0000000..aac2f9d --- /dev/null +++ b/src/javax/media/j3d/SceneGraphPath.java @@ -0,0 +1,668 @@ +/* + * Copyright 1997-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 javax.vecmath.Point3d; +import javax.vecmath.Point4d; + +/** + * A SceneGraphPath object represents the path from a Locale to a + * terminal node in the scene graph. This path consists of a Locale, a + * terminal node, and an array of internal nodes that are in the path + * from the Locale to the terminal node. The terminal node may be + * either a Leaf node or a Group node. A valid SceneGraphPath must + * uniquely identify a specific instance of the terminal node. For + * nodes that are not under a SharedGroup, the minimal SceneGraphPath + * consists of the Locale and the terminal node itself. For nodes that + * are under a SharedGroup, the minimal SceneGraphPath consists of the + * Locale, the terminal node, and a list of all Link nodes in the path + * from the Locale to the terminal node. A SceneGraphPath may optionally + * contain other interior nodes that are in the path. + * A SceneGraphPath is verified for correctness and uniqueness when + * it is sent as an argument to other methods of Java 3D. + * <p> + * In the array of internal nodes, the node at index 0 is the node + * closest to the Locale. The indices increase along the path to the + * terminal node, with the node at index length-1 being the node closest + * to the terminal node. The array of nodes does not contain either the + * Locale (which is not a node) or the terminal node. + * <p> + * When a SceneGraphPath is returned from the picking or collision + * methods of Java 3D, it will also contain the value of the + * LocalToVworld transform of the terminal node that was in effect at + * the time the pick or collision occurred. + * Note that ENABLE_PICK_REPORTING and ENABLE_COLLISION_REPORTING are + * disabled by default. This means that the picking and collision + * methods will return the minimal SceneGraphPath by default. + * + * @see Node#ENABLE_PICK_REPORTING + * @see Node#ENABLE_COLLISION_REPORTING + * @see BranchGroup#pickAll + * @see BranchGroup#pickAllSorted + * @see BranchGroup#pickClosest + * @see BranchGroup#pickAny + */ + +public class SceneGraphPath { + + Locale root = null; + Node[] interior = null; + Node item = null; + Transform3D transform = new Transform3D(); + + // Intersect Point for item when picked + Point3d intersectPoint = new Point3d(); + + double pickDistance; // distance to pick location + + /** + * Constructs a SceneGraphPath object with default parameters. + * The default values are as follows: + * <ul> + * root : null<br> + * object : null<br> + * list of (interior) nodes : null<br> + * transform : identity<br> + * </ul> + */ + public SceneGraphPath() { + // Just use defaults + } + + /** + * Constructs a new SceneGraphPath object. + * @param root the Locale object of this path + * @param object the terminal node of this path + */ + public SceneGraphPath(Locale root, Node object) { + + this.item = object; + this.root = root; + } + + /** + * Constructs a new SceneGraphPath object. + * @param root the Locale object of this path + * @param nodes an array of node objects in the path from + * the Locale to the terminal node + * @param object the terminal node of this path + */ + public SceneGraphPath(Locale root, Node nodes[], Node object) { + + this.item = object; + this.root = root; + this.interior = new Node[nodes.length]; + for (int i = 0; i < nodes.length; i++) + this.interior[i] = nodes[i]; + } + + + /** + * Constructs a new SceneGraphPath object + * @param sgp the SceneGraphPath to copy from + */ + SceneGraphPath(SceneGraphPath sgp) { + set(sgp); + } + + /** + * Sets this path's values to that of the specified path. + * @param newPath the SceneGraphPath to copy + */ + public final void set(SceneGraphPath newPath) { + this.root = newPath.root; + this.item = newPath.item; + this.transform.set(newPath.transform); + if(newPath.interior != null && newPath.interior.length > 0) { + interior = new Node[newPath.interior.length]; + for (int i = 0; i < interior.length; i++) + this.interior[i] = newPath.interior[i]; + } + else + interior = null; + } + + /** + * Sets this path's Locale to the specified Locale. + * @param newLocale The new Locale + */ + public final void setLocale(Locale newLocale) { + root = newLocale; + } + + /** + * Sets this path's terminal node to the specified node object. + * @param object the new terminal node + */ + public final void setObject(Node object) { + this.item = object; + } + + /** + * Sets this path's node objects to the specified node objects. + * @param nodes an array of node objects in the path from + * the Locale to the terminal node + */ + public final void setNodes(Node nodes[]) { + + if(nodes != null && nodes.length > 0) { + interior = new Node[nodes.length]; + for (int i = 0; i < nodes.length; i++) + this.interior[i] = nodes[i]; + } + else + interior = null; + } + + /** + * Replaces the node at the specified index with newNode. + * @param index the index of the node to replace + * @param newNode the new node + * @exception NullPointerException if the node array pointer is null. + * + */ + public final void setNode(int index, Node newNode) { + if(interior == null) + throw new NullPointerException(J3dI18N.getString("SceneGraphPath0")); + + interior[index] = newNode; + } + + /** + * Sets the transform component of this SceneGraphPath to the value of + * the passed transform. + * @param trans the transform to be copied. trans should be the + * localToVworld matrix of this SceneGraphPath object. + */ + public final void setTransform(Transform3D trans) { + transform.set(trans); + } + + /** + * Returns a copy of the transform associated with this SceneGraphPath; + * returns null if there is no transform associated. + * If this SceneGraphPath was returned by a Java 3D picking or + * collision method, the local coordinate to virtual world + * coordinate transform for this scene graph object at the + * time of the pick or collision is recorded. + * @return the local to VWorld transform + */ + public final Transform3D getTransform() { + return new Transform3D(transform); + } + + /** + * Retrieves the path's Locale + * @return this path's Locale + */ + public final Locale getLocale() { + return this.root; + } + + /** + * Retrieves the path's terminal node object. + * @return the terminal node + */ + public final Node getObject() { + return this.item; + } + + /** + * Retrieves the number of nodes in this path. The number of nodes + * does not include the Locale or the terminal node object itself. + * @return a count of the number of nodes in this path + */ + public final int nodeCount() { + if(interior == null) + return 0; + return interior.length; + } + + /** + * Retrieves the node at the specified index. + * @param index the index specifying which node to retrieve + * @return the specified node + */ + public final Node getNode(int index) { + if(interior == null) + throw new + ArrayIndexOutOfBoundsException(J3dI18N.getString("SceneGraphPath1")); + return interior[index]; + } + + /** + * Returns true if all of the data members of path testPath are + * equal to the corresponding data members in this SceneGraphPath and + * if the values of the transforms is equal. + * @param testPath the path we will compare this object's path against. + * @return true or false + */ + public boolean equals(SceneGraphPath testPath) { + boolean result = true; + try { + + if(testPath == null || root != testPath.root || item != testPath.item) + return false; + + result = transform.equals(testPath.transform); + + if(result == false) + return false; + + if(interior == null || testPath.interior == null) { + if(interior != testPath.interior) + return false; + else + result = (root == testPath.root && item == testPath.item); + + } else { + if (interior.length == testPath.interior.length) { + for (int i = 0; i < interior.length; i++) + if (interior[i] != testPath.interior[i]) { + return false; + } + } + else + return false; + } + + } + catch (NullPointerException e2) {return false;} + + return result; + } + + /** + * Returns true if the Object o1 is of type SceneGraphPath and all of the + * data members of o1 are equal to the corresponding data members in + * this SceneGraphPath and if the values of the transforms is equal. + * @param o1 the object we will compare this SceneGraphPath's path against. + * @return true or false + */ + @Override + public boolean equals(Object o1) { + boolean result = true; + + try { + SceneGraphPath testPath = (SceneGraphPath)o1; + if(testPath == null || root != testPath.root || item != testPath.item) + return false; + + result = transform.equals(testPath.transform); + + if(result == false) + return false; + + if(interior == null || testPath.interior == null) { + if(interior != testPath.interior) + return false; + else + result = (root == testPath.root && item == testPath.item); + + } else { + if (interior.length == testPath.interior.length) { + for (int i = 0; i < interior.length; i++) + if (interior[i] != testPath.interior[i]) { + return false; + } + } + else + return false; + } + + return result; + } + catch (NullPointerException e2) {return false;} + catch (ClassCastException e1) {return false;} + } + + + /** + * Returns a hash number based on the data values in this + * object. Two different SceneGraphPath objects with identical data + * values (ie, returns true for trans.equals(SceneGraphPath) ) will + * return the same hash number. Two Paths with different data members + * may return the same hash value, although this is not likely. + * @return the integer hash value + */ + @Override + public int hashCode() { + HashKey key = new HashKey(250); + // NOTE: Needed to add interior != null because this method is called + // by object.toString() when interior is null. + if(interior != null && item != null) { + for(int i=0; i<interior.length; i++) { + key.append(LinkRetained.plus).append( item.toString() ); + } + } + return( key.hashCode() + transform.hashCode() ); + } + + /** + * Determines whether two SceneGraphPath objects represent the same + * path in the scene graph; either object might include a different + * subset of internal nodes; only the internal link nodes, the Locale, + * and the Node itself are compared. The paths are not validated for + * correctness or uniqueness. + * @param testPath the SceneGraphPath to be compared to this SceneGraphPath + * @return true or false + */ + public final boolean isSamePath(SceneGraphPath testPath) { + int count=0, i; + + if(testPath == null || testPath.item != this.item || root != testPath.root) + return false; + + if(interior != null && testPath.interior != null) { + for(i=0 ; i<interior.length ; i++) { + if(interior[i] instanceof Link) { + // found Link in this, now check for matching in testPath + while(count < testPath.interior.length) { + if(testPath.interior[count] instanceof Link) { + if(testPath.interior[count] != interior[i]) { + return false; + } + count++; + break; + } + count++; + // if true, this had an extra Link + if(count == testPath.interior.length) + return false; + } + } + } + // make sure testPath doesn't have any extra Links + while(count < testPath.interior.length) { + if(testPath.interior[count] instanceof Link) + return false; + count++; + } + } else if(interior != testPath.interior) // ==> they are not both null + return false; + + return true; + } + + /** + * Returns a string representation of this object; + * the string contains the class names of all Nodes in the SceneGraphPath, + * the toString() method of any associated user data provided by + * SceneGraphObject.getUserData(), and also prints out the transform, + * if it is not null. + * @return String representation of this object + */ + @Override + public String toString() { + + StringBuffer str = new StringBuffer(); + Object obj; + + if(root == null && interior == null && item == null) + return (super.toString()); + + if(root != null) + str.append(root + " : "); + + if(interior != null) { + for(int i=0; i<interior.length; i++) { + + str.append( interior[i].getClass().getName()); + obj = interior[i].getUserData(); + if(obj == null) + str.append(" : "); + else + str.append(", " + obj + " : "); + } + } + + if(item != null) { + // str.append( item + ", "+ item.getUserData() + "--"+intersectPoint ); + str.append( item.getClass().getName() ); + obj = item.getUserData(); + if(obj != null) + str.append(", " + obj); + + try { + if (item.getClass().getName().equals("javax.media.j3d.Shape3D")) + str.append( ((Shape3D)item).getGeometry() ); + } + catch( CapabilityNotSetException e) {} + } + + str.append("\n" + "LocalToVworld Transform:\n" + transform); + + return new String(str); + } + + /** + * Determine if this SceneGraphPath is unique and valid + * The graph don't have to be live for this checking. + * Set Locale when it is null. + * Only the essential link node which led to the Locale + * is validated. + */ + boolean validate() { + NodeRetained node = (NodeRetained) item.retained; + + Locale locale = node.locale; + + if (root != null) { + if (item.isLive()) { + if (locale != root) { + return false; + } + } + } else { + root = locale; + } + + int idx = (interior == null ? 0: interior.length); + + do { + if (node instanceof SharedGroupRetained) { + if (interior == null) + return false; + while (--idx > 0) { + if (((SharedGroupRetained)node).parents.contains(interior[idx].retained)) { + break; + } + } + if (idx < 0) { + return false; + } + node = (NodeRetained) interior[idx].retained; + } else { + node = node.parent; + } + } while (node != null); + + return true; + } + + + // return key of this path or null is not in SharedGroup + void getHashKey(HashKey key) { + if (interior != null) { + key.reset(); + key.append(root.nodeId); + for(int i=0; i<interior.length; i++) { + Node node = interior[i]; + + if (!node.isLive()) { + throw new RuntimeException(J3dI18N.getString("SceneGraphPath3")); + } + + NodeRetained nodeR = (NodeRetained) node.retained; + if (nodeR.nodeType == NodeRetained.LINK) { + key.append("+").append(nodeR.nodeId); + } + } + } + } + + /** + * Determines whether this SceneGraphPath is unique and valid. The + * verification determines that all of the nodes are live, that the + * specified path is unique, that the correct Locale is specified, and + * that there is a Node specified. + */ + boolean validate(HashKey key) { + + int i; + + // verify that there is at least a Locale and Node specified + if( root == null ) + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath2")); + + if( item == null ) + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath10")); + + // verify liveness + if( !item.isLive() ) + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath3")); + + try { + getHashKey(key); + } catch (RuntimeException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } + + // The rest of the code verifies uniqueness; it traverses the retained + // hierarchy of the scene graph. This could be problematic later in + // when certain compile mode optimizations are added. */ + + NodeRetained bottomNR, currentNR, nextNR=null; + Node currentNode; + int count = 0; + + // Need to traverse the retained hierarchy on a live scene graph + // from bottom to top + // + // bottomNR = last verified node; as nodes are verified, bottomNR + // moves up the scen graph + // nextNR = Next node that the user has specified after bottomNR + // currentNR = current node; is changing as it covers all the + // nodes from bottomNR to nextNR + + // If the parent of a NodeRetained is null, we know that the parent + // is either a BranchGroupRetained at the top of a scene graph or + // it is a SharedGroupRetained, potentially with multiple parents. + + bottomNR = (NodeRetained)(item.retained); + + if(interior != null) { + for(i=interior.length-1; i >=0 ; i--) { + nextNR = (NodeRetained)(interior[i].retained); + currentNR = bottomNR.parent; + if(currentNR == null && bottomNR instanceof SharedGroupRetained) { + if(((SharedGroupRetained)(bottomNR)).parents.contains(nextNR) ) + currentNR = nextNR; + else + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5")); + + } + + while(currentNR != nextNR) { + if(currentNR == null) { + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath11")); + } + + if(currentNR instanceof SharedGroupRetained) { + if(((SharedGroupRetained) + (currentNR)).parents.contains(nextNR) ) + currentNR = nextNR; + else + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5")); + + } else { + currentNR = currentNR.parent; + } + } + bottomNR = currentNR; + } + } + + // Now go from bottomNR to Locale + currentNR = bottomNR.parent; + if(currentNR == null && bottomNR instanceof SharedGroupRetained) { + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5")); + } + + while(currentNR != null) { + if(currentNR instanceof LinkRetained) { + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5")); + } + + bottomNR = currentNR; + currentNR = currentNR.parent; + if(currentNR == null && bottomNR instanceof SharedGroupRetained) { + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath5")); + } + } + + // get the real BranchGroup from the BranchGroupRetained + currentNode = (Node)(bottomNR.source); + // now bottomNR should be a BranchGroup -- should try an assert here + if(!root.branchGroups.contains(currentNode)) { + throw new IllegalArgumentException(J3dI18N.getString("SceneGraphPath9")); + } + + return true; + } + + /** + * Returns the distance from the intersectPoint for item and + * origin. + */ + double getDistanceFrom( Point3d origin ) { + return intersectPoint.distance(origin); + } + + /** + * Returns the distance of the pick + */ + double getDistance() { + return pickDistance; + } + + final void setIntersectPoint( Point3d point ) { + intersectPoint.set(point); + } + + final void setIntersectPointDis( Point4d pickLocation ) { + // System.err.println( "setIntersectPointDis pickLocation= "+pickLocation); + intersectPoint.x = pickLocation.x; + intersectPoint.y = pickLocation.y; + intersectPoint.z = pickLocation.z; + pickDistance = pickLocation.w; + } + + final Point3d getIntersectPoint() { + return intersectPoint; + } +} |