0001: /*
0002: * $RCSfile: PickInfo.java,v $
0003: *
0004: * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0006: *
0007: * This code is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU General Public License version 2 only, as
0009: * published by the Free Software Foundation. Sun designates this
0010: * particular file as subject to the "Classpath" exception as provided
0011: * by Sun in the LICENSE file that accompanied this code.
0012: *
0013: * This code is distributed in the hope that it will be useful, but WITHOUT
0014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: * version 2 for more details (a copy is included in the LICENSE file that
0017: * accompanied this code).
0018: *
0019: * You should have received a copy of the GNU General Public License version
0020: * 2 along with this work; if not, write to the Free Software Foundation,
0021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0022: *
0023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0024: * CA 95054 USA or visit www.sun.com if you need additional information or
0025: * have any questions.
0026: *
0027: * $Revision: 1.12 $
0028: * $Date: 2008/02/28 20:17:27 $
0029: * $State: Exp $
0030: */
0031:
0032: package javax.media.j3d;
0033:
0034: import javax.vecmath.*;
0035: import java.util.*;
0036:
0037: /**
0038: * The PickInfo object contains the computed information about a pick hit.
0039: * The detailed information about each intersection of the PickShape
0040: * with the picked Node can be inquired. The PickInfo object is constructed with
0041: * basic information and more detailed information can be generated by setting the
0042: * appropriate mask to the flag argument in the pick methods of BranchGroup and
0043: * Locale.
0044: * <p>
0045: *
0046: * @see Locale
0047: * @see BranchGroup
0048: *
0049: * @since Java 3D 1.4
0050: */
0051:
0052: public class PickInfo extends Object {
0053:
0054: static final int PICK_ALL = 1;
0055:
0056: static final int PICK_ANY = 2;
0057:
0058: /* The SceneGraphPath of the intersected pickable item */
0059: private SceneGraphPath sgp;
0060:
0061: /* The intersected pickable node object */
0062: private Node node;
0063:
0064: /* A copy of LocalToVworld transform of the pickable node */
0065: private Transform3D l2vw;
0066:
0067: /* The closest intersection point */
0068: private Point3d closestIntersectionPoint;
0069:
0070: /* Distance between start point of pickShape and closest intersection point */
0071: private double closestDistance;
0072:
0073: /* An array to store intersection results */
0074: private IntersectionInfo[] intersectionInfoArr;
0075:
0076: /* The following references are for internal geometry computation use only */
0077: private ArrayList intersectionInfoList = new ArrayList();
0078: private boolean intersectionInfoListSorted = false;
0079: private Transform3D l2vwRef;
0080: private Node nodeRef;
0081:
0082: /**
0083: * Specifies a Pick using the bounds of the pickable nodes.
0084: */
0085: public static final int PICK_BOUNDS = 1;
0086:
0087: /**
0088: * Specifies a Pick using the geometry of the pickable nodes.
0089: */
0090: public static final int PICK_GEOMETRY = 2;
0091:
0092: /**
0093: * Specifies that this PickInfo returns the computed SceneGraphPath object.
0094: */
0095: public static final int SCENEGRAPHPATH = 0x01;
0096:
0097: /**
0098: * Specifies that this PickInfo returns the computed intersected Node object.
0099: */
0100: public static final int NODE = 0x02;
0101:
0102: /**
0103: * Specifies that this PickInfo returns the computed local to vworld transform.
0104: */
0105: public static final int LOCAL_TO_VWORLD = 0x04;
0106:
0107: /**
0108: * Specifies that this PickInfo returns the closest intersection point.
0109: */
0110: public static final int CLOSEST_INTERSECTION_POINT = 0x08;
0111:
0112: /**
0113: * Specifies that this PickInfo returns the closest intersection distance.
0114: */
0115: public static final int CLOSEST_DISTANCE = 0x10;
0116:
0117: /**
0118: * Specifies that this PickInfo returns only the closest intersection
0119: * geometry information.
0120: */
0121: public static final int CLOSEST_GEOM_INFO = 0x20;
0122:
0123: /**
0124: * Specifies that this PickInfo returns all the closest intersection
0125: * geometry informations.
0126: */
0127: public static final int ALL_GEOM_INFO = 0x40;
0128:
0129: /** PickInfo Constructor */
0130: PickInfo() {
0131:
0132: }
0133:
0134: void setSceneGraphPath(SceneGraphPath sgp) {
0135: this .sgp = sgp;
0136: }
0137:
0138: void setNode(Node node) {
0139: this .node = node;
0140: }
0141:
0142: void setLocalToVWorld(Transform3D l2vw) {
0143: this .l2vw = l2vw;
0144: }
0145:
0146: void setClosestIntersectionPoint(Point3d cIPt) {
0147: this .closestIntersectionPoint = cIPt;
0148: }
0149:
0150: void setClosestDistance(double cDist) {
0151: this .closestDistance = cDist;
0152: }
0153:
0154: void setLocalToVWorldRef(Transform3D l2vwRef) {
0155: this .l2vwRef = l2vwRef;
0156: }
0157:
0158: void setNodeRef(Node nodeRef) {
0159: this .nodeRef = nodeRef;
0160: }
0161:
0162: IntersectionInfo createIntersectionInfo() {
0163: return new IntersectionInfo();
0164: }
0165:
0166: void insertIntersectionInfo(IntersectionInfo iInfo) {
0167: intersectionInfoList.add(iInfo);
0168: intersectionInfoListSorted = false;
0169: }
0170:
0171: void sortIntersectionInfoArray(IntersectionInfo[] iInfoArr) {
0172:
0173: class Sort {
0174:
0175: IntersectionInfo iInfoArr[];
0176:
0177: Sort(IntersectionInfo[] iInfoArr) {
0178: // System.err.println("Sort IntersectionInfo ...");
0179: this .iInfoArr = iInfoArr;
0180: }
0181:
0182: void sorting() {
0183: if (iInfoArr.length < 7) {
0184: // System.err.println(" -- insertSort.");
0185: insertSort();
0186: } else {
0187: // System.err.println(" -- quicksort.");
0188: quicksort(0, iInfoArr.length - 1);
0189: }
0190: }
0191:
0192: // Insertion sort on smallest arrays
0193: final void insertSort() {
0194: for (int i = 0; i < iInfoArr.length; i++) {
0195: for (int j = i; j > 0
0196: && (iInfoArr[j - 1].distance > iInfoArr[j].distance); j--) {
0197: IntersectionInfo iInfo = iInfoArr[j];
0198: iInfoArr[j] = iInfoArr[j - 1];
0199: iInfoArr[j - 1] = iInfo;
0200: }
0201: }
0202: }
0203:
0204: final void quicksort(int l, int r) {
0205: int i = l;
0206: int j = r;
0207: double k = iInfoArr[(l + r) / 2].distance;
0208:
0209: do {
0210: while (iInfoArr[i].distance < k)
0211: i++;
0212: while (k < iInfoArr[j].distance)
0213: j--;
0214: if (i <= j) {
0215: IntersectionInfo iInfo = iInfoArr[i];
0216: iInfoArr[i] = iInfoArr[j];
0217: iInfoArr[j] = iInfo;
0218: i++;
0219: j--;
0220: }
0221: } while (i <= j);
0222:
0223: if (l < j)
0224: quicksort(l, j);
0225: if (l < r)
0226: quicksort(i, r);
0227: }
0228: }
0229:
0230: (new Sort(iInfoArr)).sorting();
0231: intersectionInfoListSorted = true;
0232: }
0233:
0234: static void sortPickInfoArray(PickInfo[] pickInfoArr) {
0235:
0236: class Sort {
0237:
0238: PickInfo pIArr[];
0239:
0240: Sort(PickInfo[] pIArr) {
0241: // System.err.println("Sort PickInfo ...");
0242: this .pIArr = pIArr;
0243: }
0244:
0245: void sorting() {
0246: if (pIArr.length < 7) {
0247: // System.err.println(" -- insertSort.");
0248: insertSort();
0249: } else {
0250: // System.err.println(" -- quicksort.");
0251: quicksort(0, pIArr.length - 1);
0252: }
0253: }
0254:
0255: // Insertion sort on smallest arrays
0256: final void insertSort() {
0257: for (int i = 0; i < pIArr.length; i++) {
0258: for (int j = i; j > 0
0259: && (pIArr[j - 1].closestDistance > pIArr[j].closestDistance); j--) {
0260: PickInfo pI = pIArr[j];
0261: pIArr[j] = pIArr[j - 1];
0262: pIArr[j - 1] = pI;
0263: }
0264: }
0265: }
0266:
0267: final void quicksort(int l, int r) {
0268: int i = l;
0269: int j = r;
0270: double k = pIArr[(l + r) / 2].closestDistance;
0271:
0272: do {
0273: while (pIArr[i].closestDistance < k)
0274: i++;
0275: while (k < pIArr[j].closestDistance)
0276: j--;
0277: if (i <= j) {
0278: PickInfo pI = pIArr[i];
0279: pIArr[i] = pIArr[j];
0280: pIArr[j] = pI;
0281: i++;
0282: j--;
0283: }
0284: } while (i <= j);
0285:
0286: if (l < j)
0287: quicksort(l, j);
0288: if (l < r)
0289: quicksort(i, r);
0290: }
0291: }
0292:
0293: (new Sort(pickInfoArr)).sorting();
0294:
0295: }
0296:
0297: /**
0298: * Retrieves the reference to the SceneGraphPath in this PickInfo object.
0299: * @return the SceneGraphPath object, or null if flag is not set with SCENEGRAPHPATH.
0300: * @see Locale
0301: * @see BranchGroup
0302: */
0303: public SceneGraphPath getSceneGraphPath() {
0304: return sgp;
0305: }
0306:
0307: /**
0308: * Retrieves the reference to the picked node, either a Shape3D or a Morph, in this PickInfo object.
0309: * @return the picked leaf node object, or null if flag is not set with NODE.
0310: * @see Locale
0311: * @see BranchGroup
0312: */
0313: public Node getNode() {
0314: return node;
0315: }
0316:
0317: /**
0318: * Retrieves the reference to the LocalToVworld transform of the picked node in this PickInfo object.
0319: * @return the local to vworld transform, or null if flag is not set with LOCAL_TO_VWORLD.
0320: * @see Locale
0321: * @see BranchGroup
0322: */
0323: public Transform3D getLocalToVWorld() {
0324: return l2vw;
0325: }
0326:
0327: /**
0328: * Retrieves the reference to the closest intersection point in this PickInfo object.
0329: * @return the closest intersection point, or null if flag is not set with CLOSEST_INTERSECTION_POINT.
0330: * @see Locale
0331: * @see BranchGroup
0332: */
0333: public Point3d getClosestIntersectionPoint() {
0334: return closestIntersectionPoint;
0335: }
0336:
0337: /**
0338: * Retrieves the distance between the start point of the pickShape and the closest intersection point.
0339: * @return the closest distance in double, or NaN if flag is not set with CLOSEST_INTERSECTION_POINT.
0340: * Note : If this PickInfo object is returned by either pickClosest or pickAllSorted method, the return
0341: * value is the closest distance in double even if flag is not set with CLOSET_INTERSECTION_POINT.
0342: * @see Locale
0343: * @see BranchGroup
0344: */
0345: public double getClosestDistance() {
0346: return closestDistance;
0347: }
0348:
0349: Transform3D getLocalToVWorldRef() {
0350: return l2vwRef;
0351: }
0352:
0353: Node getNodeRef() {
0354: return nodeRef;
0355: }
0356:
0357: /**
0358: * Retrieves the reference to the array of intersection results in this PickInfo object.
0359: * @return an array of 1 IntersectionInfo object if flag is to set CLOSEST_GEOM_INFO,
0360: * or an array of <i>N</i> IntersectionInfo objects containing all intersections of
0361: * the picked node in sorted order if flag is to set ALL_GEOM_INFO, or null if neither
0362: * bit is set.
0363: * @see Locale
0364: * @see BranchGroup
0365: */
0366: public IntersectionInfo[] getIntersectionInfos() {
0367: if (intersectionInfoListSorted == false) {
0368: intersectionInfoArr = new IntersectionInfo[intersectionInfoList
0369: .size()];
0370: intersectionInfoArr = (IntersectionInfo[]) intersectionInfoList
0371: .toArray(intersectionInfoArr);
0372:
0373: sortIntersectionInfoArray(intersectionInfoArr);
0374: }
0375:
0376: return intersectionInfoArr;
0377: }
0378:
0379: /**
0380: * Search the path from nodeR up to Locale.
0381: * Return the search path as ArrayList if found.
0382: * Note that the locale will not insert into path.
0383: */
0384: static ArrayList initSceneGraphPath(NodeRetained nodeR) {
0385: ArrayList path = new ArrayList(5);
0386:
0387: do {
0388: if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
0389: path.add(nodeR);
0390: }
0391: nodeR = nodeR.parent;
0392: } while (nodeR != null); // reach Locale
0393:
0394: return path;
0395: }
0396:
0397: static private Node[] createPath(NodeRetained srcNode,
0398: BranchGroupRetained bgRetained, GeometryAtom geomAtom,
0399: ArrayList initpath) {
0400:
0401: ArrayList path = retrievePath(srcNode, bgRetained,
0402: geomAtom.source.key);
0403: assert (path != null);
0404:
0405: return mergePath(path, initpath);
0406:
0407: }
0408:
0409: /**
0410: * Return true if bg is inside cachedBG or bg is null
0411: */
0412: static private boolean inside(BranchGroupRetained bgArr[],
0413: BranchGroupRetained bg) {
0414:
0415: if ((bg == null) || (bgArr == null)) {
0416: return true;
0417: }
0418:
0419: for (int i = 0; i < bgArr.length; i++) {
0420: if (bgArr[i] == bg) {
0421: return true;
0422: }
0423: }
0424: return false;
0425: }
0426:
0427: /**
0428: * search the full path from the bottom of the scene graph -
0429: * startNode, up to the Locale if endNode is null.
0430: * If endNode is not null, the path is found up to, but not
0431: * including, endNode or return null if endNode not hit
0432: * during the search.
0433: */
0434: static private ArrayList retrievePath(NodeRetained startNode,
0435: NodeRetained endNode, HashKey key) {
0436:
0437: ArrayList path = new ArrayList(5);
0438: NodeRetained nodeR = startNode;
0439:
0440: if (nodeR.inSharedGroup) {
0441: // getlastNodeId() will destroy this key
0442: key = new HashKey(key);
0443: }
0444:
0445: do {
0446: if (nodeR == endNode) { // we found it !
0447: return path;
0448: }
0449:
0450: if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
0451: path.add(nodeR);
0452: }
0453:
0454: if (nodeR instanceof SharedGroupRetained) {
0455: // retrieve the last node ID
0456: String nodeId = key.getLastNodeId();
0457: Vector parents = ((SharedGroupRetained) nodeR).parents;
0458: int sz = parents.size();
0459: NodeRetained prevNodeR = nodeR;
0460: for (int i = 0; i < sz; i++) {
0461: NodeRetained linkR = (NodeRetained) parents
0462: .elementAt(i);
0463: if (linkR.nodeId.equals(nodeId)) {
0464: nodeR = linkR;
0465: // Need to add Link to the path report
0466: path.add(nodeR);
0467: // since !(endNode instanceof Link), we
0468: // can skip the check (nodeR == endNode) and
0469: // proceed to parent of link below
0470: break;
0471: }
0472: }
0473: if (nodeR == prevNodeR) {
0474: // branch is already detach
0475: return null;
0476: }
0477: }
0478: nodeR = nodeR.parent;
0479: } while (nodeR != null); // reach Locale
0480:
0481: if (endNode == null) {
0482: // user call pickxxx(Locale locale, PickShape shape)
0483: return path;
0484: }
0485:
0486: // user call pickxxx(BranchGroup endNode, PickShape shape)
0487: // if locale is reached and endNode not hit, this is not
0488: // the path user want to select
0489: return null;
0490: }
0491:
0492: /**
0493: * copy p1, (follow by) p2 into a new array, p2 can be null
0494: * The path is then reverse before return.
0495: */
0496: static private Node[] mergePath(ArrayList p1, ArrayList p2) {
0497: int s = p1.size();
0498: int len;
0499: int i;
0500: int l;
0501: if (p2 == null) {
0502: len = s;
0503: } else {
0504: len = s + p2.size();
0505: }
0506:
0507: Node nodes[] = new Node[len];
0508: l = len - 1;
0509: for (i = 0; i < s; i++) {
0510: nodes[l - i] = (Node) ((NodeRetained) p1.get(i)).source;
0511: }
0512: for (int j = 0; i < len; i++, j++) {
0513: nodes[l - i] = (Node) ((NodeRetained) p2.get(j)).source;
0514: }
0515: return nodes;
0516: }
0517:
0518: /**
0519: * Sort the GeometryAtoms distance from shape in ascending order
0520: * geomAtoms.length must be >= 1
0521: */
0522: static void sortGeomAtoms(GeometryAtom geomAtoms[], PickShape shape) {
0523:
0524: final double distance[] = new double[geomAtoms.length];
0525: Point4d pickPos = new Point4d();
0526:
0527: for (int i = 0; i < geomAtoms.length; i++) {
0528: shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);
0529: distance[i] = pickPos.w;
0530: }
0531:
0532: class Sort {
0533:
0534: GeometryAtom atoms[];
0535:
0536: Sort(GeometryAtom[] atoms) {
0537: this .atoms = atoms;
0538: }
0539:
0540: void sorting() {
0541: if (atoms.length < 7) {
0542: insertSort();
0543: } else {
0544: quicksort(0, atoms.length - 1);
0545: }
0546: }
0547:
0548: // Insertion sort on smallest arrays
0549: final void insertSort() {
0550: for (int i = 0; i < atoms.length; i++) {
0551: for (int j = i; j > 0
0552: && (distance[j - 1] > distance[j]); j--) {
0553: double t = distance[j];
0554: distance[j] = distance[j - 1];
0555: distance[j - 1] = t;
0556: GeometryAtom p = atoms[j];
0557: atoms[j] = atoms[j - 1];
0558: atoms[j - 1] = p;
0559: }
0560: }
0561: }
0562:
0563: final void quicksort(int l, int r) {
0564: int i = l;
0565: int j = r;
0566: double k = distance[(l + r) / 2];
0567:
0568: do {
0569: while (distance[i] < k)
0570: i++;
0571: while (k < distance[j])
0572: j--;
0573: if (i <= j) {
0574: double tmp = distance[i];
0575: distance[i] = distance[j];
0576: distance[j] = tmp;
0577:
0578: GeometryAtom p = atoms[i];
0579: atoms[i] = atoms[j];
0580: atoms[j] = p;
0581: i++;
0582: j--;
0583: }
0584: } while (i <= j);
0585:
0586: if (l < j)
0587: quicksort(l, j);
0588: if (l < r)
0589: quicksort(i, r);
0590: }
0591: }
0592:
0593: (new Sort(geomAtoms)).sorting();
0594: }
0595:
0596: /**
0597: * return all PickInfo[] of the geomAtoms.
0598: * If initpath is null, the path is search from
0599: * geomAtom Shape3D/Morph Node up to Locale
0600: * (assume the same locale).
0601: * Otherwise, the path is search up to node or
0602: * null is return if it is not hit.
0603: */
0604: static ArrayList getPickInfos(ArrayList initpath,
0605: BranchGroupRetained bgRetained, GeometryAtom geomAtoms[],
0606: Locale locale, int flags, int pickType) {
0607:
0608: ArrayList pickInfoList = new ArrayList(5);
0609: NodeRetained srcNode;
0610: ArrayList text3dList = null;
0611:
0612: if ((geomAtoms == null) || (geomAtoms.length == 0)) {
0613: return null;
0614: }
0615:
0616: for (int i = 0; i < geomAtoms.length; i++) {
0617: assert ((geomAtoms[i] != null) && (geomAtoms[i].source != null));
0618:
0619: PickInfo pickInfo = null;
0620: Shape3DRetained shape = geomAtoms[i].source;
0621: srcNode = shape.sourceNode;
0622:
0623: // Fix to Issue 274 : NPE With Simultaneous View and Content Side PickingBehaviors
0624: // This node isn't under the selected BG for pick operation.
0625: if (!inside(shape.branchGroupPath, bgRetained)) {
0626: continue;
0627: }
0628:
0629: if (srcNode == null) {
0630: // The node is just detach from branch so sourceNode = null
0631: continue;
0632: }
0633:
0634: // Special case, for Text3DRetained, it is possible
0635: // for different geomAtoms pointing to the same
0636: // source Text3DRetained. So we need to combine
0637: // those cases and report only once.
0638: if (srcNode instanceof Shape3DRetained) {
0639: Shape3DRetained s3dR = (Shape3DRetained) srcNode;
0640: GeometryRetained geomR = null;
0641: for (int cnt = 0; cnt < s3dR.geometryList.size(); cnt++) {
0642: geomR = (GeometryRetained) s3dR.geometryList
0643: .get(cnt);
0644: if (geomR != null)
0645: break;
0646: }
0647:
0648: if (geomR == null)
0649: continue;
0650:
0651: if (geomR instanceof Text3DRetained) {
0652: // assume this case is not frequent, we allocate
0653: // ArrayList only when necessary and we use ArrayList
0654: // instead of HashMap since the case of when large
0655: // number of distingish Text3DRetained node hit is
0656: // rare.
0657: if (text3dList == null) {
0658: text3dList = new ArrayList(3);
0659: } else {
0660: int size = text3dList.size();
0661: boolean found = false;
0662: for (int j = 0; j < size; j++) {
0663: if (text3dList.get(j) == srcNode) {
0664: found = true;
0665: break;
0666: }
0667: }
0668: if (found) {
0669: continue; // try next geomAtom
0670: }
0671: }
0672: text3dList.add(srcNode);
0673: }
0674: }
0675:
0676: // If srcNode is instance of compile retained, then loop thru
0677: // the entire source list and add it to the scene graph path
0678: if (srcNode instanceof Shape3DCompileRetained) {
0679:
0680: Shape3DCompileRetained s3dCR = (Shape3DCompileRetained) srcNode;
0681:
0682: Node[] mpath = null;
0683: boolean first = true;
0684:
0685: for (int n = 0; n < s3dCR.srcList.length; n++) {
0686:
0687: pickInfo = null;
0688:
0689: // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
0690: if ((flags & SCENEGRAPHPATH) != 0) {
0691:
0692: if (first) {
0693: mpath = createPath(srcNode, bgRetained,
0694: geomAtoms[i], initpath);
0695: first = false;
0696: }
0697:
0698: if (mpath != null) {
0699: SceneGraphPath sgpath = new SceneGraphPath(
0700: locale, mpath,
0701: (Node) s3dCR.srcList[n]);
0702: sgpath.setTransform(shape
0703: .getCurrentLocalToVworld(0));
0704: if (pickInfo == null)
0705: pickInfo = new PickInfo();
0706: pickInfo.setSceneGraphPath(sgpath);
0707: }
0708: }
0709:
0710: // PickInfo.NODE - request for computed intersected Node.
0711: if ((flags & NODE) != 0) {
0712: if (pickInfo == null)
0713: pickInfo = new PickInfo();
0714: pickInfo.setNode((Node) s3dCR.srcList[n]);
0715: }
0716:
0717: // PickInfo.LOCAL_TO_VWORLD
0718: // - request for computed local to virtual world transform.
0719: if ((flags & LOCAL_TO_VWORLD) != 0) {
0720: Transform3D l2vw = geomAtoms[i].source
0721: .getCurrentLocalToVworld();
0722: if (pickInfo == null)
0723: pickInfo = new PickInfo();
0724: pickInfo
0725: .setLocalToVWorld(new Transform3D(l2vw));
0726: }
0727:
0728: // NOTE : Piggy bag for geometry computation by caller.
0729: if (((flags & CLOSEST_DISTANCE) != 0)
0730: || ((flags & CLOSEST_GEOM_INFO) != 0)
0731: || ((flags & CLOSEST_INTERSECTION_POINT) != 0)
0732: || ((flags & ALL_GEOM_INFO) != 0)) {
0733: if (pickInfo == null)
0734: pickInfo = new PickInfo();
0735: pickInfo.setNodeRef((Node) s3dCR.srcList[n]);
0736: Transform3D l2vw = geomAtoms[i].source
0737: .getCurrentLocalToVworld();
0738: pickInfo.setLocalToVWorldRef(l2vw);
0739: }
0740:
0741: if (pickInfo != null)
0742: pickInfoList.add(pickInfo);
0743: if (pickType == PICK_ANY) {
0744: return pickInfoList;
0745: }
0746: }
0747: } else {
0748: Node[] mpath = null;
0749:
0750: // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
0751: if ((flags & SCENEGRAPHPATH) != 0) {
0752:
0753: mpath = createPath(srcNode, bgRetained,
0754: geomAtoms[i], initpath);
0755:
0756: if (mpath != null) {
0757: SceneGraphPath sgpath = new SceneGraphPath(
0758: locale, mpath, (Node) srcNode.source);
0759: sgpath.setTransform(shape
0760: .getCurrentLocalToVworld(0));
0761: if (pickInfo == null)
0762: pickInfo = new PickInfo();
0763: pickInfo.setSceneGraphPath(sgpath);
0764: }
0765: }
0766:
0767: // PickInfo.NODE - request for computed intersected Node.
0768: if ((flags & NODE) != 0) {
0769: if (pickInfo == null)
0770: pickInfo = new PickInfo();
0771: pickInfo.setNode((Node) srcNode.source);
0772: }
0773:
0774: // PickInfo.LOCAL_TO_VWORLD
0775: // - request for computed local to virtual world transform.
0776: if ((flags & LOCAL_TO_VWORLD) != 0) {
0777: Transform3D l2vw = geomAtoms[i].source
0778: .getCurrentLocalToVworld();
0779: if (pickInfo == null)
0780: pickInfo = new PickInfo();
0781: pickInfo.setLocalToVWorld(new Transform3D(l2vw));
0782: }
0783:
0784: // NOTE : Piggy bag for geometry computation by caller.
0785: if (((flags & CLOSEST_DISTANCE) != 0)
0786: || ((flags & CLOSEST_GEOM_INFO) != 0)
0787: || ((flags & CLOSEST_INTERSECTION_POINT) != 0)
0788: || ((flags & ALL_GEOM_INFO) != 0)) {
0789: if (pickInfo == null)
0790: pickInfo = new PickInfo();
0791: pickInfo.setNodeRef((Node) srcNode.source);
0792: Transform3D l2vw = geomAtoms[i].source
0793: .getCurrentLocalToVworld();
0794: pickInfo.setLocalToVWorldRef(l2vw);
0795: }
0796:
0797: if (pickInfo != null)
0798: pickInfoList.add(pickInfo);
0799: if (pickType == PICK_ANY) {
0800: return pickInfoList;
0801: }
0802: }
0803: }
0804:
0805: return pickInfoList;
0806: }
0807:
0808: static PickInfo[] pick(Object node, GeometryAtom[] geomAtoms,
0809: int mode, int flags, PickShape pickShape, int pickType) {
0810:
0811: int pickInfoListSize;
0812: PickInfo[] pickInfoArr = null;
0813: Locale locale = null;
0814: BranchGroupRetained bgRetained = null;
0815: ArrayList initPath = null;
0816: ArrayList pickInfoList = null;
0817:
0818: if (node instanceof Locale) {
0819: locale = (Locale) node;
0820: } else if (node instanceof BranchGroupRetained) {
0821: bgRetained = (BranchGroupRetained) node;
0822: locale = bgRetained.locale;
0823: }
0824: synchronized (locale.universe.sceneGraphLock) {
0825: if (bgRetained != null) {
0826: initPath = initSceneGraphPath(bgRetained);
0827: }
0828: pickInfoList = getPickInfos(initPath, bgRetained,
0829: geomAtoms, locale, flags, pickType);
0830: }
0831:
0832: // We're done with PICK_BOUNDS case, but there is still more work for PICK_GEOMETRY case.
0833: if ((mode == PICK_GEOMETRY) && (pickInfoList != null)
0834: && ((pickInfoListSize = pickInfoList.size()) > 0)) {
0835:
0836: //System.err.println("PickInfo.pick() - In geometry case : pickInfoList.size() is " + pickInfoListSize);
0837: PickInfo pickInfo = null;
0838: Node pickNode = null;
0839:
0840: // Order is impt. Need to do in reverse order.
0841: for (int i = pickInfoListSize - 1; i >= 0; i--) {
0842: pickInfo = (PickInfo) pickInfoList.get(i);
0843:
0844: pickNode = pickInfo.getNode();
0845: if (pickNode == null) {
0846: // Use the piggy reference from getPickInfos()
0847: pickNode = pickInfo.getNodeRef();
0848: }
0849:
0850: if (pickNode instanceof Shape3D) {
0851:
0852: /*
0853: * @exception CapabilityNotSetException if the mode is
0854: * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
0855: * is not set in any Geometry objects referred to by any shape
0856: * node whose bounds intersects the PickShape.
0857: *
0858: * @exception CapabilityNotSetException if flags contains any of
0859: * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
0860: * or ALL_GEOM_INFO, and the capability bits that control reading of
0861: * coordinate data are not set in any GeometryArray object referred
0862: * to by any shape node that intersects the PickShape.
0863: * The capability bits that must be set to avoid this exception are
0864: * as follows :
0865: *
0866: * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
0867: * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
0868: * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
0869: * (in addition to one of the above)
0870: *
0871: */
0872:
0873: if (!pickNode
0874: .getCapability(Shape3D.ALLOW_GEOMETRY_READ)) {
0875: throw new CapabilityNotSetException(J3dI18N
0876: .getString("PickInfo0"));
0877: }
0878:
0879: for (int j = 0; j < ((Shape3D) pickNode)
0880: .numGeometries(); j++) {
0881: Geometry geo = ((Shape3D) pickNode)
0882: .getGeometry(j);
0883:
0884: if (geo == null) {
0885: continue;
0886: }
0887:
0888: if (!geo
0889: .getCapability(Geometry.ALLOW_INTERSECT)) {
0890: throw new CapabilityNotSetException(J3dI18N
0891: .getString("PickInfo1"));
0892: }
0893:
0894: if (geo instanceof GeometryArray) {
0895: if (!geo
0896: .getCapability(GeometryArray.ALLOW_COORDINATE_READ))
0897: throw new CapabilityNotSetException(
0898: J3dI18N.getString("PickInfo2"));
0899: if (!geo
0900: .getCapability(GeometryArray.ALLOW_COUNT_READ))
0901: throw new CapabilityNotSetException(
0902: J3dI18N.getString("PickInfo3"));
0903: if (!geo
0904: .getCapability(GeometryArray.ALLOW_FORMAT_READ))
0905: throw new CapabilityNotSetException(
0906: J3dI18N.getString("PickInfo4"));
0907: if (geo instanceof IndexedGeometryArray) {
0908: if (!geo
0909: .getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
0910: throw new CapabilityNotSetException(
0911: J3dI18N
0912: .getString("PickInfo5"));
0913: }
0914: } else if (geo instanceof CompressedGeometry) {
0915: if (!geo
0916: .getCapability(CompressedGeometry.ALLOW_GEOMETRY_READ))
0917: throw new CapabilityNotSetException(
0918: J3dI18N.getString("PickInfo0"));
0919: }
0920: }
0921:
0922: if (((Shape3DRetained) (pickNode.retained))
0923: .intersect(pickInfo, pickShape, flags) == false) {
0924: // System.err.println(" ---- geom " + i + " not intersected");
0925:
0926: pickInfoList.remove(i);
0927:
0928: } else if (pickType == PICK_ANY) {
0929: pickInfoArr = new PickInfo[1];
0930: pickInfoArr[0] = pickInfo;
0931: return pickInfoArr;
0932: }
0933: } else if (pickNode instanceof Morph) {
0934:
0935: /*
0936: * @exception CapabilityNotSetException if the mode is
0937: * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
0938: * is not set in any Geometry objects referred to by any shape
0939: * node whose bounds intersects the PickShape.
0940: *
0941: * @exception CapabilityNotSetException if flags contains any of
0942: * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
0943: * or ALL_GEOM_INFO, and the capability bits that control reading of
0944: * coordinate data are not set in any GeometryArray object referred
0945: * to by any shape node that intersects the PickShape.
0946: * The capability bits that must be set to avoid this exception are
0947: * as follows :
0948: *
0949: * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
0950: * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
0951: * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
0952: * (in addition to one of the above)
0953: *
0954: */
0955:
0956: if (!pickNode
0957: .getCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ)) {
0958: throw new CapabilityNotSetException(J3dI18N
0959: .getString("PickInfo6"));
0960: }
0961:
0962: int numGeo = ((MorphRetained) (pickNode.retained))
0963: .getNumGeometryArrays();
0964: for (int j = 0; j < numGeo; j++) {
0965: GeometryArray geo = ((Morph) pickNode)
0966: .getGeometryArray(j);
0967:
0968: if (geo == null) {
0969: continue;
0970: }
0971:
0972: if (!geo
0973: .getCapability(Geometry.ALLOW_INTERSECT)) {
0974: throw new CapabilityNotSetException(J3dI18N
0975: .getString("PickInfo1"));
0976: }
0977:
0978: if (!geo
0979: .getCapability(GeometryArray.ALLOW_COORDINATE_READ))
0980: throw new CapabilityNotSetException(J3dI18N
0981: .getString("PickInfo2"));
0982: if (!geo
0983: .getCapability(GeometryArray.ALLOW_COUNT_READ))
0984: throw new CapabilityNotSetException(J3dI18N
0985: .getString("PickInfo3"));
0986: if (!geo
0987: .getCapability(GeometryArray.ALLOW_FORMAT_READ))
0988: throw new CapabilityNotSetException(J3dI18N
0989: .getString("PickInfo4"));
0990:
0991: if (geo instanceof IndexedGeometryArray) {
0992: if (!geo
0993: .getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
0994: throw new CapabilityNotSetException(
0995: J3dI18N.getString("PickInfo5"));
0996: }
0997: }
0998:
0999: if (((MorphRetained) (pickNode.retained))
1000: .intersect(pickInfo, pickShape, flags) == false) {
1001: pickInfoList.remove(i);
1002: } else if (pickType == PICK_ANY) {
1003: pickInfoArr = new PickInfo[1];
1004: pickInfoArr[0] = pickInfo;
1005: return pickInfoArr;
1006: }
1007: }
1008: }
1009: }
1010:
1011: // System.err.println("PickInfo : pickInfoList " + pickInfoList);
1012:
1013: if ((pickInfoList != null) && (pickInfoList.size() > 0)) {
1014: // System.err.println(" --- : pickInfoList.size() " + pickInfoList.size());
1015: // System.err.println(" --- : pickInfoList's sgp " +
1016: // ((PickInfo)(pickInfoList.get(0))).getSceneGraphPath());
1017: pickInfoArr = new PickInfo[pickInfoList.size()];
1018: return (PickInfo[]) pickInfoList.toArray(pickInfoArr);
1019: }
1020:
1021: return null;
1022:
1023: }
1024:
1025: /**
1026: * The IntersectionInfo object holds extra information about an intersection
1027: * of a PickShape with a Node as part of a PickInfo. Information such as
1028: * the intersected geometry, the intersected point, and the vertex indices
1029: * can be inquired.
1030: * The local coordinates, normal, color and texture coordiantes of at the
1031: * intersection can be computed, if they are present and readable, using the
1032: * interpolation weights and vertex indices.
1033: * <p>
1034: * If the Shape3D being picked has multiple geometry arrays, the possible arrays
1035: * of IntersectionInfo are stored in the PickInfo and referred to by a geometry
1036: * index. If the picked geometry is of type, Text3D or CompressGeometry,
1037: * getVertexIndices is invalid. If the picked Node is an Morph
1038: * object, the geometry used in pick computation is alway at index 0.
1039: * <p>
1040: *
1041: * @since Java 3D 1.4
1042: */
1043:
1044: public class IntersectionInfo extends Object {
1045:
1046: /* The index to the intersected geometry in the pickable node */
1047: private int geomIndex;
1048:
1049: /* The reference to the intersected geometry in the pickable object */
1050: private Geometry geom;
1051:
1052: /* The intersection point */
1053: private Point3d intersectionPoint;
1054:
1055: /* Distance between start point of pickShape and intersection point */
1056: private double distance;
1057:
1058: /* The vertex indices of the intersected primitive in the geometry */
1059: private int[] vertexIndices;
1060:
1061: /* The interpolation weights for each of the verticies of the primitive */
1062: // private float[] weights; Not supported. Should be done in util. package
1063: /** IntersectionInfo Constructor */
1064: IntersectionInfo() {
1065:
1066: }
1067:
1068: void setGeometryIndex(int geomIndex) {
1069: this .geomIndex = geomIndex;
1070: }
1071:
1072: void setGeometry(Geometry geom) {
1073: this .geom = geom;
1074: }
1075:
1076: void setIntersectionPoint(Point3d intersectionPoint) {
1077: assert (intersectionPoint != null);
1078: this .intersectionPoint = new Point3d(intersectionPoint);
1079: }
1080:
1081: void setDistance(double distance) {
1082: this .distance = distance;
1083: }
1084:
1085: void setVertexIndices(int[] vertexIndices) {
1086: assert (vertexIndices != null);
1087: this .vertexIndices = new int[vertexIndices.length];
1088: for (int i = 0; i < vertexIndices.length; i++) {
1089: this .vertexIndices[i] = vertexIndices[i];
1090: }
1091: }
1092:
1093: /**
1094: * Retrieves the index to the intersected geometry in the picked node, either a Shape3D or Morph.
1095: * @return the index of the intersected geometry in the pickable node.
1096: */
1097: public int getGeometryIndex() {
1098: return geomIndex;
1099: }
1100:
1101: /**
1102: * Retrieves the reference to the intersected geometry in the picked object, either a Shape3D or Morph.
1103: * @return the intersected geometry in the pickable node.
1104: */
1105: public Geometry getGeometry() {
1106: return geom;
1107: }
1108:
1109: /**
1110: * Retrieves the reference to the intersection point in the pickable node.
1111: * @return the intersected point in the pickable node.
1112: */
1113: public Point3d getIntersectionPoint() {
1114: return intersectionPoint;
1115: }
1116:
1117: /**
1118: * Retrieves the distance between the start point of the pickShape and the
1119: * intersection point.
1120: * @return distance between the start point of the pickShape and the
1121: * intersection point.
1122: */
1123: public double getDistance() {
1124: return distance;
1125: }
1126:
1127: /**
1128: * Retrieves the vertex indices of the intersected primitive in the geometry.
1129: * @return the vertex indices of the intersected primitive.
1130: */
1131: public int[] getVertexIndices() {
1132: return vertexIndices;
1133: }
1134:
1135: }
1136: }
|