1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g3d.vtk.swt;
14 import java.awt.event.InputEvent;
15 import java.awt.event.MouseEvent;
16 import java.awt.event.MouseWheelEvent;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
21 import javax.vecmath.Vector3d;
23 import org.eclipse.jface.viewers.ISelection;
24 import org.eclipse.jface.viewers.ISelectionChangedListener;
25 import org.eclipse.jface.viewers.ISelectionProvider;
26 import org.eclipse.jface.viewers.SelectionChangedEvent;
27 import org.eclipse.jface.viewers.StructuredSelection;
28 import org.eclipse.swt.widgets.Display;
29 import org.simantics.g3d.tools.AdaptationUtils;
34 import vtk.vtkRenderWindow;
35 import vtk.vtkRenderer;
37 public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelectionProvider {
39 protected vtkRenderer ren;
42 protected vtkRenderWindow rw;
43 protected vtkCamera cam;
44 protected int InteractionMode = 1;
46 protected double activeRate = 5.0;
47 protected double passiveRate = 0.01;
48 protected boolean doNotRotate = true;
49 private double[] upDirection = new double[] { 0, 1, 0 };
51 public vtkCameraAndSelectorAction(InteractiveVtkComposite panel) {
53 this.ren = panel.getRenderer();
54 this.rw = panel.getRenderWindow();
55 this.cam = ren.GetActiveCamera();
58 public void setUpDirection(double[] upDirection) {
59 this.upDirection = upDirection;
66 public void UnLock() {
70 public void InteractionModeRotate() {
71 this.InteractionMode = 1;
74 public void InteractionModeTranslate() {
75 this.InteractionMode = 2;
78 public void InteractionModeZoom() {
79 this.InteractionMode = 3;
82 public void resetCameraClippingRange() {
84 ren.ResetCameraClippingRange();
88 public void resetCamera() {
94 public boolean mousePressed(MouseEvent e) {
96 if (ren.VisibleActorCount() == 0)
98 rw.SetDesiredUpdateRate(activeRate);
101 if ((e.getModifiers() == InputEvent.BUTTON2_MASK)
102 || (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK))) {
103 InteractionModeTranslate();
104 } else if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
105 InteractionModeZoom();
107 InteractionModeRotate();
112 public boolean mouseReleased(MouseEvent e) {
113 rw.SetDesiredUpdateRate(passiveRate);
117 public boolean mouseDragged(MouseEvent e) {
118 if (ren.VisibleActorCount() == 0)
123 if (this.InteractionMode == 1) {
124 cam.Elevation(clampElevationDelta(y - lastY));
126 cam.SetViewUp(upDirection);
127 cam.Azimuth(lastX - x);
128 cam.OrthogonalizeViewUp();
129 resetCameraClippingRange();
130 // panel.UpdateLight();
133 if (this.InteractionMode == 2) {
139 // get the current focal point and position
140 FPoint = cam.GetFocalPoint();
141 PPoint = cam.GetPosition();
143 // calculate the focal depth since we'll be using it a lot
144 ren.SetWorldPoint(FPoint[0], FPoint[1], FPoint[2], 1.0);
145 ren.WorldToDisplay();
146 APoint = ren.GetDisplayPoint();
148 // int[] size = rw.GetSize();
149 APoint[0] -= x - lastX;
150 APoint[1] += y - lastY;
151 ren.SetDisplayPoint(APoint);
152 ren.DisplayToWorld();
153 RPoint = ren.GetWorldPoint();
155 if (RPoint[3] != 0.0) {
156 RPoint[0] = RPoint[0] / RPoint[3];
157 RPoint[1] = RPoint[1] / RPoint[3];
158 RPoint[2] = RPoint[2] / RPoint[3];
162 * Compute a translation vector, moving everything 1/2 the distance to the
163 * cursor. (Arbitrary scale factor)
165 cam.SetFocalPoint(RPoint);
166 cam.SetPosition((RPoint[0] - FPoint[0]) + PPoint[0], (RPoint[1] - FPoint[1]) + PPoint[1],
167 (RPoint[2] - FPoint[2]) + PPoint[2]);
168 resetCameraClippingRange();
171 if (this.InteractionMode == 3) {
173 zoomFactor = Math.pow(1.02, (y - lastY));
174 cam.Dolly(zoomFactor);
176 if (cam.GetParallelProjection() == 1) {
177 updateParallelScale();
179 resetCameraClippingRange();
187 private static double dot(double[] a, double[] b) {
188 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
191 /** Calculate change in elevation, clamped to vertical directions. */
192 private double clampElevationDelta(double elevationDelta) {
194 return elevationDelta;
196 double[] direction = cam.GetDirectionOfProjection();
197 double d = Math.min(1.0, Math.max(-1.0, dot(direction, upDirection)));
198 double elevation = Math.toDegrees(Math.acos(d)) + elevationDelta;
200 elevationDelta -= elevation - 1e-5;
201 else if (elevation > 180)
202 elevationDelta -= elevation - 180 + 1e-5;
203 return elevationDelta;
207 public boolean mouseWheelMoved(MouseWheelEvent e) {
209 // double clippingRange[];
210 zoomFactor = Math.pow(1.02, (e.getWheelRotation()));
211 cam.Dolly(zoomFactor);
212 updateParallelScale();
213 resetCameraClippingRange();
218 private void updateParallelScale() {
219 // Make height of parallel projection match the distance
220 double distance = cam.GetDistance();
221 double angle = cam.GetViewAngle();
222 double scale = Math.tan(Math.toRadians(angle / 2)) * distance;
223 cam.SetParallelScale(scale);
226 protected List<vtkProp> selectActors = new ArrayList<vtkProp>();
227 protected List<vtkProp> hoverActor = new ArrayList<vtkProp>();
230 public boolean mouseClicked(MouseEvent e) {
231 if (!panel.getComponent().isFocusControl())
233 if (e.getButton() != MouseEvent.BUTTON1)
235 vtkProp spick[] = panel.pick(e.getX(), e.getY());
236 if (spick != null && spick.length > 0) {
237 for (vtkProp selectActor : spick) {
238 if (!e.isControlDown()) {
239 selectActors.clear();
240 selectActors.add(selectActor);
242 if (selectActors.contains(selectActor))
243 selectActors.remove(selectActor);
245 selectActors.add(selectActor);
248 fireSelectionChanged();
249 } else if (!e.isControlDown()) {
250 selectActors.clear();
251 fireSelectionChanged();
254 // if (e.getClickCount() > 1)
255 // updatePickRay(e.getX(), e.getY());
259 // private void updatePickRay(double x , double y) {
260 // Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(), x, y);
263 // System.out.println(ray.pos + " " + ray.dir);
264 // vtkPoints linePoints = new vtkPoints();
265 // linePoints.InsertPoint(0,ray.pos.x, ray.pos.y, ray.pos.z);
266 // linePoints.InsertPoint(1, ray.pos.x + ray.dir.x, ray.pos.y + ray.dir.y,
267 // ray.pos.z + ray.dir.z);
268 // vtkLine aLine = new vtkLine();
269 // aLine.GetPointIds().SetId(0, 0);
270 // aLine.GetPointIds().SetId(1, 1);
271 // vtkUnstructuredGrid aLineGrid = new vtkUnstructuredGrid();
272 // aLineGrid.Allocate(1, 1);
273 // aLineGrid.InsertNextCell(aLine.GetCellType(), aLine.GetPointIds());
274 // aLineGrid.SetPoints(linePoints);
275 // vtkDataSetMapper aLineMapper = new vtkDataSetMapper();
276 // aLineMapper.SetInput(aLineGrid);
277 // vtkActor aLineActor = new vtkActor();
278 // aLineActor.SetMapper(aLineMapper);
279 // aLineActor.GetProperty().SetDiffuseColor(.2, 1, 1);
281 // if (rayActor != null) {
282 // panel.GetRenderer().RemoveActor(rayActor);
283 // rayActor.Delete();
285 // rayActor = aLineActor;
286 // panel.GetRenderer().AddActor(rayActor);
288 // linePoints.Delete();
290 // aLineGrid.Delete();
291 // aLineMapper.Delete();
295 // private vtkActor rayActor;
298 public boolean mouseMoved(MouseEvent e) {
302 if (!panel.getComponent().isFocusControl())
304 List<vtkProp> prevHover = new ArrayList<vtkProp>();
305 prevHover.addAll(hoverActor);
307 vtkProp pick[] = panel.pick(e.getX(), e.getY());
309 for (vtkProp p : pick)
313 if (!prevHover.containsAll(hoverActor) || !hoverActor.containsAll(prevHover)) {
319 public List<vtkProp> getSelectActor() {
323 public List<vtkProp> getHoverActor() {
327 private List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
330 public void addSelectionChangedListener(ISelectionChangedListener listener) {
331 selectionListeners.add(listener);
335 public ISelection getSelection() {
336 return new StructuredSelection(selectActors);
340 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
341 selectionListeners.remove(listener);
345 public void setSelection(ISelection selection) {
346 setSelection(selection, false);
350 public void setSelection(ISelection selection, boolean fire) {
351 Collection<vtkProp> selectedProps = AdaptationUtils.adaptToCollection(selection, vtkProp.class);
353 selectActors.clear();
354 selectActors.addAll(selectedProps);
356 fireSelectionChanged();
359 protected void fireSelectionChanged() {
360 Display.getDefault().asyncExec(new Runnable() {
364 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this,
365 new StructuredSelection(selectActors));
366 for (ISelectionChangedListener l : selectionListeners) {
367 l.selectionChanged(evt);
374 private List<ISelectionChangedListener> hoverListeners = new ArrayList<ISelectionChangedListener>();
376 public void addHoverChangedListener(ISelectionChangedListener listener) {
377 hoverListeners.add(listener);
380 public ISelection getHoverSelection() {
381 return new StructuredSelection(hoverActor);
384 public void removeHoverChangedListener(ISelectionChangedListener listener) {
385 hoverListeners.remove(listener);
388 private void fireHoverChanged() {
389 Display.getDefault().asyncExec(new Runnable() {
392 StructuredSelection sel = null;
393 if (hoverActor == null)
394 sel = new StructuredSelection();
396 sel = new StructuredSelection(hoverActor);
397 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this, sel);
398 for (ISelectionChangedListener l : hoverListeners) {
399 l.selectionChanged(evt);
406 public void focus(double x, double y, double z) {
408 cam.SetFocalPoint(x, y, z);
410 double[] proj = cam.GetDirectionOfProjection();
411 if (Math.abs(proj[1] * upDirection[2] - proj[2] * upDirection[1]) < 1e-6 &&
412 Math.abs(proj[2] * upDirection[0] - proj[0] * upDirection[2]) < 1e-6 &&
413 Math.abs(proj[0] * upDirection[1] - proj[1] * upDirection[0]) < 1e-6)
414 cam.SetViewUp(upDirection[1], upDirection[2], upDirection[0]);
416 cam.SetViewUp(upDirection);
418 cam.OrthogonalizeViewUp();
419 resetCameraClippingRange();
420 // panel.UpdateLight();
424 public void fitToView(Collection<vtkProp3D> props) {
428 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];
429 for (vtkProp3D prop : props) {
431 for (int i = 0; i < 6; i+=2)
432 bounds[i] = Math.min(bounds[i], b[i]);
433 for (int i = 1; i < 6; i+=2)
434 bounds[i] = Math.max(bounds[i], b[i]);
440 public void fitToView(double[] bounds) {
441 Vector3d center = new Vector3d((bounds[0] + bounds[1])/2, (bounds[2] + bounds[3])/2, (bounds[4] + bounds[5])/2);
442 Vector3d viewDir = new Vector3d(cam.GetDirectionOfProjection());
443 Vector3d upDir = new Vector3d(cam.GetViewUp());
446 Vector3d sideDir = new Vector3d();
447 sideDir.cross(viewDir, upDir);
450 double width = getBoxWidth(bounds, sideDir);
451 double height = getBoxWidth(bounds, upDir);
452 double depth = getBoxWidth(bounds, viewDir);
454 int[] size = rw.GetActualSize();
456 double distance1 = height / 2 / Math.tan(Math.toRadians(cam.GetViewAngle()) / 2);
457 double distance2 = distance1 * (width / size[0] / (height / size[1]));
459 double distance = Math.max(distance1, distance2) + depth / 2;
461 viewDir.scale(-distance);
464 cam.SetPosition(viewDir.x, viewDir.y, viewDir.z);
465 focus(center.x, center.y, center.z);
467 if (cam.GetParallelProjection() == 1) {
468 cam.SetParallelScale(Math.max(height, width * size[1] / size[0]) / 2);
472 private static double getBoxWidth(double[] bounds, Vector3d dir) {
473 double dx = bounds[1] - bounds[0];
474 double dy = bounds[3] - bounds[2];
475 double dz = bounds[5] - bounds[4];
477 return Math.abs(dx * dir.x) + Math.abs(dy * dir.y) + Math.abs(dz * dir.z);