Update of layout algorithm for widths

This commit is contained in:
Knut Sveidqvist 2023-05-31 20:17:26 +02:00
parent 6fe35ef2d7
commit 0a31ebdcd1
3 changed files with 2711 additions and 2553 deletions

View File

@ -66,8 +66,9 @@
monkey -- l2 --> dog --> done2
end
subgraph "`three`"
cow -- l3 --> done3
end
cow --> horse --> done3
cow --> sheep --> done3
end
cat --> monkey
cow --> dog
</pre>
@ -79,10 +80,11 @@ swimlane LR
subgraph "`two`"
monkey -- l2 --> dog --> done2
end
subgraph "`three`"
cow -- l3 --> done3
end
cat --> monkey
subgraph "`three`"
cow --> horse --> done3
cow --> sheep --> done3
end
cat --> monkey
cow --> dog
</pre>

View File

@ -1,3 +1,4 @@
import { max } from 'lodash';
import { log } from '../../../logger.js';
import flowDb from '../flowDb.js';
@ -19,63 +20,140 @@ export const getSubgraphLookupTable = function (diagObj) {
/**
*
* @param graph
* @param subgraphLÖookupTable
* @param subgraphLookupTable
*/
export function assignRanks(graph, subgraphLookupTable) {
const visited = new Set();
let visited = new Set();
const lock = new Map();
const ranks = new Map();
let cnt = 0;
let changesDetected = true;
function dfs(nodeId, currentRank) {
if (visited.has(nodeId)) {
return;
}
visited.add(nodeId);
const existingRank = ranks.get(nodeId) || 0;
ranks.set(nodeId, Math.max(existingRank, currentRank));
console.log('APA444 DFS Base case for', nodeId, 'to', Math.max(existingRank, currentRank));
if (lock.get(nodeId) !== 1) {
ranks.set(nodeId, Math.max(existingRank, currentRank));
} else {
console.log(
'APA444 ',
nodeId,
'was locked to ',
existingRank,
'so not changing it',
ranks.get(nodeId)
);
}
const currentRankAdjusted = ranks.get(nodeId) || currentRank;
graph.successors(nodeId).forEach((targetId) => {
if (subgraphLookupTable[targetId] !== subgraphLookupTable[nodeId]) {
dfs(targetId, currentRank);
dfs(targetId, currentRankAdjusted);
} else {
dfs(targetId, currentRank + 1);
// In same line, easy increase
dfs(targetId, currentRankAdjusted + 1);
}
});
}
graph.nodes().forEach((nodeId) => {
if (graph.predecessors(nodeId).length === 0) {
dfs(nodeId, 0);
}
});
function adjustSuccessors() {
graph.nodes().forEach((nodeId) => {
if (graph.predecessors(nodeId).length === 0) {
graph.successors(nodeId).forEach((successorNodeId) => {
if (subgraphLookupTable[successorNodeId] !== subgraphLookupTable[nodeId]) {
const newRank = ranks.get(successorNodeId);
ranks.set(nodeId, newRank);
console.log('APA444 POST-process case for', nodeId, 'to', newRank);
lock.set(nodeId, 1);
changesDetected = true;
// setRankFromTopNodes();
// Adjust ranks of successors in the same subgraph
graph.successors(nodeId).forEach((sameSubGraphSuccessorNodeId) => {
if (
subgraphLookupTable[sameSubGraphSuccessorNodeId] === subgraphLookupTable[nodeId]
) {
console.log(
'APA444 Adjusting rank of',
sameSubGraphSuccessorNodeId,
'to',
newRank + 1
);
ranks.set(sameSubGraphSuccessorNodeId, newRank + 1);
lock.set(sameSubGraphSuccessorNodeId, 1);
changesDetected = true;
// dfs(sameSubGraphSuccessorNodeId, newRank + 1);
// setRankFromTopNodes();
}
});
}
});
}
});
}
function setRankFromTopNodes() {
visited = new Set();
graph.nodes().forEach((nodeId) => {
if (graph.predecessors(nodeId).length === 0) {
dfs(nodeId, 0);
}
});
adjustSuccessors();
}
while (changesDetected && cnt < 10) {
setRankFromTopNodes();
cnt++;
}
// Post-process the ranks
graph.nodes().forEach((nodeId) => {
if (graph.predecessors(nodeId).length === 0) {
graph.successors(nodeId).forEach((successorNodeId) => {
if (subgraphLookupTable[successorNodeId] !== subgraphLookupTable[nodeId]) {
const newRank = ranks.get(successorNodeId);
ranks.set(nodeId, newRank);
// Adjust ranks of successors in the same subgraph
graph.successors(nodeId).forEach((sameSubGraphSuccessorNodeId) => {
if (subgraphLookupTable[sameSubGraphSuccessorNodeId] === subgraphLookupTable[nodeId]) {
ranks.set(sameSubGraphSuccessorNodeId, newRank + 1);
}
});
}
});
}
});
return ranks;
}
/**
*
* @param graph
* @param subgraphLÖookupTable
* @param subgraphLookupTable
*/
export function assignAffinities(graph, ranks, subgraphLookupTable) {
const affinities = new Map();
const swimlaneRankAffinities = new Map();
const swimlaneMaxAffinity = new Map();
graph.nodes().forEach((nodeId) => {
const swimlane = subgraphLookupTable[nodeId];
const rank = ranks.get(nodeId);
const key = swimlane+':'+rank;
let currentAffinity = swimlaneRankAffinities.get(key);
if(typeof currentAffinity === 'undefined'){
currentAffinity = -1;
}
const newAffinity = currentAffinity + 1;
swimlaneRankAffinities.set(key, newAffinity);
affinities.set(nodeId, newAffinity);
let currentMaxAffinity = swimlaneMaxAffinity.get(swimlane);
if(typeof currentMaxAffinity === 'undefined'){
swimlaneMaxAffinity.set(swimlane, 0);
currentMaxAffinity = 0;
}
if(newAffinity > currentMaxAffinity){
swimlaneMaxAffinity.set(swimlane, newAffinity);
}
});
// console.log('APA444 affinities', swimlaneRankAffinities);
return {affinities, swimlaneMaxAffinity};
//return affinities;
}
/**
*
@ -86,22 +164,28 @@ export function swimlaneLayout(graph, diagObj) {
const subgraphLookupTable = getSubgraphLookupTable(diagObj);
const ranks = assignRanks(graph, subgraphLookupTable);
const {affinities, swimlaneMaxAffinity} = assignAffinities(graph, ranks, subgraphLookupTable);
// const affinities = assignAffinities(graph, ranks, subgraphLookupTable);
const subGraphs = diagObj.db.getSubGraphs();
const lanes = [];
const laneDb = {};
const xPos = 0;
for (let i = subGraphs.length - 1; i >= 0; i--) {
const subG = subGraphs[i];
const maxAffinity = swimlaneMaxAffinity.get(subG.id);
const lane = {
title: subG.title,
x: i * 200,
width: 200,
x: xPos,
width: 200 + maxAffinity*150,
};
xPos += lane.width;
lanes.push(lane);
laneDb[subG.id] = lane;
}
const rankWidth = [];
// Basic layout
// Basic layout, calculate the node positions based on rank
graph.nodes().forEach((nodeId) => {
const rank = ranks.get(nodeId);
if (!rankWidth[rank]) {
@ -109,7 +193,11 @@ export function swimlaneLayout(graph, diagObj) {
const lane = laneDb[laneId];
const n = graph.node(nodeId);
console.log('Node', nodeId, n);
graph.setNode(nodeId, { y: rank * 200 + 50, x: lane.x + lane.width / 2 });
const affinity = affinities.get(nodeId);
console.log('APA444', nodeId, 'rank', rank, 'affinity', affinity);
graph.setNode(nodeId, { y: rank * 200 + 50, x: lane.x + 150*affinity + lane.width / 2 });
// lane.width = Math.max(lane.width, lane.x + 150*affinity + lane.width / 4);
}
});

5098
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff