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 (