001: /*
002: * $RCSfile: PickTool.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.5 $
041: * $Date: 2007/02/09 17:20:24 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.pickfast;
046:
047: import com.sun.j3d.utils.geometry.Primitive;
048: import javax.vecmath.*;
049: import javax.media.j3d.*;
050: import com.sun.j3d.internal.*;
051:
052: /**
053: * The base class for optimized picking operations.
054: * The picking methods will return a PickInfo object for each object picked,
055: * which can then be queried to
056: * obtain more detailed information about the specific objects that were
057: * picked.
058: * <p>
059: * The pick mode specifies the detail level of picking before the PickInfo
060: * is returned:
061: * <p>
062: * <UL>
063: * <LI> PickInfo.PICK_BOUNDS - Pick using the only bounds of the pickable nodes.
064: * </LI>
065: * <LI> PickInfo.PICK_GEOMETRY will pick using the geometry of the pickable nodes.
066: * Geometry nodes in the scene must have the ALLOW_INTERSECT capability set for
067: * this mode.</LI>
068: * <p>
069: * The pick flags specifies the content of the PickInfo(s) returned by the
070: * pick methods. This is specified as one or more individual bits that are
071: * bitwise "OR"ed together to describe the PickInfo data. The flags include :
072: * <ul>
073: * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>
074: * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
075: * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
076: * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
077: * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
078: * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
079: * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
080: * </ul>
081: * </UL>
082: * <p>
083: * When using pickAllSorted or pickClosest methods, the picks
084: * will be sorted by the distance from the start point of the pick shape to
085: * the intersection point.
086: *
087: * @see Locale#pickClosest(int,int,javax.media.j3d.PickShape)
088: */
089: public class PickTool {
090:
091: /**
092: * Flag to pass to
093: * <CODE>getNode(int)</CODE>
094: * to return a
095: * <code>Shape3D</code> node from
096: * the <code>SceneGraphPath</code>.
097: */
098: public static final int TYPE_SHAPE3D = 0x1;
099:
100: /**
101: * Flag to pass to
102: * <CODE>getNode(int)</CODE>
103: * to return a
104: * <code>Morph</code> node from
105: * the <code>SceneGraphPath</code>.
106: */
107: public static final int TYPE_MORPH = 0x2;
108:
109: /**
110: * Flag to pass to
111: * <CODE>getNode(int)</CODE>
112:
113: * to return a
114: * <code>Primitive</code> node from
115: * the <code>SceneGraphPath</code>.
116: */
117: public static final int TYPE_PRIMITIVE = 0x4;
118:
119: /**
120: * Flag to pass to
121: * <CODE>getNode(int)</CODE>
122: * to return a
123: * <code>Link</code> node from
124: * the <code>SceneGraphPath</code>.
125: */
126: public static final int TYPE_LINK = 0x8;
127:
128: /**
129: * Flag to pass to
130: * <CODE>getNode(int)</CODE>
131: * to return a
132: * <code>Group</code> node from
133: * the <code>SceneGraphPath</code>.
134: */
135: public static final int TYPE_GROUP = 0x10;
136:
137: /**
138: * Flag to pass to
139: * <CODE>getNode(int)</CODE>
140: * to return a
141: * <code>TransformGroup</code> node from
142: * the <code>SceneGraphPath</code>.
143: */
144: public static final int TYPE_TRANSFORM_GROUP = 0x20;
145:
146: /**
147: * Flag to pass to
148: * <CODE>getNode(int)</CODE>
149: * to return a
150: * <code>BranchGroup</code> node from
151: * the <code>SceneGraphPath</code>.
152: */
153: public static final int TYPE_BRANCH_GROUP = 0x40;
154:
155: /**
156: * Flag to pass to
157: * <CODE>getNode(int)</CODE>
158: * to return a
159: * <code>Switch</code> node from
160: * the <code>SceneGraphPath</code>.
161: */
162: public static final int TYPE_SWITCH = 0x80;
163:
164: private static final int ALL_FLAGS = PickInfo.SCENEGRAPHPATH
165: | PickInfo.NODE | PickInfo.LOCAL_TO_VWORLD
166: | PickInfo.CLOSEST_INTERSECTION_POINT
167: | PickInfo.CLOSEST_DISTANCE | PickInfo.CLOSEST_GEOM_INFO
168: | PickInfo.ALL_GEOM_INFO;
169:
170: private final boolean debug = false;
171: protected boolean userDefineShape = false;
172:
173: PickShape pickShape;
174:
175: /** Used to store the BranchGroup used for picking */
176: BranchGroup pickRootBG = null;
177: /** Used to store the Locale used for picking */
178: Locale pickRootL = null;
179:
180: /** Used to store a reference point used in determining how "close" points
181: are.
182: */
183: Point3d start = null;
184:
185: int mode = PickInfo.PICK_BOUNDS;
186: int flags = PickInfo.NODE;
187:
188: /* ============================ METHODS ============================ */
189:
190: /**
191: * Constructor with BranchGroup to be picked.
192: */
193: public PickTool(BranchGroup b) {
194: pickRootBG = b;
195: }
196:
197: /**
198: * Constructor with the Locale to be picked.
199: */
200: public PickTool(Locale l) {
201: pickRootL = l;
202: }
203:
204: /** Returns the BranchGroup to be picked if the tool was initialized
205: with a BranchGroup, null otherwise.
206: */
207: public BranchGroup getBranchGroup() {
208: return pickRootBG;
209: }
210:
211: /**
212: * Returns the Locale to be picked if the tool was initialized with
213: * a Locale, null otherwise.
214: */
215: public Locale getLocale() {
216: return pickRootL;
217: }
218:
219: // Methods used to define the pick shape
220:
221: /** Sets the pick shape to a user-provided PickShape object
222: * @param ps The pick shape to pick against.
223: * @param startPt The start point to use for distance calculations
224: */
225: public void setShape(PickShape ps, Point3d startPt) {
226: this .pickShape = ps;
227: this .start = startPt;
228: userDefineShape = (ps != null);
229: }
230:
231: /** Sets the pick shape to use a user-provided Bounds object
232: * @param bounds The bounds to pick against.
233: * @param startPt The start point to use for distance calculations
234: */
235: public void setShapeBounds(Bounds bounds, Point3d startPt) {
236: this .pickShape = (PickShape) new PickBounds(bounds);
237: this .start = startPt;
238: userDefineShape = true;
239: }
240:
241: /** Sets the picking detail mode. The default is PickInfo.PICK_BOUNDS.
242: * @param mode One of PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY.
243: * @exception IllegalArgumentException if mode is not a legal value
244: */
245: public void setMode(int mode) {
246: if ((mode != PickInfo.PICK_BOUNDS)
247: && (mode != PickInfo.PICK_GEOMETRY)) {
248: throw new java.lang.IllegalArgumentException();
249: }
250: this .mode = mode;
251: }
252:
253: /** Gets the picking detail mode.
254: */
255: public int getMode() {
256: return mode;
257: }
258:
259: /** Sets the PickInfo content flags. The default is PickInfo.NODE.
260: * @param flags specified as one or more individual bits that are
261: * bitwise "OR"ed together :
262: * <ul>
263: * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>
264: * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
265: * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
266: * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
267: * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
268: * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
269: * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
270: * </ul>
271: * @exception IllegalArgumentException if any other bits besides the above are set.
272: */
273: public void setFlags(int flags) {
274: if ((flags & ~ALL_FLAGS) != 0) {
275: throw new java.lang.IllegalArgumentException();
276: }
277: this .flags = flags;
278: }
279:
280: /** Gets the PickInfo content flags.
281: */
282: public int getFlags() {
283: return flags;
284: }
285:
286: /** Sets the pick shape to a PickRay.
287: * @param start The start of the ray
288: * @param dir The direction of the ray
289: */
290: public void setShapeRay(Point3d start, Vector3d dir) {
291: this .pickShape = (PickShape) new PickRay(start, dir);
292: this .start = start;
293: userDefineShape = true;
294: }
295:
296: /** Sets the pick shape to a PickSegment.
297: @param start The start of the segment
298: @param end The end of the segment
299: */
300: public void setShapeSegment(Point3d start, Point3d end) {
301: this .pickShape = (PickShape) new PickSegment(start, end);
302: this .start = start;
303: userDefineShape = true;
304: }
305:
306: /** Sets the pick shape to a capped PickCylinder
307: * @param start The start of axis of the cylinder
308: * @param end The end of the axis of the cylinder
309: * @param radius The radius of the cylinder
310: */
311: public void setShapeCylinderSegment(Point3d start, Point3d end,
312: double radius) {
313: this .pickShape = (PickShape) new PickCylinderSegment(start,
314: end, radius);
315: this .start = start;
316: userDefineShape = true;
317: }
318:
319: /** Sets the pick shape to an infinite PickCylinder.
320: * @param start The start of axis of the cylinder
321: * @param dir The direction of the axis of the cylinder
322: * @param radius The radius of the cylinder
323: */
324: public void setShapeCylinderRay(Point3d start, Vector3d dir,
325: double radius) {
326: this .pickShape = (PickShape) new PickCylinderRay(start, dir,
327: radius);
328: this .start = start;
329: userDefineShape = true;
330: }
331:
332: /** Sets the pick shape to a capped PickCone
333: * @param start The start of axis of the cone
334: * @param end The end of the axis of the cone
335: * @param angle The angle of the cone
336: */
337: public void setShapeConeSegment(Point3d start, Point3d end,
338: double angle) {
339: this .pickShape = (PickShape) new PickConeSegment(start, end,
340: angle);
341: this .start = start;
342: userDefineShape = true;
343: }
344:
345: /** Sets the pick shape to an infinite PickCone.
346: * @param start The start of axis of the cone
347: * @param dir The direction of the axis of the cone
348: * @param angle The angle of the cone
349: */
350: public void setShapeConeRay(Point3d start, Vector3d dir,
351: double angle) {
352: this .pickShape = (PickShape) new PickConeRay(start, dir, angle);
353: this .start = start;
354: userDefineShape = true;
355: }
356:
357: /** Returns the PickShape for this object. */
358: public PickShape getPickShape() {
359: return pickShape;
360: }
361:
362: /** Returns the start postion used for distance measurement. */
363: public Point3d getStartPosition() {
364: return start;
365: }
366:
367: /** Selects all the nodes that intersect the PickShape.
368: @return An array of <code>PickInfo</code> objects which will contain
369: information about the picked instances. <code>null</code> if nothing was
370: picked.
371: */
372: public PickInfo[] pickAll() {
373: PickInfo[] pickInfos = null;
374: if (pickRootBG != null) {
375: pickInfos = pickRootBG.pickAll(mode, flags, pickShape);
376: } else if (pickRootL != null) {
377: pickInfos = pickRootL.pickAll(mode, flags, pickShape);
378: }
379: return pickInfos;
380: }
381:
382: /** Select one of the nodes that intersect the PickShape
383: @return A <code>PickInfo</code> object which will contain
384: information about the picked instance. <code>null</code> if nothing
385: was picked.
386: */
387: public PickInfo pickAny() {
388: PickInfo pickInfo = null;
389: if (pickRootBG != null) {
390: pickInfo = pickRootBG.pickAny(mode, flags, pickShape);
391: } else if (pickRootL != null) {
392: pickInfo = pickRootL.pickAny(mode, flags, pickShape);
393: }
394: return pickInfo;
395: }
396:
397: /** Select all the nodes that intersect the
398: PickShape, returned sorted. The "closest" object will be returned first.
399: See note above to see how "closest" is determined.
400: <p>
401: @return An array of <code>PickInfo</code> objects which will contain
402: information
403: about the picked instances. <code>null</code> if nothing was picked.
404: */
405: public PickInfo[] pickAllSorted() {
406: PickInfo[] pickInfos = null;
407: if (pickRootBG != null) {
408: pickInfos = pickRootBG
409: .pickAllSorted(mode, flags, pickShape);
410: } else if (pickRootL != null) {
411: pickInfos = pickRootL.pickAllSorted(mode, flags, pickShape);
412: }
413: return pickInfos;
414: }
415:
416: /** Select the closest node that
417: intersects the PickShape. See note above to see how "closest" is
418: determined.
419: <p>
420: @return A <code>PickInfo</code> object which will contain
421: information about the picked instance. <code>null</code> if nothing
422: was picked.
423: */
424: public PickInfo pickClosest() {
425: // System.out.println("PickTool : pickClosest ...");
426: PickInfo pickInfo = null;
427: if (pickRootBG != null) {
428: pickInfo = pickRootBG.pickClosest(mode, flags, pickShape);
429: } else if (pickRootL != null) {
430: pickInfo = pickRootL.pickClosest(mode, flags, pickShape);
431: }
432: // System.out.println(" -- pickInfo is " + pickInfo);
433:
434: return pickInfo;
435: }
436:
437: /** Get the first node of a certain type up the SceneGraphPath
438: *@param type the type of node we are interested in
439: *@return a Node object
440: *
441: * @exception NullPointerException if pickInfo does not contain a
442: * Scenegraphpath or a picked node
443: */
444:
445: public Node getNode(PickInfo pickInfo, int type) {
446:
447: // System.out.println("pickInfo is " + pickInfo);
448:
449: if (pickInfo == null) {
450: return null;
451: }
452:
453: SceneGraphPath sgp = pickInfo.getSceneGraphPath();
454: Node pickedNode = pickInfo.getNode();
455: // System.out.println("sgp = " + sgp + " pickedNode = " + pickedNode);
456:
457: /*
458: * Do not check for null for pickNode and sgp.
459: * Will throw NPE if pickedNode or sgp isn't set in pickInfo
460: */
461:
462: if ((pickedNode instanceof Shape3D)
463: && ((type & TYPE_SHAPE3D) != 0)) {
464: if (debug)
465: System.out.println("Shape3D found");
466: return pickedNode;
467: } else if ((pickedNode instanceof Morph)
468: && ((type & TYPE_MORPH) != 0)) {
469: if (debug)
470: System.out.println("Morph found");
471: return pickedNode;
472: } else {
473: for (int j = sgp.nodeCount() - 1; j >= 0; j--) {
474: Node pNode = sgp.getNode(j);
475: if (debug)
476: System.out.println("looking at node " + pNode);
477:
478: if ((pNode instanceof Primitive)
479: && ((type & TYPE_PRIMITIVE) != 0)) {
480: if (debug)
481: System.out.println("Primitive found");
482: return pNode;
483: } else if ((pNode instanceof Link)
484: && ((type & TYPE_LINK) != 0)) {
485: if (debug)
486: System.out.println("Link found");
487: return pNode;
488: } else if ((pNode instanceof Switch)
489: && ((type & TYPE_SWITCH) != 0)) {
490: if (debug)
491: System.out.println("Switch found");
492: return pNode;
493: } else if ((pNode instanceof TransformGroup)
494: && ((type & TYPE_TRANSFORM_GROUP) != 0)) {
495: if (debug)
496: System.out.println("xform group found");
497: return pNode;
498: } else if ((pNode instanceof BranchGroup)
499: && ((type & TYPE_BRANCH_GROUP) != 0)) {
500: if (debug)
501: System.out.println("Branch group found");
502: return pNode;
503: } else if ((pNode instanceof Group)
504: && ((type & TYPE_GROUP) != 0)) {
505: if (debug)
506: System.out.println("Group found");
507: return pNode;
508: }
509: }
510: }
511: return null; // should not be reached
512: }
513:
514: } // PickTool
|