/******************************************************************************* * 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 javax.vecmath.Vector3d; 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.vtkProp3D; 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; zoomFactor = Math.pow(1.02, (y - lastY)); cam.Dolly(zoomFactor); if (cam.GetParallelProjection() == 1) { updateParallelScale(); } 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())); cam.Dolly(zoomFactor); updateParallelScale(); resetCameraClippingRange(); panel.refresh(); return true; } private void updateParallelScale() { // Make height of parallel projection match the distance double distance = cam.GetDistance(); double angle = cam.GetViewAngle(); double scale = Math.tan(Math.toRadians(angle / 2)) * distance; cam.SetParallelScale(scale); } 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) { double[] proj = cam.GetDirectionOfProjection(); if (Math.abs(proj[1] * upDirection[2] - proj[2] * upDirection[1]) < 1e-6 && Math.abs(proj[2] * upDirection[0] - proj[0] * upDirection[2]) < 1e-6 && Math.abs(proj[0] * upDirection[1] - proj[1] * upDirection[0]) < 1e-6) cam.SetViewUp(upDirection[1], upDirection[2], upDirection[0]); else cam.SetViewUp(upDirection); } cam.OrthogonalizeViewUp(); resetCameraClippingRange(); // panel.UpdateLight(); UnLock(); } public void fitToView(Collection props) { if (props.isEmpty()) return; double[] bounds = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE }, b = new double[6]; for (vtkProp3D prop : props) { prop.GetBounds(b); for (int i = 0; i < 6; i+=2) bounds[i] = Math.min(bounds[i], b[i]); for (int i = 1; i < 6; i+=2) bounds[i] = Math.max(bounds[i], b[i]); } fitToView(bounds); } public void fitToView(double[] bounds) { Vector3d center = new Vector3d((bounds[0] + bounds[1])/2, (bounds[2] + bounds[3])/2, (bounds[4] + bounds[5])/2); Vector3d viewDir = new Vector3d(cam.GetDirectionOfProjection()); Vector3d upDir = new Vector3d(cam.GetViewUp()); viewDir.normalize(); upDir.normalize(); Vector3d sideDir = new Vector3d(); sideDir.cross(viewDir, upDir); sideDir.normalize(); double width = getBoxWidth(bounds, sideDir); double height = getBoxWidth(bounds, upDir); double depth = getBoxWidth(bounds, viewDir); int[] size = rw.GetActualSize(); double distance1 = height / 2 / Math.tan(Math.toRadians(cam.GetViewAngle()) / 2); double distance2 = distance1 * (width / size[0] / (height / size[1])); double distance = Math.max(distance1, distance2) + depth / 2; viewDir.scale(-distance); viewDir.add(center); cam.SetPosition(viewDir.x, viewDir.y, viewDir.z); focus(center.x, center.y, center.z); if (cam.GetParallelProjection() == 1) { cam.SetParallelScale(Math.max(height, width * size[1] / size[0]) / 2); } } private static double getBoxWidth(double[] bounds, Vector3d dir) { double dx = bounds[1] - bounds[0]; double dy = bounds[3] - bounds[2]; double dz = bounds[5] - bounds[4]; return Math.abs(dx * dir.x) + Math.abs(dy * dir.y) + Math.abs(dz * dir.z); } public void setViewDir(Vector3d direction) { Vector3d focal = new Vector3d(cam.GetFocalPoint()); Vector3d pos = new Vector3d(cam.GetPosition()); Vector3d dir = new Vector3d(pos); dir.sub(focal); double distance = dir.length(); dir.scaleAdd(distance, direction, focal); cam.SetPosition(dir.x, dir.y, dir.z); focus(focal.x, focal.y, focal.z); //panel.UpdateLight(); panel.refresh(); } }