11import { useEffect , useRef , useState , useLayoutEffect } from "react" ;
22import { 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" ;
44import { 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
3761export 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