]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java
Fix camera focus method to handle special case of vertical direction.
[simantics/3d.git] / org.simantics.g3d.vtk / src / org / simantics / g3d / vtk / swt / vtkCameraAndSelectorAction.java
1 /*******************************************************************************
2  * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g3d.vtk.swt;
13
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;
20
21 import org.eclipse.jface.viewers.ISelection;
22 import org.eclipse.jface.viewers.ISelectionChangedListener;
23 import org.eclipse.jface.viewers.ISelectionProvider;
24 import org.eclipse.jface.viewers.SelectionChangedEvent;
25 import org.eclipse.jface.viewers.StructuredSelection;
26 import org.eclipse.swt.widgets.Display;
27 import org.simantics.g3d.tools.AdaptationUtils;
28
29 import vtk.vtkCamera;
30 import vtk.vtkProp;
31 import vtk.vtkRenderWindow;
32 import vtk.vtkRenderer;
33
34 public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelectionProvider {
35
36         protected vtkRenderer ren;
37         protected int lastX;
38         protected int lastY;
39         protected vtkRenderWindow rw;
40         protected vtkCamera cam;
41         protected int InteractionMode = 1;
42
43         protected double activeRate = 5.0;
44         protected double passiveRate = 0.01;
45         protected boolean doNotRotate = true;
46         private double[] upDirection = new double[] { 0, 1, 0 };
47
48         public vtkCameraAndSelectorAction(InteractiveVtkComposite panel) {
49                 super(panel);
50                 this.ren = panel.getRenderer();
51                 this.rw = panel.getRenderWindow();
52                 this.cam = ren.GetActiveCamera();
53         }
54         
55         public void setUpDirection(double[] upDirection) {
56                 this.upDirection = upDirection;
57         }
58
59         public void Lock() {
60                 panel.lock();
61         }
62
63         public void UnLock() {
64                 panel.unlock();
65         }
66
67         public void InteractionModeRotate() {
68                 this.InteractionMode = 1;
69         }
70
71         public void InteractionModeTranslate() {
72                 this.InteractionMode = 2;
73         }
74
75         public void InteractionModeZoom() {
76                 this.InteractionMode = 3;
77         }
78
79         public void resetCameraClippingRange() {
80                 Lock();
81                 ren.ResetCameraClippingRange();
82                 UnLock();
83         }
84
85         public void resetCamera() {
86                 Lock();
87                 ren.ResetCamera();
88                 UnLock();
89         }
90
91         public boolean mousePressed(MouseEvent e) {
92
93                 if (ren.VisibleActorCount() == 0)
94                         return false;
95                 rw.SetDesiredUpdateRate(activeRate);
96                 lastX = e.getX();
97                 lastY = e.getY();
98                 if ((e.getModifiers() == InputEvent.BUTTON2_MASK)
99                                 || (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK))) {
100                         InteractionModeTranslate();
101                 } else if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
102                         InteractionModeZoom();
103                 } else {
104                         InteractionModeRotate();
105                 }
106                 return true;
107         }
108
109         public boolean mouseReleased(MouseEvent e) {
110                 rw.SetDesiredUpdateRate(passiveRate);
111                 return true;
112         }
113
114         public boolean mouseDragged(MouseEvent e) {
115                 if (ren.VisibleActorCount() == 0)
116                         return false;
117                 int x = e.getX();
118                 int y = e.getY();
119                 // rotate
120                 if (this.InteractionMode == 1) {
121                         cam.Elevation(clampElevationDelta(y - lastY));
122                         if (doNotRotate)
123                                 cam.SetViewUp(upDirection);
124                         cam.Azimuth(lastX - x);
125                         cam.OrthogonalizeViewUp();
126                         resetCameraClippingRange();
127                         // panel.UpdateLight();
128                 }
129                 // translate
130                 if (this.InteractionMode == 2) {
131                         double FPoint[];
132                         double PPoint[];
133                         double APoint[];
134                         double RPoint[];
135
136                         // get the current focal point and position
137                         FPoint = cam.GetFocalPoint();
138                         PPoint = cam.GetPosition();
139
140                         // calculate the focal depth since we'll be using it a lot
141                         ren.SetWorldPoint(FPoint[0], FPoint[1], FPoint[2], 1.0);
142                         ren.WorldToDisplay();
143                         APoint = ren.GetDisplayPoint();
144                         
145 //                      int[] size = rw.GetSize();
146                         APoint[0] -= x - lastX;
147                         APoint[1] += y - lastY;
148                         ren.SetDisplayPoint(APoint);
149                         ren.DisplayToWorld();
150                         RPoint = ren.GetWorldPoint();
151                         
152                         if (RPoint[3] != 0.0) {
153                                 RPoint[0] = RPoint[0] / RPoint[3];
154                                 RPoint[1] = RPoint[1] / RPoint[3];
155                                 RPoint[2] = RPoint[2] / RPoint[3];
156                         }
157
158                         /*
159                          * Compute a translation vector, moving everything 1/2 the distance to the
160                          * cursor. (Arbitrary scale factor)
161                          */
162                         cam.SetFocalPoint(RPoint);
163                         cam.SetPosition((RPoint[0] - FPoint[0]) + PPoint[0], (RPoint[1] - FPoint[1]) + PPoint[1],
164                                         (RPoint[2] - FPoint[2]) + PPoint[2]);
165                         resetCameraClippingRange();
166                 }
167                 // zoom
168                 if (this.InteractionMode == 3) {
169                         double zoomFactor;
170                         // double clippingRange[];
171
172                         zoomFactor = Math.pow(1.02, (y - lastY));
173                         if (cam.GetParallelProjection() == 1) {
174                                 cam.SetParallelScale(cam.GetParallelScale() / zoomFactor);
175                                 resetCameraClippingRange();
176                         } else {
177                                 cam.Dolly(zoomFactor);
178                                 resetCameraClippingRange();
179                         }
180                 }
181                 lastX = x;
182                 lastY = y;
183                 panel.refresh();
184                 return true;
185         }
186
187         private static double dot(double[] a, double[] b) {
188                 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
189         }
190         
191         /** Calculate change in elevation, clamped to vertical directions. */
192         private double clampElevationDelta(double elevationDelta) {
193                 if (!doNotRotate)
194                         return elevationDelta;
195                 
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;
199                 if (elevation < 0)
200                         elevationDelta -= elevation - 1e-5;
201                 else if (elevation > 180)
202                         elevationDelta -= elevation - 180 + 1e-5;
203                 return elevationDelta;
204         }
205
206         @Override
207         public boolean mouseWheelMoved(MouseWheelEvent e) {
208                 double zoomFactor;
209                 // double clippingRange[];
210                 zoomFactor = Math.pow(1.02, (e.getWheelRotation()));
211                 if (cam.GetParallelProjection() == 1) {
212                         cam.SetParallelScale(cam.GetParallelScale() / zoomFactor);
213                 } else {
214                         cam.Dolly(zoomFactor);
215                         resetCameraClippingRange();
216                 }
217                 panel.refresh();
218                 return true;
219         }
220
221         protected List<vtkProp> selectActors = new ArrayList<vtkProp>();
222         protected List<vtkProp> hoverActor = new ArrayList<vtkProp>();
223
224         @Override
225         public boolean mouseClicked(MouseEvent e) {
226                 if (!panel.getComponent().isFocusControl())
227                         return false;
228                 if (e.getButton() != MouseEvent.BUTTON1)
229                         return false;
230                 vtkProp spick[] = panel.pick(e.getX(), e.getY());
231                 if (spick != null && spick.length > 0) {
232                         for (vtkProp selectActor : spick) {
233                                 if (!e.isControlDown()) {
234                                         selectActors.clear();
235                                         selectActors.add(selectActor);
236                                 } else {
237                                         if (selectActors.contains(selectActor))
238                                                 selectActors.remove(selectActor);
239                                         else
240                                                 selectActors.add(selectActor);
241                                 }
242                         }
243                         fireSelectionChanged();
244                 } else if (!e.isControlDown()) {
245                         selectActors.clear();
246                         fireSelectionChanged();
247                 }
248                 return true;
249                 // if (e.getClickCount() > 1)
250                 // updatePickRay(e.getX(), e.getY());
251
252         }
253
254         // private void updatePickRay(double x , double y) {
255         // Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(), x, y);
256         //
257         //
258         // System.out.println(ray.pos + " " + ray.dir);
259         // vtkPoints linePoints = new vtkPoints();
260         // linePoints.InsertPoint(0,ray.pos.x, ray.pos.y, ray.pos.z);
261         // linePoints.InsertPoint(1, ray.pos.x + ray.dir.x, ray.pos.y + ray.dir.y,
262         // ray.pos.z + ray.dir.z);
263         // vtkLine aLine = new vtkLine();
264         // aLine.GetPointIds().SetId(0, 0);
265         // aLine.GetPointIds().SetId(1, 1);
266         // vtkUnstructuredGrid aLineGrid = new vtkUnstructuredGrid();
267         // aLineGrid.Allocate(1, 1);
268         // aLineGrid.InsertNextCell(aLine.GetCellType(), aLine.GetPointIds());
269         // aLineGrid.SetPoints(linePoints);
270         // vtkDataSetMapper aLineMapper = new vtkDataSetMapper();
271         // aLineMapper.SetInput(aLineGrid);
272         // vtkActor aLineActor = new vtkActor();
273         // aLineActor.SetMapper(aLineMapper);
274         // aLineActor.GetProperty().SetDiffuseColor(.2, 1, 1);
275         //
276         // if (rayActor != null) {
277         // panel.GetRenderer().RemoveActor(rayActor);
278         // rayActor.Delete();
279         // }
280         // rayActor = aLineActor;
281         // panel.GetRenderer().AddActor(rayActor);
282         //
283         // linePoints.Delete();
284         // aLine.Delete();
285         // aLineGrid.Delete();
286         // aLineMapper.Delete();
287         // panel.repaint();
288         // }
289         //
290         // private vtkActor rayActor;
291
292         @Override
293         public boolean mouseMoved(MouseEvent e) {
294                 lastX = e.getX();
295                 lastY = e.getY();
296
297                 if (!panel.getComponent().isFocusControl())
298                         return false;
299                 List<vtkProp> prevHover = new ArrayList<vtkProp>();
300                 prevHover.addAll(hoverActor);
301                 hoverActor.clear();
302                 vtkProp pick[] = panel.pick(e.getX(), e.getY());
303                 if (pick != null) {
304                         for (vtkProp p : pick)
305                                 hoverActor.add(p);
306                 }
307
308                 if (!prevHover.containsAll(hoverActor) || !hoverActor.containsAll(prevHover)) {
309                         fireHoverChanged();
310                 }
311                 return true;
312         }
313
314         public List<vtkProp> getSelectActor() {
315                 return selectActors;
316         }
317
318         public List<vtkProp> getHoverActor() {
319                 return hoverActor;
320         }
321
322         private List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
323
324         @Override
325         public void addSelectionChangedListener(ISelectionChangedListener listener) {
326                 selectionListeners.add(listener);
327         }
328
329         @Override
330         public ISelection getSelection() {
331                 return new StructuredSelection(selectActors);
332         }
333
334         @Override
335         public void removeSelectionChangedListener(ISelectionChangedListener listener) {
336                 selectionListeners.remove(listener);
337         }
338
339         @Override
340         public void setSelection(ISelection selection) {
341                 setSelection(selection, false);
342
343         }
344
345         public void setSelection(ISelection selection, boolean fire) {
346                 Collection<vtkProp> selectedProps = AdaptationUtils.adaptToCollection(selection, vtkProp.class);
347
348                 selectActors.clear();
349                 selectActors.addAll(selectedProps);
350                 if (fire)
351                         fireSelectionChanged();
352         }
353
354         protected void fireSelectionChanged() {
355                 Display.getDefault().asyncExec(new Runnable() {
356                         @Override
357                         public void run() {
358
359                                 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this,
360                                                 new StructuredSelection(selectActors));
361                                 for (ISelectionChangedListener l : selectionListeners) {
362                                         l.selectionChanged(evt);
363                                 }
364
365                         }
366                 });
367         }
368
369         private List<ISelectionChangedListener> hoverListeners = new ArrayList<ISelectionChangedListener>();
370
371         public void addHoverChangedListener(ISelectionChangedListener listener) {
372                 hoverListeners.add(listener);
373         }
374
375         public ISelection getHoverSelection() {
376                 return new StructuredSelection(hoverActor);
377         }
378
379         public void removeHoverChangedListener(ISelectionChangedListener listener) {
380                 hoverListeners.remove(listener);
381         }
382
383         private void fireHoverChanged() {
384                 Display.getDefault().asyncExec(new Runnable() {
385                         @Override
386                         public void run() {
387                                 StructuredSelection sel = null;
388                                 if (hoverActor == null)
389                                         sel = new StructuredSelection();
390                                 else
391                                         sel = new StructuredSelection(hoverActor);
392                                 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this, sel);
393                                 for (ISelectionChangedListener l : hoverListeners) {
394                                         l.selectionChanged(evt);
395                                 }
396
397                         }
398                 });
399         }
400
401         public void focus(double x, double y, double z) {
402                 Lock();
403                 cam.SetFocalPoint(x, y, z);
404                 if (doNotRotate) {
405                         double[] proj = cam.GetDirectionOfProjection();
406                         if (Math.abs(proj[1] * upDirection[2] - proj[2] * upDirection[1]) < 1e-6 &&
407                                 Math.abs(proj[2] * upDirection[0] - proj[0] * upDirection[2]) < 1e-6 &&
408                                 Math.abs(proj[0] * upDirection[1] - proj[1] * upDirection[0]) < 1e-6)
409                                 cam.SetViewUp(upDirection[1], upDirection[2], upDirection[0]);
410                         else
411                                 cam.SetViewUp(upDirection);
412                 }
413                 cam.OrthogonalizeViewUp();
414                 resetCameraClippingRange();
415                 // panel.UpdateLight();
416                 UnLock();
417         }
418
419 }