From 1e6419a63f7af7b8048187b7f894e19d87f7628b Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Wed, 27 Nov 2024 15:52:24 +0100 Subject: [PATCH 1/5] #6088 Updated offset calculations --- .../rendering/flowchart-elk.spec.js | 147 ++++++++++++++++++ cypress/platform/knsv2.html | 88 ++++++++--- packages/mermaid-layout-elk/src/render.ts | 34 ++-- 3 files changed, 223 insertions(+), 46 deletions(-) diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js index c3aba53ea..38bfe6440 100644 --- a/cypress/integration/rendering/flowchart-elk.spec.js +++ b/cypress/integration/rendering/flowchart-elk.spec.js @@ -900,6 +900,153 @@ flowchart LR n7@{ shape: rect} n8@{ shape: rect} +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-1: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + subgraph S2 + subgraph s1["APA"] + D{"Use the editor"} + end + + + D -- Mermaid js --> I{"fa:fa-code Text"} + D --> I + D --> I + + end +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-2: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + a + subgraph s0["APA"] + subgraph s8["APA"] + subgraph s1["APA"] + D{"X"} + E[Q] + end + subgraph s3["BAPA"] + F[Q] + I + end + D --> I + D --> I + D --> I + + I{"X"} + end + end + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-3: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- + flowchart LR + a + D{"Use the editor"} + + D -- Mermaid js --> I{"fa:fa-code Text"} + D-->I + D-->I + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-4: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + subgraph s1["Untitled subgraph"] + n1["Evaluate"] + n2["Option 1"] + n3["Option 2"] + n4["fa:fa-car Option 3"] + end + subgraph s2["Untitled subgraph"] + n5["Evaluate"] + n6["Option 1"] + n7["Option 2"] + n8["fa:fa-car Option 3"] + end + A["Start"] -- Some text --> B("Continue") + B --> C{"Evaluate"} + C -- One --> D["Option 1"] + C -- Two --> E["Option 2"] + C -- Three --> F["fa:fa-car Option 3"] + n1 -- One --> n2 + n1 -- Two --> n3 + n1 -- Three --> n4 + n5 -- One --> n6 + n5 -- Two --> n7 + n5 -- Three --> n8 + n1@{ shape: diam} + n2@{ shape: rect} + n3@{ shape: rect} + n4@{ shape: rect} + n5@{ shape: diam} + n6@{ shape: rect} + n7@{ shape: rect} + n8@{ shape: rect} + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + + it('6088-5: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + A{A} --> B & C + +`, + { flowchart: { titleTopMargin: 0 } } + ); + }); + it('6088-6: should handle diamond shape intersections', () => { + imgSnapshotTest( + `--- +config: + layout: elk +--- +flowchart LR + A{A} --> B & C + subgraph "subbe" + A + end + `, { flowchart: { titleTopMargin: 0 } } ); diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 7ec666c1a..1c7bda8e7 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -88,33 +88,61 @@ -
+    
 ---
 config:
   layout: elk
 ---
-flowchart LR
- subgraph s1["Untitled subgraph"]
-        n1["Evaluate"]
-        n2["Option 1"]
-        n3["Option 2"]
-        n4["fa:fa-car Option 3"]
-  end
-    n1 -- One --> n2
-    n1 -- Two --> n3
-    n1 -- Three --> n4
-    n5
-    n1@{ shape: diam}
-    n2@{ shape: rect}
-    n3@{ shape: rect}
-    n4@{ shape: rect}
-    A["Start"] -- Some text --> B("Continue")
-    B --> C{"Evaluate"}
-    C -- One --> D["Option 1"]
-    C -- Two --> E["Option 2"]
-    C -- Three --> F["fa:fa-car Option 3"]
+      flowchart LR
+      subgraph S2
+      subgraph s1["APA"]
+      D{"Use the editor"}
+      end
 
 
+      D -- Mermaid js --> I{"fa:fa-code Text"}
+            D --> I
+            D --> I
+
+      end
+    
+
+---
+config:
+  layout: elk
+---
+      flowchart LR
+      a
+      subgraph s0["APA"]
+      subgraph s8["APA"]
+      subgraph s1["APA"]
+        D{"X"}
+        E[Q]
+      end
+      subgraph s3["BAPA"]
+        F[Q]
+        I
+      end
+            D --> I
+            D --> I
+            D --> I
+
+      I{"X"}
+      end
+      end
+    
+
+---
+config:
+  layout: elk
+---
+      flowchart LR
+      a
+        D{"Use the editor"}
+
+      D -- Mermaid js --> I{"fa:fa-code Text"}
+      D-->I
+      D-->I
     
 ---
@@ -155,7 +183,7 @@ flowchart LR
     n8@{ shape: rect}
 
     
-
+    
 ---
 config:
   layout: elk
@@ -171,7 +199,7 @@ flowchart LR
 
 
     
-
+    
 ---
 config:
   layout: elk
@@ -180,7 +208,19 @@ flowchart LR
     A{A} --> B & C
 
-
+    
+---
+config:
+  layout: elk
+---
+flowchart LR
+    A{A} --> B & C
+    subgraph "subbe"
+      A
+    end
+
+
 ---
 config:
   layout: elk
diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts
index 1216b5dc8..59b97c557 100644
--- a/packages/mermaid-layout-elk/src/render.ts
+++ b/packages/mermaid-layout-elk/src/render.ts
@@ -705,14 +705,11 @@ export const render = async (
     bounds: { x: any; y: any; width: any; height: any; padding: any },
     isDiamond: boolean
   ) => {
-    log.debug('UIO cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
+    log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
     const points: any[] = [];
     let lastPointOutside = _points[0];
     let isInside = false;
     _points.forEach((point: any) => {
-      // const node = clusterDb[edge.toCluster].node;
-      log.debug(' checking point', point, bounds);
-
       // check if point is inside the boundary rect
       if (!outsideNode(bounds, point) && !isInside) {
         // First point inside the rect found
@@ -906,7 +903,7 @@ export const render = async (
 
       const offset = calcOffset(sourceId, targetId, parentLookupDb);
       log.debug(
-        'offset',
+        'APA18 offset',
         offset,
         sourceId,
         ' ==> ',
@@ -971,29 +968,22 @@ export const render = async (
         }
         if (startNode.shape === 'diamond' || startNode.shape === 'diam') {
           edge.points.unshift({
-            x: startNode.x + startNode.width / 2 + offset.x,
-            y: startNode.y + startNode.height / 2 + offset.y,
+            x: startNode.offset.posX + startNode.width / 2,
+            y: startNode.offset.posY + startNode.height / 2,
           });
         }
         if (endNode.shape === 'diamond' || endNode.shape === 'diam') {
-          const x = endNode.x + endNode.width / 2 + offset.x;
-          // Add a point at the center of the diamond
-          if (
-            Math.abs(edge.points[edge.points.length - 1].y - endNode.y - offset.y) > 0.01 ||
-            Math.abs(edge.points[edge.points.length - 1].x - x) > 0.001
-          ) {
-            edge.points.push({
-              x: endNode.x + endNode.width / 2 + offset.x,
-              y: endNode.y + endNode.height / 2 + offset.y,
-            });
-          }
+          edge.points.push({
+            x: endNode.offset.posX + endNode.width / 2,
+            y: endNode.offset.posY + endNode.height / 2,
+          });
         }
 
         edge.points = cutPathAtIntersect(
           edge.points.reverse(),
           {
-            x: startNode.x + startNode.width / 2 + offset.x,
-            y: startNode.y + startNode.height / 2 + offset.y,
+            x: startNode.offset.posX + startNode.width / 2,
+            y: startNode.offset.posY + startNode.height / 2,
             width: sw,
             height: startNode.height,
             padding: startNode.padding,
@@ -1004,8 +994,8 @@ export const render = async (
         edge.points = cutPathAtIntersect(
           edge.points,
           {
-            x: endNode.x + ew / 2 + endNode.offset.x,
-            y: endNode.y + endNode.height / 2 + endNode.offset.y,
+            x: endNode.offset.posX + endNode.width / 2,
+            y: endNode.offset.posY + endNode.height / 2,
             width: ew,
             height: endNode.height,
             padding: endNode.padding,

From c8e50276e877c4de7593a09ec458c99353e65af8 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Wed, 27 Nov 2024 15:54:05 +0100
Subject: [PATCH 2/5] Added changeset

---
 .changeset/hungry-guests-drive.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .changeset/hungry-guests-drive.md

diff --git a/.changeset/hungry-guests-drive.md b/.changeset/hungry-guests-drive.md
new file mode 100644
index 000000000..7a09fa1cc
--- /dev/null
+++ b/.changeset/hungry-guests-drive.md
@@ -0,0 +1,5 @@
+---
+'@mermaid-js/layout-elk': patch
+---
+
+fix: Updated offset calculations for diamond shape when handling intersections

From 4c8c48cde95c1ab9c53e147913cf4eabd6b83a93 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Thu, 28 Nov 2024 14:31:54 +0100
Subject: [PATCH 3/5] Generic solution for intersection of shapes with elk

---
 cypress/platform/knsv2.html                   |  59 +++-
 packages/mermaid-layout-elk/src/render.ts     | 308 +++---------------
 .../rendering-elements/edges.js               | 119 ++-----
 .../intersect/intersect-line.js               | 129 +++++---
 .../rendering-elements/shapes/circle.ts       |   6 +-
 .../rendering-elements/shapes/drawRect.ts     |   5 +
 .../rendering-elements/shapes/question.ts     |  31 +-
 packages/mermaid/src/rendering-util/types.ts  |   2 +
 packages/mermaid/src/types.ts                 |   6 +
 packages/mermaid/src/utils/lineWithOffset.ts  |   3 +-
 10 files changed, 234 insertions(+), 434 deletions(-)

diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index 1c7bda8e7..2ffaeae37 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -90,6 +90,29 @@
   
     
 ---
+config:
+  layout: elk
+---
+flowchart LR
+ %% subgraph s1["Untitled subgraph"]
+        C{"Evaluate"}
+ %% end
+
+    B --> C
+    
+
+---
+config:
+  layout: elk
+---
+flowchart LR
+%% A ==> B
+%% A2 --> B2
+      D --> I((I the Circle))
+            D --> I
+    
+
+---
 config:
   layout: elk
 ---
@@ -100,9 +123,9 @@ config:
       end
 
 
-      D -- Mermaid js --> I{"fa:fa-code Text"}
-            D --> I
+      D -- Mermaid js --> I(("fa:fa-code Text"))
             D --> I
+           D --> E --> I
 
       end
     
@@ -238,7 +261,7 @@ flowchart LR
-
+    
 ---
 config:
   kanban:
@@ -257,81 +280,81 @@ kanban
     task3[💻 Develop login feature]@{ ticket: 103 }
 
     
-
+    
 flowchart LR
 nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
 
     
-
+    
 flowchart LR
 nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
 style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
 nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
 A:::AClass
 classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
   nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
 
     
-
+    
 flowchart LR
 nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
 
     
-
+    
 flowchart LR
 nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
 style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
 nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
 A:::AClass
 classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
   nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
 
     
-
+    
 flowchart LR
 nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
 
     
-
+    
 flowchart LR
 nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
 style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
 nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
 A:::AClass
 classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
   nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
   A:::AClass
   classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 flowchart LR
   nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
   style A fill:#f9f,stroke:#333,stroke-width:4px
     
-
+    
 kanban
   id2[In progress]
     docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
     
-
+    
 ---
 config:
   kanban:
diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts
index 59b97c557..2e666ab8d 100644
--- a/packages/mermaid-layout-elk/src/render.ts
+++ b/packages/mermaid-layout-elk/src/render.ts
@@ -60,6 +60,7 @@ export const render = async (
       const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
       const boundingBox = childNodeEl.node()!.getBBox();
       child.domId = childNodeEl;
+      child.calcIntersect = node.calcIntersect;
       child.width = boundingBox.width;
       child.height = boundingBox.height;
     } else {
@@ -459,228 +460,6 @@ export const render = async (
     }
   }
 
-  function intersectLine(
-    p1: { y: number; x: number },
-    p2: { y: number; x: number },
-    q1: { x: any; y: any },
-    q2: { x: any; y: any }
-  ) {
-    log.debug('UIO intersectLine', p1, p2, q1, q2);
-    // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
-    // p7 and p473.
-
-    // let a1, a2, b1, b2, c1, c2;
-    // let r1, r2, r3, r4;
-    // let denom, offset, num;
-    // let x, y;
-
-    // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
-    // b1 y + c1 = 0.
-    const a1 = p2.y - p1.y;
-    const b1 = p1.x - p2.x;
-    const c1 = p2.x * p1.y - p1.x * p2.y;
-
-    // Compute r3 and r4.
-    const r3 = a1 * q1.x + b1 * q1.y + c1;
-    const r4 = a1 * q2.x + b1 * q2.y + c1;
-
-    const epsilon = 1e-6;
-
-    // Check signs of r3 and r4. If both point 3 and point 4 lie on
-    // same side of line 1, the line segments do not intersect.
-    if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
-      return /*DON'T_INTERSECT*/;
-    }
-
-    // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
-    const a2 = q2.y - q1.y;
-    const b2 = q1.x - q2.x;
-    const c2 = q2.x * q1.y - q1.x * q2.y;
-
-    // Compute r1 and r2
-    const r1 = a2 * p1.x + b2 * p1.y + c2;
-    const r2 = a2 * p2.x + b2 * p2.y + c2;
-
-    // Check signs of r1 and r2. If both point 1 and point 2 lie
-    // on same side of second line segment, the line segments do
-    // not intersect.
-    if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
-      return /*DON'T_INTERSECT*/;
-    }
-
-    // Line segments intersect: compute intersection point.
-    const denom = a1 * b2 - a2 * b1;
-    if (denom === 0) {
-      return /*COLLINEAR*/;
-    }
-
-    const offset = Math.abs(denom / 2);
-
-    // The denom/2 is to get rounding instead of truncating. It
-    // is added or subtracted to the numerator, depending upon the
-    // sign of the numerator.
-    let num = b1 * c2 - b2 * c1;
-    const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
-
-    num = a2 * c1 - a1 * c2;
-    const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
-
-    return { x: x, y: y };
-  }
-
-  function sameSign(r1: number, r2: number) {
-    return r1 * r2 > 0;
-  }
-  const diamondIntersection = (
-    bounds: { x: any; y: any; width: any; height: any },
-    outsidePoint: { x: number; y: number },
-    insidePoint: any
-  ) => {
-    const x1 = bounds.x;
-    const y1 = bounds.y;
-
-    const w = bounds.width; //+ bounds.padding;
-    const h = bounds.height; // + bounds.padding;
-
-    const polyPoints = [
-      { x: x1, y: y1 - h / 2 },
-      { x: x1 + w / 2, y: y1 },
-      { x: x1, y: y1 + h / 2 },
-      { x: x1 - w / 2, y: y1 },
-    ];
-    log.debug(
-      `APA16 diamondIntersection calc abc89:
-  outsidePoint: ${JSON.stringify(outsidePoint)}
-  insidePoint : ${JSON.stringify(insidePoint)}
-  node-bounds       : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`,
-      JSON.stringify(polyPoints)
-    );
-
-    const intersections = [];
-
-    let minX = Number.POSITIVE_INFINITY;
-    let minY = Number.POSITIVE_INFINITY;
-
-    polyPoints.forEach(function (entry) {
-      minX = Math.min(minX, entry.x);
-      minY = Math.min(minY, entry.y);
-    });
-
-    const left = x1 - w / 2 - minX;
-    const top = y1 - h / 2 - minY;
-
-    for (let i = 0; i < polyPoints.length; i++) {
-      const p1 = polyPoints[i];
-      const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
-      const intersect = intersectLine(
-        bounds,
-        outsidePoint,
-        { x: left + p1.x, y: top + p1.y },
-        { x: left + p2.x, y: top + p2.y }
-      );
-
-      if (intersect) {
-        intersections.push(intersect);
-      }
-    }
-
-    if (!intersections.length) {
-      return bounds;
-    }
-
-    log.debug('UIO intersections', intersections);
-
-    if (intersections.length > 1) {
-      // More intersections, find the one nearest to edge end point
-      intersections.sort(function (p, q) {
-        const pdx = p.x - outsidePoint.x;
-        const pdy = p.y - outsidePoint.y;
-        const distp = Math.sqrt(pdx * pdx + pdy * pdy);
-
-        const qdx = q.x - outsidePoint.x;
-        const qdy = q.y - outsidePoint.y;
-        const distq = Math.sqrt(qdx * qdx + qdy * qdy);
-
-        return distp < distq ? -1 : distp === distq ? 0 : 1;
-      });
-    }
-
-    return intersections[0];
-  };
-
-  const intersection = (
-    node: { x: any; y: any; width: number; height: number },
-    outsidePoint: { x: number; y: number },
-    insidePoint: { x: number; y: number }
-  ) => {
-    log.debug(`intersection calc abc89:
-  outsidePoint: ${JSON.stringify(outsidePoint)}
-  insidePoint : ${JSON.stringify(insidePoint)}
-  node        : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`);
-    const x = node.x;
-    const y = node.y;
-
-    const dx = Math.abs(x - insidePoint.x);
-    // const dy = Math.abs(y - insidePoint.y);
-    const w = node.width / 2;
-    let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
-    const h = node.height / 2;
-
-    const Q = Math.abs(outsidePoint.y - insidePoint.y);
-    const R = Math.abs(outsidePoint.x - insidePoint.x);
-
-    if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
-      // Intersection is top or bottom of rect.
-      const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
-      r = (R * q) / Q;
-      const res = {
-        x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
-        y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
-      };
-
-      if (r === 0) {
-        res.x = outsidePoint.x;
-        res.y = outsidePoint.y;
-      }
-      if (R === 0) {
-        res.x = outsidePoint.x;
-      }
-      if (Q === 0) {
-        res.y = outsidePoint.y;
-      }
-
-      log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line
-
-      return res;
-    } else {
-      // Intersection onn sides of rect
-      if (insidePoint.x < outsidePoint.x) {
-        r = outsidePoint.x - w - x;
-      } else {
-        // r = outsidePoint.x - w - x;
-        r = x - w - outsidePoint.x;
-      }
-      const q = (Q * r) / R;
-      //  OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w;
-      // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
-      let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
-      // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
-      let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
-      log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
-      if (r === 0) {
-        _x = outsidePoint.x;
-        _y = outsidePoint.y;
-      }
-      if (R === 0) {
-        _x = outsidePoint.x;
-      }
-      if (Q === 0) {
-        _y = outsidePoint.y;
-      }
-
-      return { x: _x, y: _y };
-    }
-  };
   const outsideNode = (
     node: { x: any; y: any; width: number; height: number },
     point: { x: number; y: number }
@@ -703,9 +482,9 @@ export const render = async (
   const cutPathAtIntersect = (
     _points: any[],
     bounds: { x: any; y: any; width: any; height: any; padding: any },
-    isDiamond: boolean
+    calcIntersect: any
   ) => {
-    log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
+    log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds);
     const points: any[] = [];
     let lastPointOutside = _points[0];
     let isInside = false;
@@ -714,20 +493,20 @@ export const render = async (
       if (!outsideNode(bounds, point) && !isInside) {
         // First point inside the rect found
         // Calc the intersection coord between the point anf the last point outside the rect
-        let inter;
-
-        if (isDiamond) {
-          const inter2 = diamondIntersection(bounds, lastPointOutside, point);
-          const distance = Math.sqrt(
-            (lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2
-          );
-          if (distance > 1) {
-            inter = inter2;
-          }
-        }
-        if (!inter) {
-          inter = intersection(bounds, lastPointOutside, point);
-        }
+        const inter = calcIntersect({ ...bounds, ...point }, lastPointOutside);
+        // console.log(
+        //   'APA30 inside',
+        //   '\nbounds',
+        //   { ...bounds, ...point },
+        //   `\npoint`,
+        //   point,
+        //   '\no outside point',
+        //   lastPointOutside,
+        //   '\npoints',
+        //   ...points,
+        //   '\nIntersection',
+        //   inter
+        // );
 
         // Check case where the intersection is the same as the last point
         let pointPresent = false;
@@ -966,43 +745,44 @@ export const render = async (
             startNode.innerHTML
           );
         }
-        if (startNode.shape === 'diamond' || startNode.shape === 'diam') {
+
+        if (startNode.calcIntersect) {
           edge.points.unshift({
             x: startNode.offset.posX + startNode.width / 2,
             y: startNode.offset.posY + startNode.height / 2,
+            width: startNode.width,
+            height: startNode.height,
           });
+          edge.points = cutPathAtIntersect(
+            edge.points.reverse(),
+            {
+              x: startNode.offset.posX + startNode.width / 2,
+              y: startNode.offset.posY + startNode.height / 2,
+              width: sw,
+              height: startNode.height,
+              padding: startNode.padding,
+            },
+            startNode.calcIntersect
+          ).reverse();
         }
-        if (endNode.shape === 'diamond' || endNode.shape === 'diam') {
+        if (endNode.calcIntersect) {
           edge.points.push({
             x: endNode.offset.posX + endNode.width / 2,
             y: endNode.offset.posY + endNode.height / 2,
           });
+          edge.points = cutPathAtIntersect(
+            edge.points,
+            {
+              x: endNode.offset.posX + endNode.width / 2,
+              y: endNode.offset.posY + endNode.height / 2,
+              width: ew,
+              height: endNode.height,
+              padding: endNode.padding,
+            },
+            endNode.calcIntersect
+          );
         }
 
-        edge.points = cutPathAtIntersect(
-          edge.points.reverse(),
-          {
-            x: startNode.offset.posX + startNode.width / 2,
-            y: startNode.offset.posY + startNode.height / 2,
-            width: sw,
-            height: startNode.height,
-            padding: startNode.padding,
-          },
-          startNode.shape === 'diamond' || startNode.shape === 'diam'
-        ).reverse();
-
-        edge.points = cutPathAtIntersect(
-          edge.points,
-          {
-            x: endNode.offset.posX + endNode.width / 2,
-            y: endNode.offset.posY + endNode.height / 2,
-            width: ew,
-            height: endNode.height,
-            padding: endNode.padding,
-          },
-          endNode.shape === 'diamond' || endNode.shape === 'diam'
-        );
-
         const paths = insertEdge(
           edgesEl,
           edge,
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
index a6a7a55f7..22fa6c1bb 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
@@ -335,90 +335,35 @@ const cutPathAtIntersect = (_points, boundaryNode) => {
   return points;
 };
 
-function extractCornerPoints(points) {
-  const cornerPoints = [];
-  const cornerPointPositions = [];
-  for (let i = 1; i < points.length - 1; i++) {
-    const prev = points[i - 1];
-    const curr = points[i];
-    const next = points[i + 1];
-    if (
-      prev.x === curr.x &&
-      curr.y === next.y &&
-      Math.abs(curr.x - next.x) > 5 &&
-      Math.abs(curr.y - prev.y) > 5
-    ) {
-      cornerPoints.push(curr);
-      cornerPointPositions.push(i);
-    } else if (
-      prev.y === curr.y &&
-      curr.x === next.x &&
-      Math.abs(curr.x - prev.x) > 5 &&
-      Math.abs(curr.y - next.y) > 5
-    ) {
-      cornerPoints.push(curr);
-      cornerPointPositions.push(i);
-    }
+const adjustForArrowHeads = function (lineData, size = 5) {
+  const newLineData = [...lineData];
+  const lastPoint = lineData[lineData.length - 1];
+  const secondLastPoint = lineData[lineData.length - 2];
+
+  const distanceBetweenLastPoints = Math.sqrt(
+    (lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2
+  );
+
+  if (distanceBetweenLastPoints < size) {
+    // Calculate the direction vector from the last point to the second last point
+    const directionX = secondLastPoint.x - lastPoint.x;
+    const directionY = secondLastPoint.y - lastPoint.y;
+
+    // Normalize the direction vector
+    const magnitude = Math.sqrt(directionX ** 2 + directionY ** 2);
+    const normalizedX = directionX / magnitude;
+    const normalizedY = directionY / magnitude;
+
+    // Calculate the new position for the second last point
+    const adjustedSecondLastPoint = {
+      x: lastPoint.x + normalizedX * size,
+      y: lastPoint.y + normalizedY * size,
+    };
+
+    // Replace the second last point in the new line data
+    newLineData[newLineData.length - 2] = adjustedSecondLastPoint;
   }
-  return { cornerPoints, cornerPointPositions };
-}
 
-const findAdjacentPoint = function (pointA, pointB, distance) {
-  const xDiff = pointB.x - pointA.x;
-  const yDiff = pointB.y - pointA.y;
-  const length = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
-  const ratio = distance / length;
-  return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff };
-};
-
-const fixCorners = function (lineData) {
-  const { cornerPointPositions } = extractCornerPoints(lineData);
-  const newLineData = [];
-  for (let i = 0; i < lineData.length; i++) {
-    if (cornerPointPositions.includes(i)) {
-      const prevPoint = lineData[i - 1];
-      const nextPoint = lineData[i + 1];
-      const cornerPoint = lineData[i];
-
-      const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5);
-      const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5);
-
-      const xDiff = newNextPoint.x - newPrevPoint.x;
-      const yDiff = newNextPoint.y - newPrevPoint.y;
-      newLineData.push(newPrevPoint);
-
-      const a = Math.sqrt(2) * 2;
-      let newCornerPoint = { x: cornerPoint.x, y: cornerPoint.y };
-      if (Math.abs(nextPoint.x - prevPoint.x) > 10 && Math.abs(nextPoint.y - prevPoint.y) >= 10) {
-        log.debug(
-          'Corner point fixing',
-          Math.abs(nextPoint.x - prevPoint.x),
-          Math.abs(nextPoint.y - prevPoint.y)
-        );
-        const r = 5;
-        if (cornerPoint.x === newPrevPoint.x) {
-          newCornerPoint = {
-            x: xDiff < 0 ? newPrevPoint.x - r + a : newPrevPoint.x + r - a,
-            y: yDiff < 0 ? newPrevPoint.y - a : newPrevPoint.y + a,
-          };
-        } else {
-          newCornerPoint = {
-            x: xDiff < 0 ? newPrevPoint.x - a : newPrevPoint.x + a,
-            y: yDiff < 0 ? newPrevPoint.y - r + a : newPrevPoint.y + r - a,
-          };
-        }
-      } else {
-        log.debug(
-          'Corner point skipping fixing',
-          Math.abs(nextPoint.x - prevPoint.x),
-          Math.abs(nextPoint.y - prevPoint.y)
-        );
-      }
-      newLineData.push(newCornerPoint, newNextPoint);
-    } else {
-      newLineData.push(lineData[i]);
-    }
-  }
   return newLineData;
 };
 
@@ -462,8 +407,10 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
   }
 
   let lineData = points.filter((p) => !Number.isNaN(p.y));
-  lineData = fixCorners(lineData);
+  lineData = adjustForArrowHeads(lineData);
+  // lineData = fixCorners(lineData);
   let curve = curveBasis;
+  // let curve = curveLinear;
   if (edge.curve) {
     curve = edge.curve;
   }
@@ -543,9 +490,9 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
   // lineData.forEach((point) => {
   //   elem
   //     .append('circle')
-  //     .style('stroke', 'blue')
-  //     .style('fill', 'blue')
-  //     .attr('r', 3)
+  //     .style('stroke', 'red')
+  //     .style('fill', 'red')
+  //     .attr('r', 1)
   //     .attr('cx', point.x)
   //     .attr('cy', point.y);
   // });
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js
index bd3eb497f..b12f8688e 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js
@@ -2,64 +2,87 @@
  * Returns the point at which two lines, p and q, intersect or returns undefined if they do not intersect.
  */
 function intersectLine(p1, p2, q1, q2) {
-  // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
-  // p7 and p473.
+  {
+    // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
+    // p7 and p473.
 
-  var a1, a2, b1, b2, c1, c2;
-  var r1, r2, r3, r4;
-  var denom, offset, num;
-  var x, y;
+    // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
+    // b1 y + c1 = 0.
+    const a1 = p2.y - p1.y;
+    const b1 = p1.x - p2.x;
+    const c1 = p2.x * p1.y - p1.x * p2.y;
 
-  // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
-  // b1 y + c1 = 0.
-  a1 = p2.y - p1.y;
-  b1 = p1.x - p2.x;
-  c1 = p2.x * p1.y - p1.x * p2.y;
+    // Compute r3 and r4.
+    const r3 = a1 * q1.x + b1 * q1.y + c1;
+    const r4 = a1 * q2.x + b1 * q2.y + c1;
 
-  // Compute r3 and r4.
-  r3 = a1 * q1.x + b1 * q1.y + c1;
-  r4 = a1 * q2.x + b1 * q2.y + c1;
+    const epsilon = 1e-6;
 
-  // Check signs of r3 and r4. If both point 3 and point 4 lie on
-  // same side of line 1, the line segments do not intersect.
-  if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
-    return /*DON'T_INTERSECT*/;
+    // Check signs of r3 and r4. If both point 3 and point 4 lie on
+    // same side of line 1, the line segments do not intersect.
+    if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
+      return /*DON'T_INTERSECT*/;
+    }
+
+    // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
+    const a2 = q2.y - q1.y;
+    const b2 = q1.x - q2.x;
+    const c2 = q2.x * q1.y - q1.x * q2.y;
+
+    // Compute r1 and r2
+    const r1 = a2 * p1.x + b2 * p1.y + c2;
+    const r2 = a2 * p2.x + b2 * p2.y + c2;
+
+    // Check signs of r1 and r2. If both point 1 and point 2 lie
+    // on same side of second line segment, the line segments do
+    // not intersect.
+    if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
+      return /*DON'T_INTERSECT*/;
+    }
+
+    // Line segments intersect: compute intersection point.
+    const denom = a1 * b2 - a2 * b1;
+    if (denom === 0) {
+      return /*COLLINEAR*/;
+    }
+
+    const offset = Math.abs(denom / 2);
+
+    // The denom/2 is to get rounding instead of truncating. It
+    // is added or subtracted to the numerator, depending upon the
+    // sign of the numerator.
+    let num = b1 * c2 - b2 * c1;
+    const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
+
+    num = a2 * c1 - a1 * c2;
+    const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
+    // console.log(
+    //   'APA30 intersectLine intersection',
+    //   '\np1: (',
+    //   p1.x,
+    //   p1.y,
+    //   ')',
+    //   '\np2: (',
+    //   p2.x,
+    //   p2.y,
+    //   ')',
+    //   '\nq1: (',
+    //   q1.x,
+    //   q1.y,
+    //   ')',
+    //   '\np1: (',
+    //   q2.x,
+    //   q2.y,
+    //   ')',
+    //   'offset:',
+    //   offset,
+    //   '\nintersection: (',
+    //   x,
+    //   y,
+    //   ')'
+    // );
+    return { x: x, y: y };
   }
-
-  // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
-  a2 = q2.y - q1.y;
-  b2 = q1.x - q2.x;
-  c2 = q2.x * q1.y - q1.x * q2.y;
-
-  // Compute r1 and r2
-  r1 = a2 * p1.x + b2 * p1.y + c2;
-  r2 = a2 * p2.x + b2 * p2.y + c2;
-
-  // Check signs of r1 and r2. If both point 1 and point 2 lie
-  // on same side of second line segment, the line segments do
-  // not intersect.
-  if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) {
-    return /*DON'T_INTERSECT*/;
-  }
-
-  // Line segments intersect: compute intersection point.
-  denom = a1 * b2 - a2 * b1;
-  if (denom === 0) {
-    return /*COLLINEAR*/;
-  }
-
-  offset = Math.abs(denom / 2);
-
-  // The denom/2 is to get rounding instead of truncating. It
-  // is added or subtracted to the numerator, depending upon the
-  // sign of the numerator.
-  num = b1 * c2 - b2 * c1;
-  x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
-
-  num = a2 * c1 - a1 * c2;
-  y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
-
-  return { x: x, y: y };
 }
 
 function sameSign(r1, r2) {
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts
index 6b3be6765..80b59bdc1 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts
@@ -6,6 +6,7 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
 import rough from 'roughjs';
 import type { D3Selection } from '../../../types.js';
 import { handleUndefinedAttr } from '../../../utils.js';
+import type { Bounds, Point } from '../../../types.js';
 
 export async function circle(parent: D3Selection, node: Node) {
   const { labelStyles, nodeStyles } = styles2String(node);
@@ -35,7 +36,10 @@ export async function circle(parent: D3Selection(
   parent: D3Selection,
@@ -62,6 +63,10 @@ export async function drawRect(
 
   updateNodeBounds(node, rect);
 
+  node.calcIntersect = function (bounds: Bounds, point: Point) {
+    return intersect.rect(bounds, point);
+  };
+
   node.intersect = function (point) {
     return intersect.rect(node, point);
   };
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
index eef958169..48511a866 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
@@ -1,4 +1,3 @@
-import { log } from '../../../logger.js';
 import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
 import intersect from '../intersect/index.js';
 import type { Node } from '../../types.js';
@@ -6,6 +5,7 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
 import rough from 'roughjs';
 import { insertPolygonShape } from './insertPolygonShape.js';
 import type { D3Selection } from '../../../types.js';
+import type { Bounds, Point } from '../../../types.js';
 
 export const createDecisionBoxPathD = (x: number, y: number, size: number): string => {
   return [
@@ -59,17 +59,28 @@ export async function question(parent: D3Selection
   }
 
   updateNodeBounds(node, polygon);
+  node.calcIntersect = function (bounds: Bounds, point: Point) {
+    const w = bbox.width + node.padding;
+    const h = bbox.height + node.padding;
+    const s = w + h;
+
+    // Define polygon points
+    const points = [
+      { x: s / 2, y: 0 },
+      { x: s, y: -s / 2 },
+      { x: s / 2, y: -s },
+      { x: 0, y: -s / 2 },
+    ];
+
+    // Calculate the intersection point
+    const res = intersect.polygon(bounds, points, point);
+
+    return { x: res.x - 0.5, y: res.y - 0.5 }; // Adjusted result
+  };
 
   node.intersect = function (point) {
-    log.debug(
-      'APA12 Intersect called SPLIT\npoint:',
-      point,
-      '\nnode:\n',
-      node,
-      '\nres:',
-      intersect.polygon(node, points, point)
-    );
-    return intersect.polygon(node, points, point);
+    // @ts-ignore TODO fix this (KNSV)
+    return this.calcIntersect(node as Bounds, point);
   };
 
   return shapeSvg;
diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts
index 86cfd50b3..8dc3ddf20 100644
--- a/packages/mermaid/src/rendering-util/types.ts
+++ b/packages/mermaid/src/rendering-util/types.ts
@@ -2,6 +2,7 @@ export type MarkdownWordType = 'normal' | 'strong' | 'em';
 import type { MermaidConfig } from '../config.type.js';
 import type { ClusterShapeID } from './rendering-elements/clusters.js';
 import type { ShapeID } from './rendering-elements/shapes.js';
+import type { Bounds, Point } from '../types.js';
 export interface MarkdownWord {
   content: string;
   type: MarkdownWordType;
@@ -43,6 +44,7 @@ interface BaseNode {
   height?: number;
   // Specific properties for State Diagram nodes TODO remove and use generic properties
   intersect?: (point: any) => any;
+  calcIntersect?: (bounds: Bounds, point: Point) => any;
 
   // Non-generic properties
   rx?: number; // Used for rounded corners in Rect, Ellipse, etc.Maybe it to specialized RectNode, EllipseNode, etc.
diff --git a/packages/mermaid/src/types.ts b/packages/mermaid/src/types.ts
index 5587ca3f4..7e219d7fe 100644
--- a/packages/mermaid/src/types.ts
+++ b/packages/mermaid/src/types.ts
@@ -18,6 +18,12 @@ export interface Point {
   x: number;
   y: number;
 }
+export interface Bounds {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+}
 
 export interface TextDimensionConfig {
   fontSize?: number;
diff --git a/packages/mermaid/src/utils/lineWithOffset.ts b/packages/mermaid/src/utils/lineWithOffset.ts
index 800a5ffaf..057944325 100644
--- a/packages/mermaid/src/utils/lineWithOffset.ts
+++ b/packages/mermaid/src/utils/lineWithOffset.ts
@@ -3,7 +3,7 @@ import type { EdgeData, Point } from '../types.js';
 // We need to draw the lines a bit shorter to avoid drawing
 // under any transparent markers.
 // The offsets are calculated from the markers' dimensions.
-const markerOffsets = {
+export const markerOffsets = {
   aggregation: 18,
   extension: 18,
   composition: 18,
@@ -104,7 +104,6 @@ export const getLineFunctionsWithOffset = (
         adjustment *= DIRECTION === 'right' ? -1 : 1;
         offset += adjustment;
       }
-
       return pointTransformer(d).x + offset;
     },
     y: function (

From 1e3ea133234590e2cb00f96a59db7a30820673e0 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Fri, 29 Nov 2024 09:07:47 +0100
Subject: [PATCH 4/5] Fix for when last point is on the intersection

---
 packages/mermaid-layout-elk/src/render.ts     | 125 ++++--------------
 .../rendering-elements/shapes/question.ts     |  20 ++-
 2 files changed, 44 insertions(+), 101 deletions(-)

diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts
index 2e666ab8d..8f49a2476 100644
--- a/packages/mermaid-layout-elk/src/render.ts
+++ b/packages/mermaid-layout-elk/src/render.ts
@@ -4,7 +4,8 @@ import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from '
 import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
 
 type Node = LayoutData['nodes'][number];
-
+// Used to calculate distances in order to avoid floating number rounding issues when comparing floating numbers
+const epsilon = 0.0001;
 interface LabelData {
   width: number;
   height: number;
@@ -17,7 +18,16 @@ interface NodeWithVertex extends Omit {
   labelData?: LabelData;
   domId?: Node['domId'] | SVGGroup | d3.Selection;
 }
-
+interface Point {
+  x: number;
+  y: number;
+}
+function distance(p1?: Point, p2?: Point): number {
+  if (!p1 || !p2) {
+    return 0;
+  }
+  return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
+}
 export const render = async (
   data4Layout: LayoutData,
   svg: SVG,
@@ -460,80 +470,6 @@ export const render = async (
     }
   }
 
-  const outsideNode = (
-    node: { x: any; y: any; width: number; height: number },
-    point: { x: number; y: number }
-  ) => {
-    const x = node.x;
-    const y = node.y;
-    const dx = Math.abs(point.x - x);
-    const dy = Math.abs(point.y - y);
-    const w = node.width / 2;
-    const h = node.height / 2;
-    if (dx >= w || dy >= h) {
-      return true;
-    }
-    return false;
-  };
-  /**
-   * This function will page a path and node where the last point(s) in the path is inside the node
-   * and return an update path ending by the border of the node.
-   */
-  const cutPathAtIntersect = (
-    _points: any[],
-    bounds: { x: any; y: any; width: any; height: any; padding: any },
-    calcIntersect: any
-  ) => {
-    log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds);
-    const points: any[] = [];
-    let lastPointOutside = _points[0];
-    let isInside = false;
-    _points.forEach((point: any) => {
-      // check if point is inside the boundary rect
-      if (!outsideNode(bounds, point) && !isInside) {
-        // First point inside the rect found
-        // Calc the intersection coord between the point anf the last point outside the rect
-        const inter = calcIntersect({ ...bounds, ...point }, lastPointOutside);
-        // console.log(
-        //   'APA30 inside',
-        //   '\nbounds',
-        //   { ...bounds, ...point },
-        //   `\npoint`,
-        //   point,
-        //   '\no outside point',
-        //   lastPointOutside,
-        //   '\npoints',
-        //   ...points,
-        //   '\nIntersection',
-        //   inter
-        // );
-
-        // Check case where the intersection is the same as the last point
-        let pointPresent = false;
-        points.forEach((p) => {
-          pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
-        });
-        // if (!pointPresent) {
-        if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
-          points.push(inter);
-        } else {
-          log.debug('abc88 no intersect', inter, points);
-        }
-        // points.push(inter);
-        isInside = true;
-      } else {
-        // Outside
-        log.debug('abc88 outside', point, lastPointOutside, points);
-        lastPointOutside = point;
-        // points.push(point);
-        if (!isInside) {
-          points.push(point);
-        }
-      }
-    });
-    return points;
-  };
-
   // @ts-ignore - ELK is not typed
   const elk = new ELK();
   const element = svg.select('g');
@@ -747,40 +683,34 @@ export const render = async (
         }
 
         if (startNode.calcIntersect) {
-          edge.points.unshift({
-            x: startNode.offset.posX + startNode.width / 2,
-            y: startNode.offset.posY + startNode.height / 2,
-            width: startNode.width,
-            height: startNode.height,
-          });
-          edge.points = cutPathAtIntersect(
-            edge.points.reverse(),
+          const intersection = startNode.calcIntersect(
             {
               x: startNode.offset.posX + startNode.width / 2,
               y: startNode.offset.posY + startNode.height / 2,
-              width: sw,
+              width: startNode.width,
               height: startNode.height,
-              padding: startNode.padding,
             },
-            startNode.calcIntersect
-          ).reverse();
+            edge.points[0]
+          );
+
+          if (distance(intersection, edge.points[0]) > epsilon) {
+            edge.points.unshift(intersection);
+          }
         }
         if (endNode.calcIntersect) {
-          edge.points.push({
-            x: endNode.offset.posX + endNode.width / 2,
-            y: endNode.offset.posY + endNode.height / 2,
-          });
-          edge.points = cutPathAtIntersect(
-            edge.points,
+          const intersection = endNode.calcIntersect(
             {
               x: endNode.offset.posX + endNode.width / 2,
               y: endNode.offset.posY + endNode.height / 2,
-              width: ew,
+              width: endNode.width,
               height: endNode.height,
-              padding: endNode.padding,
             },
-            endNode.calcIntersect
+            edge.points[edge.points.length - 1]
           );
+
+          if (distance(intersection, edge.points[edge.points.length - 1]) > epsilon) {
+            edge.points.push(intersection);
+          }
         }
 
         const paths = insertEdge(
@@ -792,7 +722,6 @@ export const render = async (
           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/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
index 48511a866..07180b090 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
@@ -60,9 +60,23 @@ export async function question(parent: D3Selection
 
   updateNodeBounds(node, polygon);
   node.calcIntersect = function (bounds: Bounds, point: Point) {
-    const w = bbox.width + node.padding;
-    const h = bbox.height + node.padding;
-    const s = w + h;
+    const s = bounds.width;
+
+    // console.log(
+    //   'APA10\nbounds width:',
+    //   bounds.width,
+    //   '\nbounds height:',
+    //   bounds.height,
+    //   'point:',
+    //   point.x,
+    //   point.y,
+    //   '\nw:',
+    //   w,
+    //   '\nh',
+    //   h,
+    //   '\ns',
+    //   s
+    // );
 
     // Define polygon points
     const points = [

From fbae6114065ea4132bb47cf63be22f98906626f7 Mon Sep 17 00:00:00 2001
From: Knut Sveidqvist 
Date: Fri, 6 Jun 2025 20:10:19 +0200
Subject: [PATCH 5/5] Refresh

---
 packages/mermaid-layout-elk/README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/mermaid-layout-elk/README.md b/packages/mermaid-layout-elk/README.md
index c32803292..5120fe85c 100644
--- a/packages/mermaid-layout-elk/README.md
+++ b/packages/mermaid-layout-elk/README.md
@@ -2,7 +2,7 @@
 
 This package provides a layout engine for Mermaid based on the [ELK](https://www.eclipse.org/elk/) layout engine.
 
-> [!NOTE]  
+> [!NOTE]
 > The ELK Layout engine will not be available in all providers that support mermaid by default.
 > The websites will have to install the `@mermaid-js/layout-elk` package to use the ELK layout engine.
 
@@ -69,4 +69,4 @@ mermaid.registerLayoutLoaders(elkLayouts);
 - `elk.mrtree`: Multi-root tree layout
 - `elk.sporeOverlap`: Spore overlap layout
 
-
+