calculateLayout static method
- ElectricalNode? root
Calculate positions for all nodes in the tree Returns a map of node IDs to their calculated positions
Implementation
static Map<String, Offset> calculateLayout(ElectricalNode? root) {
if (root == null) return {};
final Map<String, Offset> positions = {};
final Map<String, double> subtreeWidths = {};
const double nodeW = kNodeWidth;
const double siblingGap = 50.0;
const double levelHeight = 250.0;
// Phase 1: Calculate subtree widths (bottom-up)
double calculateWidth(ElectricalNode node) {
final children = _getChildren(node);
if (children.isEmpty) {
subtreeWidths[node.id] = nodeW;
return nodeW;
}
double width = 0;
for (var child in children) {
width += calculateWidth(child);
}
width += (children.length - 1) * siblingGap;
final myW = (width > nodeW ? width : nodeW);
subtreeWidths[node.id] = myW;
return myW;
}
calculateWidth(root);
// Phase 2: Assign positions (top-down)
void assignPosition(ElectricalNode node, double x, double y) {
final myWidth = subtreeWidths[node.id]!;
final centeredX = x + (myWidth - nodeW) / 2;
// Ensure every node gets a position (including Panels)
positions[node.id] = Offset(centeredX, y);
final children = _getChildren(node);
if (children.isEmpty) return;
final childY = y + levelHeight;
// Force Center children distribution
double totalChildrenWidth = 0;
for (var child in children) {
totalChildrenWidth += subtreeWidths[child.id]!;
}
if (children.length > 1) {
totalChildrenWidth += (children.length - 1) * siblingGap;
}
// Start X for children (Centered under parent's allocated width)
double currentX = x + (myWidth - totalChildrenWidth) / 2;
for (var child in children) {
assignPosition(child, currentX, childY);
currentX += subtreeWidths[child.id]! + siblingGap;
}
}
// Start layout in "middle" of canvas (matching old implementation)
// Canvas is 5000x5000, so we start at (2500, 150) for proper centering
assignPosition(root, 2500, 150);
return positions;
}