mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-12 18:49:37 +02:00
Update of layout algorithm for widths
This commit is contained in:
@@ -66,8 +66,9 @@
|
|||||||
monkey -- l2 --> dog --> done2
|
monkey -- l2 --> dog --> done2
|
||||||
end
|
end
|
||||||
subgraph "`three`"
|
subgraph "`three`"
|
||||||
cow -- l3 --> done3
|
cow --> horse --> done3
|
||||||
end
|
cow --> sheep --> done3
|
||||||
|
end
|
||||||
cat --> monkey
|
cat --> monkey
|
||||||
cow --> dog
|
cow --> dog
|
||||||
</pre>
|
</pre>
|
||||||
@@ -79,10 +80,11 @@ swimlane LR
|
|||||||
subgraph "`two`"
|
subgraph "`two`"
|
||||||
monkey -- l2 --> dog --> done2
|
monkey -- l2 --> dog --> done2
|
||||||
end
|
end
|
||||||
subgraph "`three`"
|
subgraph "`three`"
|
||||||
cow -- l3 --> done3
|
cow --> horse --> done3
|
||||||
end
|
cow --> sheep --> done3
|
||||||
cat --> monkey
|
end
|
||||||
|
cat --> monkey
|
||||||
cow --> dog
|
cow --> dog
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { max } from 'lodash';
|
||||||
import { log } from '../../../logger.js';
|
import { log } from '../../../logger.js';
|
||||||
import flowDb from '../flowDb.js';
|
import flowDb from '../flowDb.js';
|
||||||
|
|
||||||
@@ -19,12 +20,14 @@ export const getSubgraphLookupTable = function (diagObj) {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param graph
|
* @param graph
|
||||||
* @param subgraphLÖookupTable
|
|
||||||
* @param subgraphLookupTable
|
* @param subgraphLookupTable
|
||||||
*/
|
*/
|
||||||
export function assignRanks(graph, subgraphLookupTable) {
|
export function assignRanks(graph, subgraphLookupTable) {
|
||||||
const visited = new Set();
|
let visited = new Set();
|
||||||
|
const lock = new Map();
|
||||||
const ranks = new Map();
|
const ranks = new Map();
|
||||||
|
let cnt = 0;
|
||||||
|
let changesDetected = true;
|
||||||
|
|
||||||
function dfs(nodeId, currentRank) {
|
function dfs(nodeId, currentRank) {
|
||||||
if (visited.has(nodeId)) {
|
if (visited.has(nodeId)) {
|
||||||
@@ -34,48 +37,123 @@ export function assignRanks(graph, subgraphLookupTable) {
|
|||||||
visited.add(nodeId);
|
visited.add(nodeId);
|
||||||
const existingRank = ranks.get(nodeId) || 0;
|
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) => {
|
graph.successors(nodeId).forEach((targetId) => {
|
||||||
if (subgraphLookupTable[targetId] !== subgraphLookupTable[nodeId]) {
|
if (subgraphLookupTable[targetId] !== subgraphLookupTable[nodeId]) {
|
||||||
dfs(targetId, currentRank);
|
dfs(targetId, currentRankAdjusted);
|
||||||
} else {
|
} else {
|
||||||
dfs(targetId, currentRank + 1);
|
// In same line, easy increase
|
||||||
|
dfs(targetId, currentRankAdjusted + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.nodes().forEach((nodeId) => {
|
function adjustSuccessors() {
|
||||||
if (graph.predecessors(nodeId).length === 0) {
|
graph.nodes().forEach((nodeId) => {
|
||||||
dfs(nodeId, 0);
|
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
|
// 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;
|
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 subgraphLookupTable = getSubgraphLookupTable(diagObj);
|
||||||
const ranks = assignRanks(graph, subgraphLookupTable);
|
const ranks = assignRanks(graph, subgraphLookupTable);
|
||||||
|
|
||||||
|
const {affinities, swimlaneMaxAffinity} = assignAffinities(graph, ranks, subgraphLookupTable);
|
||||||
|
// const affinities = assignAffinities(graph, ranks, subgraphLookupTable);
|
||||||
|
|
||||||
const subGraphs = diagObj.db.getSubGraphs();
|
const subGraphs = diagObj.db.getSubGraphs();
|
||||||
const lanes = [];
|
const lanes = [];
|
||||||
const laneDb = {};
|
const laneDb = {};
|
||||||
|
const xPos = 0;
|
||||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
const subG = subGraphs[i];
|
const subG = subGraphs[i];
|
||||||
|
const maxAffinity = swimlaneMaxAffinity.get(subG.id);
|
||||||
const lane = {
|
const lane = {
|
||||||
title: subG.title,
|
title: subG.title,
|
||||||
x: i * 200,
|
x: xPos,
|
||||||
width: 200,
|
width: 200 + maxAffinity*150,
|
||||||
};
|
};
|
||||||
|
xPos += lane.width;
|
||||||
lanes.push(lane);
|
lanes.push(lane);
|
||||||
laneDb[subG.id] = lane;
|
laneDb[subG.id] = lane;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rankWidth = [];
|
const rankWidth = [];
|
||||||
// Basic layout
|
// Basic layout, calculate the node positions based on rank
|
||||||
graph.nodes().forEach((nodeId) => {
|
graph.nodes().forEach((nodeId) => {
|
||||||
const rank = ranks.get(nodeId);
|
const rank = ranks.get(nodeId);
|
||||||
if (!rankWidth[rank]) {
|
if (!rankWidth[rank]) {
|
||||||
@@ -109,7 +193,11 @@ export function swimlaneLayout(graph, diagObj) {
|
|||||||
const lane = laneDb[laneId];
|
const lane = laneDb[laneId];
|
||||||
const n = graph.node(nodeId);
|
const n = graph.node(nodeId);
|
||||||
console.log('Node', nodeId, n);
|
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
5098
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user