void Cluster::computeSizeAndPositions() { // This process is described in Frank van Ham's Master's thesis, p. 24 // Recurse into the tree (depth first) for (unsigned int i = 0; i < descendants.size(); ++i) { descendants[i]->computeSizeAndPositions(); } /* Compute the cluster radius r such that all states fit nicely into the * cluster. Suppose the cluster has N states and every state is visualized by * a circle with radius 0.1. Suppose we want the total area of all states * to fill a quarter of the circle that represents the cluster. This way, * there is (hopefully) enough room left for transitions and space between the * states. * So: N * pi * 0.1^2 = 0.25 * pi * r^2 * Hence: r = sqrt( N * 0.04 ) */ topRadius = sqrt(states.size()*0.04f); if (descendants.size() == 0) { baseRadius = topRadius; bc_radius = topRadius; bc_height = 1.0f; } else if (descendants.size() == 1) { Cluster* desc = *descendants.begin(); baseRadius = desc->getTopRadius(); bc_radius = max(topRadius,desc->getBCRadius()); bc_height = desc->getBCHeight() + 1.0f; desc->center(); } else // descendants.size() > 1 { // sort descendants by size in ascending order sort(descendants.begin(),descendants.end(),Comp_BCVolume()); // determine whether a unique smallest descendant exists Cluster* smallest = descendants[0]; Cluster* nextSmallest = descendants[1]; bool uniqueSmallest = ((nextSmallest->getBCVolume()-smallest->getBCVolume()) / smallest->getBCVolume()) > 0.01f; // determine whether a unique largest descendant exists Cluster* largest = descendants[descendants.size()-1]; Cluster* nextLargest = descendants[descendants.size()-2]; bool uniqueLargest = ((nextLargest->getBCVolume()-largest->getBCVolume()) / largest->getBCVolume()) < -0.01f; // invariant: descendants in range [x,y) have not been assigned a position int x = 0; int y = static_cast<int>(descendants.size()); float bcr_center = 0.0f; // BC radius of largest descendant in center float bcr_rim = largest->getBCRadius(); // BC radius of largest descendant on rim if (uniqueLargest) { // center the largest descendant largest->center(); --y; bcr_center = largest->getBCRadius(); bcr_rim = nextLargest->getBCRadius(); } else { if (uniqueSmallest) { // center the smallest descendant smallest->center(); ++x; bcr_center = smallest->getBCRadius(); } } // compute the radius of the base of the cylinder and the cluster's size float minRimRadius = 0.0f; if (y-x > 1) { minRimRadius = (float)(bcr_rim / sin(PI / (y-x))); } baseRadius = max(bcr_center + bcr_rim + 0.01f,minRimRadius); bc_radius = max(bcr_center + bcr_rim + 0.01f,minRimRadius +bcr_rim); bc_radius = max(topRadius,bc_radius); bc_height = 0.0f; for (unsigned int i = 0; i < descendants.size(); ++i) { if (descendants[i]->getBCHeight() > bc_height) { bc_height = descendants[i]->getBCHeight(); } } bc_height += 1.0f; // Divide the remaining descendants over the rim of the circle. First take // the two largest unpositioned clusters and place them opposite to each // other, then do the same for the two smallest unpositioned clusters. Keep // repeating these steps until all clusters have been positioned. So if the // list of clusters (sorted ascending by size) is [ 0, 1, 2, 3, 4, 5 ] then // the clusters are placed in the following order (starting at angle 0 and // going counter-clockwise): 5, 0, 3, 4, 1, 2 int i = 0; int h = (y-x) / 2 + (y-x) % 2; float angle = 360.0f / (y-x); while (x != y) { if (i % 2 == 1) { descendants[x]->setPosition(i*angle); ++x; if (x != y) { descendants[x]->setPosition((h+i)*angle); ++x; } } else { descendants[y-1]->setPosition(i*angle); --y; if (x != y) { descendants[y-1]->setPosition((h+i)*angle); --y; } } ++i; } } }
void Cluster::computeSizeAndPositions_FSM() { // This process is described in Frank van Ham's Master's thesis, p. 24 // Recurse into the tree (depth first) for (unsigned int i = 0; i < descendants.size(); ++i) { descendants[i]->computeSizeAndPositions_FSM(); } /* Compute the cluster radius r such that all states fit nicely into the * cluster. Suppose the cluster has N states and every state is visualized by * a circle with radius 0.1. Suppose we want the total area of all states * to fill a quarter of the circle that represents the cluster. This way, * there is (hopefully) enough room left for transitions and space between the * states. * So: N * pi * 0.1^2 = 0.25 * pi * r^2 * Hence: r = sqrt( N * 0.04 ) */ topRadius = states.size()/(2*static_cast<float>(PI)); if (descendants.size() == 0) { baseRadius = topRadius; bc_radius = topRadius; bc_height = 1.0f; } else if (descendants.size() == 1) { Cluster* desc = *descendants.begin(); baseRadius = desc->getTopRadius(); bc_radius = max(topRadius,desc->getBCRadius()); bc_height = desc->getBCHeight() + 1.0f; desc->center(); } else // descendants.size() > 1 { // sort descendants by size in ascending order sort(descendants.begin(),descendants.end(),Comp_BCRadius()); // determine whether a unique smallest descendant exists Cluster* smallest = descendants[0]; Cluster* nextSmallest = descendants[1]; bool uniqueSmallest = ((nextSmallest->getBCRadius()-smallest->getBCRadius()) / smallest->getBCRadius()) > 0.01f; // determine whether a unique largest descendant exists Cluster* largest = descendants[descendants.size()-1]; Cluster* nextLargest = descendants[descendants.size()-2]; bool uniqueLargest = ((nextLargest->getBCRadius()-largest->getBCRadius()) / largest->getBCRadius()) < -0.01f; // invariant: descendants in range [x,y) have not been assigned a position int x = 0; int y = static_cast<int>(descendants.size()); float bcr_center = 0.0f; // BC radius of largest descendant in center float bcr_rim = largest->getBCRadius(); // BC radius of largest descendant on rim if (uniqueLargest) { // center the largest descendant largest->center(); --y; bcr_center = largest->getBCRadius(); bcr_rim = nextLargest->getBCRadius(); } if (uniqueSmallest && (!uniqueLargest || !smallest->hasDescendants())) { // center the smallest descendant smallest->center(); ++x; if (!uniqueLargest) { bcr_center = smallest->getBCRadius(); } if (y-x == 0) { bcr_rim = 0.0f; } } if (y-x == 1) { ++y; bcr_rim = largest->getBCRadius(); if (smallest->isCentered()) { bcr_center = smallest->getBCRadius(); } } // compute the radius of the base of the cylinder and the cluster's size float min_radius1 = 0.0f; if (y-x > 1) { min_radius1 = (float)(bcr_rim / sin(PI / (y-x))); } float min_radius2 = bcr_center; if (y-x > 0) { min_radius2 += bcr_rim + 0.01f; } baseRadius = max(min_radius1,min_radius2); bc_radius = max(min_radius1 + bcr_rim,min_radius2); bc_radius = max(topRadius,bc_radius); bc_height = 0.0f; for (unsigned int i = 0; i < descendants.size(); ++i) { if (descendants[i]->getBCHeight() > bc_height) { bc_height = descendants[i]->getBCHeight(); } } bc_height += 1.0f; // Divide the remaining descendants over the rim of the circle. float angle = 360.0f / (y-x); int i = 0; while (x+i != y) { descendants[x+i]->setPosition(i*angle); ++i; } } }