From 6e9088a7da8087b7d02125955575254b21f5aa18 Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Thu, 26 Feb 2026 11:11:39 +0000 Subject: [PATCH] Move work on the visualiser --- .../components/VisualisetTab.tsx | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/src/modules/manager/workflowTemplates/components/VisualisetTab.tsx b/src/modules/manager/workflowTemplates/components/VisualisetTab.tsx index fc7197c..fa6d0f0 100644 --- a/src/modules/manager/workflowTemplates/components/VisualisetTab.tsx +++ b/src/modules/manager/workflowTemplates/components/VisualisetTab.tsx @@ -557,6 +557,7 @@ const VisualiserTab: React.FC = ({ targetX: number; sourceY: number; targetY: number; + curveOffset: number; }> => { const connections: Array<{ sourceGuid: string; @@ -566,6 +567,7 @@ const VisualiserTab: React.FC = ({ targetX: number; sourceY: number; targetY: number; + curveOffset: number; }> = []; tasks.forEach((task) => { @@ -589,12 +591,35 @@ const VisualiserTab: React.FC = ({ sourceY: sourcePos.y, targetX: targetPos.x, targetY: targetPos.y, + curveOffset: 0, // Will be calculated below }); } } }); }); + // Detect overlapping connections to the same target and offset them + const targetGroups = new Map(); + connections.forEach((conn) => { + const key = conn.targetGuid; + const group = targetGroups.get(key) ?? []; + group.push(conn); + targetGroups.set(key, group); + }); + + // Assign offsets to overlapping connections + targetGroups.forEach((group) => { + if (group.length > 1) { + group.forEach((conn, idx) => { + // Spread offsets: -10, 0, 10 for 3 connections, etc. + const totalOffsets = group.length; + const offsetStep = 15; + const baseOffset = -((totalOffsets - 1) * offsetStep) / 2; + conn.curveOffset = baseOffset + idx * offsetStep; + }); + } + }); + return connections; }; @@ -795,19 +820,27 @@ const VisualiserTab: React.FC = ({ preserveAspectRatio="none" > {getOutcomeConnections().map((conn, idx) => { - const controlOffsetX = 20; + // Determine curve direction based on flow direction + // Backward flow (higher Y to lower Y) curves left, forward flow curves right + const isBackwardFlow = conn.sourceY > conn.targetY; + const curveDirection = isBackwardFlow ? -1 : 1; + + const baseControlOffsetX = 20 + Math.abs(conn.curveOffset) * 0.3; const controlOffsetY = (Math.abs(conn.targetY - conn.sourceY) / 2) * 0.3; + // Apply horizontal offset to spread overlapping curves + const offsetX = conn.curveOffset; + // Calculate text position at the curve's midpoint (t=0.5 on Bezier curve) const t = 0.5; const p0 = { x: conn.sourceX, y: conn.sourceY }; const p1 = { - x: conn.sourceX - controlOffsetX, + x: conn.sourceX + baseControlOffsetX * curveDirection + offsetX, y: conn.sourceY + controlOffsetY, }; const p2 = { - x: conn.targetX - controlOffsetX, + x: conn.targetX + baseControlOffsetX * curveDirection + offsetX, y: conn.targetY - controlOffsetY, }; const p3 = { x: conn.targetX, y: conn.targetY }; @@ -824,6 +857,35 @@ const VisualiserTab: React.FC = ({ 3 * (1 - t) * Math.pow(t, 2) * p2.y + Math.pow(t, 3) * p3.y; + // Calculate arrow position at t=0.3 for directional indicator + const tArrow = 0.3; + const arrowX = + Math.pow(1 - tArrow, 3) * p0.x + + 3 * Math.pow(1 - tArrow, 2) * tArrow * p1.x + + 3 * (1 - tArrow) * Math.pow(tArrow, 2) * p2.x + + Math.pow(tArrow, 3) * p3.x; + const arrowY = + Math.pow(1 - tArrow, 3) * p0.y + + 3 * Math.pow(1 - tArrow, 2) * tArrow * p1.y + + 3 * (1 - tArrow) * Math.pow(tArrow, 2) * p2.y + + Math.pow(tArrow, 3) * p3.y; + + // Calculate tangent for arrow rotation + const tArrowDelta = 0.31; + const arrowX2 = + Math.pow(1 - tArrowDelta, 3) * p0.x + + 3 * Math.pow(1 - tArrowDelta, 2) * tArrowDelta * p1.x + + 3 * (1 - tArrowDelta) * Math.pow(tArrowDelta, 2) * p2.x + + Math.pow(tArrowDelta, 3) * p3.x; + const arrowY2 = + Math.pow(1 - tArrowDelta, 3) * p0.y + + 3 * Math.pow(1 - tArrowDelta, 2) * tArrowDelta * p1.y + + 3 * (1 - tArrowDelta) * Math.pow(tArrowDelta, 2) * p2.y + + Math.pow(tArrowDelta, 3) * p3.y; + + const angle = + Math.atan2(arrowY2 - arrowY, arrowX2 - arrowX) * (180 / Math.PI); + return ( = ({ + {/* Directional arrow along curve */} + + {/* End arrow */}