/*
* 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.
*
* 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.
*
* 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:
*
* root : null
* object : null
* list of (interior) nodes : null
* transform : identity
*
*/
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 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 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=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;
}
}