Wires.svelte (2809B)
1 <script lang="ts"> 2 import { onMount } from 'svelte'; 3 import { sinksStore, unitStore } from './stores'; 4 import type { Input, NumberRange, Pos } from './types'; 5 6 let cvs: HTMLCanvasElement | null = null; 7 8 type VisitCb = (start: Pos, end: Pos) => void; 9 const visitInput = (ownerQuery: string, input: Input, cb: VisitCb) => { 10 const ownerEl = document.querySelector(ownerQuery); 11 if (ownerEl === null) { 12 return; 13 } else { 14 for (let src of input.sources) { 15 const id = src.id; 16 const el = document.querySelector(`div[data-unit-id='${id}'] .output-dragger`); 17 18 if (el) { 19 cb(ownerEl.getBoundingClientRect(), el.getBoundingClientRect()); 20 } 21 } 22 } 23 }; 24 25 const visitConnections = (cb: VisitCb) => { 26 // just use query selectors to find the elements to connect. 27 28 // controls 29 for (let [id, unit] of Object.entries($unitStore)) { 30 const unitQuery = `div[data-unit-id='${id}']`; 31 for (let [controlName, control] of Object.entries(unit.controls)) { 32 const query = `${unitQuery} div[data-control-name='${controlName}']`; 33 visitInput(query, control, cb); 34 } 35 } 36 // sinks 37 for (let ch of ['l', 'c', 'h'] as const) { 38 // console.log($sinksStore[ch]); 39 visitInput(`div[data-sink-ch='${ch}']`, $sinksStore[ch], cb); 40 } 41 }; 42 const draw = () => { 43 const ctx = cvs?.getContext('2d'); 44 const OFFSET = 8; 45 46 if (ctx) { 47 ctx.reset(); 48 visitConnections((start, end) => { 49 if (ctx) { 50 ctx.strokeStyle = ctx.fillStyle = 'red'; 51 ctx.lineWidth = 5; 52 53 ctx.beginPath(); 54 ctx.ellipse(start.x + OFFSET, start.y + OFFSET, 6, 6, 0, 0, 2 * Math.PI); 55 ctx.closePath(); 56 ctx.fill(); 57 58 ctx.beginPath(); 59 ctx.ellipse(end.x + OFFSET, end.y + OFFSET, 6, 6, 0, 0, 2 * Math.PI); 60 ctx.closePath(); 61 ctx.fill(); 62 63 ctx.beginPath(); 64 ctx.moveTo(start.x + OFFSET, start.y + OFFSET); 65 ctx.lineTo(end.x + OFFSET, end.y + OFFSET); 66 ctx.closePath(); 67 ctx.stroke(); 68 } 69 }); 70 } 71 }; 72 const loop = () => { 73 // setInterval(draw, 1000); 74 draw(); 75 requestAnimationFrame(loop); 76 }; 77 loop(); 78 79 onMount(() => { 80 const resizeObserver = new ResizeObserver((entries) => { 81 const entry = entries.find((entry) => entry.target === cvs); 82 if (entry && cvs) { 83 const width = entry.devicePixelContentBoxSize[0].inlineSize; 84 const height = entry.devicePixelContentBoxSize[0].blockSize; 85 cvs.width = width; 86 cvs.height = height; 87 } 88 }); 89 90 if (cvs) resizeObserver.observe(cvs, { box: 'device-pixel-content-box' }); 91 92 // This callback cleans up the observer 93 return () => { 94 if (cvs) resizeObserver.unobserve(cvs); 95 }; 96 }); 97 </script> 98 99 <canvas bind:this={cvs} /> 100 101 <style> 102 canvas { 103 position: absolute; 104 z-index: 100; 105 top: 0; 106 left: 0; 107 height: 100vh; 108 width: 100vw; 109 opacity: 0.5; 110 pointer-events: none; 111 } 112 </style>