1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkStackedTreeLayoutStrategy.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   Copyright 2008 Sandia Corporation.
17   Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
18   the U.S. Government retains certain rights in this software.
19 -------------------------------------------------------------------------*/
20 
21 #include "vtkStackedTreeLayoutStrategy.h"
22 
23 #include "vtkCellArray.h"
24 #include "vtkCellData.h"
25 #include "vtkDoubleArray.h"
26 #include "vtkMath.h"
27 #include "vtkInformation.h"
28 #include "vtkInformationVector.h"
29 #include "vtkObjectFactory.h"
30 #include "vtkPoints.h"
31 #include "vtkPointData.h"
32 #include "vtkFloatArray.h"
33 #include "vtkDataArray.h"
34 #include "vtkIntArray.h"
35 #include "vtkTree.h"
36 #include "vtkTreeDFSIterator.h"
37 #include "vtkTreeLevelsFilter.h"
38 
39 #include "vtkSmartPointer.h"
40 #define VTK_CREATE(type, name)                                  \
41   vtkSmartPointer<type> name = vtkSmartPointer<type>::New()
42 
43 vtkStandardNewMacro(vtkStackedTreeLayoutStrategy);
44 
vtkStackedTreeLayoutStrategy()45 vtkStackedTreeLayoutStrategy::vtkStackedTreeLayoutStrategy()
46 {
47   this->InteriorRadius = 6.0;
48   this->RingThickness = 1.0;
49   this->RootStartAngle = 0.;
50   this->RootEndAngle = 360.;
51   this->UseRectangularCoordinates = false;
52   this->Reverse = false;
53   this->InteriorLogSpacingValue = 1.0;
54 }
55 
56 vtkStackedTreeLayoutStrategy::~vtkStackedTreeLayoutStrategy() = default;
57 
PrintSelf(ostream & os,vtkIndent indent)58 void vtkStackedTreeLayoutStrategy::PrintSelf(ostream& os, vtkIndent indent)
59 {
60   this->Superclass::PrintSelf(os,indent);
61   os << indent << "InteriorRadius: " << this->InteriorRadius << endl;
62   os << indent << "RingThickness: " << this->RingThickness << endl;
63   os << indent << "RootStartAngle: " << this->RootStartAngle << endl;
64   os << indent << "RootEndAngle: " << this->RootEndAngle << endl;
65   os << indent << "UseRectangularCoordinates: " << this->UseRectangularCoordinates << endl;
66   os << indent << "Reverse: " << this->Reverse << endl;
67   os << indent << "InteriorLogSpacingValue: " << this->InteriorLogSpacingValue << endl;
68 }
69 
Layout(vtkTree * inputTree,vtkDataArray * coordsArray,vtkDataArray * sizeArray)70 void vtkStackedTreeLayoutStrategy::Layout(vtkTree* inputTree,
71                                           vtkDataArray* coordsArray,
72                                           vtkDataArray* sizeArray)
73 {
74   if( !inputTree || inputTree->GetNumberOfVertices() == 0 )
75   {
76     return;
77   }
78   if (!coordsArray)
79   {
80     vtkErrorMacro("Area array not defined.");
81     return;
82   }
83 
84   vtkDataSetAttributes* data = inputTree->GetVertexData();
85 
86   VTK_CREATE(vtkDoubleArray, textRotationArray);
87   textRotationArray->SetName( "TextRotation" );
88   textRotationArray->SetNumberOfComponents(1);
89   textRotationArray->SetNumberOfTuples(inputTree->GetNumberOfVertices());
90   data->AddArray( textRotationArray );
91 
92   VTK_CREATE(vtkDoubleArray, textBoundedSizeArray);
93   textBoundedSizeArray->SetName( "TextBoundedSize" );
94   textBoundedSizeArray->SetNumberOfComponents(2);
95   textBoundedSizeArray->SetNumberOfTuples(inputTree->GetNumberOfVertices());
96   data->AddArray( textBoundedSizeArray );
97 
98   double outer_radius = 0.0;
99   if (this->Reverse)
100   {
101     VTK_CREATE(vtkTreeLevelsFilter, levelFilter);
102     VTK_CREATE(vtkTree, newTree);
103     newTree->ShallowCopy( inputTree );
104     levelFilter->SetInputData( newTree );
105     levelFilter->Update();
106     vtkTree* levelTree = levelFilter->GetOutput();
107 
108     vtkIntArray *levelArray = vtkArrayDownCast<vtkIntArray>(
109         levelTree->GetVertexData()->GetAbstractArray("level"));
110     int max_level = 0;
111     for( int i = 0; i < levelTree->GetNumberOfVertices(); i++ )
112     {
113       int level = levelArray->GetValue(i);
114       if( level > max_level )
115       {
116         max_level = level;
117       }
118     }
119     outer_radius = max_level*this->RingThickness + this->InteriorRadius;
120   }
121 
122   // Get the root vertex and set it
123   vtkIdType rootId = inputTree->GetRoot();
124   float coords[4] = {this->RootStartAngle, this->RootEndAngle, 0.0, 0.0};
125   if (this->Reverse)
126   {
127     coords[2] = outer_radius - this->RingThickness;
128     coords[3] = outer_radius;
129   }
130   else
131   {
132     coords[3] = this->InteriorRadius;
133   }
134   coordsArray->SetTuple(rootId, coords);
135 
136   // Now layout the children vertices
137   this->LayoutChildren(inputTree, coordsArray, sizeArray,
138       inputTree->GetNumberOfChildren(rootId),
139       rootId, 0, coords[2], coords[3], coords[0], coords[1]);
140 
141   vtkPoints* points = vtkPoints::New();
142   vtkIdType numVerts = inputTree->GetNumberOfVertices();
143   points->SetNumberOfPoints(numVerts);
144   for( vtkIdType i = 0; i < numVerts; i++ )
145   {
146     double sector_coords[4];
147     coordsArray->GetTuple( i, sector_coords );
148     double x, y, z;
149     if( this->UseRectangularCoordinates )
150     {
151       x = 0.5*(sector_coords[0] + sector_coords[1]);
152       y = 0.5*(sector_coords[2] + sector_coords[3]);
153       z = 0.;
154 
155       textRotationArray->SetValue( i, 0 );
156       textBoundedSizeArray->SetValue( 2*i, sector_coords[1] - sector_coords[0]);
157       textBoundedSizeArray->SetValue( 2*i + 1, sector_coords[3] - sector_coords[2] );
158     }
159     else
160     {
161       if( i == rootId )
162       {
163         x = y = z = 0.;
164 
165         textRotationArray->SetValue( i, 0 );
166         textBoundedSizeArray->SetValue( 2*i, 0 );
167         textBoundedSizeArray->SetValue( 2*i + 1, 0 );
168       }
169       else
170       {
171         double r = (0.5*(sector_coords[3] - sector_coords[2])) + sector_coords[2];
172         double theta = sector_coords[0] + (0.5*(sector_coords[1]-sector_coords[0]));
173         x = r * cos( vtkMath::RadiansFromDegrees( theta ) );
174         y = r * sin( vtkMath::RadiansFromDegrees( theta ) );
175         z = 0.;
176 
177         double sector_arc_length = r * vtkMath::RadiansFromDegrees(sector_coords[1] - sector_coords[0]);
178         double radial_arc_length = sector_coords[3] - sector_coords[2];
179         double aspect_ratio = sector_arc_length / radial_arc_length;
180         if( aspect_ratio > 1 )
181         {
182           //sector length is greater than radial length;
183           // align text with the sector
184           if( theta > 0. && theta < 180. )
185           {
186             textRotationArray->SetValue( i, theta - 90. );
187           }
188           else
189           {
190             textRotationArray->SetValue( i, theta + 90. );
191           }
192           textBoundedSizeArray->SetValue( 2*i, sector_arc_length );
193           textBoundedSizeArray->SetValue( 2*i + 1, radial_arc_length );
194         }
195         else
196         {
197           //radial length is greater than sector length;
198           // align text radially...
199           if( theta > 90. && theta < 270. )
200           {
201             textRotationArray->SetValue( i, theta - 180. );
202           }
203           else
204           {
205             textRotationArray->SetValue( i, theta );
206           }
207           textBoundedSizeArray->SetValue( 2*i, radial_arc_length );
208           textBoundedSizeArray->SetValue( 2*i + 1, sector_arc_length );
209         }
210       }
211     }
212     points->SetPoint(i, x, y, z);
213   }
214   inputTree->SetPoints(points);
215   points->Delete();
216 }
217 
LayoutEdgePoints(vtkTree * inputTree,vtkDataArray * sectorsArray,vtkDataArray * vtkNotUsed (sizeArray),vtkTree * outputTree)218 void vtkStackedTreeLayoutStrategy::LayoutEdgePoints(
219   vtkTree* inputTree,
220   vtkDataArray* sectorsArray,
221   vtkDataArray* vtkNotUsed(sizeArray),
222   vtkTree* outputTree)
223 {
224   VTK_CREATE(vtkTreeLevelsFilter, levelFilter);
225   VTK_CREATE(vtkTree, newTree);
226   newTree->ShallowCopy( inputTree );
227   levelFilter->SetInputData( newTree );
228   levelFilter->Update();
229   vtkTree* levelTree = levelFilter->GetOutput();
230   outputTree->ShallowCopy( levelTree );
231 
232   vtkIntArray* levelArray = vtkArrayDownCast<vtkIntArray>(
233       levelTree->GetVertexData()->GetAbstractArray("level"));
234 
235   double exteriorRadius = VTK_DOUBLE_MAX;
236   double sector_coords[4];
237   int max_level = 0;
238   for( int i = 0; i < outputTree->GetNumberOfVertices(); i++ )
239   {
240     int level = levelArray->GetValue(i);
241     if( level > max_level )
242     {
243       max_level = level;
244     }
245     if (inputTree->IsLeaf(i))
246     {
247       sectorsArray->GetTuple( i, sector_coords );
248       if (sector_coords[2] < exteriorRadius)
249       {
250         exteriorRadius = sector_coords[2];
251       }
252     }
253   }
254 
255   double spacing = this->InteriorLogSpacingValue;
256 
257   // The distance between level L-1 and L is s^L.
258   // Thus, if s < 1 then the distance between levels gets smaller in higher levels,
259   //       if s = 1 the distance remains the same, and
260   //       if s > 1 the distance get larger.
261   // The height (distance from the root) of level L, then, is
262   // s + s^2 + s^3 + ... + s^L, where s is the log spacing value.
263   // The max height (used for normalization) is
264   // s + s^2 + s^3 + ... + s^maxLevel.
265   // The quick formula for computing this is
266   // sum_{i=1}^{n} s^i = (s^(n+1) - 1)/(s - 1) - 1        if s != 1
267   //                   = n                                if s == 1
268   double maxHeight = max_level;
269   double eps = 1e-8;
270   double diff = spacing - 1.0 > 0 ? spacing - 1.0 : 1.0 - spacing;
271   if (diff > eps)
272   {
273     maxHeight = (pow(spacing, max_level+1.0) - 1.0)/(spacing - 1.0) - 1.0;
274   }
275 
276   vtkPoints* points = vtkPoints::New();
277   vtkIdType rootId = outputTree->GetRoot();
278   vtkIdType numVerts = outputTree->GetNumberOfVertices();
279   points->SetNumberOfPoints(numVerts);
280   for( vtkIdType i = 0; i < numVerts; i++ )
281   {
282     if( !this->UseRectangularCoordinates && i == rootId )
283     {
284       points->SetPoint( i, 0, 0, 0 );
285       continue;
286     }
287 
288     sectorsArray->GetTuple( i, sector_coords );
289 
290     double x = 0.0;
291     double y = 0.0;
292     double z = 0.0;
293     if( this->UseRectangularCoordinates )
294     {
295       if( inputTree->IsLeaf(i) )
296       {
297         if( this->Reverse )
298         {
299           y = sector_coords[2];
300         }
301         else
302         {
303           y = sector_coords[3];
304         }
305       }
306       else
307       {
308         if( this->Reverse )
309         {
310           y = this->InteriorRadius - this->RingThickness*(maxHeight + maxHeight - inputTree->GetLevel(i));
311         }
312         else
313         {
314           y = this->InteriorRadius + this->RingThickness*(maxHeight + maxHeight - inputTree->GetLevel(i));
315         }
316       }
317       x = 0.5*(sector_coords[0] + sector_coords[1]);
318       z = 0.;
319     }
320     else
321     {
322       double r;
323       if( inputTree->IsLeaf(i) )
324       {
325         r = sector_coords[2];
326       }
327       else
328       {
329         if (diff <= eps)
330         {
331           r = outputTree->GetLevel(i)/maxHeight;
332         }
333         else
334         {
335           r = ((pow(spacing, outputTree->GetLevel(i)+1.0) - 1.0)/(spacing - 1.0) - 1.0)/maxHeight;
336         }
337         // scale the spacing value based on the radius of the
338         // circle we have to work with...
339         r *= exteriorRadius;
340       }
341 
342       double theta = sector_coords[0] + (0.5*(sector_coords[1]-sector_coords[0]));
343       x = r * cos( vtkMath::RadiansFromDegrees( theta ) );
344       y = r * sin( vtkMath::RadiansFromDegrees( theta ) );
345       z = 0.;
346     }
347     points->SetPoint(i, x, y, z);
348   }
349   outputTree->SetPoints(points);
350   points->Delete();
351 }
352 
LayoutChildren(vtkTree * tree,vtkDataArray * coordsArray,vtkDataArray * sizeArray,vtkIdType nchildren,vtkIdType parent,vtkIdType begin,float parentInnerRad,float parentOuterRad,float parentStartAng,float parentEndAng)353 void vtkStackedTreeLayoutStrategy::LayoutChildren(
354   vtkTree *tree, vtkDataArray *coordsArray, vtkDataArray *sizeArray,
355   vtkIdType nchildren, vtkIdType parent, vtkIdType begin,
356   float parentInnerRad, float parentOuterRad,
357   float parentStartAng, float parentEndAng)
358 {
359   double new_interior_rad = 0.0;
360   double new_outer_rad = 0.0;
361   if (this->Reverse)
362   {
363     new_interior_rad = parentInnerRad - this->RingThickness;
364     new_outer_rad = parentInnerRad;
365   }
366   else
367   {
368     new_interior_rad = parentOuterRad;
369     new_outer_rad = new_interior_rad + this->RingThickness;
370   }
371   //FIXME - we may want to do this instead...
372   //double new_outer_rad = new_interior_rad +this->RingThickness[level];
373 
374   double radial_spacing = this->ShrinkPercentage * this->RingThickness;
375   new_outer_rad -= radial_spacing;
376   //new_interior_rad += 0.5*radial_spacing;
377 
378   //now calculate the width of each of the sectors for each vertex
379   // first calculate the total summed weight for each of the children vertices
380   double total_weighted_sum = 0;
381   vtkIdType i;
382   for( i = begin; i < nchildren; i++)
383   {
384     if (sizeArray)
385     {
386       total_weighted_sum +=
387         static_cast<float>(sizeArray->GetTuple1(tree->GetChild(parent, i)));
388     }
389     else
390     {
391       total_weighted_sum += 1.0;
392     }
393   }
394 
395   // If we are doing radial layout, put extra space on the full rings
396   // so the first and last children don't butt up against each other.
397   vtkIdType num_spaces = nchildren - 1;
398   double parent_angle = parentEndAng - parentStartAng;
399   if (!this->UseRectangularCoordinates && parent_angle == 360.0)
400   {
401     num_spaces = nchildren;
402   }
403   double available_angle = parent_angle;
404   double conversion = vtkMath::Pi()/180.0;
405   double spacing = 0.0;
406   if (nchildren > 1)
407   {
408     double parent_length;
409     if (this->UseRectangularCoordinates)
410     {
411       parent_length = parent_angle;
412     }
413     else
414     {
415       parent_length = conversion * parent_angle * new_outer_rad;
416     }
417     double spacing_length;
418     if (radial_spacing * num_spaces > 0.25 * parent_length)
419     {
420       spacing_length = 0.25 * parent_length;
421     }
422     else
423     {
424       spacing_length = radial_spacing * num_spaces;
425     }
426     double total_space;
427     if (this->UseRectangularCoordinates)
428     {
429       total_space = spacing_length;
430     }
431     else
432     {
433       total_space = spacing_length / new_outer_rad / conversion;
434     }
435     spacing = total_space / num_spaces;
436     available_angle -= total_space;
437   }
438 
439   float coords[4];
440   double current_angle = parentStartAng;
441   for( i = begin; i < nchildren; i++)
442   {
443     int id = tree->GetChild(parent, i);
444     float cur_size = 1.0;
445     if (sizeArray)
446     {
447       cur_size = static_cast<float>(sizeArray->GetTuple1(id));
448     }
449     double this_arc = available_angle *
450       ( cur_size / total_weighted_sum );
451 
452     coords[2] = new_interior_rad;
453     coords[3] = new_outer_rad;
454     coords[0] = current_angle;
455     coords[1] = current_angle + this_arc;
456 
457     coordsArray->SetTuple(id, coords);
458 
459     current_angle += this_arc + spacing;
460 
461     vtkIdType numNewChildren = tree->GetNumberOfChildren(id);
462     if (numNewChildren > 0)
463     {
464       this->LayoutChildren(tree, coordsArray, sizeArray, numNewChildren, id, 0,
465                            coords[2], coords[3], coords[0], coords[1]);
466     }
467   }
468 }
469 
FindVertex(vtkTree * otree,vtkDataArray * array,float pnt[2])470 vtkIdType vtkStackedTreeLayoutStrategy::FindVertex(
471     vtkTree* otree,
472     vtkDataArray* array,
473     float pnt[2])
474 {
475   if (this->UseRectangularCoordinates)
476   {
477     float blimits[4];
478     vtkIdType vertex = otree->GetRoot();
479     if(vertex < 0)
480     {
481       return vertex;
482     }
483     vtkFloatArray *boundsInfo = vtkArrayDownCast<vtkFloatArray>(array);
484 
485     // Now try to find the vertex that contains the point
486     boundsInfo->GetTypedTuple(vertex, blimits); // Get the extents of the root
487     if( ((pnt[1] > blimits[2]) && (pnt[1] < blimits[3])) &&
488         ((pnt[0] > blimits[0]) && (pnt[0] < blimits[1])) )
489     {
490       // Point is at the root vertex.
491       return vertex;
492     }
493 
494     // Now traverse the children to try and find
495     // the vertex that contains the point
496     vtkIdType child;
497     VTK_CREATE(vtkTreeDFSIterator, it);
498     it->SetTree( otree );
499     it->SetStartVertex( vertex );
500 
501     while (it->HasNext())
502     {
503       child = it->Next();
504       boundsInfo->GetTypedTuple(child, blimits); // Get the extents of the child
505       bool beyond_radial_bounds = false;
506       bool beyond_angle_bounds = false;
507       if( (pnt[1] < blimits[2]) || (pnt[1] > blimits[3]))
508         beyond_radial_bounds = true;
509       if( (pnt[0] < blimits[0]) || (pnt[0] > blimits[1]))
510         beyond_angle_bounds = true;
511 
512       if( beyond_radial_bounds || beyond_angle_bounds )
513       {
514         continue;
515       }
516       // If we are here then the point is contained by the child
517       return child;
518     }
519   }
520   else
521   {
522     // Radial layout
523     float polar_location[2];
524     polar_location[0] = sqrt( ( pnt[0] * pnt[0] ) + ( pnt[1] * pnt[1] ) );
525     polar_location[1] = vtkMath::DegreesFromRadians( atan2( pnt[1], pnt[0] ) );
526     if( polar_location[1] < 0 )
527       polar_location[1] += 360.;
528 
529     float blimits[4];
530     vtkIdType vertex = otree->GetRoot();
531     if(vertex < 0)
532     {
533       return vertex;
534     }
535     vtkFloatArray *boundsInfo = vtkArrayDownCast<vtkFloatArray>(array);
536 
537     // Now try to find the vertex that contains the point
538     boundsInfo->GetTypedTuple(vertex, blimits); // Get the extents of the root
539     if( ((polar_location[0] > blimits[2]) && (polar_location[0] < blimits[3])) &&
540         ((polar_location[1] > blimits[0]) && (polar_location[1] < blimits[1])) )
541     {
542       // Point is at the root vertex.
543       // but we don't want the root to be pickable, so return -1.
544       // This won't work for blimits spanning the 0/360 rollover, but the test below
545       // catches that case.
546       return -1;
547     }
548 
549     // Now traverse the children to try and find
550     // the vertex that contains the point
551     vtkIdType child;
552     VTK_CREATE(vtkTreeDFSIterator, it);
553     it->SetTree( otree );
554     it->SetStartVertex( vertex );
555 
556     while (it->HasNext())
557     {
558       child = it->Next();
559       // if the root boundary starts anywhere but zero, the root node will have passed
560       // the earlier test.  This will skip the root and prevent it from being picked.
561       if (child == vertex)
562       {
563         continue;
564       }
565       boundsInfo->GetTypedTuple(child, blimits); // Get the extents of the child
566 
567       // the range checking below doesn't work if either or both of blimits > 360
568       if ((blimits[0] > 360.0) && (blimits[1] > 360.0))
569       {
570         blimits[0] -= 360.0;
571         blimits[1] -= 360.0;
572       }
573       else if ((blimits[0] < 360.0) && (blimits[1] > 360.0) && (polar_location[1] < 360.0))
574       {  // if the range spans the rollover at 0/360 on the circle
575         if (polar_location[1] < 90.0)
576         {
577           blimits[0] = 0.0;
578           blimits[1] -= 360.0;
579         }
580         else if (polar_location[1] > 270.)
581         {
582           blimits[1] = 360.0;
583         }
584       }
585       bool beyond_radial_bounds = false;
586       bool beyond_angle_bounds = false;
587       if( (polar_location[0] < blimits[2]) || (polar_location[0] > blimits[3]))
588         beyond_radial_bounds = true;
589       if( (polar_location[1] < blimits[0]) || (polar_location[1] > blimits[1]))
590         beyond_angle_bounds = true;
591 
592       if( beyond_radial_bounds || beyond_angle_bounds )
593       {
594         continue;
595       }
596       // If we are here then the point is contained by the child
597       return child;
598     }
599   }
600   return -1;
601 }
602 
603