0001: /*
0002: * $RCSfile: PickTool.java,v $
0003: *
0004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * - Redistribution of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * - Redistribution in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * Neither the name of Sun Microsystems, Inc. or the names of
0019: * contributors may be used to endorse or promote products derived
0020: * from this software without specific prior written permission.
0021: *
0022: * This software is provided "AS IS," without a warranty of any
0023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034: * POSSIBILITY OF SUCH DAMAGES.
0035: *
0036: * You acknowledge that this software is not designed, licensed or
0037: * intended for use in the design, construction, operation or
0038: * maintenance of any nuclear facility.
0039: *
0040: * $Revision: 1.7 $
0041: * $Date: 2007/02/09 17:20:26 $
0042: * $State: Exp $
0043: */
0044:
0045: package com.sun.j3d.utils.picking;
0046:
0047: import javax.vecmath.*;
0048: import javax.media.j3d.*;
0049: import com.sun.j3d.internal.*;
0050:
0051: /**
0052: * The base class for picking operations.
0053: * The picking methods will return a PickResult object for each object picked,
0054: * which can then be queried to
0055: * obtain more detailed information about the specific objects that were
0056: * picked.
0057: * <p>
0058: * The pick mode specifies the detail level of picking before the PickResult
0059: * is returned:
0060: * <p>
0061: * <UL>
0062: * <LI> PickTool.BOUNDS - Pick using the bounds of the pickable nodes. The
0063: * PickResult returned will contain the SceneGraphPath to the picked Node.
0064: * </LI>
0065: * <LI> PickTool.GEOMETRY will pick using the geometry of the pickable nodes.
0066: * The PickResult returned will contain the SceneGraphPath to the picked Node.
0067: * Geometry nodes in the scene must have the ALLOW_INTERSECT capability set for
0068: * this mode.</LI>
0069: * <LI> PickTool.GEOMETRY_INTERSECT_INFO -is the same as GEOMETRY, but the
0070: * the PickResult will also include information on each intersection
0071: * of the pick shape with the geometry. The intersection information includes
0072: * the sub-primitive picked (that is, the point, line, triangle or quad),
0073: * the closest vertex to the center of the pick shape, and
0074: * the intersection's coordinate, normal, color and texture coordinates.
0075: * To allow this information to be generated, Shape3D and Morph nodes must have
0076: * the ALLOW_GEOMETRY_READ capability set and GeometryArrays must have the
0077: * ALLOW_FORMAT_READ,
0078: * ALLOW_COUNT_READ, and ALLOW_COORDINATE_READ capabilities set, plus the
0079: * ALLOW_COORDINATE_INDEX_READ capability for indexed geometry.
0080: * To inquire
0081: * the intersection color, normal or texture coordinates
0082: * the corresponding READ capability bits must be set on the GeometryArray.
0083: * </LI>
0084: * </UL>
0085: * <p> The utility method
0086: * <A HREF="PickTool.html#setCapabilities(javax.media.j3d.Node, int)">
0087: * <code>PickTool.setCapabilities(Node, int)</code></A>
0088: * can be used before the scene graph is
0089: * made live to set the
0090: * capabilities of Shape3D, Morph or Geometry
0091: * nodes to allow picking.
0092: * <p>
0093: * A PickResult from a lower level of detail pick can be used to
0094: * inquire more detailed information if the capibility bits are set.
0095: * This can be used to filter the PickResults
0096: * before the more computationally intensive intersection processing.
0097: * For example,
0098: * the application can do a BOUNDS pick and then selectively inquire
0099: * intersections on some of the PickResults. This will save the effort of
0100: * doing intersection computation on the other PickResults.
0101: * However, inquiring the intersections from a GEOMETRY pick will make
0102: * the intersection computation happen twice, use GEOMETRY_INTERSECT_INFO
0103: * if you want to inquire the intersection information on all the PickResults.
0104: * <p>
0105: * When using pickAllSorted or pickClosest methods, the picks
0106: * will be sorted by the distance from the start point of the pick shape to
0107: * the intersection point.
0108: * <p>
0109: * Morph nodes cannot be picked using the displayed geometry in
0110: * GEOMETRY_INTERSECT_INFO mode due to limitations in the current Java3D core
0111: * API (the current
0112: * geometry of the the Morph cannot be inquired). Instead they are picked
0113: * using
0114: * the geometry at index 0 in the Morph, this limitation may be eliminated in a
0115: * future release of Java3D.
0116: * <p>
0117: * If the pick shape is a PickBounds, the pick result will contain only the
0118: * scene graph path, even if the mode is GEOMETRY_INTERSECT_INFO.
0119: */
0120: public class PickTool {
0121:
0122: /* OPEN ISSUES:
0123: -- pickClosest() and pickAllSorted() using GEOMETRY and a non-PickRay
0124: shape => unsorted picking.
0125: -- Need to implement Morph geometry index 0 picking.
0126: */
0127:
0128: private final boolean debug = true;
0129: protected boolean userDefineShape = false;
0130:
0131: PickShape pickShape;
0132:
0133: /** Used to store the BranchGroup used for picking */
0134: BranchGroup pickRootBG = null;
0135: /** Used to store the Locale used for picking */
0136: Locale pickRootL = null;
0137:
0138: /** Used to store a reference point used in determining how "close" points
0139: are.
0140: */
0141: Point3d start = null;
0142:
0143: /* pick mode, one of BOUNDS, GEOMETRY, etc. */
0144: int mode = BOUNDS;
0145:
0146: /** Use this mode to pick by bounds and get basic information
0147: on the pick.
0148: */
0149: public static final int BOUNDS = 0x200;
0150:
0151: /** Use this mode to pick by geometry and get basic
0152: information on the pick.
0153: */
0154: public static final int GEOMETRY = 0x100;
0155:
0156: /** Use this mode to pick by geometry and save
0157: information about the intersections (intersected primitive,
0158: intersection point and closest vertex).
0159: */
0160: public static final int GEOMETRY_INTERSECT_INFO = 0x400;
0161:
0162: // Flags for the setCapabilities() method
0163: /**
0164: * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
0165: * the Node's capabilities to allow intersection tests, but not
0166: * inquire information about the intersections (use for GEOMETRY mode).
0167: * @see PickTool#setCapabilities
0168: */
0169: public static final int INTERSECT_TEST = 0x1001;
0170:
0171: /**
0172: * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
0173: * the Node's capabilities to allow inquiry of the intersection
0174: * coordinate information.
0175: * @see PickTool#setCapabilities
0176: */
0177: public static final int INTERSECT_COORD = 0x1002;
0178:
0179: /**
0180: * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
0181: * the Node's capabilities to allow inquiry of all intersection
0182: * information.
0183: * @see PickTool#setCapabilities
0184: */
0185: public static final int INTERSECT_FULL = 0x1004;
0186:
0187: /* ============================ METHODS ============================ */
0188:
0189: /**
0190: * Constructor with BranchGroup to be picked.
0191: */
0192: public PickTool(BranchGroup b) {
0193: pickRootBG = b;
0194: }
0195:
0196: /** Returns the BranchGroup to be picked if the tool was initialized
0197: with a BranchGroup, null otherwise.
0198: */
0199: public BranchGroup getBranchGroup() {
0200: return pickRootBG;
0201: }
0202:
0203: /**
0204: * Constructor with the Locale to be picked.
0205: */
0206: public PickTool(Locale l) {
0207: pickRootL = l;
0208: }
0209:
0210: /**
0211: * Returns the Locale to be picked if the tool was initialized with
0212: * a Locale, null otherwise.
0213: */
0214: public Locale getLocale() {
0215: return pickRootL;
0216: }
0217:
0218: /**
0219: * @deprecated This method does nothing other than return its
0220: * input parameter.
0221: */
0222: public Locale setBranchGroup(Locale l) {
0223: return l;
0224: }
0225:
0226: /**
0227: * Sets the capabilities on the Node and it's components to allow
0228: * picking at the specified detail level.
0229: * <p>
0230: * Note that by default all com.sun.j3d.utils.geometry.Primitive
0231: * objects with the same parameters share their geometry (e.g.,
0232: * you can have 50 spheres in your scene, but the geometry is
0233: * stored only once). Therefore the capabilities of the geometry
0234: * are also shared, and once a shared node is live, the
0235: * capabilities cannot be changed. To assign capabilities to
0236: * Primitives with the same parameters, either set the
0237: * capabilities before the primitive is set live, or specify the
0238: * Primitive.GEOMETRY_NOT_SHARED constructor parameter when
0239: * creating the primitive.
0240: * @param node The node to modify
0241: * @param level The capability level, must be one of INTERSECT_TEST,
0242: * INTERSECT_COORD or INTERSECT_FULL
0243: * @throws IllegalArgumentException if Node is not a Shape3D or Morph or
0244: * if the flag value is not valid.
0245: * @throws javax.media.j3d.RestrictedAccessException if the node is part
0246: * of a live or compiled scene graph. */
0247: static public void setCapabilities(Node node, int level) {
0248: if (node instanceof Morph) {
0249: Morph morph = (Morph) node;
0250: switch (level) {
0251: case INTERSECT_FULL:
0252: /* intentional fallthrough */
0253: case INTERSECT_COORD:
0254: morph.setCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ);
0255: /* intentional fallthrough */
0256: case INTERSECT_TEST:
0257: break;
0258: default:
0259: throw new IllegalArgumentException("Improper level");
0260: }
0261: double[] weights = morph.getWeights();
0262: for (int i = 0; i < weights.length; i++) {
0263: GeometryArray ga = morph.getGeometryArray(i);
0264: setCapabilities(ga, level);
0265: }
0266: } else if (node instanceof Shape3D) {
0267: Shape3D shape = (Shape3D) node;
0268: switch (level) {
0269: case INTERSECT_FULL:
0270: /* intentional fallthrough */
0271: case INTERSECT_COORD:
0272: shape.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
0273: /* intentional fallthrough */
0274: case INTERSECT_TEST:
0275: break;
0276: default:
0277: throw new IllegalArgumentException("Improper level");
0278: }
0279: for (int i = 0; i < shape.numGeometries(); i++) {
0280: Geometry geo = shape.getGeometry(i);
0281: if (geo instanceof GeometryArray) {
0282: setCapabilities((GeometryArray) geo, level);
0283: } else if (geo instanceof CompressedGeometry) {
0284: setCapabilities((CompressedGeometry) geo, level);
0285: }
0286: }
0287: } else {
0288: throw new IllegalArgumentException("Improper node type");
0289: }
0290: }
0291:
0292: static private void setCapabilities(GeometryArray ga, int level) {
0293: switch (level) {
0294: case INTERSECT_FULL:
0295: ga.setCapability(GeometryArray.ALLOW_COLOR_READ);
0296: ga.setCapability(GeometryArray.ALLOW_NORMAL_READ);
0297: ga.setCapability(GeometryArray.ALLOW_TEXCOORD_READ);
0298: /* intential fallthrough */
0299: case INTERSECT_COORD:
0300: ga.setCapability(GeometryArray.ALLOW_COUNT_READ);
0301: ga.setCapability(GeometryArray.ALLOW_FORMAT_READ);
0302: ga.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
0303: /* intential fallthrough */
0304: case INTERSECT_TEST:
0305: ga.setCapability(GeometryArray.ALLOW_INTERSECT);
0306: break;
0307: }
0308: if (ga instanceof IndexedGeometryArray) {
0309: setCapabilities((IndexedGeometryArray) ga, level);
0310: }
0311: }
0312:
0313: static private void setCapabilities(IndexedGeometryArray iga,
0314: int level) {
0315: switch (level) {
0316: case INTERSECT_FULL:
0317: iga
0318: .setCapability(IndexedGeometryArray.ALLOW_COLOR_INDEX_READ);
0319: iga
0320: .setCapability(IndexedGeometryArray.ALLOW_NORMAL_INDEX_READ);
0321: iga
0322: .setCapability(IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_READ);
0323: /* intential fallthrough */
0324: case INTERSECT_COORD:
0325: iga
0326: .setCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ);
0327: /* intential fallthrough */
0328: case INTERSECT_TEST:
0329: break;
0330: }
0331: }
0332:
0333: static private void setCapabilities(CompressedGeometry cg, int level) {
0334: switch (level) {
0335: case INTERSECT_FULL:
0336: /* intential fallthrough */
0337: case INTERSECT_COORD:
0338: cg.setCapability(CompressedGeometry.ALLOW_GEOMETRY_READ);
0339: /* intential fallthrough */
0340: case INTERSECT_TEST:
0341: cg.setCapability(CompressedGeometry.ALLOW_INTERSECT);
0342: break;
0343: }
0344: }
0345:
0346: // Methods used to define the pick shape
0347:
0348: /** Sets the pick shape to a user-provided PickShape object
0349: * @param ps The pick shape to pick against.
0350: * @param startPt The start point to use for distance calculations
0351: */
0352: public void setShape(PickShape ps, Point3d startPt) {
0353: this .pickShape = ps;
0354: this .start = startPt;
0355: userDefineShape = (ps != null);
0356: }
0357:
0358: /** Sets the pick shape to use a user-provided Bounds object
0359: * @param bounds The bounds to pick against.
0360: * @param startPt The start point to use for distance calculations
0361: */
0362: public void setShapeBounds(Bounds bounds, Point3d startPt) {
0363: this .pickShape = (PickShape) new PickBounds(bounds);
0364: this .start = startPt;
0365: userDefineShape = true;
0366: }
0367:
0368: /** Sets the picking detail mode. The default is BOUNDS.
0369: * @param mode One of BOUNDS, GEOMETRY, GEOMETRY_INTERSECT_INFO, or
0370: * @exception IllegalArgumentException if mode is not a legal value
0371: */
0372: public void setMode(int mode) {
0373: if ((mode != BOUNDS) && (mode != GEOMETRY)
0374: && (mode != GEOMETRY_INTERSECT_INFO)) {
0375: throw new java.lang.IllegalArgumentException();
0376: }
0377: this .mode = mode;
0378: }
0379:
0380: /** Gets the picking detail mode.
0381: */
0382: public int getMode() {
0383: return mode;
0384: }
0385:
0386: /** Sets the pick shape to a PickRay.
0387: * @param start The start of the ray
0388: * @param dir The direction of the ray
0389: */
0390: public void setShapeRay(Point3d start, Vector3d dir) {
0391: this .pickShape = (PickShape) new PickRay(start, dir);
0392: this .start = start;
0393: userDefineShape = true;
0394: }
0395:
0396: /** Sets the pick shape to a PickSegment.
0397: @param start The start of the segment
0398: p @param end The end of the segment
0399: */
0400: public void setShapeSegment(Point3d start, Point3d end) {
0401: this .pickShape = (PickShape) new PickSegment(start, end);
0402: this .start = start;
0403: userDefineShape = true;
0404: }
0405:
0406: /** Sets the pick shape to a capped PickCylinder
0407: * @param start The start of axis of the cylinder
0408: * @param end The end of the axis of the cylinder
0409: * @param radius The radius of the cylinder
0410: */
0411: public void setShapeCylinderSegment(Point3d start, Point3d end,
0412: double radius) {
0413: this .pickShape = (PickShape) new PickCylinderSegment(start,
0414: end, radius);
0415: this .start = start;
0416: userDefineShape = true;
0417: }
0418:
0419: /** Sets the pick shape to an infinite PickCylinder.
0420: * @param start The start of axis of the cylinder
0421: * @param dir The direction of the axis of the cylinder
0422: * @param radius The radius of the cylinder
0423: */
0424: public void setShapeCylinderRay(Point3d start, Vector3d dir,
0425: double radius) {
0426: this .pickShape = (PickShape) new PickCylinderRay(start, dir,
0427: radius);
0428: this .start = start;
0429: userDefineShape = true;
0430: }
0431:
0432: /** Sets the pick shape to a capped PickCone
0433: * @param start The start of axis of the cone
0434: * @param end The end of the axis of the cone
0435: * @param angle The angle of the cone
0436: */
0437: public void setShapeConeSegment(Point3d start, Point3d end,
0438: double angle) {
0439: this .pickShape = (PickShape) new PickConeSegment(start, end,
0440: angle);
0441: this .start = start;
0442: userDefineShape = true;
0443: }
0444:
0445: /** Sets the pick shape to an infinite PickCone.
0446: * @param start The start of axis of the cone
0447: * @param dir The direction of the axis of the cone
0448: * @param angle The angle of the cone
0449: */
0450: public void setShapeConeRay(Point3d start, Vector3d dir,
0451: double angle) {
0452: this .pickShape = (PickShape) new PickConeRay(start, dir, angle);
0453: this .start = start;
0454: userDefineShape = true;
0455: }
0456:
0457: /** Returns the PickShape for this object. */
0458: public PickShape getPickShape() {
0459: return pickShape;
0460: }
0461:
0462: /** Returns the start postion used for distance measurement. */
0463: public Point3d getStartPosition() {
0464: return start;
0465: }
0466:
0467: /** Selects all the nodes that intersect the PickShape.
0468: @return An array of <code>PickResult</code> objects which will contain
0469: information about the picked instances. <code>null</code> if nothing was
0470: picked.
0471: */
0472: public PickResult[] pickAll() {
0473: PickResult[] retval = null;
0474: switch (mode) {
0475: case BOUNDS:
0476: retval = pickAll(pickShape);
0477: break;
0478: case GEOMETRY:
0479: retval = pickGeomAll(pickShape);
0480: break;
0481: case GEOMETRY_INTERSECT_INFO:
0482: retval = pickGeomAllIntersect(pickShape);
0483: break;
0484: default:
0485: throw new RuntimeException("Invalid pick mode");
0486: }
0487: return retval;
0488: }
0489:
0490: /** Select one of the nodes that intersect the PickShape
0491: @return A <code>PickResult</code> object which will contain
0492: information about the picked instance. <code>null</code> if nothing
0493: was picked.
0494: */
0495: public PickResult pickAny() {
0496: PickResult retval = null;
0497: switch (mode) {
0498: case BOUNDS:
0499: retval = pickAny(pickShape);
0500: break;
0501: case GEOMETRY:
0502: retval = pickGeomAny(pickShape);
0503: break;
0504: case GEOMETRY_INTERSECT_INFO:
0505: retval = pickGeomAnyIntersect(pickShape);
0506: break;
0507: default:
0508: throw new RuntimeException("Invalid pick mode");
0509: }
0510: return retval;
0511: }
0512:
0513: /** Select all the nodes that intersect the
0514: PickShape, returned sorted. The "closest" object will be returned first.
0515: See note above to see how "closest" is determined.
0516: <p>
0517: @return An array of <code>PickResult</code> objects which will contain
0518: information
0519: about the picked instances. <code>null</code> if nothing was picked.
0520: */
0521: public PickResult[] pickAllSorted() {
0522: PickResult[] retval = null;
0523:
0524: // System.out.println ("PickTool.pickAllSorted.");
0525:
0526: switch (mode) {
0527: case BOUNDS:
0528: // System.out.println ("PickTool.pickAllSorted : Bounds");
0529: retval = pickAllSorted(pickShape);
0530: break;
0531: case GEOMETRY:
0532: // System.out.println ("PickTool.pickAllSorted : Geometry");
0533: // TODO - BugId 4351050.
0534: // pickGeomAllSorted is broken for PickCone and PickCylinder :
0535: // The current Shape3D.intersect() API doesn't return the distance for
0536: // PickCone and PickCylinder.
0537: // 2) TODO - BugId 4351579.
0538: // pickGeomClosest is broken for multi-geometry Shape3D node :
0539: // The current Shape3D.intersect() API does't return the closest intersected
0540: // geometry.
0541: retval = pickGeomAllSorted(pickShape);
0542:
0543: break;
0544: case GEOMETRY_INTERSECT_INFO:
0545: // System.out.println ("PickShape " + pickShape);
0546: // System.out.println ("PickTool.pickAllSorted : GEOMETRY_INTERSECT_INFO");
0547: retval = pickGeomAllSortedIntersect(pickShape);
0548: break;
0549: default:
0550: throw new RuntimeException("Invalid pick mode");
0551: }
0552: return retval;
0553: }
0554:
0555: /** Select the closest node that
0556: intersects the PickShape. See note above to see how "closest" is
0557: determined.
0558: <p>
0559: @return A <code>PickResult</code> object which will contain
0560: information about the picked instance. <code>null</code> if nothing
0561: was picked.
0562: */
0563: public PickResult pickClosest() {
0564: PickResult retval = null;
0565: switch (mode) {
0566: case BOUNDS:
0567: retval = pickClosest(pickShape);
0568: break;
0569: case GEOMETRY:
0570: // System.out.println("pickCloset -- Geometry based picking");
0571: // 1) TODO - BugId 4351050.
0572: // pickGeomClosest is broken for PickCone and PickCylinder :
0573: // The current Shape3D.intersect() API doesn't return the distance for
0574: // PickCone and PickCylinder.
0575: // 2) TODO - BugId 4351579.
0576: // pickGeomClosest is broken for multi-geometry Shape3D node :
0577: // The current Shape3D.intersect() API does't return the closest intersected
0578: // geometry.
0579: retval = pickGeomClosest(pickShape);
0580:
0581: break;
0582: case GEOMETRY_INTERSECT_INFO:
0583: // System.out.println ("PickShape " + pickShape);
0584: // System.out.println ("PickTool.pickClosest : GEOMETRY_INTERSECT_INFO");
0585: retval = pickGeomClosestIntersect(pickShape);
0586: break;
0587: default:
0588: throw new RuntimeException("Invalid pick mode");
0589: }
0590: return retval;
0591: }
0592:
0593: private PickResult[] pickAll(PickShape pickShape) {
0594: PickResult[] pr = null;
0595: SceneGraphPath[] sgp = null;
0596:
0597: if (pickRootBG != null) {
0598: sgp = pickRootBG.pickAll(pickShape);
0599: } else if (pickRootL != null) {
0600: sgp = pickRootL.pickAll(pickShape);
0601: }
0602: if (sgp == null)
0603: return null; // no match
0604:
0605: // Create PickResult array
0606: pr = new PickResult[sgp.length];
0607: for (int i = 0; i < sgp.length; i++) {
0608: pr[i] = new PickResult(sgp[i], pickShape);
0609: }
0610: return pr;
0611: }
0612:
0613: private PickResult[] pickAllSorted(PickShape pickShape) {
0614: PickResult[] pr = null;
0615: SceneGraphPath[] sgp = null;
0616:
0617: if (pickRootBG != null) {
0618: sgp = pickRootBG.pickAllSorted(pickShape);
0619: } else if (pickRootL != null) {
0620: sgp = pickRootL.pickAllSorted(pickShape);
0621: }
0622: if (sgp == null)
0623: return null; // no match
0624:
0625: // Create PickResult array
0626: pr = new PickResult[sgp.length];
0627: for (int i = 0; i < sgp.length; i++) {
0628: pr[i] = new PickResult(sgp[i], pickShape);
0629: }
0630: return pr;
0631: }
0632:
0633: private PickResult pickAny(PickShape pickShape) {
0634: PickResult pr = null;
0635: SceneGraphPath sgp = null;
0636:
0637: if (pickRootBG != null) {
0638: sgp = pickRootBG.pickAny(pickShape);
0639: } else if (pickRootL != null) {
0640: sgp = pickRootL.pickAny(pickShape);
0641: }
0642: if (sgp == null)
0643: return null; // no match
0644:
0645: // Create PickResult object
0646: pr = new PickResult(sgp, pickShape);
0647: return pr;
0648: }
0649:
0650: private PickResult pickClosest(PickShape pickShape) {
0651: PickResult pr = null;
0652: SceneGraphPath sgp = null;
0653:
0654: if (pickRootBG != null) {
0655: sgp = pickRootBG.pickClosest(pickShape);
0656: } else if (pickRootL != null) {
0657: sgp = pickRootL.pickClosest(pickShape);
0658: }
0659: if (sgp == null)
0660: return null; // no match
0661:
0662: // Create PickResult object
0663: pr = new PickResult(sgp, pickShape);
0664: return pr;
0665: }
0666:
0667: // ================================================================
0668: // GEOMETRY METHODS
0669: // ================================================================
0670:
0671: private PickResult[] pickGeomAll(PickShape pickShape) {
0672: SceneGraphPath[] sgp = null;
0673: Node obj[] = null;
0674: int i, cnt = 0;
0675:
0676: // First pass
0677: if (pickRootBG != null) {
0678: sgp = pickRootBG.pickAll(pickShape);
0679: } else if (pickRootL != null) {
0680: sgp = pickRootL.pickAll(pickShape);
0681: }
0682: if (sgp == null)
0683: return null; // no match
0684:
0685: // Second pass, check to see if geometries intersected
0686: boolean found[] = new boolean[sgp.length];
0687:
0688: obj = new Node[sgp.length];
0689: PickResult[] pr = new PickResult[sgp.length];
0690: for (i = 0; i < sgp.length; i++) {
0691: obj[i] = sgp[i].getObject();
0692: pr[i] = new PickResult(sgp[i], pickShape);
0693:
0694: if (obj[i] instanceof Shape3D) {
0695: found[i] = ((Shape3D) obj[i]).intersect(sgp[i],
0696: pickShape);
0697: } else if (obj[i] instanceof Morph) {
0698: found[i] = ((Morph) obj[i])
0699: .intersect(sgp[i], pickShape);
0700: }
0701: if (found[i] == true)
0702: cnt++;
0703: }
0704:
0705: if (cnt == 0)
0706: return null; // no match
0707:
0708: PickResult[] newpr = new PickResult[cnt];
0709: cnt = 0; // reset for reuse.
0710: for (i = 0; i < sgp.length; i++) {
0711: if (found[i] == true)
0712: pr[cnt++] = pr[i];
0713: }
0714:
0715: return pr;
0716: }
0717:
0718: private PickResult[] pickGeomAllSorted(PickShape pickShape) {
0719: SceneGraphPath[] sgp = null;
0720: Node[] obj = null;
0721: int i, cnt = 0;
0722: double[] dist = new double[1];
0723:
0724: // First pass
0725: if (pickRootBG != null) {
0726: sgp = pickRootBG.pickAll(pickShape);
0727: } else if (pickRootL != null) {
0728: sgp = pickRootL.pickAll(pickShape);
0729: }
0730: if (sgp == null)
0731: return null; // no match
0732:
0733: /*
0734: System.out.println ("PickTool.pickGeomAllSorted: bounds " +
0735: "picking found "+sgp.length+" nodes");
0736: */
0737: // Second pass, check to see if geometries intersected
0738: boolean[] found = new boolean[sgp.length];
0739: double[] distArr = new double[sgp.length];
0740: obj = new Node[sgp.length];
0741: PickResult[] pr = new PickResult[sgp.length];
0742:
0743: for (i = 0; i < sgp.length; i++) {
0744: obj[i] = sgp[i].getObject();
0745: pr[i] = new PickResult(sgp[i], pickShape);
0746: if (obj[i] instanceof Shape3D) {
0747: found[i] = ((Shape3D) obj[i]).intersect(sgp[i],
0748: pickShape, dist);
0749: distArr[i] = dist[0];
0750: } else if (obj[i] instanceof Morph) {
0751: found[i] = ((Morph) obj[i]).intersect(sgp[i],
0752: pickShape, dist);
0753: distArr[i] = dist[0];
0754: }
0755: if (found[i] == true)
0756: cnt++;
0757: }
0758: if (cnt == 0)
0759: return null; // no match
0760:
0761: PickResult[] npr = new PickResult[cnt];
0762: double[] distance = new double[cnt];
0763: cnt = 0; // reset for reuse.
0764: for (i = 0; i < sgp.length; i++) {
0765: if (found[i] == true) {
0766: distance[cnt] = distArr[i];
0767: npr[cnt++] = pr[i];
0768: }
0769: }
0770: if (cnt > 1) {
0771: return sortPickResults(npr, distance);
0772: } else { // Don't have to sort if only one item
0773: return npr;
0774: }
0775: }
0776:
0777: private PickResult pickGeomAny(PickShape pickShape) {
0778: Node obj = null;
0779: int i;
0780: SceneGraphPath[] sgpa = null;
0781:
0782: if (pickRootBG != null) {
0783: sgpa = pickRootBG.pickAll(pickShape);
0784: } else if (pickRootL != null) {
0785: sgpa = pickRootL.pickAll(pickShape);
0786: }
0787:
0788: if (sgpa == null)
0789: return null; // no match
0790:
0791: for (i = 0; i < sgpa.length; i++) {
0792: obj = sgpa[i].getObject();
0793: PickResult pr = new PickResult(sgpa[i], pickShape);
0794: if (obj instanceof Shape3D) {
0795: if (((Shape3D) obj).intersect(sgpa[i], pickShape)) {
0796: return pr;
0797: }
0798: } else if (obj instanceof Morph) {
0799: if (((Morph) obj).intersect(sgpa[i], pickShape)) {
0800: return pr;
0801: }
0802: }
0803: }
0804:
0805: return null;
0806: }
0807:
0808: private PickResult pickGeomClosest(PickShape pickShape) {
0809: // System.out.println("pickGeomCloset -- Geometry based picking");
0810: PickResult[] pr = pickGeomAllSorted(pickShape);
0811: if (pr == null) {
0812: return null;
0813: } else {
0814: return pr[0];
0815: }
0816: }
0817:
0818: // ================================================================
0819: // NEW METHODS, return additional information
0820: // ================================================================
0821:
0822: private PickResult[] pickGeomAllIntersect(PickShape pickShape) {
0823: SceneGraphPath[] sgp = null;
0824: Node obj[] = null;
0825: int i, cnt = 0;
0826:
0827: // First pass
0828: if (pickRootBG != null) {
0829: sgp = pickRootBG.pickAll(pickShape);
0830: } else if (pickRootL != null) {
0831: sgp = pickRootL.pickAll(pickShape);
0832: }
0833: if (sgp == null)
0834: return null; // no match
0835:
0836: // Second pass, check to see if geometries intersected
0837: boolean found[] = new boolean[sgp.length];
0838:
0839: PickResult[] pr = new PickResult[sgp.length];
0840: for (i = 0; i < sgp.length; i++) {
0841: pr[i] = new PickResult(sgp[i], pickShape);
0842: if (pr[i].numIntersections() > 0) {
0843: found[i] = true;
0844: cnt++;
0845: }
0846: }
0847:
0848: if (cnt == 0)
0849: return null; // no match
0850:
0851: PickResult[] newpr = new PickResult[cnt];
0852: cnt = 0; // reset for reuse.
0853: for (i = 0; i < sgp.length; i++) {
0854: if (found[i] == true)
0855: pr[cnt++] = pr[i];
0856: }
0857:
0858: return pr;
0859: }
0860:
0861: private PickResult[] pickGeomAllSortedIntersect(PickShape pickShape) {
0862: SceneGraphPath[] sgp = null;
0863: Node[] obj = null;
0864: int i, cnt = 0;
0865: double[] dist = new double[1];
0866:
0867: // First pass
0868: if (pickRootBG != null) {
0869: sgp = pickRootBG.pickAll(pickShape);
0870: } else if (pickRootL != null) {
0871: sgp = pickRootL.pickAll(pickShape);
0872: }
0873: if (sgp == null)
0874: return null; // no match
0875:
0876: // System.out.println ("PickTool.pickGeomAllSortedIntersect: bounds " +
0877: // " picking found "+sgp.length+" nodes");
0878:
0879: // Second pass, check to see if geometries intersected
0880: boolean[] found = new boolean[sgp.length];
0881: double[] distArr = new double[sgp.length];
0882:
0883: PickResult[] pr = new PickResult[sgp.length];
0884: for (i = 0; i < sgp.length; i++) {
0885: pr[i] = new PickResult(sgp[i], pickShape);
0886: int numIntersection = pr[i].numIntersections();
0887: if (numIntersection > 0) {
0888: // System.out.println ("numIntersection " + numIntersection);
0889: found[i] = true;
0890: double minDist;
0891: double tempDist;
0892:
0893: int minIndex;
0894: boolean needToSwap = false;
0895: minDist = pr[i].getIntersection(0).getDistance();
0896: minIndex = 0;
0897: for (int j = 1; j < numIntersection; j++) {
0898: // System.out.println ("Distance " + pr[i].getIntersection(j).getDistance());
0899: //System.out.println ("Geom Index " + pr[i].getIntersection(j).getGeometryArrayIndex());
0900: tempDist = pr[i].getIntersection(j).getDistance();
0901: if (minDist > tempDist) {
0902: minDist = tempDist;
0903: minIndex = j;
0904: needToSwap = true;
0905: }
0906: }
0907:
0908: //Swap if necc.
0909: if (needToSwap) {
0910: // System.out.println ("Swap is needed");
0911: PickIntersection pi0 = pr[i].getIntersection(0);
0912: PickIntersection piMin = pr[i]
0913: .getIntersection(minIndex);
0914: pr[i].intersections.set(0, piMin);
0915: pr[i].intersections.set(minIndex, pi0);
0916: }
0917:
0918: distArr[i] = pr[i].getIntersection(0).getDistance();
0919: cnt++;
0920: }
0921: }
0922:
0923: // System.out.println ("PickTool.pickGeomAllSortedIntersect: geometry intersect check "
0924: // + " cnt " + cnt);
0925:
0926: if (cnt == 0)
0927: return null; // no match
0928:
0929: PickResult[] npr = new PickResult[cnt];
0930: double[] distance = new double[cnt];
0931: cnt = 0; // reset for reuse.
0932: for (i = 0; i < sgp.length; i++) {
0933: if (found[i] == true) {
0934: distance[cnt] = distArr[i];
0935: npr[cnt++] = pr[i];
0936: }
0937: }
0938:
0939: if (cnt > 1) {
0940: return sortPickResults(npr, distance);
0941: } else { // Don't have to sort if only one item
0942: return npr;
0943: }
0944: }
0945:
0946: private PickResult pickGeomClosestIntersect(PickShape pickShape) {
0947: PickResult[] pr = pickGeomAllSortedIntersect(pickShape);
0948: /*
0949: System.out.println ("PickTool.pickGeomClosestIntersect: pr.length "
0950: + pr.length);
0951: for(int i=0;i<pr.length;i++) {
0952: System.out.println ("pr["+i+"] " + pr[i]);
0953: }
0954: */
0955:
0956: if (pr == null) {
0957: return null;
0958: } else {
0959: return pr[0];
0960: }
0961: }
0962:
0963: private PickResult pickGeomAnyIntersect(PickShape pickShape) {
0964: Node obj = null;
0965: int i;
0966: SceneGraphPath[] sgpa = null;
0967:
0968: if (pickRootBG != null) {
0969: sgpa = pickRootBG.pickAll(pickShape);
0970: } else if (pickRootL != null) {
0971: sgpa = pickRootL.pickAll(pickShape);
0972: }
0973: if (sgpa == null)
0974: return null; // no match
0975: for (i = 0; i < sgpa.length; i++) {
0976: PickResult pr = new PickResult(sgpa[i], pickShape);
0977: pr.setFirstIntersectOnly(true);
0978: if (pr.numIntersections() > 0) {
0979: return pr;
0980: }
0981: }
0982:
0983: return null;
0984: }
0985:
0986: // ================================================================
0987: // Sort Methods
0988: // ================================================================
0989: private PickResult[] sortPickResults(PickResult[] pr, double[] dist) {
0990: int[] pos = new int[pr.length];
0991: PickResult[] prsorted = new PickResult[pr.length];
0992:
0993: // Initialize position array
0994: for (int i = 0; i < pr.length; i++) {
0995: pos[i] = i;
0996: }
0997: // Do sort
0998: quicksort(0, dist.length - 1, dist, pos);
0999:
1000: // Create new array
1001: for (int i = 0; i < pr.length; i++) {
1002: prsorted[i] = pr[pos[i]];
1003: }
1004: return prsorted;
1005: }
1006:
1007: private final void quicksort(int l, int r, double[] dist, int[] pos) {
1008: int p, i, j;
1009: double tmp, k;
1010:
1011: i = l;
1012: j = r;
1013: k = dist[(l + r) / 2];
1014: do {
1015: while (dist[i] < k)
1016: i++;
1017: while (k < dist[j])
1018: j--;
1019: if (i <= j) {
1020: tmp = dist[i];
1021: dist[i] = dist[j];
1022: dist[j] = tmp;
1023:
1024: p = pos[i];
1025: pos[i] = pos[j];
1026: pos[j] = p;
1027: i++;
1028: j--;
1029: }
1030: } while (i <= j);
1031:
1032: if (l < j)
1033: quicksort(l, j, dist, pos);
1034: if (l < r)
1035: quicksort(i, r, dist, pos);
1036: }
1037:
1038: } // PickTool
|