1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkInteractorStyleMultiTouchCamera.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkInteractorStyleMultiTouchCamera.h"
16 
17 #include "vtkCamera.h"
18 #include "vtkCallbackCommand.h"
19 #include "vtkMath.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkRenderWindow.h"
22 #include "vtkRenderWindowInteractor.h"
23 #include "vtkRenderer.h"
24 
25 vtkStandardNewMacro(vtkInteractorStyleMultiTouchCamera);
26 
27 //----------------------------------------------------------------------------
vtkInteractorStyleMultiTouchCamera()28 vtkInteractorStyleMultiTouchCamera::vtkInteractorStyleMultiTouchCamera()
29 {
30   this->MotionFactor   = 10.0;
31   this->PointersDownCount = 0;
32   for (int i = 0; i < VTKI_MAX_POINTERS; ++i)
33     {
34     this->PointersDown[i] = 0;
35     }
36 }
37 
38 //----------------------------------------------------------------------------
~vtkInteractorStyleMultiTouchCamera()39 vtkInteractorStyleMultiTouchCamera::~vtkInteractorStyleMultiTouchCamera()
40 {
41 }
42 
43 //----------------------------------------------------------------------------
OnMouseMove()44 void vtkInteractorStyleMultiTouchCamera::OnMouseMove()
45 {
46   int pointer = this->Interactor->GetPointerIndex();
47 
48   this->FindPokedRenderer(this->Interactor->GetEventPositions(pointer)[0],
49                           this->Interactor->GetEventPositions(pointer)[1]);
50   if (this->State == VTKIS_TWO_POINTER)
51     {
52     this->AdjustCamera();
53     this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
54     }
55   else
56     {
57     this->Superclass::OnMouseMove();
58     }
59 }
60 
61 //----------------------------------------------------------------------------
OnLeftButtonDown()62 void vtkInteractorStyleMultiTouchCamera::OnLeftButtonDown()
63 {
64   int pointer = this->Interactor->GetPointerIndex();
65 
66   // if it is already down ignore this event
67   if (this->PointersDown[pointer])
68     {
69     return;
70     }
71 
72   this->FindPokedRenderer(this->Interactor->GetEventPositions(pointer)[0],
73                           this->Interactor->GetEventPositions(pointer)[1]);
74   if (this->CurrentRenderer == NULL)
75     {
76     return;
77     }
78 
79   this->PointersDown[pointer] = 1;
80   this->PointersDownCount++;
81 
82   // do the standard single pointer event handling
83   if (this->PointersDownCount == 1)
84     {
85     this->Superclass::OnLeftButtonDown();
86     return;
87     }
88 
89   // if going from 1 to 2 pointers stop the one pointer action
90   if (this->PointersDownCount == 2)
91     {
92     switch (this->State)
93       {
94       case VTKIS_DOLLY:
95         this->EndDolly();
96         break;
97 
98       case VTKIS_PAN:
99         this->EndPan();
100         break;
101 
102       case VTKIS_SPIN:
103         this->EndSpin();
104         break;
105 
106       case VTKIS_ROTATE:
107         this->EndRotate();
108         break;
109       }
110     // start the multipointer action
111     this->StartTwoPointer();
112     return;
113     }
114 
115   // if going from 2 to 3 pointers stop the two pointer action
116   if (this->PointersDownCount == 3 && this->State == VTKIS_TWO_POINTER)
117     {
118     this->EndTwoPointer();
119     }
120 
121 }
122 
123 //----------------------------------------------------------------------------
OnLeftButtonUp()124 void vtkInteractorStyleMultiTouchCamera::OnLeftButtonUp()
125 {
126   int pointer = this->Interactor->GetPointerIndex();
127 
128   // if it is already up, ignore this event
129   if (!this->PointersDown[pointer])
130     {
131     return;
132     }
133 
134   this->PointersDownCount--;
135   this->PointersDown[pointer] = 0;
136 
137   // if we were just one pointer then do the usual handling
138   if (this->PointersDownCount == 0)
139     {
140     this->Superclass::OnLeftButtonUp();
141     return;
142     }
143 
144   switch (this->State)
145     {
146     case VTKIS_TWO_POINTER:
147       this->EndTwoPointer();
148       break;
149     }
150 
151   if ( this->Interactor )
152     {
153     this->ReleaseFocus();
154     }
155 }
156 
distance2D(int * a,int * b)157 double distance2D(int *a, int *b)
158 {
159   return sqrt((double)(a[0] - b[0])*(a[0] - b[0]) + (double)(a[1]-b[1])*(a[1]-b[1]));
160 }
161 
162 //----------------------------------------------------------------------------
AdjustCamera()163 void vtkInteractorStyleMultiTouchCamera::AdjustCamera()
164 {
165   if ( this->CurrentRenderer == NULL )
166     {
167     return;
168     }
169 
170   vtkRenderWindowInteractor *rwi = this->Interactor;
171 
172   // OK we have two pointers, that means 4 constraints
173   // P1.x P1.y P2.x P2.y
174   //
175   // We use those 4 contraints to control:
176   // Zoom (variant is the distance between points - 1 DOF)
177   // Roll (variant is the angle formed by the line connecting the points - 1 DOF)
178   // Position (variant is the X,Y position of the midpoint of the line - 2 DOF)
179   //
180 
181 
182   // find the moving and non moving points
183   int eventPI = rwi->GetPointerIndex();
184   int otherPI = 0;
185 
186   for (int i = 0; i < VTKI_MAX_POINTERS; ++i)
187     {
188     if (this->PointersDown[i] > 0 && i != eventPI)
189       {
190       otherPI = i;
191       break;
192       }
193     }
194 
195   // compute roll - 1 DOF
196   double oldAngle =
197     vtkMath::DegreesFromRadians( atan2( (double)rwi->GetLastEventPositions(eventPI)[1] - rwi->GetLastEventPositions(otherPI)[1],
198                                         (double)rwi->GetLastEventPositions(eventPI)[0] - rwi->GetLastEventPositions(otherPI)[0] ) );
199 
200   double newAngle =
201     vtkMath::DegreesFromRadians( atan2( (double)rwi->GetEventPositions(eventPI)[1] - rwi->GetEventPositions(otherPI)[1],
202                                         (double)rwi->GetEventPositions(eventPI)[0] - rwi->GetEventPositions(otherPI)[0] ) );
203 
204 
205   vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
206   camera->Roll( newAngle - oldAngle );
207 
208   // compute dolly/scale - 1 DOF
209   double oldDist = distance2D(rwi->GetLastEventPositions(otherPI), rwi->GetLastEventPositions(eventPI));
210   double newDist = distance2D(rwi->GetEventPositions(otherPI), rwi->GetEventPositions(eventPI));
211 
212   double dyf = newDist/oldDist;
213   if (camera->GetParallelProjection())
214     {
215     camera->SetParallelScale(camera->GetParallelScale() / dyf);
216     }
217   else
218     {
219     camera->Dolly(dyf);
220     if (this->AutoAdjustCameraClippingRange)
221       {
222       this->CurrentRenderer->ResetCameraClippingRange();
223       }
224     }
225 
226   // handle panning - 2 DOF
227   double viewFocus[4], focalDepth, viewPoint[3];
228   double newPickPoint[4], oldPickPoint[4], motionVector[3];
229 
230   // Calculate the focal depth since we'll be using it a lot
231   camera->GetFocalPoint(viewFocus);
232   this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
233                               viewFocus);
234   focalDepth = viewFocus[2];
235 
236   this->ComputeDisplayToWorld((rwi->GetEventPositions(eventPI)[0] + rwi->GetEventPositions(otherPI)[0])/2.0,
237                               (rwi->GetEventPositions(eventPI)[1] + rwi->GetEventPositions(otherPI)[1])/2.0,
238                               focalDepth,
239                               newPickPoint);
240 
241   // Has to recalc old mouse point since the viewport has moved,
242   // so can't move it outside the loop
243   this->ComputeDisplayToWorld((rwi->GetLastEventPositions(eventPI)[0] + rwi->GetLastEventPositions(otherPI)[0])/2.0,
244                               (rwi->GetLastEventPositions(eventPI)[1] + rwi->GetLastEventPositions(otherPI)[1])/2.0,
245                               focalDepth,
246                               oldPickPoint);
247 
248   // Camera motion is reversed
249   motionVector[0] = oldPickPoint[0] - newPickPoint[0];
250   motionVector[1] = oldPickPoint[1] - newPickPoint[1];
251   motionVector[2] = oldPickPoint[2] - newPickPoint[2];
252 
253   camera->GetFocalPoint(viewFocus);
254   camera->GetPosition(viewPoint);
255   camera->SetFocalPoint(motionVector[0] + viewFocus[0],
256                         motionVector[1] + viewFocus[1],
257                         motionVector[2] + viewFocus[2]);
258 
259   camera->SetPosition(motionVector[0] + viewPoint[0],
260                       motionVector[1] + viewPoint[1],
261                       motionVector[2] + viewPoint[2]);
262 
263   // clean up
264   if (this->Interactor->GetLightFollowCamera())
265     {
266     this->CurrentRenderer->UpdateLightsGeometryToFollowCamera();
267     }
268   camera->OrthogonalizeViewUp();
269 
270   rwi->Render();
271 }
272 
273 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)274 void vtkInteractorStyleMultiTouchCamera::PrintSelf(ostream& os, vtkIndent indent)
275 {
276   this->Superclass::PrintSelf(os,indent);
277   os << indent << "MotionFactor: " << this->MotionFactor << "\n";
278 }
279