]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java
Added vtkCameraAndSelectorAction.setViewDir(Vector3d)
[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 javax.vecmath.Vector3d;
22
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;
30
31 import vtk.vtkCamera;
32 import vtk.vtkProp;
33 import vtk.vtkProp3D;
34 import vtk.vtkRenderWindow;
35 import vtk.vtkRenderer;
36
37 public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelectionProvider {
38
39         protected vtkRenderer ren;
40         protected int lastX;
41         protected int lastY;
42         protected vtkRenderWindow rw;
43         protected vtkCamera cam;
44         protected int InteractionMode = 1;
45
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 };
50
51         public vtkCameraAndSelectorAction(InteractiveVtkComposite panel) {
52                 super(panel);
53                 this.ren = panel.getRenderer();
54                 this.rw = panel.getRenderWindow();
55                 this.cam = ren.GetActiveCamera();
56         }
57         
58         public void setUpDirection(double[] upDirection) {
59                 this.upDirection = upDirection;
60         }
61
62         public void Lock() {
63                 panel.lock();
64         }
65
66         public void UnLock() {
67                 panel.unlock();
68         }
69
70         public void InteractionModeRotate() {
71                 this.InteractionMode = 1;
72         }
73
74         public void InteractionModeTranslate() {
75                 this.InteractionMode = 2;
76         }
77
78         public void InteractionModeZoom() {
79                 this.InteractionMode = 3;
80         }
81
82         public void resetCameraClippingRange() {
83                 Lock();
84                 ren.ResetCameraClippingRange();
85                 UnLock();
86         }
87
88         public void resetCamera() {
89                 Lock();
90                 ren.ResetCamera();
91                 UnLock();
92         }
93
94         public boolean mousePressed(MouseEvent e) {
95
96                 if (ren.VisibleActorCount() == 0)
97                         return false;
98                 rw.SetDesiredUpdateRate(activeRate);
99                 lastX = e.getX();
100                 lastY = e.getY();
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();
106                 } else {
107                         InteractionModeRotate();
108                 }
109                 return true;
110         }
111
112         public boolean mouseReleased(MouseEvent e) {
113                 rw.SetDesiredUpdateRate(passiveRate);
114                 return true;
115         }
116
117         public boolean mouseDragged(MouseEvent e) {
118                 if (ren.VisibleActorCount() == 0)
119                         return false;
120                 int x = e.getX();
121                 int y = e.getY();
122                 // rotate
123                 if (this.InteractionMode == 1) {
124                         cam.Elevation(clampElevationDelta(y - lastY));
125                         if (doNotRotate)
126                                 cam.SetViewUp(upDirection);
127                         cam.Azimuth(lastX - x);
128                         cam.OrthogonalizeViewUp();
129                         resetCameraClippingRange();
130                         // panel.UpdateLight();
131                 }
132                 // translate
133                 if (this.InteractionMode == 2) {
134                         double FPoint[];
135                         double PPoint[];
136                         double APoint[];
137                         double RPoint[];
138
139                         // get the current focal point and position
140                         FPoint = cam.GetFocalPoint();
141                         PPoint = cam.GetPosition();
142
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();
147                         
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();
154                         
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];
159                         }
160
161                         /*
162                          * Compute a translation vector, moving everything 1/2 the distance to the
163                          * cursor. (Arbitrary scale factor)
164                          */
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();
169                 }
170                 // zoom
171                 if (this.InteractionMode == 3) {
172                         double zoomFactor;
173                         zoomFactor = Math.pow(1.02, (y - lastY));
174                         cam.Dolly(zoomFactor);
175                         
176                         if (cam.GetParallelProjection() == 1) {
177                                 updateParallelScale();
178                         }
179                         resetCameraClippingRange();
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                 cam.Dolly(zoomFactor);
212                 updateParallelScale();
213                 resetCameraClippingRange();
214                 panel.refresh();
215                 return true;
216         }
217
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);
224         }
225
226         protected List<vtkProp> selectActors = new ArrayList<vtkProp>();
227         protected List<vtkProp> hoverActor = new ArrayList<vtkProp>();
228
229         @Override
230         public boolean mouseClicked(MouseEvent e) {
231                 if (!panel.getComponent().isFocusControl())
232                         return false;
233                 if (e.getButton() != MouseEvent.BUTTON1)
234                         return false;
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);
241                                 } else {
242                                         if (selectActors.contains(selectActor))
243                                                 selectActors.remove(selectActor);
244                                         else
245                                                 selectActors.add(selectActor);
246                                 }
247                         }
248                         fireSelectionChanged();
249                 } else if (!e.isControlDown()) {
250                         selectActors.clear();
251                         fireSelectionChanged();
252                 }
253                 return true;
254                 // if (e.getClickCount() > 1)
255                 // updatePickRay(e.getX(), e.getY());
256
257         }
258
259         // private void updatePickRay(double x , double y) {
260         // Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(), x, y);
261         //
262         //
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);
280         //
281         // if (rayActor != null) {
282         // panel.GetRenderer().RemoveActor(rayActor);
283         // rayActor.Delete();
284         // }
285         // rayActor = aLineActor;
286         // panel.GetRenderer().AddActor(rayActor);
287         //
288         // linePoints.Delete();
289         // aLine.Delete();
290         // aLineGrid.Delete();
291         // aLineMapper.Delete();
292         // panel.repaint();
293         // }
294         //
295         // private vtkActor rayActor;
296
297         @Override
298         public boolean mouseMoved(MouseEvent e) {
299                 lastX = e.getX();
300                 lastY = e.getY();
301
302                 if (!panel.getComponent().isFocusControl())
303                         return false;
304                 List<vtkProp> prevHover = new ArrayList<vtkProp>();
305                 prevHover.addAll(hoverActor);
306                 hoverActor.clear();
307                 vtkProp pick[] = panel.pick(e.getX(), e.getY());
308                 if (pick != null) {
309                         for (vtkProp p : pick)
310                                 hoverActor.add(p);
311                 }
312
313                 if (!prevHover.containsAll(hoverActor) || !hoverActor.containsAll(prevHover)) {
314                         fireHoverChanged();
315                 }
316                 return true;
317         }
318
319         public List<vtkProp> getSelectActor() {
320                 return selectActors;
321         }
322
323         public List<vtkProp> getHoverActor() {
324                 return hoverActor;
325         }
326
327         private List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
328
329         @Override
330         public void addSelectionChangedListener(ISelectionChangedListener listener) {
331                 selectionListeners.add(listener);
332         }
333
334         @Override
335         public ISelection getSelection() {
336                 return new StructuredSelection(selectActors);
337         }
338
339         @Override
340         public void removeSelectionChangedListener(ISelectionChangedListener listener) {
341                 selectionListeners.remove(listener);
342         }
343
344         @Override
345         public void setSelection(ISelection selection) {
346                 setSelection(selection, false);
347
348         }
349
350         public void setSelection(ISelection selection, boolean fire) {
351                 Collection<vtkProp> selectedProps = AdaptationUtils.adaptToCollection(selection, vtkProp.class);
352
353                 selectActors.clear();
354                 selectActors.addAll(selectedProps);
355                 if (fire)
356                         fireSelectionChanged();
357         }
358
359         protected void fireSelectionChanged() {
360                 Display.getDefault().asyncExec(new Runnable() {
361                         @Override
362                         public void run() {
363
364                                 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this,
365                                                 new StructuredSelection(selectActors));
366                                 for (ISelectionChangedListener l : selectionListeners) {
367                                         l.selectionChanged(evt);
368                                 }
369
370                         }
371                 });
372         }
373
374         private List<ISelectionChangedListener> hoverListeners = new ArrayList<ISelectionChangedListener>();
375
376         public void addHoverChangedListener(ISelectionChangedListener listener) {
377                 hoverListeners.add(listener);
378         }
379
380         public ISelection getHoverSelection() {
381                 return new StructuredSelection(hoverActor);
382         }
383
384         public void removeHoverChangedListener(ISelectionChangedListener listener) {
385                 hoverListeners.remove(listener);
386         }
387
388         private void fireHoverChanged() {
389                 Display.getDefault().asyncExec(new Runnable() {
390                         @Override
391                         public void run() {
392                                 StructuredSelection sel = null;
393                                 if (hoverActor == null)
394                                         sel = new StructuredSelection();
395                                 else
396                                         sel = new StructuredSelection(hoverActor);
397                                 SelectionChangedEvent evt = new SelectionChangedEvent(vtkCameraAndSelectorAction.this, sel);
398                                 for (ISelectionChangedListener l : hoverListeners) {
399                                         l.selectionChanged(evt);
400                                 }
401
402                         }
403                 });
404         }
405
406         public void focus(double x, double y, double z) {
407                 Lock();
408                 cam.SetFocalPoint(x, y, z);
409                 if (doNotRotate) {
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]);
415                         else
416                                 cam.SetViewUp(upDirection);
417                 }
418                 cam.OrthogonalizeViewUp();
419                 resetCameraClippingRange();
420                 // panel.UpdateLight();
421                 UnLock();
422         }
423         
424         public void fitToView(Collection<vtkProp3D> props) {
425                 if (props.isEmpty())
426                         return;
427                 
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) {
430                         prop.GetBounds(b);
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]);
435                 }
436                 
437                 fitToView(bounds);
438         }
439
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());
444                 viewDir.normalize();
445                 upDir.normalize();
446                 Vector3d sideDir = new Vector3d();
447                 sideDir.cross(viewDir, upDir);
448                 sideDir.normalize();
449                 
450                 double width = getBoxWidth(bounds, sideDir);
451                 double height = getBoxWidth(bounds, upDir);
452                 double depth = getBoxWidth(bounds, viewDir);
453                 
454                 int[] size = rw.GetActualSize();
455                 
456                 double distance1 = height / 2 / Math.tan(Math.toRadians(cam.GetViewAngle()) / 2);
457                 double distance2 = distance1 * (width / size[0] / (height / size[1]));
458                 
459                 double distance = Math.max(distance1, distance2) + depth / 2;
460                 
461                 viewDir.scale(-distance);
462                 viewDir.add(center);
463                 
464                 cam.SetPosition(viewDir.x, viewDir.y, viewDir.z);
465                 focus(center.x, center.y, center.z);
466                 
467                 if (cam.GetParallelProjection() == 1) {
468                         cam.SetParallelScale(Math.max(height, width * size[1] / size[0]) / 2);
469                 }
470         }
471
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];
476                 
477                 return Math.abs(dx * dir.x) + Math.abs(dy * dir.y) + Math.abs(dz * dir.z);
478         }
479
480         public void setViewDir(Vector3d direction) {
481                 Vector3d focal = new Vector3d(cam.GetFocalPoint());
482                 Vector3d pos = new Vector3d(cam.GetPosition());
483                 Vector3d dir = new Vector3d(pos);
484                 dir.sub(focal);
485                 double distance = dir.length();
486                 dir.scaleAdd(distance, direction, focal);
487                 cam.SetPosition(dir.x, dir.y, dir.z);
488                 focus(focal.x, focal.y, focal.z);
489                 //panel.UpdateLight();
490                 panel.refresh();
491         }
492
493 }