namespace StellaOps.ElkSharp; internal static partial class ElkNodePlacement { internal static void RefineHorizontalPlacement( Dictionary positionedNodes, IReadOnlyList layers, IReadOnlyDictionary> incomingNodeIds, IReadOnlyDictionary> outgoingNodeIds, IReadOnlyDictionary nodesById, double nodeSpacing, int iterationCount, ElkLayoutDirection direction) { if (iterationCount <= 0) { return; } for (var iteration = 0; iteration < iterationCount; iteration++) { var layerIndices = iteration % 2 == 0 ? Enumerable.Range(0, layers.Count) : Enumerable.Range(0, layers.Count).Reverse(); foreach (var layerIndex in layerIndices) { var layer = layers[layerIndex]; if (layer.Length == 0) { continue; } var desiredY = new double[layer.Length]; for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++) { var node = layer[nodeIndex]; var preferredCenter = ElkNodePlacementPreferredCenter.ResolvePreferredCenter( node.Id, incomingNodeIds, outgoingNodeIds, positionedNodes, horizontal: true); desiredY[nodeIndex] = preferredCenter.HasValue ? preferredCenter.Value - (node.Height / 2d) : positionedNodes[node.Id].Y; } for (var nodeIndex = 1; nodeIndex < layer.Length; nodeIndex++) { var minY = desiredY[nodeIndex - 1] + layer[nodeIndex - 1].Height + nodeSpacing; if (desiredY[nodeIndex] < minY) { desiredY[nodeIndex] = minY; } } for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++) { var current = positionedNodes[layer[nodeIndex].Id]; positionedNodes[layer[nodeIndex].Id] = ElkLayoutHelpers.CreatePositionedNode( nodesById[layer[nodeIndex].Id], current.X, desiredY[nodeIndex], direction); } } } } internal static void RefineVerticalPlacement( Dictionary positionedNodes, IReadOnlyList layers, IReadOnlyDictionary> incomingNodeIds, IReadOnlyDictionary> outgoingNodeIds, IReadOnlyDictionary nodesById, double nodeSpacing, int iterationCount, ElkLayoutDirection direction) { if (iterationCount <= 0) { return; } for (var iteration = 0; iteration < iterationCount; iteration++) { var layerIndices = iteration % 2 == 0 ? Enumerable.Range(0, layers.Count) : Enumerable.Range(0, layers.Count).Reverse(); foreach (var layerIndex in layerIndices) { var layer = layers[layerIndex]; if (layer.Length == 0) { continue; } var desiredX = new double[layer.Length]; for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++) { var node = layer[nodeIndex]; var preferredCenter = ElkNodePlacementPreferredCenter.ResolvePreferredCenter( node.Id, incomingNodeIds, outgoingNodeIds, positionedNodes, horizontal: false); desiredX[nodeIndex] = preferredCenter.HasValue ? preferredCenter.Value - (node.Width / 2d) : positionedNodes[node.Id].X; } for (var nodeIndex = 1; nodeIndex < layer.Length; nodeIndex++) { var minX = desiredX[nodeIndex - 1] + layer[nodeIndex - 1].Width + nodeSpacing; if (desiredX[nodeIndex] < minX) { desiredX[nodeIndex] = minX; } } for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++) { var current = positionedNodes[layer[nodeIndex].Id]; positionedNodes[layer[nodeIndex].Id] = ElkLayoutHelpers.CreatePositionedNode( nodesById[layer[nodeIndex].Id], desiredX[nodeIndex], current.Y, direction); } } } } internal static void SnapOriginalPrimaryAxes( Dictionary positionedNodes, IReadOnlyList layers, IReadOnlySet dummyNodeIds, IReadOnlyDictionary> incomingNodeIds, IReadOnlyDictionary> outgoingNodeIds, IReadOnlyDictionary originalNodesById, double nodeSpacing, ElkLayoutDirection direction) { for (var iteration = 0; iteration < 3; iteration++) { foreach (var layer in layers) { var actualNodes = layer .Where(node => !dummyNodeIds.Contains(node.Id) && originalNodesById.ContainsKey(node.Id)) .Select(node => originalNodesById[node.Id]) .ToArray(); if (actualNodes.Length == 0) { continue; } var nodeDesiredPairs = new (ElkNode Node, double Desired)[actualNodes.Length]; for (var nodeIndex = 0; nodeIndex < actualNodes.Length; nodeIndex++) { var positioned = positionedNodes[actualNodes[nodeIndex].Id]; var preferredCenter = ElkNodePlacementPreferredCenter.ResolveOriginalPreferredCenter( actualNodes[nodeIndex].Id, incomingNodeIds, outgoingNodeIds, positionedNodes, horizontal: direction == ElkLayoutDirection.LeftToRight); nodeDesiredPairs[nodeIndex] = (actualNodes[nodeIndex], preferredCenter.HasValue ? preferredCenter.Value - ((direction == ElkLayoutDirection.LeftToRight ? positioned.Height : positioned.Width) / 2d) : (direction == ElkLayoutDirection.LeftToRight ? positioned.Y : positioned.X)); } Array.Sort(nodeDesiredPairs, (a, b) => a.Desired.CompareTo(b.Desired)); var sortedNodes = nodeDesiredPairs.Select(p => p.Node).ToArray(); var desiredCoordinates = nodeDesiredPairs.Select(p => p.Desired).ToArray(); EnforceLinearSpacing( sortedNodes, desiredCoordinates, nodeSpacing, 0d, horizontal: direction == ElkLayoutDirection.LeftToRight); for (var nodeIndex = 0; nodeIndex < sortedNodes.Length; nodeIndex++) { var current = positionedNodes[sortedNodes[nodeIndex].Id]; positionedNodes[sortedNodes[nodeIndex].Id] = direction == ElkLayoutDirection.LeftToRight ? ElkLayoutHelpers.CreatePositionedNode(sortedNodes[nodeIndex], current.X, desiredCoordinates[nodeIndex], direction) : ElkLayoutHelpers.CreatePositionedNode(sortedNodes[nodeIndex], desiredCoordinates[nodeIndex], current.Y, direction); } } } } }