source: vtkvis/branches/1.7/vtkRpAxisFollower.cpp @ 4807

Last change on this file since 4807 was 3479, checked in by ldelgass, 12 years ago

Update custom axes to VTK 6 versions, use local customized classes instead of
patched VTK runtime versions.

  • Property svn:eol-style set to native
File size: 16.6 KB
Line 
1/*=========================================================================
2
3  Program:   Visualization Toolkit
4  Module:    vtkRpAxisFollower.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 "vtkRpAxisFollower.h"
17
18#include "vtkRpAxisActor.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
33vtkStandardNewMacro(vtkRpAxisFollower);
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
40namespace
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
66vtkRpAxisFollower::vtkRpAxisFollower() : 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//----------------------------------------------------------------------
87vtkRpAxisFollower::~vtkRpAxisFollower()
88{
89  this->InternalMatrix->Delete();
90}
91
92//----------------------------------------------------------------------
93void vtkRpAxisFollower::SetAxis(vtkRpAxisActor *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//----------------------------------------------------------------------
111vtkRpAxisActor* vtkRpAxisFollower::GetAxis()
112{
113  return this->Axis.GetPointer();
114}
115
116//----------------------------------------------------------------------------
117void vtkRpAxisFollower::CalculateOrthogonalVectors(double rX[3], double rY[3],
118  double rZ[3], vtkRpAxisActor *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//----------------------------------------------------------------------------
198double vtkRpAxisFollower::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//----------------------------------------------------------------------------
238void vtkRpAxisFollower::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//-----------------------------------------------------------------------------
325void vtkRpAxisFollower::ComputeRotationAndTranlation(vtkRenderer *ren, double translation[3],
326                                                   double rX[3], double rY[3], double rZ[3],
327                                                   vtkRpAxisActor *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//----------------------------------------------------------------------
389void vtkRpAxisFollower::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//----------------------------------------------------------------------
429int vtkRpAxisFollower::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//----------------------------------------------------------------------
466void vtkRpAxisFollower::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//----------------------------------------------------------------------
491void vtkRpAxisFollower::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//----------------------------------------------------------------------
513int vtkRpAxisFollower::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//-----------------------------------------------------------------------------
536int vtkRpAxisFollower::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//-----------------------------------------------------------------------------
559void vtkRpAxisFollower::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  if(this->VisibleAtCurrentViewAngle)
588    {
589    this->Device->Render(ren,this->Mapper);
590    }
591  else
592    {
593    this->SetVisibility(this->VisibleAtCurrentViewAngle);
594    }
595}
596
597//----------------------------------------------------------------------
598void vtkRpAxisFollower::ShallowCopy(vtkProp *prop)
599{
600  vtkRpAxisFollower *f = vtkRpAxisFollower::SafeDownCast(prop);
601  if ( f != NULL )
602    {
603    this->SetAutoCenter(f->GetAutoCenter());
604    this->SetEnableDistanceLOD(f->GetEnableDistanceLOD());
605    this->SetDistanceLODThreshold(f->GetDistanceLODThreshold());
606    this->SetEnableViewAngleLOD(f->GetEnableViewAngleLOD());
607    this->SetViewAngleLODThreshold(f->GetViewAngleLODThreshold());
608    this->SetScreenOffset(f->GetScreenOffset());
609    this->SetAxis(f->GetAxis());
610    }
611
612  // Now do superclass
613  this->Superclass::ShallowCopy(prop);
614}
615
616bool vtkRpAxisFollower::IsTextUpsideDown( double* a, double* b )
617{
618  double angle = vtkMath::RadiansFromDegrees(this->Orientation[2]);
619  return (b[0] - a[0]) * cos(angle) - (b[1] - a[1]) * sin(angle) < 0;
620}
Note: See TracBrowser for help on using the repository browser.