mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 04:44:08 +01:00 
			
		
		
		
	#2028 Updated layout algorithm
This commit is contained in:
		@@ -57,7 +57,7 @@
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <pre style="background:black;color:white">
 | 
			
		||||
    <pre style="background: black; color: white">
 | 
			
		||||
      swimlane LR
 | 
			
		||||
          subgraph "`one`"
 | 
			
		||||
            start -- l1 --> cat --> rat
 | 
			
		||||
@@ -71,7 +71,8 @@
 | 
			
		||||
          end
 | 
			
		||||
          cat --> monkey
 | 
			
		||||
          cow --> dog
 | 
			
		||||
          </pre>
 | 
			
		||||
          </pre
 | 
			
		||||
    >
 | 
			
		||||
    <pre id="diagram" class="mermaid">
 | 
			
		||||
swimlane LR
 | 
			
		||||
    subgraph "`one`"
 | 
			
		||||
@@ -80,16 +81,31 @@ swimlane LR
 | 
			
		||||
    subgraph "`two`"
 | 
			
		||||
      monkey -- l2 --> dog --> done2
 | 
			
		||||
      end
 | 
			
		||||
      subgraph "`three`"
 | 
			
		||||
    subgraph "`three`"
 | 
			
		||||
      cow --> horse --> done3
 | 
			
		||||
      cow --> sheep --> done3
 | 
			
		||||
    end
 | 
			
		||||
    subgraph "`four`"
 | 
			
		||||
      panda -->
 | 
			
		||||
      kangaroo --> done4
 | 
			
		||||
    end
 | 
			
		||||
cat --> monkey
 | 
			
		||||
    cow --> dog
 | 
			
		||||
    kangaroo --> sheep
 | 
			
		||||
    </pre>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    monkey dog --> cat monkey --> cow -->
 | 
			
		||||
    <pre id="diagram" class="mermaid2">
 | 
			
		||||
swimlane LR
 | 
			
		||||
    subgraph "`three`"
 | 
			
		||||
      bat --> cow
 | 
			
		||||
      cow --> sheep
 | 
			
		||||
    end
 | 
			
		||||
    subgraph "`four`"
 | 
			
		||||
      panda -->
 | 
			
		||||
      kangaroo --> done4
 | 
			
		||||
    end
 | 
			
		||||
    kangaroo --> sheep
 | 
			
		||||
    </pre>
 | 
			
		||||
    <!-- <div id="cy"></div> -->
 | 
			
		||||
    <!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
 | 
			
		||||
    <!-- <script src="./mermaid-example-diagram-detector.js"></script>    -->
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,11 @@ export function assignRanks(graph, subgraphLookupTable) {
 | 
			
		||||
  let cnt = 0;
 | 
			
		||||
  let changesDetected = true;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   *
 | 
			
		||||
   * @param nodeId
 | 
			
		||||
   * @param currentRank
 | 
			
		||||
   */
 | 
			
		||||
  function dfs(nodeId, currentRank) {
 | 
			
		||||
    if (visited.has(nodeId)) {
 | 
			
		||||
      return;
 | 
			
		||||
@@ -36,7 +41,7 @@ export function assignRanks(graph, subgraphLookupTable) {
 | 
			
		||||
    visited.add(nodeId);
 | 
			
		||||
    const existingRank = ranks.get(nodeId) || 0;
 | 
			
		||||
 | 
			
		||||
    console.log('APA444 DFS Base case for', nodeId, 'to', 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 {
 | 
			
		||||
@@ -61,42 +66,52 @@ export function assignRanks(graph, subgraphLookupTable) {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   *
 | 
			
		||||
   */
 | 
			
		||||
  function adjustSuccessors() {
 | 
			
		||||
    console.log('APA444 Adjusting successors');
 | 
			
		||||
    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();
 | 
			
		||||
      console.log('APA444 Going through nodes', nodeId);
 | 
			
		||||
      // if (graph.predecessors(nodeId).length === 0) {
 | 
			
		||||
      console.log('APA444 has no predecessors', nodeId);
 | 
			
		||||
      graph.successors(nodeId).forEach((successorNodeId) => {
 | 
			
		||||
        console.log('APA444 has checking successor', 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();
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
          // 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();
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          console.log('APA444 Node', nodeId, ' and ', successorNodeId, ' is in the same lane');
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      // }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   *
 | 
			
		||||
   */
 | 
			
		||||
  function setRankFromTopNodes() {
 | 
			
		||||
    visited = new Set();
 | 
			
		||||
    graph.nodes().forEach((nodeId) => {
 | 
			
		||||
@@ -120,6 +135,7 @@ export function assignRanks(graph, subgraphLookupTable) {
 | 
			
		||||
 *
 | 
			
		||||
 * @param graph
 | 
			
		||||
 * @param subgraphLÖookupTable
 | 
			
		||||
 * @param ranks
 | 
			
		||||
 * @param subgraphLookupTable
 | 
			
		||||
 */
 | 
			
		||||
export function assignAffinities(graph, ranks, subgraphLookupTable) {
 | 
			
		||||
@@ -130,27 +146,27 @@ export function assignAffinities(graph, ranks, subgraphLookupTable) {
 | 
			
		||||
  graph.nodes().forEach((nodeId) => {
 | 
			
		||||
    const swimlane = subgraphLookupTable[nodeId];
 | 
			
		||||
    const rank = ranks.get(nodeId);
 | 
			
		||||
    const key = swimlane+':'+rank;
 | 
			
		||||
    const key = swimlane + ':' + rank;
 | 
			
		||||
    let currentAffinity = swimlaneRankAffinities.get(key);
 | 
			
		||||
    if(typeof currentAffinity === 'undefined'){
 | 
			
		||||
    if (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'){
 | 
			
		||||
    if (currentMaxAffinity === undefined) {
 | 
			
		||||
      swimlaneMaxAffinity.set(swimlane, 0);
 | 
			
		||||
      currentMaxAffinity = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if(newAffinity > currentMaxAffinity){
 | 
			
		||||
    if (newAffinity > currentMaxAffinity) {
 | 
			
		||||
      swimlaneMaxAffinity.set(swimlane, newAffinity);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // console.log('APA444 affinities', swimlaneRankAffinities);
 | 
			
		||||
 | 
			
		||||
  return {affinities, swimlaneMaxAffinity};
 | 
			
		||||
  return { affinities, swimlaneMaxAffinity };
 | 
			
		||||
  //return affinities;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -163,20 +179,19 @@ export function swimlaneLayout(graph, diagObj) {
 | 
			
		||||
  const subgraphLookupTable = getSubgraphLookupTable(diagObj);
 | 
			
		||||
  const ranks = assignRanks(graph, subgraphLookupTable);
 | 
			
		||||
 | 
			
		||||
  const {affinities, swimlaneMaxAffinity} = assignAffinities(graph, ranks, subgraphLookupTable);
 | 
			
		||||
  const { affinities, swimlaneMaxAffinity } = assignAffinities(graph, ranks, subgraphLookupTable);
 | 
			
		||||
  // const affinities = assignAffinities(graph, ranks, subgraphLookupTable);
 | 
			
		||||
 | 
			
		||||
  const subGraphs = diagObj.db.getSubGraphs();
 | 
			
		||||
  const lanes = [];
 | 
			
		||||
  const laneDb = {};
 | 
			
		||||
  let xPos = 0;
 | 
			
		||||
  for (let i = 0; i < subGraphs.length; i++) {
 | 
			
		||||
    const subG = subGraphs[i];
 | 
			
		||||
  for (const subG of subGraphs) {
 | 
			
		||||
    const maxAffinity = swimlaneMaxAffinity.get(subG.id);
 | 
			
		||||
    const lane = {
 | 
			
		||||
      title: subG.title,
 | 
			
		||||
      x: xPos,
 | 
			
		||||
      width: 200 + maxAffinity*150,
 | 
			
		||||
      width: 200 + maxAffinity * 150,
 | 
			
		||||
    };
 | 
			
		||||
    xPos += lane.width;
 | 
			
		||||
    lanes.push(lane);
 | 
			
		||||
@@ -187,6 +202,7 @@ export function swimlaneLayout(graph, diagObj) {
 | 
			
		||||
  // Basic layout, calculate the node positions based on rank
 | 
			
		||||
  graph.nodes().forEach((nodeId) => {
 | 
			
		||||
    const rank = ranks.get(nodeId);
 | 
			
		||||
 | 
			
		||||
    if (!rankWidth[rank]) {
 | 
			
		||||
      const laneId = subgraphLookupTable[nodeId];
 | 
			
		||||
      const lane = laneDb[laneId];
 | 
			
		||||
@@ -195,7 +211,7 @@ export function swimlaneLayout(graph, diagObj) {
 | 
			
		||||
      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 });
 | 
			
		||||
      graph.setNode(nodeId, { y: rank * 200 + 50, x: lane.x + 150 * affinity + 100 });
 | 
			
		||||
      // lane.width = Math.max(lane.width, lane.x + 150*affinity + lane.width / 4);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user