1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkInteractorStyleTrackballActor.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 "vtkInteractorStyleTrackballActor.h"
16 
17 #include "vtkCallbackCommand.h"
18 #include "vtkCamera.h"
19 #include "vtkCellPicker.h"
20 #include "vtkMath.h"
21 #include "vtkMatrix4x4.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkProp3D.h"
24 #include "vtkRenderWindowInteractor.h"
25 #include "vtkRenderer.h"
26 #include "vtkTransform.h"
27 
28 vtkStandardNewMacro(vtkInteractorStyleTrackballActor);
29 
30 //------------------------------------------------------------------------------
vtkInteractorStyleTrackballActor()31 vtkInteractorStyleTrackballActor::vtkInteractorStyleTrackballActor()
32 {
33   this->MotionFactor = 10.0;
34   this->InteractionProp = nullptr;
35   this->InteractionPicker = vtkCellPicker::New();
36   this->InteractionPicker->SetTolerance(0.001);
37 }
38 
39 //------------------------------------------------------------------------------
~vtkInteractorStyleTrackballActor()40 vtkInteractorStyleTrackballActor::~vtkInteractorStyleTrackballActor()
41 {
42   this->InteractionPicker->Delete();
43 }
44 
45 //------------------------------------------------------------------------------
OnMouseMove()46 void vtkInteractorStyleTrackballActor::OnMouseMove()
47 {
48   int x = this->Interactor->GetEventPosition()[0];
49   int y = this->Interactor->GetEventPosition()[1];
50 
51   switch (this->State)
52   {
53     case VTKIS_ROTATE:
54       this->FindPokedRenderer(x, y);
55       this->Rotate();
56       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
57       break;
58 
59     case VTKIS_PAN:
60       this->FindPokedRenderer(x, y);
61       this->Pan();
62       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
63       break;
64 
65     case VTKIS_DOLLY:
66       this->FindPokedRenderer(x, y);
67       this->Dolly();
68       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
69       break;
70 
71     case VTKIS_SPIN:
72       this->FindPokedRenderer(x, y);
73       this->Spin();
74       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
75       break;
76 
77     case VTKIS_USCALE:
78       this->FindPokedRenderer(x, y);
79       this->UniformScale();
80       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
81       break;
82   }
83 }
84 
85 //------------------------------------------------------------------------------
OnLeftButtonDown()86 void vtkInteractorStyleTrackballActor::OnLeftButtonDown()
87 {
88   int x = this->Interactor->GetEventPosition()[0];
89   int y = this->Interactor->GetEventPosition()[1];
90 
91   this->FindPokedRenderer(x, y);
92   this->FindPickedActor(x, y);
93   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
94   {
95     return;
96   }
97 
98   this->GrabFocus(this->EventCallbackCommand);
99   if (this->Interactor->GetShiftKey())
100   {
101     this->StartPan();
102   }
103   else if (this->Interactor->GetControlKey())
104   {
105     this->StartSpin();
106   }
107   else
108   {
109     this->StartRotate();
110   }
111 }
112 
113 //------------------------------------------------------------------------------
OnLeftButtonUp()114 void vtkInteractorStyleTrackballActor::OnLeftButtonUp()
115 {
116   switch (this->State)
117   {
118     case VTKIS_PAN:
119       this->EndPan();
120       break;
121 
122     case VTKIS_SPIN:
123       this->EndSpin();
124       break;
125 
126     case VTKIS_ROTATE:
127       this->EndRotate();
128       break;
129   }
130 
131   if (this->Interactor)
132   {
133     this->ReleaseFocus();
134   }
135 }
136 
137 //------------------------------------------------------------------------------
OnMiddleButtonDown()138 void vtkInteractorStyleTrackballActor::OnMiddleButtonDown()
139 {
140   int x = this->Interactor->GetEventPosition()[0];
141   int y = this->Interactor->GetEventPosition()[1];
142 
143   this->FindPokedRenderer(x, y);
144   this->FindPickedActor(x, y);
145   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
146   {
147     return;
148   }
149 
150   this->GrabFocus(this->EventCallbackCommand);
151   if (this->Interactor->GetControlKey())
152   {
153     this->StartDolly();
154   }
155   else
156   {
157     this->StartPan();
158   }
159 }
160 
161 //------------------------------------------------------------------------------
OnMiddleButtonUp()162 void vtkInteractorStyleTrackballActor::OnMiddleButtonUp()
163 {
164   switch (this->State)
165   {
166     case VTKIS_DOLLY:
167       this->EndDolly();
168       break;
169 
170     case VTKIS_PAN:
171       this->EndPan();
172       break;
173   }
174 
175   if (this->Interactor)
176   {
177     this->ReleaseFocus();
178   }
179 }
180 
181 //------------------------------------------------------------------------------
OnRightButtonDown()182 void vtkInteractorStyleTrackballActor::OnRightButtonDown()
183 {
184   int x = this->Interactor->GetEventPosition()[0];
185   int y = this->Interactor->GetEventPosition()[1];
186 
187   this->FindPokedRenderer(x, y);
188   this->FindPickedActor(x, y);
189   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
190   {
191     return;
192   }
193 
194   this->GrabFocus(this->EventCallbackCommand);
195   this->StartUniformScale();
196 }
197 
198 //------------------------------------------------------------------------------
OnRightButtonUp()199 void vtkInteractorStyleTrackballActor::OnRightButtonUp()
200 {
201   switch (this->State)
202   {
203     case VTKIS_USCALE:
204       this->EndUniformScale();
205       break;
206   }
207 
208   if (this->Interactor)
209   {
210     this->ReleaseFocus();
211   }
212 }
213 
214 //------------------------------------------------------------------------------
Rotate()215 void vtkInteractorStyleTrackballActor::Rotate()
216 {
217   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
218   {
219     return;
220   }
221 
222   vtkRenderWindowInteractor* rwi = this->Interactor;
223   vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
224 
225   // First get the origin of the assembly
226   double* obj_center = this->InteractionProp->GetCenter();
227 
228   // GetLength gets the length of the diagonal of the bounding box
229   double boundRadius = this->InteractionProp->GetLength() * 0.5;
230 
231   // Get the view up and view right vectors
232   double view_up[3], view_look[3], view_right[3];
233 
234   cam->OrthogonalizeViewUp();
235   cam->ComputeViewPlaneNormal();
236   cam->GetViewUp(view_up);
237   vtkMath::Normalize(view_up);
238   cam->GetViewPlaneNormal(view_look);
239   vtkMath::Cross(view_up, view_look, view_right);
240   vtkMath::Normalize(view_right);
241 
242   // Get the furtherest point from object position+origin
243   double outsidept[3];
244 
245   outsidept[0] = obj_center[0] + view_right[0] * boundRadius;
246   outsidept[1] = obj_center[1] + view_right[1] * boundRadius;
247   outsidept[2] = obj_center[2] + view_right[2] * boundRadius;
248 
249   // Convert them to display coord
250   double disp_obj_center[3];
251 
252   this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center);
253 
254   this->ComputeWorldToDisplay(outsidept[0], outsidept[1], outsidept[2], outsidept);
255 
256   double radius = sqrt(vtkMath::Distance2BetweenPoints(disp_obj_center, outsidept));
257   double nxf = (rwi->GetEventPosition()[0] - disp_obj_center[0]) / radius;
258 
259   double nyf = (rwi->GetEventPosition()[1] - disp_obj_center[1]) / radius;
260 
261   double oxf = (rwi->GetLastEventPosition()[0] - disp_obj_center[0]) / radius;
262 
263   double oyf = (rwi->GetLastEventPosition()[1] - disp_obj_center[1]) / radius;
264 
265   if (((nxf * nxf + nyf * nyf) <= 1.0) && ((oxf * oxf + oyf * oyf) <= 1.0))
266   {
267     double newXAngle = vtkMath::DegreesFromRadians(asin(nxf));
268     double newYAngle = vtkMath::DegreesFromRadians(asin(nyf));
269     double oldXAngle = vtkMath::DegreesFromRadians(asin(oxf));
270     double oldYAngle = vtkMath::DegreesFromRadians(asin(oyf));
271 
272     double scale[3];
273     scale[0] = scale[1] = scale[2] = 1.0;
274 
275     double** rotate = new double*[2];
276 
277     rotate[0] = new double[4];
278     rotate[1] = new double[4];
279 
280     rotate[0][0] = newXAngle - oldXAngle;
281     rotate[0][1] = view_up[0];
282     rotate[0][2] = view_up[1];
283     rotate[0][3] = view_up[2];
284 
285     rotate[1][0] = oldYAngle - newYAngle;
286     rotate[1][1] = view_right[0];
287     rotate[1][2] = view_right[1];
288     rotate[1][3] = view_right[2];
289 
290     this->Prop3DTransform(this->InteractionProp, obj_center, 2, rotate, scale);
291 
292     delete[] rotate[0];
293     delete[] rotate[1];
294     delete[] rotate;
295 
296     if (this->AutoAdjustCameraClippingRange)
297     {
298       this->CurrentRenderer->ResetCameraClippingRange();
299     }
300 
301     rwi->Render();
302   }
303 }
304 
305 //------------------------------------------------------------------------------
Spin()306 void vtkInteractorStyleTrackballActor::Spin()
307 {
308   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
309   {
310     return;
311   }
312 
313   vtkRenderWindowInteractor* rwi = this->Interactor;
314   vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
315 
316   // Get the axis to rotate around = vector from eye to origin
317 
318   double* obj_center = this->InteractionProp->GetCenter();
319 
320   double motion_vector[3];
321   double view_point[3];
322 
323   if (cam->GetParallelProjection())
324   {
325     // If parallel projection, want to get the view plane normal...
326     cam->ComputeViewPlaneNormal();
327     cam->GetViewPlaneNormal(motion_vector);
328   }
329   else
330   {
331     // Perspective projection, get vector from eye to center of actor
332     cam->GetPosition(view_point);
333     motion_vector[0] = view_point[0] - obj_center[0];
334     motion_vector[1] = view_point[1] - obj_center[1];
335     motion_vector[2] = view_point[2] - obj_center[2];
336     vtkMath::Normalize(motion_vector);
337   }
338 
339   double disp_obj_center[3];
340 
341   this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center);
342 
343   double newAngle =
344     vtkMath::DegreesFromRadians(atan2(rwi->GetEventPosition()[1] - disp_obj_center[1],
345       rwi->GetEventPosition()[0] - disp_obj_center[0]));
346 
347   double oldAngle =
348     vtkMath::DegreesFromRadians(atan2(rwi->GetLastEventPosition()[1] - disp_obj_center[1],
349       rwi->GetLastEventPosition()[0] - disp_obj_center[0]));
350 
351   double scale[3];
352   scale[0] = scale[1] = scale[2] = 1.0;
353 
354   double** rotate = new double*[1];
355   rotate[0] = new double[4];
356 
357   rotate[0][0] = newAngle - oldAngle;
358   rotate[0][1] = motion_vector[0];
359   rotate[0][2] = motion_vector[1];
360   rotate[0][3] = motion_vector[2];
361 
362   this->Prop3DTransform(this->InteractionProp, obj_center, 1, rotate, scale);
363 
364   delete[] rotate[0];
365   delete[] rotate;
366 
367   if (this->AutoAdjustCameraClippingRange)
368   {
369     this->CurrentRenderer->ResetCameraClippingRange();
370   }
371 
372   rwi->Render();
373 }
374 
375 //------------------------------------------------------------------------------
Pan()376 void vtkInteractorStyleTrackballActor::Pan()
377 {
378   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
379   {
380     return;
381   }
382 
383   vtkRenderWindowInteractor* rwi = this->Interactor;
384 
385   // Use initial center as the origin from which to pan
386 
387   double* obj_center = this->InteractionProp->GetCenter();
388 
389   double disp_obj_center[3], new_pick_point[4];
390   double old_pick_point[4], motion_vector[3];
391 
392   this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center);
393 
394   this->ComputeDisplayToWorld(
395     rwi->GetEventPosition()[0], rwi->GetEventPosition()[1], disp_obj_center[2], new_pick_point);
396 
397   this->ComputeDisplayToWorld(rwi->GetLastEventPosition()[0], rwi->GetLastEventPosition()[1],
398     disp_obj_center[2], old_pick_point);
399 
400   motion_vector[0] = new_pick_point[0] - old_pick_point[0];
401   motion_vector[1] = new_pick_point[1] - old_pick_point[1];
402   motion_vector[2] = new_pick_point[2] - old_pick_point[2];
403 
404   if (this->InteractionProp->GetUserMatrix() != nullptr)
405   {
406     vtkTransform* t = vtkTransform::New();
407     t->PostMultiply();
408     t->SetMatrix(this->InteractionProp->GetUserMatrix());
409     t->Translate(motion_vector[0], motion_vector[1], motion_vector[2]);
410     this->InteractionProp->GetUserMatrix()->DeepCopy(t->GetMatrix());
411     t->Delete();
412   }
413   else
414   {
415     this->InteractionProp->AddPosition(motion_vector[0], motion_vector[1], motion_vector[2]);
416   }
417 
418   if (this->AutoAdjustCameraClippingRange)
419   {
420     this->CurrentRenderer->ResetCameraClippingRange();
421   }
422 
423   rwi->Render();
424 }
425 
426 //------------------------------------------------------------------------------
Dolly()427 void vtkInteractorStyleTrackballActor::Dolly()
428 {
429   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
430   {
431     return;
432   }
433 
434   vtkRenderWindowInteractor* rwi = this->Interactor;
435   vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
436 
437   double view_point[3], view_focus[3];
438   double motion_vector[3];
439 
440   cam->GetPosition(view_point);
441   cam->GetFocalPoint(view_focus);
442 
443   double* center = this->CurrentRenderer->GetCenter();
444 
445   int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
446   double yf = dy / center[1] * this->MotionFactor;
447   double dollyFactor = pow(1.1, yf);
448 
449   dollyFactor -= 1.0;
450   motion_vector[0] = (view_point[0] - view_focus[0]) * dollyFactor;
451   motion_vector[1] = (view_point[1] - view_focus[1]) * dollyFactor;
452   motion_vector[2] = (view_point[2] - view_focus[2]) * dollyFactor;
453 
454   if (this->InteractionProp->GetUserMatrix() != nullptr)
455   {
456     vtkTransform* t = vtkTransform::New();
457     t->PostMultiply();
458     t->SetMatrix(this->InteractionProp->GetUserMatrix());
459     t->Translate(motion_vector[0], motion_vector[1], motion_vector[2]);
460     this->InteractionProp->GetUserMatrix()->DeepCopy(t->GetMatrix());
461     t->Delete();
462   }
463   else
464   {
465     this->InteractionProp->AddPosition(motion_vector);
466   }
467 
468   if (this->AutoAdjustCameraClippingRange)
469   {
470     this->CurrentRenderer->ResetCameraClippingRange();
471   }
472 
473   rwi->Render();
474 }
475 
476 //------------------------------------------------------------------------------
UniformScale()477 void vtkInteractorStyleTrackballActor::UniformScale()
478 {
479   if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
480   {
481     return;
482   }
483 
484   vtkRenderWindowInteractor* rwi = this->Interactor;
485 
486   int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
487 
488   double* obj_center = this->InteractionProp->GetCenter();
489   double* center = this->CurrentRenderer->GetCenter();
490 
491   double yf = dy / center[1] * this->MotionFactor;
492   double scaleFactor = pow(1.1, yf);
493 
494   double** rotate = nullptr;
495 
496   double scale[3];
497   scale[0] = scale[1] = scale[2] = scaleFactor;
498 
499   this->Prop3DTransform(this->InteractionProp, obj_center, 0, rotate, scale);
500 
501   if (this->AutoAdjustCameraClippingRange)
502   {
503     this->CurrentRenderer->ResetCameraClippingRange();
504   }
505 
506   rwi->Render();
507 }
508 
509 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)510 void vtkInteractorStyleTrackballActor::PrintSelf(ostream& os, vtkIndent indent)
511 {
512   this->Superclass::PrintSelf(os, indent);
513 }
514 
515 //------------------------------------------------------------------------------
FindPickedActor(int x,int y)516 void vtkInteractorStyleTrackballActor::FindPickedActor(int x, int y)
517 {
518   this->InteractionPicker->Pick(x, y, 0.0, this->CurrentRenderer);
519   vtkProp* prop = this->InteractionPicker->GetViewProp();
520   if (prop != nullptr)
521   {
522     this->InteractionProp = vtkProp3D::SafeDownCast(prop);
523   }
524   else
525   {
526     this->InteractionProp = nullptr;
527   }
528 }
529 
530 //------------------------------------------------------------------------------
Prop3DTransform(vtkProp3D * prop3D,double * boxCenter,int numRotation,double ** rotate,double * scale)531 void vtkInteractorStyleTrackballActor::Prop3DTransform(
532   vtkProp3D* prop3D, double* boxCenter, int numRotation, double** rotate, double* scale)
533 {
534   vtkMatrix4x4* oldMatrix = vtkMatrix4x4::New();
535   prop3D->GetMatrix(oldMatrix);
536 
537   double orig[3];
538   prop3D->GetOrigin(orig);
539 
540   vtkTransform* newTransform = vtkTransform::New();
541   newTransform->PostMultiply();
542   if (prop3D->GetUserMatrix() != nullptr)
543   {
544     newTransform->SetMatrix(prop3D->GetUserMatrix());
545   }
546   else
547   {
548     newTransform->SetMatrix(oldMatrix);
549   }
550 
551   newTransform->Translate(-(boxCenter[0]), -(boxCenter[1]), -(boxCenter[2]));
552 
553   for (int i = 0; i < numRotation; i++)
554   {
555     newTransform->RotateWXYZ(rotate[i][0], rotate[i][1], rotate[i][2], rotate[i][3]);
556   }
557 
558   if ((scale[0] * scale[1] * scale[2]) != 0.0)
559   {
560     newTransform->Scale(scale[0], scale[1], scale[2]);
561   }
562 
563   newTransform->Translate(boxCenter[0], boxCenter[1], boxCenter[2]);
564 
565   // now try to get the composite of translate, rotate, and scale
566   newTransform->Translate(-(orig[0]), -(orig[1]), -(orig[2]));
567   newTransform->PreMultiply();
568   newTransform->Translate(orig[0], orig[1], orig[2]);
569 
570   if (prop3D->GetUserMatrix() != nullptr)
571   {
572     newTransform->GetMatrix(prop3D->GetUserMatrix());
573   }
574   else
575   {
576     prop3D->SetPosition(newTransform->GetPosition());
577     prop3D->SetScale(newTransform->GetScale());
578     prop3D->SetOrientation(newTransform->GetOrientation());
579   }
580   oldMatrix->Delete();
581   newTransform->Delete();
582 }
583