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