/* * Project Info: http://jcae.sourceforge.net * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * (C) Copyright 2008, by EADS France */ package org.jcae.vtk; import java.awt.Point; import javax.vecmath.Point3d; import javax.vecmath.Vector4d; import org.jcae.geometry.BoundingBox; import org.jcae.geometry.BoundingPolytope; /** * The FrustumPicker object defines a picker for rubber band selection. * * @author Denis Barbier */ public class FrustumPicker extends PickContext { private final Point firstPoint; private final Point secondPoint; private final Vector4d[] planes = new Vector4d[6]; private final Point3d frustumLower = new Point3d(); private final Point3d frustumUpper = new Point3d(); private final boolean oneCell; private final RayPicker rayPicker; /** * Constructor. * * @param canvas underlying canvas * @param visible if <code>true</code>, only visible objects are picked, * otherwise any pickable object can be picked * @param firstPoint mouse position when button was pressed * @param secondPoint mouse position when button was released */ public FrustumPicker(Canvas canvas, boolean visible, Point firstPoint, Point secondPoint) { super(canvas, visible); oneCell = false; this.firstPoint = firstPoint; this.secondPoint = secondPoint; canvas.lock(); double[] verts = Utils.computeVerticesFrustum(firstPoint.x, firstPoint.y, secondPoint.x, secondPoint.y, canvas.GetRenderer()); canvas.unlock(); computePlanes(verts); rayPicker = null; } /** * Sometimes picked objects are very small and selection * by a ray picker would be uncomfortable. This constructor transforms * a RayPicker object into a FrustumPicker object by enlarging pick point * by the desired number of pixels in each direction. * The create FrustrumPicker and the RayPicker share the same selected node * list. That is adding a node to the FrustrumPicker will make it available * to the underlying RayPicker. * * @param picker picker to enlarge * @param tolerance pixel tolerance */ public FrustumPicker(RayPicker picker, int tolerance) { super(picker.getCanvas(), picker.onlyVisible()); selectionNode = picker.selectionNode; oneCell = true; int [] pickPosition = picker.getPressPosition(); this.firstPoint = new Point(pickPosition[0] - tolerance, pickPosition[1] - tolerance); this.secondPoint = new Point(pickPosition[0] + tolerance, pickPosition[1] + tolerance); Canvas canvas = picker.getCanvas(); canvas.lock(); double[] verts = Utils.computeVerticesFrustum(firstPoint.x, firstPoint.y, secondPoint.x, secondPoint.y, canvas.GetRenderer()); canvas.unlock(); computePlanes(verts); rayPicker = picker; } /** * Return the ray picker used to create this frustrum picker or null if * this frustrum picker as been created without ray picker */ public RayPicker getRayPicker() { return rayPicker; } private final void computePlanes(double[] verts) { BoundingPolytope frustum = Utils.computePolytope(verts); for (int i = 0; i < planes.length; i++) planes[i] = new Vector4d(); frustum.getPlanes(planes); frustumLower.x = verts[0]; frustumLower.y = verts[1]; frustumLower.z = verts[2]; for (int i = 1; i < 8; i++) { if (frustumLower.x > verts[4*i]) frustumLower.x = verts[4*i]; if (frustumLower.y > verts[4*i+1]) frustumLower.y = verts[4*i+1]; if (frustumLower.z > verts[4*i+2]) frustumLower.z = verts[4*i+2]; } frustumUpper.x = verts[0]; frustumUpper.y = verts[1]; frustumUpper.z = verts[2]; for (int i = 1; i < 8; i++) { if (frustumUpper.x < verts[4*i]) frustumUpper.x = verts[4*i]; if (frustumUpper.y < verts[4*i+1]) frustumUpper.y = verts[4*i+1]; if (frustumUpper.z < verts[4*i+2]) frustumUpper.z = verts[4*i+2]; } } /** * Get the mouse position when button was pressed. * * @return the position of mouse when button was pressed */ @Override public final int[] getPressPosition() { return new int[] { firstPoint.x, firstPoint.y }; } /** * Get the mouse position when button was released. * * @return Get the position of mouse when button was released */ @Override public final int[] getReleasePosition() { return new int[] { secondPoint.x, secondPoint.y }; } /** * Tell whether a box intersects this picker. * * @param bbox box to be checked for * @return <code>true</code> if this box is intersected by the frustum, * <code>false</code> otherwise. */ @Override public boolean intersect(BoundingBox bbox) { Point3d lower = new Point3d(); Point3d upper = new Point3d(); bbox.getLower(lower); bbox.getUpper(upper); // Fast intersection test with bounding boxes if (upper.x < frustumLower.x || lower.x > frustumUpper.x || upper.y < frustumLower.y || lower.y > frustumUpper.y || upper.z < frustumLower.z || lower.z > frustumUpper.z) return false; // Precise intersection test with frustum planes double [] x = new double[24]; double [] extents = new double[] { lower.x, upper.x, lower.y, upper.y, lower.z, upper.z }; int cnt = 0; for (int i = 0; i < 2; i++) for (int j = 2; j < 4; j++) for (int k = 4; k < 6; k++) { x[cnt++] = extents[i]; x[cnt++] = extents[j]; x[cnt++] = extents[k]; } // ignore front and back planes for (int j = 0; j < 4; j++) { Vector4d p = planes[j]; boolean in = false; for (int i = 0; i < 8; i++) { if (p.x * x[3*i] + p.y * x[3*i+1] + p.z * x[3*i+2] + p.w < 0.0) { in = true; break; } } if (!in) return false; } return true; } @Override public boolean isOneCell() { return oneCell; } }