diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index bfa6592113..5aa3063990 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -101,7 +101,6 @@ const pathString = buildWirePathString(outputPortRect, inputPortRect, verticalOut, verticalIn); const dataType = (outputPort.getAttribute("data-datatype") as FrontendGraphDataType) || "General"; const thick = verticalIn && verticalOut; - return { pathString, dataType, thick, dashed }; } @@ -136,68 +135,495 @@ function buildWirePathLocations(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): { x: number; y: number }[] { if (!nodesContainer) return []; - const VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP = 1; + const scale = $nodeGraph.transform.scale; // Get the current scale + const lineWidth = 2; + const VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP = 1; const containerBounds = nodesContainer.getBoundingClientRect(); const outX = verticalOut ? outputBounds.x + outputBounds.width / 2 : outputBounds.x + outputBounds.width - 1; const outY = verticalOut ? outputBounds.y + VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP : outputBounds.y + outputBounds.height / 2; - const outConnectorX = (outX - containerBounds.x) / $nodeGraph.transform.scale; - const outConnectorY = (outY - containerBounds.y) / $nodeGraph.transform.scale; + const outConnectorX = Math.round((outX - containerBounds.x) / scale); // Adjust for scale + const outConnectorY = Math.round((outY - containerBounds.y) / scale); // Adjust for scale const inX = verticalIn ? inputBounds.x + inputBounds.width / 2 : inputBounds.x + 1; const inY = verticalIn ? inputBounds.y + inputBounds.height - VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP : inputBounds.y + inputBounds.height / 2; - const inConnectorX = (inX - containerBounds.x) / $nodeGraph.transform.scale; - const inConnectorY = (inY - containerBounds.y) / $nodeGraph.transform.scale; - const horizontalGap = Math.abs(outConnectorX - inConnectorX); - const verticalGap = Math.abs(outConnectorY - inConnectorY); - - // TODO: Finish this commented out code replacement for the code below it based on this diagram: - // // Straight: stacking lines which are always straight, or a straight horizontal wire between two aligned nodes - // if ((verticalOut && verticalIn) || (!verticalOut && !verticalIn && verticalGap === 0)) { - // return [ - // { x: outConnectorX, y: outConnectorY }, - // { x: inConnectorX, y: inConnectorY }, - // ]; - // } - - // // L-shape bend - // if (verticalOut !== verticalIn) { - // } - - const curveLength = 24; - const curveFalloffRate = curveLength * Math.PI * 2; - - const horizontalCurveAmount = -(2 ** ((-10 * horizontalGap) / curveFalloffRate)) + 1; - const verticalCurveAmount = -(2 ** ((-10 * verticalGap) / curveFalloffRate)) + 1; - const horizontalCurve = horizontalCurveAmount * curveLength; - const verticalCurve = verticalCurveAmount * curveLength; - - return [ - { x: outConnectorX, y: outConnectorY }, - { x: verticalOut ? outConnectorX : outConnectorX + horizontalCurve, y: verticalOut ? outConnectorY - verticalCurve : outConnectorY }, - { x: verticalIn ? inConnectorX : inConnectorX - horizontalCurve, y: verticalIn ? inConnectorY + verticalCurve : inConnectorY }, - { x: inConnectorX, y: inConnectorY }, - ]; + const inConnectorX = Math.round((inX - containerBounds.x) / scale); // Adjust for scale + const inConnectorY = Math.round((inY - containerBounds.y) / scale); // Adjust for scale + + function calculateMidX() { + return (inConnectorX + outConnectorX) / 2 + (((inConnectorX + outConnectorX) / 2) % gridSpacing); + } + function calculateMidY() { + return (inConnectorY + outConnectorY) / 2 - (((inConnectorY + outConnectorY) / 2) % gridSpacing); + } + + // Handle straight lines + if ((verticalOut && verticalIn && outConnectorX === inConnectorX) || (!verticalOut && !verticalIn && outConnectorY === inConnectorY)) { + if (!verticalOut && !verticalIn && outConnectorY === inConnectorY && outConnectorX > inConnectorX) { + // outConnector point and inConnector point lying on the same horizontal grid line and outConnector point lies to the right of inConnector point. + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + + // Handle L-shaped paths + if ((verticalOut && !verticalIn && outConnectorX === inConnectorX) || (!verticalOut && verticalIn && outConnectorY === inConnectorY)) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + + // Handle standard right-angle paths + + // Start vertical, then horizontal + if (verticalOut && inConnectorX > outConnectorX) { + //outConnector point lies to the left of inConnector point. + + if (outConnectorY < inConnectorY) { + // outConnector point lies above inConnector point. + if (-4 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX < -3 * gridSpacing) { + //outConnector point lies on the vertical grid line 4 units to the left of inConnector point point. + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + if (-3 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= -1 * gridSpacing) { + // outConnector point lying on vertical grid lines 3 and 2 units to the left of inConnector point. + if (-2 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing }, + { x: outConnectorX + gridSpacing, y: outConnectorY - gridSpacing }, + { x: outConnectorX + gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + gridSpacing + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + gridSpacing + lineWidth, y: inConnectorY }, + + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + 2 * gridSpacing + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + 2 * gridSpacing + lineWidth, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else if (-1 * gridSpacing < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 0 * gridSpacing) { + // outConnector point lying on vertical grid line 1 units to the left of inConnector point. + if (-2 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) { + // outConnector point lying on horizontal grid line 1 unit above inConnector point. + return [ + { x: outConnectorX, y: outConnectorY + 4 * lineWidth }, + { x: outConnectorX + gridSpacing, y: outConnectorY + 4 * lineWidth }, + { x: inConnectorX + lineWidth, y: inConnectorY }, + ]; + } else if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) { + // outConnector point lying on the same horizontal grid line as inConnector point. + return [ + { x: outConnectorX, y: outConnectorY + 5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing }, + { x: outConnectorX + 2 * gridSpacing, y: outConnectorY - gridSpacing }, + { x: outConnectorX + 2 * gridSpacing, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX() + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX() + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else { + // outConnector point lies below inConnector point. + if (-1 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 0 * gridSpacing) { + // outConnector point lying on vertical grid line 1 unit to the left of inConnector point. + if (0 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 2 * gridSpacing) { + //outConnector point lying on the horizontal grid lines 1 and 2 units below the inConnector point. + return [ + { x: outConnectorX, y: outConnectorY + 4 * lineWidth }, + { x: outConnectorX - gridSpacing, y: outConnectorY + 4 * lineWidth }, + { x: outConnectorX - gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: calculateMidY() }, + { x: inConnectorX - 2 * gridSpacing, y: calculateMidY() }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + } else if (verticalOut && inConnectorX <= outConnectorX) { + //outConnector point lies to the right of inConnector point. + + if (outConnectorY < inConnectorY) { + // outConnector point lying on any horizontal grid line above inConnector point. + + if (-2 * gridSpacing < outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) { + // outConnector point lying on horizontal grid line 1 unit above inConnector point. + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (-1 * gridSpacing < outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) { + // outConnector point lying on the same horizontal grid line as inConnector point. + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - 2 * gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: outConnectorY - 2 * gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 3 * gridSpacing) { + // outConnector point lying on vertical grid lines 1 and 2 units to the right of inConnector point. + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + 2 * gridSpacing, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX + 2 * gridSpacing, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX() + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX() + lineWidth, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY - 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else { + //outConnector point lies below inConnector point. + if (outConnectorY - inConnectorY <= gridSpacing) { + //outConnector point lies on the horizontal grid line 1 unit below the inConnector Point + if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 13 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY + 6 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing, y: outConnectorY + 6 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (13 < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 18 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX - 4 * gridSpacing, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX - 4 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX(), y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX(), y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else if (1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 2 * gridSpacing) { + // outConnector point lies on the horizontal grid line 2 units below outConnector point. + + if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 13 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY + 5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: outConnectorY + 5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (13 < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 18 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX - 4 * gridSpacing, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: outConnectorX - 4 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX(), y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: calculateMidX(), y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY + 2 * gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + + if (outConnectorY - inConnectorY <= 4 * gridSpacing) { + //0 to 4 units below the outConnector Point + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: outConnectorY - gridSpacing + 5.5 * lineWidth }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX, y: calculateMidY() }, + { x: inConnectorX - 2 * gridSpacing, y: calculateMidY() }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + } else if (verticalIn) { + // Start horizontal, then vertical + + if (outConnectorY > inConnectorY) { + // when outConnector lies below inConnector + if (outConnectorX < inConnectorX) { + // outConnectorX lies to the left of inConnectorX + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: inConnectorX, y: outConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + // outConnectorX lies to the right of inConnectorX. + if (outConnectorY - inConnectorY <= gridSpacing) { + if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= gridSpacing) { + // outConnector point directly below inConnector point + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: inConnectorX + gridSpacing, y: outConnectorY }, + { x: inConnectorX + gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX, y: outConnectorY - gridSpacing }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + // outConnector point lies below inConnector point and strictly to the right of inConnector Point. + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: inConnectorY + gridSpacing + 6.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY + gridSpacing + 6.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX, y: outConnectorY - gridSpacing }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + } else { + //outConnectorY lies on or above the inConnectorY point. + if (-6 * gridSpacing < inConnectorX - outConnectorX && inConnectorX - outConnectorX < 4 * gridSpacing) { + // edge case: outConnector point lying on vertical grid lines ranging from 4 units to left to 5 units to right of inConnector point. + + if (-1 * gridSpacing < inConnectorX - outConnectorX && inConnectorX - outConnectorX < 4 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX - 4 * gridSpacing, y: outConnectorY - gridSpacing }, + { x: inConnectorX - 4 * gridSpacing, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: inConnectorX + 8 * gridSpacing, y: outConnectorY }, + { x: inConnectorX + 8 * gridSpacing, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else if (4 * gridSpacing < inConnectorX - outConnectorX) { + // left of edge case: outConnector point lying on vertical grid lines more than 4 units to left of inConnector point. + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: calculateMidX() - 2 * lineWidth, y: outConnectorY }, + { x: calculateMidX() - 2 * lineWidth, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else if (6 * gridSpacing > inConnectorX - outConnectorX) { + // right of edge case: outConnector point lying on the vertical grid lines more than 5 units to right of inConnector point. + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY + gridSpacing - 5.5 * lineWidth }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + } else { + // Both horizontal - use horizontal middle point + // When inConnector point is one of the two closest diagonally opposite points. + if (0 <= inConnectorX - outConnectorX && inConnectorX - outConnectorX <= gridSpacing && inConnectorY - outConnectorY >= -1 * gridSpacing && inConnectorY - outConnectorY <= gridSpacing) { + return [ + { x: outConnectorX - 2 * lineWidth, y: outConnectorY }, + { x: outConnectorX - 2 * lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + + // When inConnector point lies on the horizontal line 1 unit above and below the outConnector point. + else if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= gridSpacing && outConnectorX > inConnectorX) { + if (inConnectorY < outConnectorY) { + //horizontal line above outConnectorY + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: inConnectorY - gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY - gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + // horizontal line below outConnectorY + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: outConnectorY }, + { x: outConnectorX + gridSpacing, y: inConnectorY + gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY + gridSpacing }, + { x: inConnectorX - 2 * gridSpacing, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } else if (outConnectorX > inConnectorX - gridSpacing) { + // outConnector point to the right of inConnector point + const midY = (inConnectorY + outConnectorY) / 2 + (((inConnectorY + outConnectorY) / 2) % gridSpacing); + + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: midY }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: midY }, + { x: inConnectorX - 2 * gridSpacing + lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + // when inConnector point lies on the vertical grid line two units to the right of outConnector point. + else if (gridSpacing <= inConnectorX - outConnectorX && inConnectorX - outConnectorX <= 2 * gridSpacing) { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: outConnectorY }, + { x: outConnectorX + gridSpacing - 2 * lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } else { + return [ + { x: outConnectorX, y: outConnectorY }, + { x: calculateMidX() - lineWidth, y: outConnectorY }, + { x: calculateMidX() - lineWidth, y: inConnectorY }, + { x: inConnectorX, y: inConnectorY }, + ]; + } + } + return []; } function buildWirePathString(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): string { const locations = buildWirePathLocations(outputBounds, inputBounds, verticalOut, verticalIn); if (locations.length === 0) return "[error]"; - const SMOOTHING = 0.5; - const delta01 = { x: (locations[1].x - locations[0].x) * SMOOTHING, y: (locations[1].y - locations[0].y) * SMOOTHING }; - const delta23 = { x: (locations[3].x - locations[2].x) * SMOOTHING, y: (locations[3].y - locations[2].y) * SMOOTHING }; - return ` - M${locations[0].x},${locations[0].y} - L${locations[1].x},${locations[1].y} - C${locations[1].x + delta01.x},${locations[1].y + delta01.y} - ${locations[2].x - delta23.x},${locations[2].y - delta23.y} - ${locations[2].x},${locations[2].y} - L${locations[3].x},${locations[3].y} - ` - .split("\n") - .map((line) => line.trim()) - .join(" "); + + if (locations.length === 2) { + return `M${locations[0].x},${locations[0].y} L${locations[1].x},${locations[1].y}`; + } + + const CORNER_RADIUS = 10; + + // Create path with rounded corners + let path = `M${locations[0].x},${locations[0].y}`; + + for (let i = 1; i < locations.length - 1; i++) { + const prev = locations[i - 1]; + const curr = locations[i]; + const next = locations[i + 1]; + + // Calculate corner points + const isVertical = curr.x === prev.x; + const cornerStart = { + x: curr.x + (isVertical ? 0 : prev.x < curr.x ? -CORNER_RADIUS : CORNER_RADIUS), + y: curr.y + (isVertical ? (prev.y < curr.y ? -CORNER_RADIUS : CORNER_RADIUS) : 0), + }; + const cornerEnd = { + x: curr.x + (isVertical ? (next.x < curr.x ? -CORNER_RADIUS : CORNER_RADIUS) : 0), + y: curr.y + (isVertical ? 0 : next.y < curr.y ? -CORNER_RADIUS : CORNER_RADIUS), + }; + + // Add line to corner start, quadratic curve for corner, then continue to next point + path += ` L${cornerStart.x},${cornerStart.y}`; + path += ` Q${curr.x},${curr.y} ${cornerEnd.x},${cornerEnd.y}`; + } + + path += ` L${locations[locations.length - 1].x},${locations[locations.length - 1].y}`; + return path; } function toggleLayerDisplay(displayAsLayer: boolean, toggleId: bigint) {