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