/*========================================================================= Program: Visualization Toolkit Module: vtkConeLayoutStrategy.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*------------------------------------------------------------------------- Copyright 2008 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -------------------------------------------------------------------------*/ #include "vtkConeLayoutStrategy.h" #include "vtkGraph.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPoints.h" #include "vtkOutEdgeIterator.h" #include "vtkMutableDirectedGraph.h" #include "vtkGraphEdge.h" #include "vtkSmartPointer.h" #define VTK_CREATE(type, name) \ vtkSmartPointer name = vtkSmartPointer::New() vtkStandardNewMacro(vtkConeLayoutStrategy); vtkConeLayoutStrategy::vtkConeLayoutStrategy() { this->Compression = 0; this->Compactness = 0.75; this->Spacing = 1.0; } vtkConeLayoutStrategy::~vtkConeLayoutStrategy() { } double vtkConeLayoutStrategy::LocalPlacement(vtkIdType node, vtkPoints *points) { vtkIdType child, prevChild, nrChildren; VTK_CREATE( vtkOutEdgeIterator, children ); double circum = 0; // circum of cone below this node double radius; // radius of cone double dAlpha; double alpha = 0; double largest = 0.0; // size of largest cone of any child double *radii; double cx = 0, cy = 0, cr = 0; double vx, vy, norm, i1x, i1y, i3x, i3y; // Initially position this node at the origin ... points->SetPoint(node, 0.0, 0.0, 0.0); nrChildren = this->Graph->GetOutDegree(node); // If there are no children, we are done. if (nrChildren == 0) { return 1.0; } this->Graph->GetOutEdges(node, children); if (nrChildren == 1) { // For one child, simply position that child; radius required // for the tree will then depend on radius required for that // child. radius = this->LocalPlacement( children->NextGraphEdge()->GetTarget(), points); return radius; } // If there is more than one child nodes ... radii = new double[nrChildren]; for (vtkIdType i = 0; i < nrChildren; i++) { // Layout the next child and record its raidus. If // necessary update the size of largest child cone. // Accumulate the (approximate) arc-length // required by child nodes as "circum". child = children->NextGraphEdge()->GetTarget(); radii[i] = this->LocalPlacement(child, points); circum += radii[i]*2.0; if (radii[i] > largest) { largest = radii[i]; } } radius = circum / (2.0*vtkMath::Pi()); // Iterate over the children, assigning the node of each // a position around a circle of the required radius, // based on the radii of that child and its predecessor, prevChild = nrChildren-1; this->Graph->GetOutEdges(node, children); for (vtkIdType j = 0; j < nrChildren; j++) { child = children->NextGraphEdge()->GetTarget(); dAlpha = (radii[j] + radii[prevChild]) / radius; alpha += dAlpha; points->SetPoint(child, radius*cos(alpha), radius*sin(alpha), 0.0); prevChild = j; if (j == 0) { cx = radius*cos(alpha); cy = radius*sin(alpha); cr = radius; } else { vx = cx - radius*cos(alpha); vy = cy = radius*sin(alpha); norm = sqrt(vx*vx + vy*vy); if (norm == 0.0) { continue; } vx /= norm; vy /= norm; i1x = cx - vx*cr; i1y = cy - vy*cr; i3x = radius*cos(alpha) - radius*vx; i3y = radius*sin(alpha) - radius*vy; norm = sqrt((i1x-i3x)*(i1x-i3x) + (i1y-i3y)*(i1y-i3y)); if (radius > norm) { cx = radius*cos(alpha); cy = radius*sin(alpha); cr = radius; } else { if (norm > cr) { cx = (i1x + i3x)/2.0; cy = (i1y + i3y)/2.0; cr = norm/2.0; } } } } delete [] radii; // Update statistics, used when height of cones is calculated. if (radius < this->MinRadius) { this->MinRadius = radius; } if (radius > this->MaxRadius) { this->MaxRadius = radius; } this->SumOfRadii += radius; this->NrCones++; // For compact placement, space is allocated simply for the // radius of this cone plus "a little" extra. For non-compact // placement, allow also for the radius allocated to the // largest child. return radius + (this->Compression ? 1 : cr); } // Calculate the final layout for a node, given its level in // the tree, and the (final) position of its parent. void vtkConeLayoutStrategy::GlobalPlacement( vtkIdType root, vtkPoints *points, double refX, // parent's X coord double refY, // parent's Y coord double level ) // level of this node in the tree. { vtkIdType child; VTK_CREATE( vtkOutEdgeIterator, children ); double final[3]; points->GetPoint(root, final); final[0] += refX; final[1] += refY; if (this->Compression) { final[2] = level*this->Spacing; } else { final[2] = level*this->Spacing*(this->MaxRadius*this->Compactness); } points->SetPoint(root, final); // Having fixed the position of "root", now iterate over its // children and fix their positions, remembering that these // are one level further down the tree. this->Graph->GetOutEdges(root, children); while (children->HasNext()) { child = children->NextGraphEdge()->GetTarget(); this->GlobalPlacement(child, points, final[0], final[1], level+1); } } void vtkConeLayoutStrategy::Layout() { VTK_CREATE( vtkMutableDirectedGraph, superGraph ); VTK_CREATE( vtkPoints, points ); vtkIdType numVerts = this->Graph->GetNumberOfVertices(); vtkGraph *temp; vtkIdType node, root, nrRoots; VTK_CREATE( vtkPoints, tmpPoints ); tmpPoints->SetNumberOfPoints(numVerts+1); // Allow for artificial root. points->SetNumberOfPoints(numVerts); // Allow for artificial root. // Assume that graph is either a tree or a forest. // Force it to be a tree by installing a new root and linking this to all existing // nodes with in-degree 0. superGraph->DeepCopy(this->Graph); temp = this->Graph; this->Graph = superGraph; nrRoots = 0; root = superGraph->AddVertex(); for (node = 0; node < numVerts; node++) { if (superGraph->GetInDegree(node) == 0) { superGraph->AddEdge(root,node); nrRoots++; } } if (nrRoots == 0) { vtkErrorMacro(<<"No roots found in input dataset - output may be ill-defined."); } this->MinRadius = 1.0E10F; this->MaxRadius = 0.0; this->SumOfRadii = 0.0; this->NrCones = 0; // Layout is performed in two passes. First, find a provisional location // for each node, via a bottom-up traversal. Then calculate a final // position for each node, by performing a top-down traversal, placing // the root at the origin, and then positioning each child using the // provisional location of the child and final location of the parent. this->LocalPlacement(root, tmpPoints); // Second pass: fix absolute node position. this->GlobalPlacement(root, tmpPoints, 0.0, 0.0, 0.0); vtkIdType c; double p[3]; for (c = 0; c < numVerts; c++) { tmpPoints->GetPoint(c, p); points->SetPoint(c,p); } this->Graph = temp; this->Graph->SetPoints(points); } void vtkConeLayoutStrategy::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Spacing: " << this->Spacing << endl; os << indent << "Compactness: " << this->Compactness << endl; os << indent << "Compression: " << this->Compression << endl; }