/* * 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 gnu.trove.map.hash.TLongObjectHashMap; import gnu.trove.iterator.TLongObjectIterator; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.jcae.geometry.BoundingBox; import vtk.vtkActor; import vtk.vtkActorCollection; import vtk.vtkCanvas; import vtk.vtkHardwareSelector; import vtk.vtkIdTypeArray; import vtk.vtkPlaneCollection; import vtk.vtkProp; import vtk.vtkSelection; import vtk.vtkSelectionNode; /** * This class is used to make picking on a tree node. It permit to keep the association * between a node and his actor with a map (idActorToNode). * @author ibarz */ public class Scene implements AbstractNode.ActorListener { private final static Logger LOGGER = Logger.getLogger(Scene.class.getName()); private final TLongObjectHashMap<AbstractNode> idActorToNode = new TLongObjectHashMap<AbstractNode>(); private boolean actorFiltering = true; private boolean checkColorDepth = Boolean.parseBoolean( System.getProperty("org.jcae.vtk.checkColorDepth", "true")); /** * Store the previous pickability of the scene. * Boolean.TRUE means the node is pickable * Boolean.FALSE means the node is not pickable */ private Map<AbstractNode, Boolean> pickBackup; public void addNode(AbstractNode node) { node.addActorListener(this); } public void removeNode(AbstractNode node) { node.removeActorListener(this); } @Override public void actorCreated(AbstractNode node, vtkActor actor) { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Create actor id="+actor.GetVTKId()+" hashcode="+Integer.toHexString(actor.hashCode())); idActorToNode.put(actor.GetVTKId(), node); } @Override public void actorDeleted(AbstractNode node, vtkActor actor) { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Delete actor id="+actor.GetVTKId()+" hashcode="+Integer.toHexString(actor.hashCode())); idActorToNode.remove(actor.GetVTKId()); } private Collection<AbstractNode> getNodes() { //there may be more than one actor by node (selection actor) so we need //a set to remove duplicate entries. HashSet<AbstractNode> nodes = new HashSet<AbstractNode>(idActorToNode.size()); TLongObjectIterator<AbstractNode> it = idActorToNode.iterator(); while(it.hasNext()) { it.advance(); nodes.add(it.value()); } return nodes; } public void setPickable(boolean pickable) { Collection<AbstractNode> nodes = getNodes(); if (pickable) { for (AbstractNode node : getNodes()) { Boolean p = pickBackup == null ? null : pickBackup.get(node); node.setPickable( p == null ? pickable : p); } } else { pickBackup = new HashMap<AbstractNode, Boolean>(nodes.size()); for (AbstractNode node : nodes) { pickBackup.put(node, node.isPickable()); node.setPickable(false); } } } public void setActorFiltering(boolean actorFiltering) { this.actorFiltering = actorFiltering; } /** * Warning : If you are making highlight with offset (by default this is down) then the selection will not take in * case the highlighted objects because the z-buffer is not cleaned and so the normal geometry * will not be drawned for selection because the highlighted geometry is nearest of the camera * due to the offset. If you want bypass this you have to take care of the highlighted objects : * _ draw them in rendering selection i.e. make them pickable. * _ find the initial geometry corresponding to the selected highlighted object. * @param pickContext */ public void select(PickContext pickContext) { if (pickContext.onlyVisible()) selectVisibleNodes(pickContext); else selectAllNodes(pickContext); } /** * Fast selection based on bounding box intersection. * * @param <T> LeafNode derived class which implements BoundedNode * @param pickContext * @param candidates list of T instances to check */ public static <T extends LeafNode & BoundedNode> void selectIntersectedNodes(PickContext pickContext, Iterable<T> candidates) { for (T leaf : candidates) { if (pickContext.intersect(leaf.getBoundingBox())) pickContext.addToSelectedNodes(leaf); } } private void selectVisibleNodes(PickContext pickContext) { vtkCanvas canvas = pickContext.getCanvas(); int [] firstPoint = pickContext.getPressPosition(); int [] secondPoint = pickContext.getReleasePosition(); if (checkColorDepth) { int cbs = canvas.getColorModel().getPixelSize(); checkColorDepth = false; if (cbs < 24) throw new RuntimeException(){ //set localized message for better report in netbeans @Override public String getLocalizedMessage() { return "Color depth is lower than 24 bits, picking does not work"; } }; } int[] pickableActorBackup = null; if (actorFiltering) { vtkActorCollection actors = canvas.GetRenderer().GetActors(); pickableActorBackup = new int[actors.GetNumberOfItems()]; actors.InitTraversal(); int j = 0; for (vtkActor actor; (actor = actors.GetNextActor()) != null; ++j) { pickableActorBackup[j] = actor.GetPickable(); if (pickableActorBackup[j] == 0) { continue; } double[] bounds = actor.GetBounds(); BoundingBox box = new BoundingBox(); box.setLower(bounds[0], bounds[2], bounds[4]); box.setUpper(bounds[1], bounds[3], bounds[5]); if (!pickContext.intersect(box)) actor.PickableOff(); } } vtkHardwareSelector selector = new vtkHardwareSelector(); selector.SetRenderer(canvas.GetRenderer()); int xMin = Math.min(firstPoint[0], secondPoint[0]); int xMax = Math.max(firstPoint[0], secondPoint[0]); int yMin = Math.min(firstPoint[1], secondPoint[1]); int yMax = Math.max(firstPoint[1], secondPoint[1]); selector.SetArea(xMin, yMin, xMax, yMax); selector.SetFieldAssociation(1); canvas.lock(); vtkSelection selection = selector.Select(); canvas.unlock(); if(Boolean.getBoolean("sun.java2d.opengl")) { //Bug of VTK or Java ? If java3d opengl is enabled the //vtkHardwareSelector blank the canvas so we need to force a refresh. //UpdateLight for the refresh canvas.UpdateLight(); canvas.Render(); } if (actorFiltering) { vtkActorCollection actors = canvas.GetRenderer().GetActors(); actors.InitTraversal(); int j = 0; for (vtkActor actor; (actor = actors.GetNextActor()) != null; ++j) { actor.SetPickable(pickableActorBackup[j]); } } // Find the ID Selection of the actor for (int i = 0; i < selection.GetNumberOfNodes(); ++i) { vtkSelectionNode child = selection.GetNode(i); vtkProp prop = (vtkProp) child.GetProperties().Get(child.PROP()); if (prop != null) { AbstractNode node = idActorToNode.get(prop.GetVTKId()); if (node != null) { vtkIdTypeArray ids = (vtkIdTypeArray) child.GetSelectionList(); child = null; int[] values = Utils.getValues(ids); if(pickContext.isOneCell() && values.length > 1) values = new int[]{values[0]}; node.setCellSelection(pickContext, values); LOGGER.finest("Actor picked id: "+prop.GetVTKId()); LOGGER.finest("Picked node: "+node); if(pickContext.isOneCell() && values.length > 0) break; } } } } private void selectAllNodes(PickContext pickContext) { vtkCanvas canvas = pickContext.getCanvas(); vtkActorCollection actors = canvas.GetRenderer().GetActors(); actors.InitTraversal(); for (vtkActor actor; (actor = actors.GetNextActor()) != null; ) { if (actor.GetPickable() == 0) continue; double[] bounds = actor.GetBounds(); BoundingBox box = new BoundingBox(); box.setLower(bounds[0], bounds[2], bounds[4]); box.setUpper(bounds[1], bounds[3], bounds[5]); if (pickContext.intersect(box)) { AbstractNode node = idActorToNode.get(actor.GetVTKId()); if (node instanceof LeafNode) pickContext.addToSelectedNodes((LeafNode) node); else { for (LeafNode leaf : node.getLeaves()) { LeafNode.DataProvider leafProvider = leaf.getDataProvider(); leafProvider.load(); leaf.createData(leafProvider); bounds = leaf.data.GetBounds(); box.setLower(bounds[0], bounds[2], bounds[4]); box.setUpper(bounds[1], bounds[3], bounds[5]); leaf.deleteData(); leafProvider.unLoad(); if (pickContext.intersect(box)) pickContext.addToSelectedNodes(leaf); } } } } } public void setClippingPlanes(vtkPlaneCollection planes) { AbstractNode[] nodes = new AbstractNode[idActorToNode.size()]; idActorToNode.values(nodes); for (AbstractNode node : nodes) node.getActor().GetMapper().SetClippingPlanes(planes); } }