Blazor.Diagrams
Blazor.Diagrams copied to clipboard
[Contribution] Horizontal graph auto-layout code
Altought this code is specific for the model of my project (w/ InPorts & OutPorts) I've made a recursive method to organize the graph layout horizontally. Example: Before auto layout call & After layout call
In case this helps someone integration I'll drop the code here.
The function itself:
//NodeData is the custom NodeModel implementation
public delegate void SearchAction(NodeData node, int depthLevel, int branchesOffset);
public int DFS_SearchGraph(SearchAction searchAction, NodeData currentNode, int depthLevel, int maxBranchesPrevChilds)
{
var maxBranchesCurrent = 0;
int maxBranchesPrevChild = 0;
for (int i = 0; i < currentNode.OutPort.connectedNodes.Count; i++)
{
maxBranchesPrevChild = DFS_SearchGraph(searchAction, currentNode.OutPort.connectedNodes[i], depthLevel + 1, maxBranchesPrevChilds + maxBranchesCurrent);
if (i < currentNode.OutPort.connectedNodes.Count-1) //Has next branch
maxBranchesCurrent += maxBranchesPrevChild + 1;
else
maxBranchesCurrent += maxBranchesPrevChild;
}
searchAction(currentNode, depthLevel, maxBranchesPrevChilds);
return maxBranchesCurrent;
}
The delegate implementation & function call:
public void OrganizeLayout()
{
const int marginX = 400;
const int marginY = 120;
var rootNode = FindRootNode();
if (rootNode is null) return;
Dictionary<int, (double?, double?)> nodesPos = new();
foreach (NodeData node in diagram.Nodes)
nodesPos.Add(node.DataId, (null, null));
SearchAction organizeLayoutAction = (n, l, m) =>
{
var xFromCurrent = l * marginX;
var yFromCurrent = m * marginY;
if (nodesPos[n.DataId].Item1.HasValue) //If node already visited
{
var xSet = nodesPos[n.DataId].Item1.Value;
var ySet = nodesPos[n.DataId].Item2.Value;
nodesPos[n.DataId] = (xFromCurrent > xSet ? xFromCurrent : xSet, yFromCurrent < ySet ? yFromCurrent : ySet);
}
else
{
nodesPos[n.DataId] = (xFromCurrent, yFromCurrent);
}
};
DFS_SearchGraph(organizeLayoutAction, rootNode, 0, 0);
foreach (var nodePos in nodesPos)
{
var test = nodePos;
var node = diagram.Nodes.Where(x => ((NodeData)x).DataId == nodePos.Key).FirstOrDefault();
node?.SetPosition(nodePos.Value.Item1.Value, nodePos.Value.Item2.Value);
}
diagram.Refresh();
}
public NodeData FindRootNode()
{
foreach (var node in diagram.Nodes)
if (((NodeData)node).InPort.IsPortEmpty())
return (NodeData)node;
return null;
}
Hello, would it be possible to see how this looks like?
Thank you for the initiative!
Hi, I've already placed two pictures above with the previous & after result (in the above comment). Later on I can try to simplify the project and make public branch of it.
That's looking very neat! Was there any article you based yourself on? I'd like to implement these algorithms, but I kind of want the same for multiple directions/orientations.
The graph search algorithm is based on DFS but the rest was made by head w/ some trial and error, if that makes sense? To have multiple directions/orientations I'm guessing: You can probably just alter the marginX & marginY values to negative and invert the flow direction. Or use depthLevel for Y position & branchesOffset for X position to make it flow vertically. But never tried it out tho
Thanks so much for posting this. I was thinking of how to get this to align in an 'org chart' type manner, and this will be a good starting point!
Hi there,
I began trying to put this in place, but there are some properties I'm not seeing in the standard node implementation:
- 'Outport' on nodes
- 'connectedNodes' on ports
- dataId on nodes
@ud-waffle would you mind providing an example node that implements these?
Never mind @ud-waffle, I implemented those. Thanks for the very handy code!
Hi @glinkot , would you mind sharing your implementation?