/******************************************************************************* * Copyright (c) 2012, 2013 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g3d.vtk.swt; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Display; import org.simantics.g3d.tools.AdaptationUtils; import vtk.vtkCamera; import vtk.vtkProp; import vtk.vtkRenderWindow; import vtk.vtkRenderer; public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelectionProvider { protected vtkRenderer ren; protected int lastX; protected int lastY; protected vtkRenderWindow rw; protected vtkCamera cam; protected int InteractionMode = 1; protected double activeRate = 5.0; protected double passiveRate = 0.01; protected boolean doNotRotate = true; private double[] upDirection = new double[] { 0, 1, 0 }; public vtkCameraAndSelectorAction(InteractiveVtkComposite panel) { super(panel); this.ren = panel.getRenderer(); this.rw = panel.getRenderWindow(); this.cam = ren.GetActiveCamera(); } public void setUpDirection(double[] upDirection) { this.upDirection = upDirection; } public void Lock() { panel.lock(); } public void UnLock() { panel.unlock(); } public void InteractionModeRotate() { this.InteractionMode = 1; } public void InteractionModeTranslate() { this.InteractionMode = 2; } public void InteractionModeZoom() { this.InteractionMode = 3; } public void resetCameraClippingRange() { Lock(); ren.ResetCameraClippingRange(); UnLock(); } public void resetCamera() { Lock(); ren.ResetCamera(); UnLock(); } public boolean mousePressed(MouseEvent e) { if (ren.VisibleActorCount() == 0) return false; rw.SetDesiredUpdateRate(activeRate); lastX = e.getX(); lastY = e.getY(); if ((e.getModifiers() == InputEvent.BUTTON2_MASK) || (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK))) { InteractionModeTranslate(); } else if (e.getModifiers() == InputEvent.BUTTON3_MASK) { InteractionModeZoom(); } else { InteractionModeRotate(); } return true; } public boolean mouseReleased(MouseEvent e) { rw.SetDesiredUpdateRate(passiveRate); return true; } public boolean mouseDragged(MouseEvent e) { if (ren.VisibleActorCount() == 0) return false; int x = e.getX(); int y = e.getY(); // rotate if (this.InteractionMode == 1) { cam.Elevation(clampElevationDelta(y - lastY)); if (doNotRotate) cam.SetViewUp(upDirection); cam.Azimuth(lastX - x); cam.OrthogonalizeViewUp(); resetCameraClippingRange(); // panel.UpdateLight(); } // translate if (this.InteractionMode == 2) { double FPoint[]; double PPoint[]; double APoint[]; double RPoint[]; // get the current focal point and position FPoint = cam.GetFocalPoint(); PPoint = cam.GetPosition(); // calculate the focal depth since we'll be using it a lot ren.SetWorldPoint(FPoint[0], FPoint[1], FPoint[2], 1.0); ren.WorldToDisplay(); APoint = ren.GetDisplayPoint(); // int[] size = rw.GetSize(); APoint[0] -= x - lastX; APoint[1] += y - lastY; ren.SetDisplayPoint(APoint); ren.DisplayToWorld(); RPoint = ren.GetWorldPoint(); if (RPoint[3] != 0.0) { RPoint[0] = RPoint[0] / RPoint[3]; RPoint[1] = RPoint[1] / RPoint[3]; RPoint[2] = RPoint[2] / RPoint[3]; } /* * Compute a translation vector, moving everything 1/2 the distance to the * cursor. (Arbitrary scale factor) */ cam.SetFocalPoint(RPoint); cam.SetPosition((RPoint[0] - FPoint[0]) + PPoint[0], (RPoint[1] - FPoint[1]) + PPoint[1], (RPoint[2] - FPoint[2]) + PPoint[2]); resetCameraClippingRange(); } // zoom if (this.InteractionMode == 3) { double zoomFactor; // double clippingRange[]; zoomFactor = Math.pow(1.02, (y - lastY)); if (cam.GetParallelProjection() == 1) { cam.SetParallelScale(cam.GetParallelScale() / zoomFactor); resetCameraClippingRange(); } else { cam.Dolly(zoomFactor); resetCameraClippingRange(); } } lastX = x; lastY = y; panel.refresh(); return true; } private static double dot(double[] a, double[] b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } /** Calculate change in elevation, clamped to vertical directions. */ private double clampElevationDelta(double elevationDelta) { if (!doNotRotate) return elevationDelta; double[] direction = cam.GetDirectionOfProjection(); double d = Math.min(1.0, Math.max(-1.0, dot(direction, upDirection))); double elevation = Math.toDegrees(Math.acos(d)) + elevationDelta; if (elevation < 0) elevationDelta -= elevation - 1e-5; else if (elevation > 180) elevationDelta -= elevation - 180 + 1e-5; return elevationDelta; } @Override public boolean mouseWheelMoved(MouseWheelEvent e) { double zoomFactor; // double clippingRange[]; zoomFactor = Math.pow(1.02, (e.getWheelRotation())); if (cam.GetParallelProjection() == 1) { cam.SetParallelScale(cam.GetParallelScale() / zoomFactor); } else { cam.Dolly(zoomFactor); resetCameraClippingRange(); } panel.refresh(); return true; } protected List selectActors = new ArrayList(); protected List hoverActor = new ArrayList(); @Override public boolean mouseClicked(MouseEvent e) { if (!panel.getComponent().isFocusControl()) return false; if (e.getButton() != MouseEvent.BUTTON1) return false; vtkProp spick[] = panel.pick(e.getX(), e.getY()); if (spick != null && spick.length > 0) { for (vtkProp selectActor : spick) { if (!e.isControlDown()) { selectActors.clear(); selectActors.add(selectActor); } else { if (selectActors.contains(selectActor)) selectActors.remove(selectActor); else selectActors.add(selectActor); } } fireSelectionChanged(); } else if (!e.isControlDown()) { selectActors.clear(); fireSelectionChanged(); } return true; // if (e.getClickCount() > 1) // updatePickRay(e.getX(), e.getY()); } // private void updatePickRay(double x , double y) { // Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(), x, y); // // // System.out.println(ray.pos + " " + ray.dir); // vtkPoints linePoints = new vtkPoints(); // linePoints.InsertPoint(0,ray.pos.x, ray.pos.y, ray.pos.z); // linePoints.InsertPoint(1, ray.pos.x + ray.dir.x, ray.pos.y + ray.dir.y, // ray.pos.z + ray.dir.z); // vtkLine aLine = new vtkLine(); // aLine.GetPointIds().SetId(0, 0); // aLine.GetPointIds().SetId(1, 1); // vtkUnstructuredGrid aLineGrid = new vtkUnstructuredGrid(); // aLineGrid.Allocate(1, 1); // aLineGrid.InsertNextCell(aLine.GetCellType(), aLine.GetPointIds()); // aLineGrid.SetPoints(linePoints); // vtkDataSetMapper aLineMapper = new vtkDataSetMapper(); // aLineMapper.SetInput(aLineGrid); // vtkActor aLineActor = new vtkActor(); // aLineActor.SetMapper(aLineMapper); // aLineActor.GetProperty().SetDiffuseColor(.2, 1, 1); // // if (rayActor != null) { // panel.GetRenderer().RemoveActor(rayActor); // rayActor.Delete(); // } // rayActor = aLineActor; // panel.GetRenderer().AddActor(rayActor); // // linePoints.Delete(); // aLine.Delete(); // aLineGrid.Delete(); // aLineMapper.Delete(); // panel.repaint(); // } // // private vtkActor rayActor; @Override public boolean mouseMoved(MouseEvent e) { lastX = e.getX(); lastY = e.getY(); if (!panel.getComponent().isFocusControl()) return false; List prevHover = new ArrayList(); prevHover.addAll(hoverActor); hoverActor.clear(); vtkProp pick[] = panel.pick(e.getX(), e.getY()); if (pick != null) { for (vtkProp p : pick) hoverActor.add(p); } if (!prevHover.containsAll(hoverActor) || !hoverActor.containsAll(prevHover)) { fireHoverChanged(); } return true; } public List getSelectActor() { return selectActors; } public List getHoverActor() { return hoverActor; } private List selectionListeners = new ArrayList(); @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { selectionListeners.add(listener); } @Override public ISelection getSelection() { return new StructuredSelection(selectActors); } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { selectionListeners.remove(listener); } @Override public void setSelection(ISelection selection) { setSelection(selection, false); } public void setSelection(ISelection selection, boolean fire) { Collection selectedProps = AdaptationUtils.adaptToCollection(selection, vtkProp.class); selectActors.clear(); selectActors.addAll(selectedProps); if (fire) fireSelectionChanged(); } protected void fireSelectionChanged() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this, new StructuredSelection(selectActors)); for (ISelectionChangedListener l : selectionListeners) { l.selectionChanged(evt); } } }); } private List hoverListeners = new ArrayList(); public void addHoverChangedListener(ISelectionChangedListener listener) { hoverListeners.add(listener); } public ISelection getHoverSelection() { return new StructuredSelection(hoverActor); } public void removeHoverChangedListener(ISelectionChangedListener listener) { hoverListeners.remove(listener); } private void fireHoverChanged() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { StructuredSelection sel = null; if (hoverActor == null) sel = new StructuredSelection(); else sel = new StructuredSelection(hoverActor); SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this, sel); for (ISelectionChangedListener l : hoverListeners) { l.selectionChanged(evt); } } }); } public void focus(double x, double y, double z) { Lock(); cam.SetFocalPoint(x, y, z); if (doNotRotate) cam.SetViewUp(upDirection); cam.OrthogonalizeViewUp(); resetCameraClippingRange(); // panel.UpdateLight(); UnLock(); } }