1 /*
2  * $RCSfile: PickInfo.java,v $
3  *
4  * Copyright 2005-2008 Sun Microsystems, Inc.  All Rights Reserved.
5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6  *
7  * This code is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 only, as
9  * published by the Free Software Foundation.  Sun designates this
10  * particular file as subject to the "Classpath" exception as provided
11  * by Sun in the LICENSE file that accompanied this code.
12  *
13  * This code is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * version 2 for more details (a copy is included in the LICENSE file that
17  * accompanied this code).
18  *
19  * You should have received a copy of the GNU General Public License version
20  * 2 along with this work; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24  * CA 95054 USA or visit www.sun.com if you need additional information or
25  * have any questions.
26  *
27  * $Revision: 1.12 $
28  * $Date: 2008/02/28 20:17:27 $
29  * $State: Exp $
30  */
31 
32 package javax.media.j3d;
33 
34 import javax.vecmath.*;
35 import java.util.*;
36 
37 /**
38  * The PickInfo object contains the computed information about a pick hit.
39  * The detailed information about each intersection of the PickShape
40  * with the picked Node can be inquired.  The PickInfo object is constructed with
41  * basic information and more detailed information can be generated by setting the
42  * appropriate mask to the flag argument in the pick methods of BranchGroup and
43  * Locale.
44  * <p>
45  *
46  * @see Locale
47  * @see BranchGroup
48  *
49  * @since Java 3D 1.4
50  */
51 
52 
53 public class PickInfo extends Object {
54 
55     static final int PICK_ALL = 1;
56 
57     static final int PICK_ANY = 2;
58 
59     /* The SceneGraphPath of the intersected pickable item */
60     private SceneGraphPath sgp;
61 
62     /* The intersected pickable node object */
63     private  Node node;
64 
65     /* A copy of LocalToVworld transform of the pickable node */
66     private Transform3D l2vw;
67 
68     /* The closest intersection point */
69     private Point3d closestIntersectionPoint;
70 
71     /* Distance between start point of pickShape and closest intersection point */
72     private double  closestDistance;
73 
74     /* An array to store intersection results */
75     private IntersectionInfo[] intersectionInfoArr;
76 
77     /* The following references are for internal geometry computation use only */
78     private ArrayList intersectionInfoList = new ArrayList();
79     private boolean intersectionInfoListSorted = false;
80     private Transform3D l2vwRef;
81     private Node nodeRef;
82 
83     /**
84      * Specifies a Pick using the bounds of the pickable nodes.
85      */
86     public static final int PICK_BOUNDS = 1;
87 
88     /**
89      * Specifies a Pick using the geometry of the pickable nodes.
90      */
91     public static final int PICK_GEOMETRY = 2;
92 
93     /**
94    * Specifies that this PickInfo returns the computed SceneGraphPath object.
95    */
96     public static final int SCENEGRAPHPATH  = 0x01;
97 
98     /**
99      * Specifies that this PickInfo returns the computed intersected Node object.
100      */
101     public static final int NODE = 0x02;
102 
103     /**
104      * Specifies that this PickInfo returns the computed local to vworld transform.
105      */
106     public static final int LOCAL_TO_VWORLD = 0x04;
107 
108     /**
109      * Specifies that this PickInfo returns the closest intersection point.
110      */
111     public static final int CLOSEST_INTERSECTION_POINT = 0x08;
112 
113     /**
114      * Specifies that this PickInfo returns the closest intersection distance.
115      */
116     public static final int CLOSEST_DISTANCE = 0x10;
117 
118     /**
119      * Specifies that this PickInfo returns only the closest intersection
120      * geometry information.
121      */
122     public static final int CLOSEST_GEOM_INFO = 0x20;
123 
124     /**
125      * Specifies that this PickInfo returns all the closest intersection
126      * geometry informations.
127      */
128     public static final int ALL_GEOM_INFO = 0x40;
129 
130 
131     /** PickInfo Constructor */
PickInfo()132     PickInfo() {
133 
134     }
135 
setSceneGraphPath(SceneGraphPath sgp)136     void setSceneGraphPath(SceneGraphPath sgp) {
137         this.sgp = sgp;
138     }
139 
setNode(Node node)140     void setNode(Node node) {
141         this.node = node;
142     }
143 
setLocalToVWorld(Transform3D l2vw)144     void setLocalToVWorld(Transform3D l2vw) {
145         this.l2vw = l2vw;
146     }
147 
setClosestIntersectionPoint(Point3d cIPt)148     void setClosestIntersectionPoint(Point3d cIPt) {
149         this.closestIntersectionPoint = cIPt;
150     }
151 
setClosestDistance(double cDist)152     void setClosestDistance(double cDist) {
153         this.closestDistance = cDist;
154     }
155 
setLocalToVWorldRef(Transform3D l2vwRef)156     void setLocalToVWorldRef(Transform3D l2vwRef) {
157         this.l2vwRef = l2vwRef;
158     }
159 
setNodeRef(Node nodeRef)160     void setNodeRef(Node nodeRef) {
161         this.nodeRef = nodeRef;
162     }
163 
createIntersectionInfo()164     IntersectionInfo createIntersectionInfo() {
165         return new IntersectionInfo();
166     }
167 
insertIntersectionInfo(IntersectionInfo iInfo)168     void insertIntersectionInfo(IntersectionInfo iInfo) {
169         intersectionInfoList.add(iInfo);
170         intersectionInfoListSorted = false;
171     }
172 
sortIntersectionInfoArray(IntersectionInfo[] iInfoArr)173     void sortIntersectionInfoArray(IntersectionInfo[] iInfoArr) {
174 
175         class Sort {
176 
177 	    IntersectionInfo iInfoArr[];
178 
179 	    Sort(IntersectionInfo[] iInfoArr) {
180                 // System.err.println("Sort IntersectionInfo ...");
181 		this.iInfoArr = iInfoArr;
182 	    }
183 
184 	    void sorting() {
185 		if (iInfoArr.length < 7) {
186                     // System.err.println(" -- insertSort.");
187 		    insertSort();
188 	    	} else {
189                     // System.err.println(" -- quicksort.");
190 		    quicksort(0, iInfoArr.length-1);
191     		}
192 	    }
193 
194 	    // Insertion sort on smallest arrays
195 	    final void insertSort() {
196 		for (int i=0; i<iInfoArr.length; i++) {
197 		    for (int j=i; j>0 &&
198                              (iInfoArr[j-1].distance > iInfoArr[j].distance); j--) {
199 			IntersectionInfo iInfo = iInfoArr[j];
200 			iInfoArr[j] = iInfoArr[j-1];
201 			iInfoArr[j-1] = iInfo;
202 		    }
203 		}
204 	    }
205 
206             final void quicksort( int l, int r ) {
207 		int i = l;
208 		int j = r;
209 		double k = iInfoArr[(l+r) / 2].distance;
210 
211 		do {
212 		    while (iInfoArr[i].distance<k) i++;
213 		    while (k<iInfoArr[j].distance) j--;
214 		    if (i<=j) {
215 			IntersectionInfo iInfo = iInfoArr[i];
216 			iInfoArr[i] = iInfoArr[j];
217 			iInfoArr[j] = iInfo;
218 			i++;
219 			j--;
220 		    }
221 		} while (i<=j);
222 
223 		if (l<j) quicksort(l,j);
224 		if (l<r) quicksort(i,r);
225 	    }
226 	}
227 
228 	(new Sort(iInfoArr)).sorting();
229         intersectionInfoListSorted = true;
230     }
231 
sortPickInfoArray(PickInfo[] pickInfoArr)232     static void sortPickInfoArray(PickInfo[] pickInfoArr) {
233 
234         class Sort {
235 
236 	    PickInfo pIArr[];
237 
238 	    Sort(PickInfo[] pIArr) {
239                 // System.err.println("Sort PickInfo ...");
240 		this.pIArr = pIArr;
241 	    }
242 
243 	    void sorting() {
244 		if (pIArr.length < 7) {
245                     // System.err.println(" -- insertSort.");
246 		    insertSort();
247 	    	} else {
248                     // System.err.println(" -- quicksort.");
249 		    quicksort(0, pIArr.length-1);
250     		}
251 	    }
252 
253 	    // Insertion sort on smallest arrays
254 	    final void insertSort() {
255 		for (int i=0; i<pIArr.length; i++) {
256 		    for (int j=i; j>0 &&
257                              (pIArr[j-1].closestDistance > pIArr[j].closestDistance); j--) {
258 			PickInfo pI = pIArr[j];
259 			pIArr[j] = pIArr[j-1];
260 			pIArr[j-1] = pI;
261 		    }
262 		}
263 	    }
264 
265             final void quicksort( int l, int r ) {
266 		int i = l;
267 		int j = r;
268 		double k = pIArr[(l+r) / 2].closestDistance;
269 
270 		do {
271 		    while (pIArr[i].closestDistance<k) i++;
272 		    while (k<pIArr[j].closestDistance) j--;
273 		    if (i<=j) {
274 			PickInfo pI = pIArr[i];
275 			pIArr[i] = pIArr[j];
276 			pIArr[j] = pI;
277 			i++;
278 			j--;
279 		    }
280 		} while (i<=j);
281 
282 		if (l<j) quicksort(l,j);
283 		if (l<r) quicksort(i,r);
284 	    }
285 	}
286 
287 	(new Sort(pickInfoArr)).sorting();
288 
289     }
290 
291 
292     /**
293      * Retrieves the reference to the SceneGraphPath in this PickInfo object.
294      * @return the SceneGraphPath object, or null if  flag is not set with SCENEGRAPHPATH.
295      * @see Locale
296      * @see BranchGroup
297      */
getSceneGraphPath()298     public SceneGraphPath getSceneGraphPath() {
299 	return sgp;
300     }
301 
302     /**
303      * Retrieves the reference to the picked node, either a Shape3D or a Morph, in this PickInfo object.
304      * @return the picked leaf node object, or null if  flag is not set with NODE.
305      * @see Locale
306      * @see BranchGroup
307      */
getNode()308     public Node getNode() {
309 	return node;
310     }
311 
312     /**
313      * Retrieves the reference to the LocalToVworld transform of the picked node in this PickInfo object.
314      * @return the local to vworld transform, or null if  flag is not set with LOCAL_TO_VWORLD.
315      * @see Locale
316      * @see BranchGroup
317      */
getLocalToVWorld()318     public Transform3D getLocalToVWorld() {
319 	return l2vw;
320     }
321 
322     /**
323      * Retrieves the reference to the closest intersection point in this PickInfo object.
324      * @return the closest intersection point, or null if  flag is not set with CLOSEST_INTERSECTION_POINT.
325      * @see Locale
326      * @see BranchGroup
327      */
getClosestIntersectionPoint()328     public Point3d getClosestIntersectionPoint() {
329 	return closestIntersectionPoint;
330     }
331 
332     /**
333      * Retrieves the distance between the start point of the pickShape and the closest intersection point.
334      * @return the closest distance in double, or NaN if  flag is not set with CLOSEST_INTERSECTION_POINT.
335      * Note : If this PickInfo object is returned by either pickClosest or pickAllSorted method, the return
336      * value is the closest distance in double even if flag is not set with CLOSET_INTERSECTION_POINT.
337      * @see Locale
338      * @see BranchGroup
339      */
getClosestDistance()340     public double getClosestDistance() {
341 	return closestDistance;
342     }
343 
getLocalToVWorldRef()344     Transform3D getLocalToVWorldRef() {
345         return l2vwRef;
346     }
347 
getNodeRef()348     Node getNodeRef() {
349         return nodeRef;
350     }
351 
352     /**
353      * Retrieves the reference to the array of intersection results in this PickInfo object.
354      * @return an array of 1 IntersectionInfo object if flag is to set  CLOSEST_GEOM_INFO,
355      * or an array of <i>N</i> IntersectionInfo objects containing all intersections of
356      * the picked node in sorted order if flag is to set ALL_GEOM_INFO, or null if neither
357      * bit is set.
358      * @see Locale
359      * @see BranchGroup
360      */
getIntersectionInfos()361     public IntersectionInfo[] getIntersectionInfos() {
362         if (intersectionInfoListSorted == false) {
363             intersectionInfoArr = new IntersectionInfo[intersectionInfoList.size()];
364             intersectionInfoArr =
365                     (IntersectionInfo []) intersectionInfoList.toArray(intersectionInfoArr);
366 
367             sortIntersectionInfoArray(intersectionInfoArr);
368          }
369 
370         return intersectionInfoArr;
371     }
372 
373     /**
374      * Search the path from nodeR up to Locale.
375      * Return the search path as ArrayList if found.
376      * Note that the locale will not insert into path.
377      */
initSceneGraphPath(NodeRetained nodeR)378     static ArrayList initSceneGraphPath(NodeRetained nodeR) {
379 	ArrayList path = new ArrayList(5);
380 
381 	do {
382 	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)){
383 		path.add(nodeR);
384 	    }
385 	    nodeR = nodeR.parent;
386 	} while (nodeR != null);  // reach Locale
387 
388 	return path;
389     }
390 
createPath(NodeRetained srcNode, BranchGroupRetained bgRetained, GeometryAtom geomAtom, ArrayList initpath)391     static private Node[] createPath(NodeRetained srcNode,
392 				     BranchGroupRetained bgRetained,
393 				     GeometryAtom geomAtom,
394 				     ArrayList initpath) {
395 
396         ArrayList path = retrievePath(srcNode, bgRetained,
397 				      geomAtom.source.key);
398         assert(path != null);
399 
400         return mergePath(path, initpath);
401 
402     }
403 
404 
405     /**
406      * Return true if bg is inside cachedBG or bg is null
407      */
inside(BranchGroupRetained bgArr[], BranchGroupRetained bg)408     static private boolean inside(BranchGroupRetained bgArr[],
409 				  BranchGroupRetained bg) {
410 
411 	if ((bg == null) || (bgArr == null)) {
412 	    return true;
413 	}
414 
415 	for (int i=0; i < bgArr.length; i++) {
416 	    if (bgArr[i] == bg) {
417 		return true;
418 	    }
419 	}
420 	return false;
421     }
422 
423     /**
424      * search the full path from the bottom of the scene graph -
425      * startNode, up to the Locale if endNode is null.
426      * If endNode is not null, the path is found up to, but not
427      * including, endNode or return null if endNode not hit
428      * during the search.
429      */
retrievePath(NodeRetained startNode, NodeRetained endNode, HashKey key)430     static private ArrayList retrievePath(NodeRetained startNode,
431 					  NodeRetained endNode,
432 					  HashKey key) {
433 
434 	ArrayList path = new ArrayList(5);
435 	NodeRetained nodeR = startNode;
436 
437 	if (nodeR.inSharedGroup) {
438 	    // getlastNodeId() will destroy this key
439 	    key = new HashKey(key);
440 	}
441 
442 	do {
443 	    if (nodeR == endNode) { // we found it !
444 		return path;
445 	    }
446 
447 	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
448 		path.add(nodeR);
449 	    }
450 
451 	    if (nodeR instanceof SharedGroupRetained) {
452 		// retrieve the last node ID
453 		String nodeId = key.getLastNodeId();
454 		Vector parents = ((SharedGroupRetained) nodeR).parents;
455 		int sz = parents.size();
456 		NodeRetained prevNodeR = nodeR;
457 		for(int i=0; i< sz; i++) {
458 		    NodeRetained linkR = (NodeRetained) parents.elementAt(i);
459 		    if (linkR.nodeId.equals(nodeId)) {
460 			nodeR = linkR;
461 			// Need to add Link to the path report
462 			path.add(nodeR);
463 			// since !(endNode instanceof Link), we
464 			// can skip the check (nodeR == endNode) and
465 			// proceed to parent of link below
466 			break;
467 		    }
468 		}
469 		if (nodeR == prevNodeR) {
470 		    // branch is already detach
471 		    return null;
472 		}
473 	    }
474 	    nodeR = nodeR.parent;
475 	} while (nodeR != null); // reach Locale
476 
477 	if (endNode == null) {
478 	    // user call pickxxx(Locale locale, PickShape shape)
479 	    return path;
480 	}
481 
482 	// user call pickxxx(BranchGroup endNode, PickShape shape)
483 	// if locale is reached and endNode not hit, this is not
484 	// the path user want to select
485 	return null;
486     }
487 
488     /**
489      * copy p1, (follow by) p2 into a new array, p2 can be null
490      * The path is then reverse before return.
491      */
mergePath(ArrayList p1, ArrayList p2)492     static private Node[] mergePath(ArrayList p1, ArrayList p2) {
493 	int s = p1.size();
494 	int len;
495 	int i;
496 	int l;
497 	if (p2 == null) {
498 	    len = s;
499 	} else {
500 	    len = s + p2.size();
501 	}
502 
503 	Node nodes[] = new Node[len];
504 	l = len-1;
505 	for (i=0; i < s; i++) {
506 	    nodes[l-i] = (Node) ((NodeRetained) p1.get(i)).source;
507 	}
508 	for (int j=0; i< len; i++, j++) {
509 	    nodes[l-i] = (Node) ((NodeRetained) p2.get(j)).source;
510 	}
511 	return nodes;
512     }
513 
514     /**
515      * Sort the GeometryAtoms distance from shape in ascending order
516      * geomAtoms.length must be >= 1
517      */
sortGeomAtoms(GeometryAtom geomAtoms[], PickShape shape)518     static void sortGeomAtoms(GeometryAtom geomAtoms[],
519 				      PickShape shape) {
520 
521 	final double distance[] = new double[geomAtoms.length];
522 	Point4d pickPos = new Point4d();
523 
524 	for (int i=0; i < geomAtoms.length; i++) {
525 	    shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);
526 	    distance[i] = pickPos.w;
527 	}
528 
529 	class Sort {
530 
531 	    GeometryAtom atoms[];
532 
533 	    Sort(GeometryAtom[] atoms) {
534 		this.atoms = atoms;
535 	    }
536 
537 	    void sorting() {
538 		if (atoms.length < 7) {
539 		    insertSort();
540 	    	} else {
541 		    quicksort(0, atoms.length-1);
542     		}
543 	    }
544 
545 	    // Insertion sort on smallest arrays
546 	    final void insertSort() {
547 		for (int i=0; i<atoms.length; i++) {
548 		    for (int j=i; j>0 &&
549 			     (distance[j-1] > distance[j]); j--) {
550 			double t = distance[j];
551 			distance[j] = distance[j-1];
552 			distance[j-1] = t;
553 			GeometryAtom p = atoms[j];
554 			atoms[j] = atoms[j-1];
555 			atoms[j-1] = p;
556 		    }
557 		}
558 	    }
559 
560             final void quicksort( int l, int r ) {
561 		int i = l;
562 		int j = r;
563 		double k = distance[(l+r) / 2];
564 
565 		do {
566 		    while (distance[i]<k) i++;
567 		    while (k<distance[j]) j--;
568 		    if (i<=j) {
569 			double tmp = distance[i];
570 			distance[i] =distance[j];
571 			distance[j] = tmp;
572 
573 			GeometryAtom p=atoms[i];
574 			atoms[i]=atoms[j];
575 			atoms[j]=p;
576 			i++;
577 			j--;
578 		    }
579 		} while (i<=j);
580 
581 		if (l<j) quicksort(l,j);
582 		if (l<r) quicksort(i,r);
583 	    }
584 	}
585 
586 	(new Sort(geomAtoms)).sorting();
587     }
588 
589 
590     /**
591      * return all PickInfo[] of the geomAtoms.
592      * If initpath is null, the path is search from
593      * geomAtom Shape3D/Morph Node up to Locale
594      * (assume the same locale).
595      * Otherwise, the path is search up to node or
596      * null is return if it is not hit.
597      */
getPickInfos(ArrayList initpath, BranchGroupRetained bgRetained, GeometryAtom geomAtoms[], Locale locale, int flags, int pickType)598     static ArrayList getPickInfos(ArrayList initpath,
599                                   BranchGroupRetained bgRetained,
600 				  GeometryAtom geomAtoms[],
601 			          Locale locale, int flags, int pickType) {
602 
603         ArrayList pickInfoList = new ArrayList(5);
604         NodeRetained srcNode;
605         ArrayList text3dList = null;
606 
607         if ((geomAtoms == null) || (geomAtoms.length == 0)) {
608             return null;
609         }
610 
611 	for (int i=0; i < geomAtoms.length; i++) {
612             assert((geomAtoms[i] != null) &&
613                     (geomAtoms[i].source != null));
614 
615 	    PickInfo pickInfo = null;
616             Shape3DRetained shape = geomAtoms[i].source;
617             srcNode = shape.sourceNode;
618 
619             // Fix to Issue 274 : NPE With Simultaneous View and Content Side PickingBehaviors
620             // This node isn't under the selected BG for pick operation.
621             if (!inside(shape.branchGroupPath,bgRetained)) {
622                 continue;
623             }
624 
625             if (srcNode == null) {
626                 // The node is just detach from branch so sourceNode = null
627                 continue;
628             }
629 
630 
631             // Special case, for Text3DRetained, it is possible
632             // for different geomAtoms pointing to the same
633             // source Text3DRetained. So we need to combine
634             // those cases and report only once.
635             if (srcNode instanceof Shape3DRetained) {
636                 Shape3DRetained s3dR = (Shape3DRetained) srcNode;
637                 GeometryRetained geomR = null;
638                 for(int cnt=0; cnt<s3dR.geometryList.size(); cnt++) {
639                     geomR = (GeometryRetained) s3dR.geometryList.get(cnt);
640                     if(geomR != null)
641                         break;
642                 }
643 
644                 if (geomR == null)
645                     continue;
646 
647                 if (geomR instanceof Text3DRetained) {
648                     // assume this case is not frequent, we allocate
649                     // ArrayList only when necessary and we use ArrayList
650                     // instead of HashMap since the case of when large
651                     // number of distingish Text3DRetained node hit is
652                     // rare.
653                     if (text3dList == null) {
654                         text3dList = new ArrayList(3);
655                     } else {
656                         int size = text3dList.size();
657                         boolean found = false;
658                         for (int j=0; j < size; j++) {
659                             if (text3dList.get(j) == srcNode) {
660                                 found = true;
661                                 break;
662                             }
663                         }
664                         if (found) {
665                             continue;  // try next geomAtom
666                         }
667                     }
668                     text3dList.add(srcNode);
669                 }
670             }
671 
672             // If srcNode is instance of compile retained, then loop thru
673             // the entire source list and add it to the scene graph path
674             if (srcNode instanceof Shape3DCompileRetained) {
675 
676                 Shape3DCompileRetained s3dCR = (Shape3DCompileRetained)srcNode;
677 
678                 Node[] mpath = null;
679                 boolean first = true;
680 
681                 for (int n = 0; n < s3dCR.srcList.length; n++) {
682 
683                     pickInfo = null;
684 
685                     // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
686                     if ((flags & SCENEGRAPHPATH) != 0){
687 
688                         if(first) {
689                             mpath = createPath(srcNode, bgRetained, geomAtoms[i], initpath);
690                             first = false;
691                         }
692 
693                         if(mpath != null) {
694                             SceneGraphPath sgpath = new SceneGraphPath(locale,
695                                     mpath, (Node) s3dCR.srcList[n]);
696                             sgpath.setTransform(shape.getCurrentLocalToVworld(0));
697 			    if(pickInfo == null)
698 				pickInfo = new PickInfo();
699                             pickInfo.setSceneGraphPath(sgpath);
700                         }
701                     }
702 
703                     // PickInfo.NODE - request for computed intersected Node.
704                     if ((flags & NODE) != 0) {
705 			if(pickInfo == null)
706 			    pickInfo = new PickInfo();
707                         pickInfo.setNode((Node) s3dCR.srcList[n]);
708                     }
709 
710                     // PickInfo.LOCAL_TO_VWORLD
711                     //    - request for computed local to virtual world transform.
712                     if ((flags & LOCAL_TO_VWORLD) != 0) {
713                         Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
714 			if(pickInfo == null)
715 			    pickInfo = new PickInfo();
716                         pickInfo.setLocalToVWorld( new Transform3D(l2vw));
717                     }
718 
719                     // NOTE : Piggy bag for geometry computation by caller.
720                     if (((flags & CLOSEST_DISTANCE) != 0) ||
721                         ((flags & CLOSEST_GEOM_INFO) != 0) ||
722                         ((flags & CLOSEST_INTERSECTION_POINT) != 0) ||
723                         ((flags & ALL_GEOM_INFO) != 0)) {
724 			if(pickInfo == null)
725 			    pickInfo = new PickInfo();
726                         pickInfo.setNodeRef((Node) s3dCR.srcList[n]);
727                         Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
728 			pickInfo.setLocalToVWorldRef(l2vw);
729                     }
730 
731 		    if(pickInfo != null)
732 			pickInfoList.add(pickInfo);
733                     if(pickType == PICK_ANY) {
734                         return pickInfoList;
735                     }
736                 }
737             }
738             else {
739                 Node[] mpath = null;
740 
741                 // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
742                 if ((flags & SCENEGRAPHPATH) != 0) {
743 
744                     mpath = createPath(srcNode, bgRetained, geomAtoms[i], initpath);
745 
746                     if(mpath != null) {
747                         SceneGraphPath sgpath = new SceneGraphPath(locale, mpath,
748                                 (Node) srcNode.source);
749                         sgpath.setTransform(shape.getCurrentLocalToVworld(0));
750                         if(pickInfo == null)
751                             pickInfo = new PickInfo();
752                         pickInfo.setSceneGraphPath(sgpath);
753                     }
754                 }
755 
756                 // PickInfo.NODE - request for computed intersected Node.
757                 if ((flags & NODE) != 0) {
758 		    if(pickInfo == null)
759 			pickInfo = new PickInfo();
760                     pickInfo.setNode((Node) srcNode.source);
761                 }
762 
763                 // PickInfo.LOCAL_TO_VWORLD
764                 //    - request for computed local to virtual world transform.
765                 if ((flags & LOCAL_TO_VWORLD) != 0) {
766                     Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
767 		    if(pickInfo == null)
768 			pickInfo = new PickInfo();
769                     pickInfo.setLocalToVWorld( new Transform3D(l2vw));
770                 }
771 
772                 // NOTE : Piggy bag for geometry computation by caller.
773                 if (((flags & CLOSEST_DISTANCE) != 0) ||
774                     ((flags & CLOSEST_GEOM_INFO) != 0) ||
775                     ((flags & CLOSEST_INTERSECTION_POINT) != 0) ||
776                     ((flags & ALL_GEOM_INFO) != 0)) {
777 		    if(pickInfo == null)
778 			pickInfo = new PickInfo();
779                     pickInfo.setNodeRef((Node) srcNode.source);
780                     Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
781                     pickInfo.setLocalToVWorldRef(l2vw);
782                 }
783 
784 		if(pickInfo != null)
785 		    pickInfoList.add(pickInfo);
786                 if(pickType == PICK_ANY) {
787                     return pickInfoList;
788                 }
789             }
790         }
791 
792 	return pickInfoList;
793     }
794 
pick(Object node, GeometryAtom[] geomAtoms, int mode, int flags, PickShape pickShape, int pickType)795     static PickInfo[] pick(Object node, GeometryAtom[] geomAtoms,
796             int mode, int flags, PickShape pickShape, int pickType) {
797 
798         int pickInfoListSize;
799         PickInfo[] pickInfoArr = null;
800         Locale locale = null;
801         BranchGroupRetained bgRetained = null;
802         ArrayList initPath = null;
803         ArrayList pickInfoList = null;
804 
805         if (node instanceof Locale) {
806             locale = (Locale) node;
807         }
808         else if ( node instanceof BranchGroupRetained) {
809             bgRetained = (BranchGroupRetained) node;
810             locale = bgRetained.locale;
811         }
812         synchronized (locale.universe.sceneGraphLock) {
813             if ( bgRetained != null) {
814                 initPath = initSceneGraphPath(bgRetained);
815             }
816             pickInfoList = getPickInfos(initPath, bgRetained, geomAtoms,
817                 locale, flags, pickType);
818         }
819 
820         // We're done with PICK_BOUNDS case, but there is still more work for PICK_GEOMETRY case.
821         if((mode == PICK_GEOMETRY) && (pickInfoList != null) &&
822 	   ((pickInfoListSize = pickInfoList.size()) > 0)) {
823 
824             //System.err.println("PickInfo.pick() - In geometry case : pickInfoList.size() is " + pickInfoListSize);
825             PickInfo pickInfo = null;
826             Node pickNode = null;
827 
828             // Order is impt. Need to do in reverse order.
829             for(int i = pickInfoListSize - 1; i >= 0; i--) {
830                 pickInfo = (PickInfo) pickInfoList.get(i);
831 
832                 pickNode = pickInfo.getNode();
833                 if( pickNode == null) {
834                     // Use the piggy reference from getPickInfos()
835                     pickNode = pickInfo.getNodeRef();
836                 }
837 
838                 if (pickNode instanceof Shape3D) {
839 
840 		    /*
841 		     * @exception CapabilityNotSetException if the mode is
842 		     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
843 		     * is not set in any Geometry objects referred to by any shape
844 		     * node whose bounds intersects the PickShape.
845 		     *
846                      * @exception CapabilityNotSetException if flags contains any of
847 		     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
848 		     * or ALL_GEOM_INFO, and the capability bits that control reading of
849 		     * coordinate data are not set in any GeometryArray object referred
850 		     * to by any shape node that intersects the PickShape.
851 		     * The capability bits that must be set to avoid this exception are
852                      * as follows :
853 		     *
854 		     * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
855 		     * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
856 		     * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
857 		     * (in addition to one of the above)
858 		     *
859 		     */
860 
861                     if (!pickNode.getCapability(Shape3D.ALLOW_GEOMETRY_READ)) {
862 			throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0"));
863 		    }
864 
865 		    for (int j = 0; j < ((Shape3D)pickNode).numGeometries(); j++) {
866 			Geometry geo = ((Shape3D)pickNode).getGeometry(j);
867 
868 			if(geo == null) {
869 			    continue;
870 			}
871 
872 			if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) {
873 			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1"));
874 			}
875 
876 			if (geo instanceof GeometryArray) {
877 			    if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ))
878 				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2"));
879 			    if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ))
880 				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3"));
881 			    if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ))
882 				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4"));
883 			    if (geo instanceof IndexedGeometryArray) {
884 				if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
885 				    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5"));
886 			    }
887 			} else if (geo instanceof CompressedGeometry) {
888 			    if(!geo.getCapability(CompressedGeometry.ALLOW_GEOMETRY_READ))
889 				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0"));
890 			}
891 		    }
892 
893 		    if (((Shape3DRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) {
894 			// System.err.println("  ---- geom " + i + " not intersected");
895 
896                         pickInfoList.remove(i);
897 
898                     }
899                     else if(pickType == PICK_ANY) {
900                         pickInfoArr = new PickInfo[1];
901                         pickInfoArr[0] = pickInfo;
902                         return pickInfoArr;
903                     }
904                 } else if (pickNode instanceof Morph) {
905 
906 		    /*
907 		     * @exception CapabilityNotSetException if the mode is
908 		     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
909 		     * is not set in any Geometry objects referred to by any shape
910 		     * node whose bounds intersects the PickShape.
911 		     *
912                      * @exception CapabilityNotSetException if flags contains any of
913 		     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
914 		     * or ALL_GEOM_INFO, and the capability bits that control reading of
915 		     * coordinate data are not set in any GeometryArray object referred
916 		     * to by any shape node that intersects the PickShape.
917 		     * The capability bits that must be set to avoid this exception are
918                      * as follows :
919 		     *
920 		     * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
921 		     * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
922 		     * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
923 		     * (in addition to one of the above)
924 		     *
925 		     */
926 
927                     if (!pickNode.getCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ)) {
928 			throw new CapabilityNotSetException(J3dI18N.getString("PickInfo6"));
929 		    }
930 
931 		    int numGeo = ((MorphRetained)(pickNode.retained)).getNumGeometryArrays();
932 		    for (int j = 0; j < numGeo; j++) {
933 			GeometryArray geo = ((Morph)pickNode).getGeometryArray(j);
934 
935 			if(geo == null) {
936 			    continue;
937 			}
938 
939 			if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) {
940 			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1"));
941 			}
942 
943 			if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ))
944 			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2"));
945 			if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ))
946 			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3"));
947 			if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ))
948 			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4"));
949 
950 			if (geo instanceof IndexedGeometryArray) {
951 			    if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
952 				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5"));
953 			}
954 		    }
955 
956                     if (((MorphRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) {
957                         pickInfoList.remove(i);
958                     }
959                     else if(pickType == PICK_ANY) {
960                         pickInfoArr = new PickInfo[1];
961                         pickInfoArr[0] = pickInfo;
962                         return pickInfoArr;
963                     }
964                 }
965             }
966         }
967 
968 	// System.err.println("PickInfo : pickInfoList " + pickInfoList);
969 
970         if ((pickInfoList != null) && (pickInfoList.size() > 0)) {
971 	    // System.err.println("   ---  : pickInfoList.size() " + pickInfoList.size());
972 	    // System.err.println("   ---  : pickInfoList's sgp " +
973 	    // ((PickInfo)(pickInfoList.get(0))).getSceneGraphPath());
974 	    pickInfoArr = new PickInfo[pickInfoList.size()];
975 	    return (PickInfo []) pickInfoList.toArray(pickInfoArr);
976 	}
977 
978 	return null;
979 
980     }
981 
982     /**
983      * The IntersectionInfo object holds extra information about an intersection
984      * of a PickShape with a Node as part of a PickInfo. Information such as
985      * the intersected geometry, the intersected point, and the vertex indices
986      * can be inquired.
987      * The local coordinates, normal, color and texture coordiantes of at the
988      * intersection can be computed, if they are present and readable, using the
989      * interpolation weights and vertex indices.
990      * <p>
991      * If the Shape3D being picked has multiple geometry arrays, the possible arrays
992      * of IntersectionInfo are stored in the PickInfo and referred to by a geometry
993      * index. If the picked geometry is of type, Text3D or CompressGeometry,
994      * getVertexIndices is invalid. If the picked Node is an Morph
995      * object, the geometry used in pick computation is alway at index 0.
996      * <p>
997      *
998      * @since Java 3D 1.4
999      */
1000 
1001     public class IntersectionInfo extends Object {
1002 
1003 	/* The index to the intersected geometry in the pickable node */
1004 	private int geomIndex;
1005 
1006         /* The reference to the intersected geometry in the pickable object */
1007 	private Geometry geom;
1008 
1009 	/* The intersection point */
1010 	private Point3d intersectionPoint;
1011 
1012 	/* Distance between start point of pickShape and intersection point */
1013 	private double  distance;
1014 
1015 	/* The vertex indices of the intersected primitive in the geometry */
1016 	private int[] vertexIndices;
1017 
1018 	/* The interpolation weights for each of the verticies of the primitive */
1019 	// private float[] weights;  Not supported. Should be done in util. package
1020 
1021 	/** IntersectionInfo Constructor */
IntersectionInfo()1022 	IntersectionInfo() {
1023 
1024 	}
1025 
setGeometryIndex(int geomIndex)1026         void setGeometryIndex(int geomIndex) {
1027             this.geomIndex = geomIndex;
1028         }
1029 
setGeometry(Geometry geom)1030         void setGeometry(Geometry geom) {
1031             this.geom = geom;
1032         }
1033 
setIntersectionPoint(Point3d intersectionPoint)1034         void setIntersectionPoint(Point3d intersectionPoint) {
1035 	    assert(intersectionPoint != null);
1036 	    this.intersectionPoint = new Point3d(intersectionPoint);
1037         }
1038 
setDistance(double distance)1039         void setDistance(double distance) {
1040             this.distance = distance;
1041         }
1042 
setVertexIndices(int[] vertexIndices)1043         void setVertexIndices(int[] vertexIndices) {
1044 	    assert(vertexIndices != null);
1045 	    this.vertexIndices = new int[vertexIndices.length];
1046 	    for(int i=0; i<vertexIndices.length; i++) {
1047 		this.vertexIndices[i] = vertexIndices[i];
1048 	    }
1049 	}
1050 
1051 
1052 	/**
1053 	 * Retrieves the index to the intersected geometry in the picked node, either a Shape3D or Morph.
1054 	 * @return the index of the intersected geometry in the pickable node.
1055 	 */
getGeometryIndex()1056 	public int getGeometryIndex() {
1057 	    return geomIndex;
1058 	}
1059 
1060 	/**
1061 	 * Retrieves the reference to the intersected geometry in the picked object, either a Shape3D or Morph.
1062 	 * @return the intersected geometry in the pickable node.
1063 	 */
getGeometry()1064 	public Geometry getGeometry() {
1065 	    return geom;
1066 	}
1067 
1068 	/**
1069 	 * Retrieves the reference to the intersection point in the pickable node.
1070 	 * @return the intersected point in the pickable node.
1071 	 */
getIntersectionPoint()1072 	public Point3d getIntersectionPoint() {
1073 	    return intersectionPoint;
1074 	}
1075 
1076 	/**
1077 	 * Retrieves the distance between the start point of the pickShape and the
1078 	 * intersection point.
1079 	 * @return distance between the start point of the pickShape and the
1080 	 * intersection point.
1081 	 */
getDistance()1082 	public double getDistance() {
1083 	    return distance;
1084 	}
1085 
1086 	/**
1087 	 * Retrieves the vertex indices of the intersected primitive in the geometry.
1088 	 * @return the vertex indices of the intersected primitive.
1089 	 */
getVertexIndices()1090 	public int[] getVertexIndices() {
1091 	    return vertexIndices;
1092 	}
1093 
1094     }
1095 }
1096 
1097 
1098