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