color-synth

a synth that generates colors instead of sounds
Log | Files | Refs | README

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>