Skip to content

Commit 83d9951

Browse files
committed
wires come out cleanly of outputs
1 parent c3467c8 commit 83d9951

1 file changed

Lines changed: 41 additions & 15 deletions

File tree

src/components/Wires.jsx

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
import { useEffect, useRef, useState, useLayoutEffect } from "react";
22
import { useSynthStore } from "../store/useSynthStore.js";
3-
import { PORT_TYPE, listStaticPorts } from "../audio/graph/types.js";
3+
import { PORT_TYPE, PORT_DIR, listStaticPorts } from "../audio/graph/types.js";
44
import { byType } from "../modules/_registry.js";
55

6-
function lookupPortType(modules, fromId, fromPort) {
7-
const m = modules.find((x) => x.id === fromId);
6+
function lookupPort(modules, moduleId, portName) {
7+
const m = modules.find((x) => x.id === moduleId);
88
if (!m) return null;
99
const manifest = byType(m.type);
1010
if (!manifest) return null;
11-
const port = listStaticPorts(manifest.Cls).find((p) => p.name === fromPort);
12-
return port?.type || null;
11+
return listStaticPorts(manifest.Cls).find((p) => p.name === portName) || null;
1312
}
1413

14+
// Mirrors the placement rule in ModulePorts.jsx — audio sits left/right,
15+
// CV/pitch/gate sit top/bottom — so the bezier handle leaves the port in the
16+
// direction the port actually emerges from the module.
17+
function portEdge(port) {
18+
if (!port) return "right";
19+
if (port.type === PORT_TYPE.AUDIO) return port.dir === PORT_DIR.IN ? "left" : "right";
20+
return port.dir === PORT_DIR.OUT ? "top" : "bottom";
21+
}
22+
23+
const EDGE_TANGENT = {
24+
right: [ 1, 0],
25+
left: [-1, 0],
26+
top: [ 0, -1],
27+
bottom: [ 0, 1],
28+
};
29+
1530
// Unified wire overlay. Reads every connection in the store, looks up each
1631
// endpoint's screen position via [data-port-id="<moduleId>:<portName>"]
1732
// querySelector, and draws an SVG bezier between them.
@@ -24,14 +39,23 @@ const TYPE_COLOR = {
2439
};
2540

2641

27-
// Build a smooth bezier between two screen points. Inputs/outputs are roughly
28-
// horizontal in our layout, so a horizontal-handle cubic Bezier looks natural.
29-
function pathBetween(from, to) {
30-
const dx = Math.abs(to.x - from.x);
31-
const handle = Math.max(40, dx * 0.45);
32-
const c1x = from.x + handle;
33-
const c2x = to.x - handle;
34-
return `M ${from.x} ${from.y} C ${c1x} ${from.y}, ${c2x} ${to.y}, ${to.x} ${to.y}`;
42+
// Build a smooth cubic bezier whose end-tangents point outward from each
43+
// endpoint's edge — so a top-edge CV output leaves vertically up, a right-edge
44+
// audio out leaves horizontally right, etc. Handle length scales with the
45+
// distance between endpoints so the curve looks tight on short hops and
46+
// generous on long ones.
47+
function pathBetween(from, to, fromEdge, toEdge) {
48+
const dist = Math.hypot(to.x - from.x, to.y - from.y);
49+
const h = Math.max(40, dist * 0.4);
50+
const [fdx, fdy] = EDGE_TANGENT[fromEdge] || EDGE_TANGENT.right;
51+
const [tdx, tdy] = EDGE_TANGENT[toEdge] || EDGE_TANGENT.left;
52+
const c1x = from.x + h * fdx;
53+
const c1y = from.y + h * fdy;
54+
// toTangent points OUT of the destination port; the incoming handle is the
55+
// mirror of that (the curve approaches the port from outside).
56+
const c2x = to.x + h * tdx;
57+
const c2y = to.y + h * tdy;
58+
return `M ${from.x} ${from.y} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${to.x} ${to.y}`;
3559
}
3660

3761
export function Wires({ containerRef }) {
@@ -66,10 +90,12 @@ export function Wires({ containerRef }) {
6690
const tr = toEl.getBoundingClientRect();
6791
const from = { x: fr.left + fr.width / 2 - cRect.left, y: fr.top + fr.height / 2 - cRect.top };
6892
const to = { x: tr.left + tr.width / 2 - cRect.left, y: tr.top + tr.height / 2 - cRect.top };
93+
const fromPort = lookupPort(modules, conn.fromId, conn.fromPort);
94+
const toPort = lookupPort(modules, conn.toId, conn.toPort);
6995
next.push({
7096
id: conn.id,
71-
d: pathBetween(from, to),
72-
type: lookupPortType(modules, conn.fromId, conn.fromPort) || PORT_TYPE.AUDIO,
97+
d: pathBetween(from, to, portEdge(fromPort), portEdge(toPort)),
98+
type: fromPort?.type || PORT_TYPE.AUDIO,
7399
midX: (from.x + to.x) / 2,
74100
midY: (from.y + to.y) / 2,
75101
});

0 commit comments

Comments
 (0)