Improve rendering
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static class ElkSharpLayoutInitialPlacement
|
||||
{
|
||||
internal static void PlaceNodesLeftToRight(
|
||||
Dictionary<string, ElkPositionedNode> positionedNodes, ElkNode[][] layers,
|
||||
DummyNodeResult dummyResult, Dictionary<string, List<string>> augmentedIncoming,
|
||||
Dictionary<string, List<string>> augmentedOutgoing, Dictionary<string, ElkNode> augmentedNodesById,
|
||||
Dictionary<string, List<string>> incomingNodeIds, Dictionary<string, List<string>> outgoingNodeIds,
|
||||
Dictionary<string, ElkNode> nodesById, double adaptiveNodeSpacing,
|
||||
ElkLayoutOptions options, int placementIterations)
|
||||
{
|
||||
var globalNodeHeight = augmentedNodesById.Values
|
||||
.Where(n => !dummyResult.DummyNodeIds.Contains(n.Id))
|
||||
.Max(x => x.Height);
|
||||
var edgeDensityFactor = adaptiveNodeSpacing / options.NodeSpacing;
|
||||
var adaptiveLayerSpacing = options.LayerSpacing * Math.Min(1.15d, 0.92d + (Math.Max(0d, edgeDensityFactor - 1d) * 0.35d));
|
||||
|
||||
var layerXPositions = new double[layers.Length];
|
||||
var currentX = 0d;
|
||||
for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
|
||||
{
|
||||
layerXPositions[layerIndex] = currentX;
|
||||
currentX += layers[layerIndex].Max(x => x.Width) + adaptiveLayerSpacing;
|
||||
}
|
||||
|
||||
var slotHeight = globalNodeHeight;
|
||||
for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
|
||||
{
|
||||
var layer = layers[layerIndex];
|
||||
var desiredY = new double[layer.Length];
|
||||
|
||||
for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
var node = layer[nodeIndex];
|
||||
var centers = new List<double>();
|
||||
foreach (var srcId in augmentedIncoming[node.Id])
|
||||
{
|
||||
if (positionedNodes.TryGetValue(srcId, out var srcPos))
|
||||
{
|
||||
centers.Add(srcPos.Y + (srcPos.Height / 2d));
|
||||
}
|
||||
}
|
||||
|
||||
if (centers.Count > 0)
|
||||
{
|
||||
centers.Sort();
|
||||
var mid = centers.Count / 2;
|
||||
var median = centers.Count % 2 == 1
|
||||
? centers[mid]
|
||||
: (centers[mid - 1] + centers[mid]) / 2d;
|
||||
desiredY[nodeIndex] = median - (node.Height / 2d);
|
||||
}
|
||||
else
|
||||
{
|
||||
desiredY[nodeIndex] = nodeIndex * (slotHeight + adaptiveNodeSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
for (var nodeIndex = 1; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
var prevIsDummy = dummyResult.DummyNodeIds.Contains(layer[nodeIndex - 1].Id);
|
||||
var currIsDummy = dummyResult.DummyNodeIds.Contains(layer[nodeIndex].Id);
|
||||
var pairSpacing = (prevIsDummy && currIsDummy) ? 2d
|
||||
: (prevIsDummy || currIsDummy) ? Math.Min(adaptiveNodeSpacing, options.NodeSpacing * 0.5d)
|
||||
: adaptiveNodeSpacing;
|
||||
var minY = desiredY[nodeIndex - 1] + layer[nodeIndex - 1].Height + pairSpacing;
|
||||
if (desiredY[nodeIndex] < minY)
|
||||
{
|
||||
desiredY[nodeIndex] = minY;
|
||||
}
|
||||
}
|
||||
|
||||
for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
positionedNodes[layer[nodeIndex].Id] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
layer[nodeIndex], layerXPositions[layerIndex], desiredY[nodeIndex], options.Direction);
|
||||
}
|
||||
}
|
||||
|
||||
var minNodeY = positionedNodes.Values.Min(n => n.Y);
|
||||
if (minNodeY < -0.01d)
|
||||
{
|
||||
foreach (var nodeId in positionedNodes.Keys.ToArray())
|
||||
{
|
||||
var pos = positionedNodes[nodeId];
|
||||
positionedNodes[nodeId] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
augmentedNodesById[nodeId], pos.X, pos.Y - minNodeY, options.Direction);
|
||||
}
|
||||
}
|
||||
|
||||
ElkNodePlacement.RefineHorizontalPlacement(positionedNodes, layers,
|
||||
incomingNodeIds, outgoingNodeIds, augmentedNodesById,
|
||||
options.NodeSpacing, placementIterations, options.Direction);
|
||||
|
||||
ElkNodePlacement.SnapOriginalPrimaryAxes(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, outgoingNodeIds,
|
||||
nodesById, options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.CompactTowardIncomingFlow(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, nodesById,
|
||||
options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacement.SnapOriginalPrimaryAxes(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, outgoingNodeIds,
|
||||
nodesById, options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacementPreferredCenter.AlignDummyNodesToFlow(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, augmentedIncoming, augmentedOutgoing,
|
||||
augmentedNodesById, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.CenterMultiIncomingNodes(
|
||||
positionedNodes, incomingNodeIds, nodesById, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.PropagateSuccessorPositionBackward(
|
||||
positionedNodes, outgoingNodeIds, nodesById, options.Direction);
|
||||
|
||||
minNodeY = positionedNodes.Values.Min(n => n.Y);
|
||||
if (minNodeY < -0.01d)
|
||||
{
|
||||
foreach (var nodeId in positionedNodes.Keys.ToArray())
|
||||
{
|
||||
var pos = positionedNodes[nodeId];
|
||||
positionedNodes[nodeId] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
augmentedNodesById[nodeId], pos.X, pos.Y - minNodeY, options.Direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PlaceNodesTopToBottom(
|
||||
Dictionary<string, ElkPositionedNode> positionedNodes, ElkNode[][] layers,
|
||||
DummyNodeResult dummyResult, Dictionary<string, List<string>> augmentedIncoming,
|
||||
Dictionary<string, List<string>> augmentedOutgoing, Dictionary<string, ElkNode> augmentedNodesById,
|
||||
Dictionary<string, List<string>> incomingNodeIds, Dictionary<string, List<string>> outgoingNodeIds,
|
||||
Dictionary<string, ElkNode> nodesById, double globalNodeWidth,
|
||||
double adaptiveNodeSpacing, ElkLayoutOptions options, int placementIterations)
|
||||
{
|
||||
var layerYPositions = new double[layers.Length];
|
||||
var currentY = 0d;
|
||||
for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
|
||||
{
|
||||
layerYPositions[layerIndex] = currentY;
|
||||
currentY += layers[layerIndex].Max(x => x.Height) + options.LayerSpacing;
|
||||
}
|
||||
|
||||
var slotWidth = globalNodeWidth;
|
||||
for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
|
||||
{
|
||||
var layer = layers[layerIndex];
|
||||
var desiredX = new double[layer.Length];
|
||||
|
||||
for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
var node = layer[nodeIndex];
|
||||
var centers = new List<double>();
|
||||
foreach (var srcId in augmentedIncoming[node.Id])
|
||||
{
|
||||
if (positionedNodes.TryGetValue(srcId, out var srcPos))
|
||||
{
|
||||
centers.Add(srcPos.X + (srcPos.Width / 2d));
|
||||
}
|
||||
}
|
||||
|
||||
if (centers.Count > 0)
|
||||
{
|
||||
centers.Sort();
|
||||
var mid = centers.Count / 2;
|
||||
var median = centers.Count % 2 == 1
|
||||
? centers[mid]
|
||||
: (centers[mid - 1] + centers[mid]) / 2d;
|
||||
desiredX[nodeIndex] = median - (node.Width / 2d);
|
||||
}
|
||||
else
|
||||
{
|
||||
desiredX[nodeIndex] = nodeIndex * (slotWidth + adaptiveNodeSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
for (var nodeIndex = 1; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
var prevIsDummyX = dummyResult.DummyNodeIds.Contains(layer[nodeIndex - 1].Id);
|
||||
var currIsDummyX = dummyResult.DummyNodeIds.Contains(layer[nodeIndex].Id);
|
||||
var pairSpacingX = (prevIsDummyX && currIsDummyX) ? 2d
|
||||
: (prevIsDummyX || currIsDummyX) ? Math.Min(adaptiveNodeSpacing, options.NodeSpacing * 0.5d)
|
||||
: adaptiveNodeSpacing;
|
||||
var minX = desiredX[nodeIndex - 1] + layer[nodeIndex - 1].Width + pairSpacingX;
|
||||
if (desiredX[nodeIndex] < minX)
|
||||
{
|
||||
desiredX[nodeIndex] = minX;
|
||||
}
|
||||
}
|
||||
|
||||
for (var nodeIndex = 0; nodeIndex < layer.Length; nodeIndex++)
|
||||
{
|
||||
positionedNodes[layer[nodeIndex].Id] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
layer[nodeIndex], desiredX[nodeIndex], layerYPositions[layerIndex], options.Direction);
|
||||
}
|
||||
}
|
||||
|
||||
var minNodeX = positionedNodes.Values.Min(n => n.X);
|
||||
if (minNodeX < -0.01d)
|
||||
{
|
||||
foreach (var nodeId in positionedNodes.Keys.ToArray())
|
||||
{
|
||||
var pos = positionedNodes[nodeId];
|
||||
positionedNodes[nodeId] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
augmentedNodesById[nodeId], pos.X - minNodeX, pos.Y, options.Direction);
|
||||
}
|
||||
}
|
||||
|
||||
ElkNodePlacement.RefineVerticalPlacement(positionedNodes, layers,
|
||||
incomingNodeIds, outgoingNodeIds, augmentedNodesById,
|
||||
options.NodeSpacing, placementIterations, options.Direction);
|
||||
|
||||
ElkNodePlacement.SnapOriginalPrimaryAxes(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, outgoingNodeIds,
|
||||
nodesById, options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.CompactTowardIncomingFlow(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, nodesById,
|
||||
options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacement.SnapOriginalPrimaryAxes(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, incomingNodeIds, outgoingNodeIds,
|
||||
nodesById, options.NodeSpacing, options.Direction);
|
||||
|
||||
ElkNodePlacementPreferredCenter.AlignDummyNodesToFlow(positionedNodes, layers,
|
||||
dummyResult.DummyNodeIds, augmentedIncoming, augmentedOutgoing,
|
||||
augmentedNodesById, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.CenterMultiIncomingNodes(
|
||||
positionedNodes, incomingNodeIds, nodesById, options.Direction);
|
||||
|
||||
ElkNodePlacementAlignment.PropagateSuccessorPositionBackward(
|
||||
positionedNodes, outgoingNodeIds, nodesById, options.Direction);
|
||||
|
||||
minNodeX = positionedNodes.Values.Min(n => n.X);
|
||||
if (minNodeX < -0.01d)
|
||||
{
|
||||
foreach (var nodeId in positionedNodes.Keys.ToArray())
|
||||
{
|
||||
var pos = positionedNodes[nodeId];
|
||||
positionedNodes[nodeId] = ElkLayoutHelpers.CreatePositionedNode(
|
||||
augmentedNodesById[nodeId], pos.X - minNodeX, pos.Y, options.Direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user