1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkAxisFollower.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 
16 #include "vtkAxisFollower.h"
17 
18 #include "vtkAxisActor.h"
19 #include "vtkBoundingBox.h"
20 #include "vtkCamera.h"
21 #include "vtkCoordinate.h"
22 #include "vtkMath.h"
23 #include "vtkMatrix4x4.h"
24 #include "vtkObjectFactory.h"
25 #include "vtkPolyDataMapper.h"
26 #include "vtkProperty.h"
27 #include "vtkRenderer.h"
28 #include "vtkTexture.h"
29 #include "vtkTransform.h"
30 
31 #include <math.h>
32 
33 vtkStandardNewMacro(vtkAxisFollower);
34 
35 // List of vectors per axis (depending on which one needs to be
36 // followed.
37 // Order here is X, Y, and Z.
38 // Set of two axis aligned vectors that would define the Y vector.
39 // Order is MINMIN, MINMAX, MAXMAX, MAXMIN
40 namespace
41 {
42   const double AxisAlignedY[3][4][2][3] =
43   {
44     { {{0.0,  1.0, 0.0}, {0.0, 0.0,  1.0}},
45       {{0.0,  1.0, 0.0}, {0.0, 0.0, -1.0}},
46       {{0.0, -1.0, 0.0}, {0.0, 0.0, -1.0}},
47       {{0.0, -1.0, 0.0}, {0.0, 0.0,  1.0}}
48     },
49     {
50       {{ 1.0, 0.0, 0.0}, {0.0, 0.0,  1.0}},
51       {{ 1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
52       {{-1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
53       {{-1.0, 0.0, 0.0}, {0.0, 0.0,  1.0}}
54     },
55     {
56       {{ 1.0, 0.0, 0.0},  {0.0,  1.0, 0.0}},
57       {{ 1.0, 0.0, 0.0},  {0.0, -1.0, 0.0}},
58       {{-1.0, 0.0, 0.0},  {0.0, -1.0, 0.0}},
59       {{-1.0, 0.0, 0.0},  {0.0,  1.0, 0.0}}
60     }
61   };
62 }
63 
64 //----------------------------------------------------------------------
65 // Creates a follower with no camera set
vtkAxisFollower()66 vtkAxisFollower::vtkAxisFollower() : vtkFollower()
67 {
68   this->AutoCenter                = 1;
69 
70   this->EnableDistanceLOD         = 0;
71   this->DistanceLODThreshold      = 0.80;
72 
73   this->EnableViewAngleLOD        = 1;
74   this->ViewAngleLODThreshold     = 0.34;
75 
76   this->ScreenOffset              = 10.0;
77 
78   this->Axis                      = NULL;
79 
80   this->TextUpsideDown          = -1;
81   this->VisibleAtCurrentViewAngle = -1;
82 
83   this->InternalMatrix = vtkMatrix4x4::New();
84 }
85 
86 //----------------------------------------------------------------------
~vtkAxisFollower()87 vtkAxisFollower::~vtkAxisFollower()
88 {
89   this->InternalMatrix->Delete();
90 }
91 
92 //----------------------------------------------------------------------
SetAxis(vtkAxisActor * axis)93 void vtkAxisFollower::SetAxis(vtkAxisActor *axis)
94 {
95   if(!axis)
96     {
97     vtkErrorMacro("Invalid or NULL axis\n");
98     return;
99     }
100 
101   if(this->Axis != axis)
102     {
103     // \NOTE: Don't increment the ref count of axis as it could lead to
104     // circular references.
105     this->Axis = axis;
106     this->Modified();
107     }
108 }
109 
110 //----------------------------------------------------------------------
GetAxis()111 vtkAxisActor* vtkAxisFollower::GetAxis()
112 {
113   return this->Axis.GetPointer();
114 }
115 
116 //----------------------------------------------------------------------------
CalculateOrthogonalVectors(double rX[3],double rY[3],double rZ[3],vtkAxisActor * axis,double * dop,vtkRenderer * ren)117 void vtkAxisFollower::CalculateOrthogonalVectors(double rX[3], double rY[3],
118   double rZ[3], vtkAxisActor *axis, double *dop, vtkRenderer *ren)
119 {
120   if(!rX || !rY || !rZ)
121     {
122     vtkErrorMacro("Invalid or NULL direction vectors\n");
123     return;
124     }
125 
126   if(!axis)
127     {
128     vtkErrorMacro("Invalid or NULL axis\n");
129     return;
130     }
131 
132   if(!dop)
133     {
134     vtkErrorMacro("Invalid or NULL direction of projection vector\n");
135     return;
136     }
137 
138   if(!ren)
139     {
140     vtkErrorMacro("Invalid or NULL renderer\n");
141     return;
142     }
143 
144   vtkMatrix4x4* cameraMatrix = this->Camera->GetViewTransformMatrix();
145 
146   vtkCoordinate *c1Axis =  axis->GetPoint1Coordinate();
147   vtkCoordinate *c2Axis =  axis->GetPoint2Coordinate();
148   double *axisPt1 = c1Axis->GetComputedWorldValue(ren);
149   double *axisPt2 = c2Axis->GetComputedWorldValue(ren);
150 
151   rX[0] = axisPt2[0] - axisPt1[0];
152   rX[1] = axisPt2[1] - axisPt1[1];
153   rX[2] = axisPt2[2] - axisPt1[2];
154   vtkMath::Normalize(rX);
155 
156   // Get Y
157   vtkMath::Cross(rX, dop, rY);
158   vtkMath::Normalize(rY);
159 
160   // Get Z
161   vtkMath::Cross(rX, rY, rZ);
162   vtkMath::Normalize(rZ);
163 
164   double a[3], b[3];
165 
166   // Need homogeneous points.
167   double homoPt1[4] = {axisPt1[0], axisPt1[1], axisPt1[2], 1.0};
168   double homoPt2[4] = {axisPt2[0], axisPt2[1], axisPt2[2], 1.0};
169 
170   double *viewCoordinatePt1 = cameraMatrix->MultiplyDoublePoint(homoPt1);
171   a[0] = viewCoordinatePt1[0];
172   a[1] = viewCoordinatePt1[1];
173   a[2] = viewCoordinatePt1[2];
174 
175   double *viewCoordinatePt2 = cameraMatrix->MultiplyDoublePoint(homoPt2);
176   b[0] = viewCoordinatePt2[0];
177   b[1] = viewCoordinatePt2[1];
178   b[2] = viewCoordinatePt2[2];
179 
180   // If the text is upside down, we make a 180 rotation to keep it readable.
181   if(this->IsTextUpsideDown(a, b))
182     {
183     this->TextUpsideDown = 1;
184     rX[0] = -rX[0];
185     rX[1] = -rX[1];
186     rX[2] = -rX[2];
187     rZ[0] = -rZ[0];
188     rZ[1] = -rZ[1];
189     rZ[2] = -rZ[2];
190     }
191   else
192     {
193     this->TextUpsideDown = 0;
194     }
195 }
196 
197 //----------------------------------------------------------------------------
AutoScale(vtkViewport * viewport,vtkCamera * camera,double screenSize,double position[3])198 double vtkAxisFollower::AutoScale(vtkViewport *viewport, vtkCamera *camera,
199                                   double screenSize, double position[3])
200 {
201   double newScale = 0.0;
202 
203   if(!viewport)
204     {
205     std::cerr << "Invalid or NULL viewport \n";
206     return newScale;
207     }
208 
209   if(!camera)
210     {
211     std::cerr << "Invalid or NULL camera \n";
212     return newScale;
213     }
214 
215   if(!position)
216     {
217     std::cerr << "Invalid or NULL position \n";
218     return newScale;
219     }
220 
221   double factor = 1;
222   if (viewport->GetSize()[1] > 0)
223     {
224     factor = 2.0 * screenSize
225       * tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()/2.0))
226       / viewport->GetSize()[1];
227     }
228 
229     double dist = sqrt(
230           vtkMath::Distance2BetweenPoints(position,
231                                           camera->GetPosition()));
232     newScale = factor * dist;
233 
234     return newScale;
235 }
236 
237 //----------------------------------------------------------------------------
ComputeTransformMatrix(vtkRenderer * ren)238 void vtkAxisFollower::ComputeTransformMatrix(vtkRenderer *ren)
239 {
240   if(!this->Axis)
241     {
242     vtkErrorMacro("ERROR: Invalid axis\n");
243     return;
244     }
245 
246   // check whether or not need to rebuild the matrix
247   if ( this->GetMTime() > this->MatrixMTime ||
248        (this->Camera && this->Camera->GetMTime() > this->MatrixMTime) )
249     {
250     this->GetOrientation();
251     this->Transform->Push();
252     this->Transform->Identity();
253     this->Transform->PostMultiply();
254 
255     double pivotPoint[3] =
256     {
257       this->Origin[0],
258       this->Origin[1],
259       this->Origin[2]
260     };
261 
262     if(this->AutoCenter)
263       {
264       this->GetMapper()->GetCenter(pivotPoint);
265       }
266 
267     // Move pivot point to origin
268     this->Transform->Translate(-pivotPoint[0],
269                                -pivotPoint[1],
270                                -pivotPoint[2]);
271     // Scale
272     this->Transform->Scale(this->Scale[0],
273                            this->Scale[1],
274                            this->Scale[2]);
275 
276     // Rotate
277     this->Transform->RotateY(this->Orientation[1]);
278     this->Transform->RotateX(this->Orientation[0]);
279     this->Transform->RotateZ(this->Orientation[2]);
280 
281     double translation[3] = {0.0, 0.0, 0.0};
282     if (this->Axis)
283       {
284       vtkMatrix4x4 *matrix = this->InternalMatrix;
285       matrix->Identity();
286       double rX[3], rY[3], rZ[3];
287 
288       this->ComputeRotationAndTranlation(ren, translation, rX, rY, rZ, this->Axis);
289 
290       vtkMath::Normalize(rX);
291       vtkMath::Normalize(rY);
292       vtkMath::Normalize(rZ);
293 
294       matrix->Element[0][0] = rX[0];
295       matrix->Element[1][0] = rX[1];
296       matrix->Element[2][0] = rX[2];
297       matrix->Element[0][1] = rY[0];
298       matrix->Element[1][1] = rY[1];
299       matrix->Element[2][1] = rY[2];
300       matrix->Element[0][2] = rZ[0];
301       matrix->Element[1][2] = rZ[1];
302       matrix->Element[2][2] = rZ[2];
303 
304       this->Transform->Concatenate(matrix);
305       }
306 
307     this->Transform->Translate(this->Origin[0] + this->Position[0] + translation[0],
308                                this->Origin[1] + this->Position[1] + translation[1],
309                                this->Origin[2] + this->Position[2] + translation[2]);
310 
311     // Apply user defined matrix last if there is one
312     if (this->UserMatrix)
313       {
314       this->Transform->Concatenate(this->UserMatrix);
315       }
316 
317     this->Transform->PreMultiply();
318     this->Transform->GetMatrix(this->Matrix);
319     this->MatrixMTime.Modified();
320     this->Transform->Pop();
321     }
322 }
323 
324 //-----------------------------------------------------------------------------
ComputeRotationAndTranlation(vtkRenderer * ren,double translation[3],double rX[3],double rY[3],double rZ[3],vtkAxisActor * axis)325 void vtkAxisFollower::ComputeRotationAndTranlation(vtkRenderer *ren, double translation[3],
326                                                    double rX[3], double rY[3], double rZ[3],
327                                                    vtkAxisActor *axis)
328 {
329   double autoScaleFactor =
330     this->AutoScale(ren, this->Camera, this->ScreenOffset, this->Position);
331 
332   double dop[3];
333   this->Camera->GetDirectionOfProjection(dop);
334   vtkMath::Normalize(dop);
335 
336   this->CalculateOrthogonalVectors(rX, rY, rZ, axis, dop, ren);
337 
338   double dotVal = vtkMath::Dot(rZ, dop);
339 
340   double origRy[3] = {0.0, 0.0, 0.0};
341 
342   origRy[0] = rY[0];
343   origRy[1] = rY[1];
344   origRy[2] = rY[2];
345 
346   // NOTE: Basically the idea here is that dotVal will be positive
347   // only when we have projection direction aligned with our z directon
348   // and when that happens it means that our Y is inverted.
349   if(dotVal > 0)
350     {
351     rY[0] = -rY[0];
352     rY[1] = -rY[1];
353     rY[2] = -rY[2];
354     }
355 
356   // Check visibility at current view angle.
357   if(this->EnableViewAngleLOD)
358     {
359     this->ExecuteViewAngleVisibility(rZ);
360     }
361 
362   // Since we already stored all the possible Y axes that are geometry aligned,
363   // we compare our vertical vector with these vectors and if it aligns then we
364   // translate in opposite direction.
365   int axisPosition = this->Axis->GetAxisPosition();
366 
367   double dotVal1 = vtkMath::Dot(AxisAlignedY[this->Axis->GetAxisType()][axisPosition][0], origRy) ;
368   double dotVal2 = vtkMath::Dot(AxisAlignedY[this->Axis->GetAxisType()][axisPosition][1], origRy) ;
369 
370   if(fabs(dotVal1) > fabs(dotVal2))
371     {
372     int sign = (dotVal1 > 0 ? -1 : 1);
373 
374     translation[0] =  origRy[0] * autoScaleFactor * sign;
375     translation[1] =  origRy[1] * autoScaleFactor * sign;
376     translation[2] =  origRy[2] * autoScaleFactor * sign;
377     }
378   else
379     {
380     int sign = (dotVal2 > 0 ? -1 : 1);
381 
382     translation[0] =  origRy[0] * autoScaleFactor * sign;
383     translation[1] =  origRy[1] * autoScaleFactor * sign;
384     translation[2] =  origRy[2] * autoScaleFactor * sign;
385     }
386 }
387 
388 //----------------------------------------------------------------------
ComputerAutoCenterTranslation(const double & vtkNotUsed (autoScaleFactor),double translation[3])389 void vtkAxisFollower::ComputerAutoCenterTranslation(
390   const double& vtkNotUsed(autoScaleFactor), double translation[3])
391 {
392   if(!translation)
393     {
394     vtkErrorMacro("ERROR: Invalid or NULL translation\n");
395     return;
396     }
397 
398   double *bounds = this->GetMapper()->GetBounds();
399 
400   // Offset by half of width.
401   double halfWidth  = (bounds[1] - bounds[0]) * 0.5 * this->Scale[0];
402 
403   if(this->TextUpsideDown == 1)
404     {
405     halfWidth  = -halfWidth;
406     }
407 
408   if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_X)
409     {
410     translation[0] = translation[0] - halfWidth;
411     }
412   else if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_Y)
413     {
414     translation[1] = translation[1] - halfWidth;
415     }
416   else if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_Z)
417     {
418     translation[2] = translation[2] - halfWidth;
419     }
420   else
421     {
422     // Do nothing.
423     }
424 
425   return;
426 }
427 
428 //----------------------------------------------------------------------
TestDistanceVisibility()429 int vtkAxisFollower::TestDistanceVisibility()
430 {
431   if(!this->Camera->GetParallelProjection())
432     {
433     double cameraClippingRange[2];
434 
435     this->Camera->GetClippingRange(cameraClippingRange);
436 
437     // We are considering the far clip plane for evaluation. In certain
438     // odd conditions it might not work.
439     const double maxVisibleDistanceFromCamera = this->DistanceLODThreshold * (cameraClippingRange[1]);
440 
441     double dist = sqrt(vtkMath::Distance2BetweenPoints(this->Camera->GetPosition(),
442                                                        this->Position));
443 
444     if(dist > maxVisibleDistanceFromCamera)
445       {
446       // Need to make sure we are not looking at a flat axis and therefore should enable it anyway
447       if(this->Axis)
448         {
449         vtkBoundingBox bbox(this->Axis->GetBounds());
450         return (bbox.GetDiagonalLength() > (cameraClippingRange[1] - cameraClippingRange[0])) ? 1 : 0;
451         }
452       return 0;
453       }
454     else
455       {
456       return 1;
457       }
458     }
459   else
460     {
461     return 1;
462     }
463 }
464 
465 //----------------------------------------------------------------------
ExecuteViewAngleVisibility(double normal[3])466 void vtkAxisFollower::ExecuteViewAngleVisibility(double normal[3])
467 {
468   if(!normal)
469     {
470     vtkErrorMacro("ERROR: Invalid or NULL normal\n");
471     return;
472     }
473 
474   double *cameraPos = this->Camera->GetPosition();
475   double  dir[3] = {this->Position[0] - cameraPos[0],
476                     this->Position[1] - cameraPos[1],
477                     this->Position[2] - cameraPos[2]};
478   vtkMath::Normalize(dir);
479   double dotDir = vtkMath::Dot(dir, normal);
480   if( fabs(dotDir) < this->ViewAngleLODThreshold )
481     {
482     this->VisibleAtCurrentViewAngle = 0;
483     }
484   else
485     {
486     this->VisibleAtCurrentViewAngle = 1;
487     }
488 }
489 
490 //----------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)491 void vtkAxisFollower::PrintSelf(ostream& os, vtkIndent indent)
492 {
493   this->Superclass::PrintSelf(os,indent);
494 
495   os << indent << "AutoCenter: ("  << this->AutoCenter   << ")\n";
496   os << indent << "EnableDistanceLOD: ("   << this->EnableDistanceLOD    << ")\n";
497   os << indent << "DistanceLODThreshold: ("   << this->DistanceLODThreshold    << ")\n";
498   os << indent << "EnableViewAngleLOD: ("   << this->EnableViewAngleLOD    << ")\n";
499   os << indent << "ViewAngleLODThreshold: ("   << this->ViewAngleLODThreshold    << ")\n";
500   os << indent << "ScreenOffset: ("<< this->ScreenOffset << ")\n";
501 
502   if ( this->Axis )
503     {
504     os << indent << "Axis: (" << this->Axis << ")\n";
505     }
506   else
507     {
508     os << indent << "Axis: (none)\n";
509     }
510 }
511 
512 //----------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * vp)513 int vtkAxisFollower::RenderOpaqueGeometry(vtkViewport *vp)
514 {
515   if ( ! this->Mapper )
516     {
517     return 0;
518     }
519 
520   if (!this->Property)
521     {
522     // force creation of a property
523     this->GetProperty();
524     }
525 
526   if (this->GetIsOpaque())
527     {
528     vtkRenderer *ren = static_cast<vtkRenderer *>(vp);
529     this->Render(ren);
530     return 1;
531     }
532   return 0;
533 }
534 
535 //-----------------------------------------------------------------------------
RenderTranslucentPolygonalGeometry(vtkViewport * vp)536 int vtkAxisFollower::RenderTranslucentPolygonalGeometry(vtkViewport *vp)
537 {
538   if ( ! this->Mapper )
539     {
540     return 0;
541     }
542 
543   if (!this->Property)
544     {
545     // force creation of a property
546     this->GetProperty();
547     }
548 
549   if (!this->GetIsOpaque())
550     {
551     vtkRenderer *ren = static_cast<vtkRenderer *>(vp);
552     this->Render(ren);
553     return 1;
554     }
555   return 0;
556 }
557 
558 //-----------------------------------------------------------------------------
Render(vtkRenderer * ren)559 void vtkAxisFollower::Render(vtkRenderer *ren)
560 {
561   if(this->EnableDistanceLOD && !this->TestDistanceVisibility())
562     {
563     this->SetVisibility(0);
564     return;
565     }
566 
567   this->Property->Render(this, ren);
568 
569   this->Device->SetProperty (this->Property);
570   this->Property->Render(this, ren);
571   if (this->BackfaceProperty)
572     {
573     this->BackfaceProperty->BackfaceRender(this, ren);
574     this->Device->SetBackfaceProperty(this->BackfaceProperty);
575     }
576 
577   /* render the texture */
578   if (this->Texture)
579     {
580     this->Texture->Render(ren);
581     }
582 
583   // make sure the device has the same matrix
584   this->ComputeTransformMatrix(ren);
585   this->Device->SetUserMatrix(this->Matrix);
586 
587   this->SetVisibility(this->VisibleAtCurrentViewAngle);
588   if(this->VisibleAtCurrentViewAngle)
589     {
590     this->Device->Render(ren,this->Mapper);
591     }
592 }
593 
594 //----------------------------------------------------------------------
ShallowCopy(vtkProp * prop)595 void vtkAxisFollower::ShallowCopy(vtkProp *prop)
596 {
597   vtkAxisFollower *f = vtkAxisFollower::SafeDownCast(prop);
598   if ( f != NULL )
599     {
600     this->SetAutoCenter(f->GetAutoCenter());
601     this->SetEnableDistanceLOD(f->GetEnableDistanceLOD());
602     this->SetDistanceLODThreshold(f->GetDistanceLODThreshold());
603     this->SetEnableViewAngleLOD(f->GetEnableViewAngleLOD());
604     this->SetViewAngleLODThreshold(f->GetViewAngleLODThreshold());
605     this->SetScreenOffset(f->GetScreenOffset());
606     this->SetAxis(f->GetAxis());
607     }
608 
609   // Now do superclass
610   this->Superclass::ShallowCopy(prop);
611 }
612 
IsTextUpsideDown(double * a,double * b)613 bool vtkAxisFollower::IsTextUpsideDown( double* a, double* b )
614 {
615   double angle = vtkMath::RadiansFromDegrees(this->Orientation[2]);
616   return (b[0] - a[0]) * cos(angle) - (b[1] - a[1]) * sin(angle) < 0;
617 }
618