/* * 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.AWTEvent; import java.awt.Color; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import vtk.vtkInteractorStyleRubberBand3D; import vtk.vtkInteractorStyleTrackballCamera; import vtk.vtkPlaneCollection; import vtk.vtkRenderer; import vtk.vtkNativeLibrary; /** * * @author Julian Ibarz */ public class View extends Canvas { private final static Logger LOGGER = Logger.getLogger(View.class.getName()); protected final ArrayList<Viewable> viewables = new ArrayList<Viewable>(); protected Viewable currentViewable; private final CameraManager cameraManager = new CameraManager(this); private MouseMode mouseMode = MouseMode.POINT_SELECTION; private Point pressPosition; private Point releasePosition; private boolean interactive = true; private boolean appendSelection; private boolean SELECT_ONLY_VISIBLE = Boolean.parseBoolean( System.getProperty("org.jcae.vtk.selectOnlyVisible", "true")); static { vtkNativeLibrary.LoadAllNativeLibraries(); Utils.setVTKLogFile(new File(System.getProperty("user.home"), ".vtk.log").getPath()); VTKMemoryManager.GC.SetDebug(Boolean.getBoolean("vtk.gc.debug")); VTKMemoryManager.GC.SetAutoGarbageCollection(true); } public enum MouseMode { POINT_SELECTION, RECTANGLE_SELECTION, CLIPPING_PLANE, CHANGE_ROTATION_CENTER } public View() { try { // By default the translucent objects can be picked vtkRenderer r = GetRenderer(); Color bgCol = Color.getColor("org.jcae.vtk.color.background", Color.BLACK); r.SetBackground(bgCol.getRed() / 255.0, bgCol.getGreen() / 255.0, bgCol.getBlue() / 255.0); Method m = r.getClass().getMethod("PickTranslucentOn"); m.invoke(r); } catch (NoSuchMethodException ex) { LOGGER.log(Level.INFO, "vtkRenderer.PickTranslucentOn not available."+ " Picking on translucent actors may not work"); } catch(Exception ex) { LOGGER.log(Level.SEVERE, null, ex); } } @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); switch (e.getKeyCode()) { case KeyEvent.VK_SPACE: setMouseMode(MouseMode.RECTANGLE_SELECTION); break; case KeyEvent.VK_C: setMouseMode(MouseMode.CLIPPING_PLANE); break; case KeyEvent.VK_R: setMouseMode(MouseMode.CHANGE_ROTATION_CENTER); break; case KeyEvent.VK_CONTROL: currentViewable.appendSelection = true; break; } } @Override public void keyReleased(KeyEvent e) { super.keyReleased(e); switch (e.getKeyCode()) { case KeyEvent.VK_CONTROL: currentViewable.appendSelection = false; break; } } public MouseMode getMouseMode() { return mouseMode; } @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); pressPosition = new Point(e.getPoint()); pressPosition.y = e.getComponent().getHeight() - pressPosition.y; } @Override public void mouseReleased(MouseEvent e) { super.mouseReleased(e); if(e.getButton() != MouseEvent.BUTTON1) return; releasePosition = new Point(e.getPoint()); releasePosition.y = e.getComponent().getHeight() - releasePosition.y; switch(mouseMode) { case RECTANGLE_SELECTION: if(currentViewable != null) { PickContext context = new FrustumPicker(this, SELECT_ONLY_VISIBLE, pressPosition, releasePosition); currentViewable.performSelection(context); } setMouseMode(MouseMode.POINT_SELECTION); break; case CLIPPING_PLANE: vtkPlaneCollection planes = Utils.computeClippingPlane(this, pressPosition, releasePosition); for(Viewable viewable : getViewables()) viewable.setClippingPlanes(planes); setMouseMode(MouseMode.POINT_SELECTION); RenderSecured(); break; case POINT_SELECTION: // If the press and release positions are not close, this is // not a selection if(pressPosition == null || pressPosition.distance(releasePosition) > 5.) return; if(currentViewable != null) { if (!currentViewable.appendSelection) currentViewable.unselectAll(); PickContext context = new RayPicker(this, true, pressPosition); if (currentViewable.getPixelTolerance() != 0) context = new FrustumPicker((RayPicker) context, currentViewable.getPixelTolerance()); currentViewable.performSelection(context); } break; case CHANGE_ROTATION_CENTER: // If the press and release positions are not close, this is // not a selection if(pressPosition.distance(releasePosition) > 5.) return; cameraManager.centerRotationSelection(releasePosition); setMouseMode(MouseMode.POINT_SELECTION); break; } } public boolean isAppendSelection() { return appendSelection; } public void setAppendSelection(boolean appendSelection) { this.appendSelection = appendSelection; for(Viewable viewable : viewables) viewable.setAppendSelection(appendSelection); } public CameraManager getCameraManager() { return cameraManager; } public boolean isInteractive() { return interactive; } public void setInteractive(boolean interactive) { this.interactive = interactive; } @Override protected void processEvent(AWTEvent e) { if(interactive) super.processEvent(e); } public void remove(Viewable interactor) { LOGGER.fine("Remove an interactor, left " + (viewables.size() - 1) + "viewables"); viewables.remove(interactor); interactor.removeCanvas(this); if(viewables.size() != 0) setCurrentViewable(viewables.get(0)); else setCurrentViewable(null); } public void setMouseMode(MouseMode mode) { boolean actualIsPoint = mouseMode == MouseMode.POINT_SELECTION || mouseMode == MouseMode.CHANGE_ROTATION_CENTER; boolean futureIsPoint = mode == MouseMode.POINT_SELECTION || mode == MouseMode.CHANGE_ROTATION_CENTER; vtkInteractorStyleTrackballCamera i; if (actualIsPoint && !futureIsPoint) { vtkInteractorStyleRubberBand3D it = new vtkInteractorStyleRubberBand3D(); getIren().SetInteractorStyle(it); } else if (!actualIsPoint && futureIsPoint) { vtkInteractorStyleTrackballCamera it = new vtkInteractorStyleTrackballCamera(); getIren().SetInteractorStyle(it); } mouseMode = mode; } /** * Add a viewable to this view and make his canvas the view canvas and make it current. If viewable implements * PropertyChangeListener it will be notified when the global node selection * changes. * * @param interactor * @param viewable */ public void add(Viewable interactor) { // If already added return if(viewables.contains(interactor)) return; viewables.add(interactor); interactor.setAppendSelection(appendSelection); interactor.addCanvas(this); // Set it the current viewable setCurrentViewable(interactor); // TODO : know why the relative axis disappear, this is a hack to make visible the axes boolean isVisible = cameraManager.isRelativeAxisVisible(); if(isVisible) { cameraManager.setRelativeAxisVisible(false); cameraManager.setRelativeAxisVisible(true); } } /** * @param viewable */ public void setCurrentViewable(Viewable viewable) { if(currentViewable != null) currentViewable.setPickable(false); currentViewable = viewable; if(currentViewable != null) currentViewable.setPickable(true); } public Viewable getCurrentViewable() { return currentViewable; } public List<Viewable> getViewables() { return viewables; } /** * Notify Viewables that they should no longer concider as being displayed * in this view. This must called before destroying a view. */ public void detachAllViewables() { for(Viewable v:viewables) v.removeCanvas(this); } }