From 7fbe1661ec62b677efb27c028e4276bcca9faafe Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Sun, 9 Jun 2024 15:32:28 +0200 Subject: [PATCH] #5237 Fix for edges in when using elk and subgraphs regarding offset and direction of marker in some edge cases --- cypress/platform/knsv2.html | 36 ++++--- packages/mermaid-layout-elk/src/render.ts | 17 ++-- .../mermaid/src/diagrams/flowchart/flowDb.ts | 95 +++++++++++++------ packages/mermaid/src/rendering-util/render.ts | 4 + .../rendering-elements/edges.js | 20 ++-- .../rendering-elements/markers.js | 12 +-- packages/mermaid/src/utils/lineWithOffset.ts | 2 +- 7 files changed, 119 insertions(+), 67 deletions(-) diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 514f98409..b2598bfbc 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -75,22 +75,28 @@ -
-stateDiagram-v2
-    state if_state <>
-    [*] --> IsPositive
-    IsPositive --> if_state
-    if_state --> False: if n < 0
-    if_state --> True : if n >= 0
+    
+stateDiagram
+direction LR
+      state Gorilla0 {
+        state Apa0 {
+          A0 --> B0
+        }
+
+      }
+      Apa0 --> C0
+      A0 --> C0
       
 flowchart LR
-    subgraph Apa
-      A[Start] --> B
+    subgraph Gorilla
+      subgraph Apa
+        A[A] --- B
+      end
     end
-    Apa --> C
-    A --> C
+    Apa --- C
+    A --x C
 
       
@@ -117,7 +123,7 @@ flowchart LR if_state --> True : if n >= 0
-
+    
       %%{init: {"layout": "elk", "mergeEdges": false, "elk.nodePlacement.strategy": "SIMPLE"} }%%
       stateDiagram
     state if_state <<choice>>
@@ -132,8 +138,10 @@ flowchart LR
 stateDiagram
   direction TB
   State T1 {
-    T11
+    T11 --> T12
   }
+  T1 --> T2
+  T11 --> T2
       
@@ -220,7 +228,7 @@ stateDiagram-v2
         look: 'handdrawn',
         // 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
         // layout: 'dagre',
-        // layout: 'elk',
+        layout: 'elk',
         // layout: 'fixed',
         // htmlLabels: false,
         flowchart: { titleTopMargin: 10 },
diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts
index b5ed9a8d3..379cffefd 100644
--- a/packages/mermaid-layout-elk/src/render.ts
+++ b/packages/mermaid-layout-elk/src/render.ts
@@ -60,7 +60,7 @@ export const addVertex = async (nodeEl, graph, nodeArr, node) => {
   graph.children.push(child);
   nodeDb[node.id] = child;
 
-  //     // Add the element to the DOM
+  // Add the element to the DOM
   if (!node.isGroup) {
     const childNodeEl = await insertNode(nodeEl, node, node.dir);
     boundingBox = childNodeEl.node().getBBox();
@@ -93,7 +93,7 @@ export const addVertex = async (nodeEl, graph, nodeArr, node) => {
 
 export const addVertices = async function (nodeEl, nodeArr, graph, parentId) {
   const siblings = nodeArr.filter((node) => node.parentId === parentId);
-  log.info('addVertices DAGA', siblings, parentId);
+  log.info('addVertices APA12', siblings, parentId);
   // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
   await Promise.all(
     siblings.map(async (node) => {
@@ -512,9 +512,7 @@ export const render = async (data4Layout, svg, element, algorithm) => {
     const node = nodeDb[n.id];
 
     // Subgraph
-    console.log('Subgraph XCX before');
     if (parentLookupDb.childrenById[node.id] !== undefined) {
-      console.log('Subgraph XCX', node.id, node, node.labelData);
       node.labels = [
         {
           text: node.labelText,
@@ -553,9 +551,9 @@ export const render = async (data4Layout, svg, element, algorithm) => {
     }
   });
 
-  console.log('before layout', JSON.stringify(elkGraph, null, 2));
+  // log.info('before layout', JSON.stringify(elkGraph, null, 2));
   const g = await elk.layout(elkGraph);
-  log.info('after layout', JSON.stringify(g));
+  // log.info('after layout', JSON.stringify(g));
 
   // debugger;
   drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
@@ -563,11 +561,11 @@ export const render = async (data4Layout, svg, element, algorithm) => {
     // (elem, edge, clusterDb, diagramType, graph, id)
     const startNode = nodeDb[edge.sources[0]];
     const endNode = nodeDb[edge.targets[0]];
-    const sourceId = edge.start.id;
-    const targetId = edge.end.id;
+    const sourceId = edge.start;
+    const targetId = edge.end;
 
     const offset = calcOffset(sourceId, targetId, parentLookupDb);
-
+    log.info('APA12 offset', offset, sourceId, targetId, edge);
     if (edge.sections) {
       const src = edge.sections[0].startPoint;
       const dest = edge.sections[0].endPoint;
@@ -590,6 +588,7 @@ export const render = async (data4Layout, svg, element, algorithm) => {
         endNode,
         data4Layout.diagramId
       );
+      log.info('APA12 edge points after insert', JSON.stringify(edge.points));
 
       edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
       edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
index b3976adf5..b2c38efe8 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
@@ -767,35 +767,22 @@ const getTypeFromVertex = (vertex: FlowVertex) => {
   return vertex.type || 'squareRect';
 };
 
-export const getData = () => {
-  const config = getConfig();
-  const nodes: Node[] = [];
-  const edges: Edge[] = [];
+const findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id);
 
-  // extract(getRootDocV2());
-  // const diagramStates = getStates();
-  const useRough = config.look === 'handdrawn';
-  const subGraphs = getSubGraphs();
-  log.info('Subgraphs - ', subGraphs);
-  const parentDB = new Map();
-  const subGraphDB = new Map();
+const addNodeFromVertex = (
+  vertex: FlowVertex,
+  nodes: Node[],
+  parentDB: Map,
+  subGraphDB: Map,
+  config: any,
+  useRough: boolean
+): Node => {
+  let parentId = parentDB.get(vertex.id);
+  let isGroup = subGraphDB.get(vertex.id) || false;
 
-  for (let i = subGraphs.length - 1; i >= 0; i--) {
-    const subGraph = subGraphs[i];
-    if (subGraph.nodes.length > 0) {
-      subGraphDB.set(subGraph.id, true);
-    }
-    subGraph.nodes.forEach((id) => {
-      parentDB.set(id, subGraph.id);
-    });
-  }
-
-  const n = getVertices();
-  n.forEach((vertex) => {
-    let parentId = parentDB.get(vertex.id);
-    let isGroup = subGraphDB.get(vertex.id) || false;
-
-    const node: Node = {
+  let node = findNode(nodes, vertex.id);
+  if (!node) {
+    nodes.push({
       id: vertex.id,
       label: vertex.text,
       labelStyle: '',
@@ -809,10 +796,59 @@ export const getData = () => {
       type: isGroup ? 'group' : undefined,
       isGroup,
       useRough,
-    };
-    nodes.push(node);
+    });
+  }
+};
+
+export const getData = () => {
+  const config = getConfig();
+  const nodes: Node[] = [];
+  const edges: Edge[] = [];
+
+  // extract(getRootDocV2());
+  // const diagramStates = getStates();
+  const useRough = config.look === 'handdrawn';
+  const subGraphs = getSubGraphs();
+  log.info('Subgraphs - APA12', subGraphs);
+  const parentDB = new Map();
+  const subGraphDB = new Map();
+
+  for (let i = subGraphs.length - 1; i >= 0; i--) {
+    const subGraph = subGraphs[i];
+    if (subGraph.nodes.length > 0) {
+      subGraphDB.set(subGraph.id, true);
+    }
+    subGraph.nodes.forEach((id) => {
+      parentDB.set(id, subGraph.id);
+    });
+    nodes.push({
+      id: subGraph.id,
+      label: subGraph.title,
+      labelStyle: '',
+      parentId: parentDB.get(subGraph.id),
+      padding: config.flowchart?.padding || 8,
+      cssStyles: '',
+      cssClasses: '',
+      shape: 'rect',
+      dir: subGraph.dir,
+      domId: subGraph.domId,
+      type: 'group',
+      isGroup: true,
+      useRough,
+    });
+  }
+  console.log('APA12 nodes - 1', nodes.length);
+
+  const n = getVertices();
+  n.forEach((vertex) => {
+    const node = addNodeFromVertex(vertex, nodes, parentDB, subGraphDB, config, useRough);
+    if (node) {
+      nodes.push(node);
+    }
   });
 
+  console.log('APA12 nodes', nodes.length);
+
   const e = getEdges();
   e.forEach((rawEdge, index) => {
     const edge: Edge = {
@@ -829,6 +865,7 @@ export const getData = () => {
       classes: 'edge-thickness-normal edge-pattern-solid flowchart-link',
       arrowhead: 'none',
       arrowTypeEnd: 'arrow_point',
+      // arrowTypeEnd: 'arrow_barb',
       arrowheadStyle: 'fill: #333',
       // stroke: rawEdge.pattern,
       pattern: rawEdge.stroke,
diff --git a/packages/mermaid/src/rendering-util/render.ts b/packages/mermaid/src/rendering-util/render.ts
index 489e6ba58..442780c75 100644
--- a/packages/mermaid/src/rendering-util/render.ts
+++ b/packages/mermaid/src/rendering-util/render.ts
@@ -24,6 +24,10 @@ const registerDefaultLayoutLoaders = () => {
       name: 'dagre',
       loader: async () => await import('./layout-algorithms/dagre/index.js'),
     },
+    // {
+    //   name: 'elk',
+    //   loader: async () => await import('../../../mermaid-layout-elk/src/render.js'),
+    // },
   ]);
 };
 
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
index 8b9e03fc7..cf1da0909 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
@@ -533,14 +533,14 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
   let lineData = points.filter((p) => !Number.isNaN(p.y));
   const { cornerPoints, cornerPointPositions } = extractCornerPoints(lineData);
   lineData = fixCorners(lineData);
-  let lastPoint = lineData[0];
+  let lastPoint = lineData[lineData.length - 1];
   if (lineData.length > 1) {
     lastPoint = lineData[lineData.length - 1];
     const secondLastPoint = lineData[lineData.length - 2];
     // Calculate the mid point of the last two points
-    const diffX = (lastPoint.x - secondLastPoint.x) / 4;
-    const diffY = (lastPoint.y - secondLastPoint.y) / 4;
-    const midPoint = { x: secondLastPoint.x + 3 * diffX, y: secondLastPoint.y + 3 * diffY };
+    const diffX = (lastPoint.x - secondLastPoint.x) / 2;
+    const diffY = (lastPoint.y - secondLastPoint.y) / 2;
+    const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY };
     lineData.splice(-1, 0, midPoint);
   }
   // This is the accessor function we talked about above
@@ -596,11 +596,16 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
   let useRough = edge.useRough;
   let svgPath;
   let path = '';
-
+  let linePath = lineFunction(lineData);
   if (useRough) {
     const rc = rough.svg(elem);
     const ld = Object.assign([], lineData);
-    const svgPathNode = rc.path(lineFunction(ld.splice(0, ld.length - 1)), {
+    // const svgPathNode = rc.path(lineFunction(ld.splice(0, ld.length-1)), {
+    // const svgPathNode = rc.path(lineFunction(ld), {
+    //   roughness: 0.3,
+    //   seed: handdrawnSeed,
+    // });
+    const svgPathNode = rc.path(linePath, {
       roughness: 0.3,
       seed: handdrawnSeed,
     });
@@ -614,13 +619,12 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
       .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
       .attr('style', edge.style);
     let d = svgPath.attr('d');
-    d = d + ' L ' + lastPoint.x + ' ' + lastPoint.y;
     svgPath.attr('d', d);
     elem.node().appendChild(svgPath.node());
   } else {
     svgPath = elem
       .append('path')
-      .attr('d', lineFunction(lineData))
+      .attr('d', linePath)
       .attr('id', edge.id)
       .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
       .attr('style', edge.style);
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/markers.js b/packages/mermaid/src/rendering-util/rendering-elements/markers.js
index c7cfcfe7f..00d45e47d 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/markers.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/markers.js
@@ -159,11 +159,11 @@ const point = (elem, type, id) => {
     .attr('id', id + '_' + type + '-pointEnd')
     .attr('class', 'marker ' + type)
     .attr('viewBox', '0 0 10 10')
-    .attr('refX', 6)
+    .attr('refX', 5)
     .attr('refY', 5)
     .attr('markerUnits', 'userSpaceOnUse')
-    .attr('markerWidth', 12)
-    .attr('markerHeight', 12)
+    .attr('markerWidth', 8)
+    .attr('markerHeight', 8)
     .attr('orient', 'auto')
     .append('path')
     .attr('d', 'M 0 0 L 10 5 L 0 10 z')
@@ -178,8 +178,8 @@ const point = (elem, type, id) => {
     .attr('refX', 4.5)
     .attr('refY', 5)
     .attr('markerUnits', 'userSpaceOnUse')
-    .attr('markerWidth', 12)
-    .attr('markerHeight', 12)
+    .attr('markerWidth', 11)
+    .attr('markerHeight', 11)
     .attr('orient', 'auto')
     .append('path')
     .attr('d', 'M 0 5 L 10 10 L 10 0 z')
@@ -272,7 +272,7 @@ const barb = (elem, type, id) => {
     .attr('refY', 7)
     .attr('markerWidth', 20)
     .attr('markerHeight', 14)
-    .attr('markerUnits', 'strokeWidth')
+    .attr('markerUnits', 'userSpaceOnUse')
     .attr('orient', 'auto')
     .append('path')
     .attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z');
diff --git a/packages/mermaid/src/utils/lineWithOffset.ts b/packages/mermaid/src/utils/lineWithOffset.ts
index af0cd3b46..8f82111f9 100644
--- a/packages/mermaid/src/utils/lineWithOffset.ts
+++ b/packages/mermaid/src/utils/lineWithOffset.ts
@@ -9,7 +9,7 @@ const markerOffsets = {
   composition: 18,
   dependency: 6,
   lollipop: 13.5,
-  arrow_point: 5.3,
+  arrow_point: 4,
 } as const;
 
 /**