commit b1a7aa45fd457a8f8efcf6e94cb112a7b4f939b8
parent bc65021938b86b284cf07a62f69cdeccc401f005
Author: massi <mdsiboldi@gmail.com>
Date: Tue, 18 Jul 2023 23:28:02 -0700
Starting drag n drop units
Diffstat:
4 files changed, 174 insertions(+), 31 deletions(-)
diff --git a/src/lib/Sink.svelte b/src/lib/Sink.svelte
@@ -0,0 +1,44 @@
+<script lang="ts">
+ import type { UnitId } from '$lib/types';
+ export let id: UnitId;
+ export let channel: 'l' | 'c' | 'h';
+ export let onSinkConnect: null | ((ch: string) => void);
+
+ let _onSinkConnect: null | (() => void) = null;
+ $: _onSinkConnect = onSinkConnect ? () => onSinkConnect && onSinkConnect(channel) : null;
+</script>
+
+<div class="sink">
+ {#if onSinkConnect}
+ <div class="connect" on:mouseup={_onSinkConnect} />
+ {/if}
+ <button class={Boolean(_onSinkConnect) ? 'hl' : ''}>{channel}</button>
+</div>
+
+<style>
+ .hl {
+ background: lightgreen;
+ }
+ .sink {
+ position: relative;
+ }
+ .connect {
+ position: absolute;
+ top: -5px;
+ left: -5px;
+ bottom: -5px;
+ right: -5px;
+ opacity: 0.5;
+ }
+ .connect:hover {
+ background: green;
+ }
+ button {
+ margin: 5px;
+ width: 40px;
+ height: 40px;
+ padding: 10px 15px;
+ font-size: 16px;
+ border-radius: 100%;
+ }
+</style>
diff --git a/src/lib/engine.worker.ts b/src/lib/engine.worker.ts
@@ -136,15 +136,18 @@ function drawSquares() {
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
- color[0] = wrangle(v(config.sinks.red), range.signal, {
+ const l = config.sinks.l == null ? 0 : v({ id: config.sinks.l });
+ const c = config.sinks.c == null ? 0 : v({ id: config.sinks.c });
+ const h = config.sinks.h == null ? 0 : v({ id: config.sinks.h });
+ color[0] = wrangle(l, range.signal, {
min: 0,
max: 100,
});
- color[1] = wrangle(v(config.sinks.green), range.signal, {
+ color[1] = wrangle(c, range.signal, {
min: 0,
max: 0.5,
});
- color[2] = wrangle(v(config.sinks.blue), range.signal, {
+ color[2] = wrangle(h, range.signal, {
min: 0,
max: 360,
});
diff --git a/src/lib/types.ts b/src/lib/types.ts
@@ -35,9 +35,10 @@ export type Unit =
export type UnitId = string;
-// To support units with multiple outputs.
export type Input = { id: UnitId };
+export type Pos = { x: number; y: number };
+
export type RescaleUnit = {
kind: "rescale";
input: Input;
@@ -45,7 +46,6 @@ export type RescaleUnit = {
max: Input;
};
-// uses rate and amount to output a sine wave going that fast and loud. TBD: what units the values are, etc.
export type OscUnit = {
kind: "osc";
coarse: Input;
@@ -63,6 +63,7 @@ export type NoiseUnit = {
export type ConstUnit = {
kind: "const";
value: number;
+ pos: Pos;
};
export type CombinatorUnit = {
@@ -74,9 +75,9 @@ export type UnitMap = Map<UnitId, Unit>;
export type UnitStateMap = Map<UnitId, UnitState>;
export type UnitState = any;
export type Sinks = {
- red?: Input;
- green?: Input;
- blue?: Input;
+ l: Input | null;
+ c: Input | null;
+ h: Input | null;
};
type Range = {
@@ -209,6 +210,13 @@ export const is = {
export const ensure = {
unit: {
+ const: (u: Unit): ConstUnit => {
+ if (is.unit.const(u)) {
+ return u;
+ } else {
+ throw new Error("this is not a const");
+ }
+ },
osc: (u: Unit): OscUnit => {
if (is.unit.osc(u)) {
return u;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
@@ -1,8 +1,9 @@
<script lang="ts">
import { onMount } from 'svelte';
import { debounce } from 'lodash';
+ import Sink from '$lib/Sink.svelte';
import Slider from '$lib/Slider.svelte';
- import { rescale, range, is, getUnit as _getUnit, INPUT_RANGE } from '$lib/types';
+ import { ensure, rescale, range, is, getUnit as _getUnit, INPUT_RANGE } from '$lib/types';
import type { Input, Unit, UnitId, ConstUnit, UnitMap, Sinks } from '$lib/types';
let cvs: HTMLCanvasElement | undefined;
@@ -153,42 +154,129 @@
}
};
- const shared = new Array(10).fill(0).map(() => mk.c());
-
- const red = mk.add(
- mk.c(), //
- mk.osc(mk.add(mk.c(), mk.osc()))
- );
- const green = mk.add(
- mk.c(), //
- mk.osc(mk.add(mk.c(), mk.osc()))
- );
- const blue = mk.add(
- mk.c(), //
- mk.osc(mk.add(mk.c(), mk.osc()))
- );
-
- sinks = { red, green, blue };
+ sinks = { l: null, c: null, h: null };
function updateUnit(k: UnitId, value: number) {
const unit = get.constUnit(k);
unit.value = value;
units = units;
}
+
+ const addConst = () => {
+ addUnit({ kind: 'const', value: randConst(), pos: { x: 5, y: 5 } });
+ units = units;
+ };
+
+ let dragging: false | UnitId = false;
+
+ const handleMouseMove = (e) => {
+ if (!dragging) return;
+ ensure.unit.const(getUnit(dragging)).pos = {
+ x: e.clientX,
+ y: e.clientY
+ };
+ units = units;
+ };
+
+ const handleMouseUp = () => {
+ dragging = false;
+ document.removeEventListener('mousemove', handleMouseMove);
+ document.removeEventListener('mouseup', handleMouseUp);
+ };
+
+ const handleMouseDown = (id: UnitId) => {
+ console.log('mouse down', id);
+ dragging = id;
+ document.addEventListener('mousemove', handleMouseMove);
+ document.addEventListener('mouseup', handleMouseUp);
+ };
+
+ let signalDragging: false | { id: UnitId } = false;
+
+ const handleSignalStart = (obj: { id: UnitId }) => {
+ console.log('signal start', obj);
+ const unit = getUnit(obj.id);
+ switch (unit.kind) {
+ case 'const': {
+ signalDragging = obj;
+ break;
+ }
+ default:
+ throw new Error('handleSignalStart nyi');
+ }
+ };
+
+ const _onSinkConnect = (ch: 'l' | 'c' | 'h'): void => {
+ if (!signalDragging) {
+ throw new Error('cant connect sink to nonexistant signal');
+ }
+ sinks[ch] = signalDragging.id;
+ engineWorker && engineWorker.postMessage({ kind: 'config', content: { sinks, units } });
+ console.log(sinks);
+ signalDragging = false;
+ };
+
+ $: onSinkConnect = signalDragging ? _onSinkConnect : null;
+
+ $: unitEntries = units.entries();
+ $: sinkEntries = Object.entries(sinks);
</script>
<canvas bind:this={cvs} />
-<div id="sliders">
- {#each [...Object.entries(sinks)] as [label, input]}
- <div>
- <h2>{label}</h2>
- <Slider id={input.id} {units} handleInput={updateUnit} handleChange={updateUrl} />
+<div id="buttons">
+ <button on:click={(e) => addConst()}>add const</button>
+</div>
+
+<h1>HI {unitEntries}</h1>
+<div id="sinks">
+ {#each [...sinkEntries] as [channel, id]}
+ <Sink {id} {channel} {onSinkConnect} />
+ {/each}
+</div>
+<div id="units">
+ {#each [...unitEntries] as [id, unit]}
+ <div class="unit" style={`top: ${unit.pos.y}px; left: ${unit.pos.x}px`}>
+ <h2 on:mousedown={(e) => handleMouseDown(id)}>{id} {unit.kind}</h2>
+ <Slider {id} {units} handleInput={updateUnit} handleChange={updateUrl} />
+ <div
+ class="output-dragger"
+ on:mousedown={(e) => {
+ e.preventDefault();
+ handleSignalStart({ id });
+ }}
+ />
</div>
{/each}
</div>
<style>
+ .output-dragger {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ width: 20px;
+ background: purple;
+ }
+ #sinks {
+ position: absolute;
+ top: 0;
+ right: 0;
+ display: flex;
+ flex-direction: column;
+ }
+ .unit {
+ position: absolute;
+ z-index: 1;
+ background: black;
+ }
+ #buttons {
+ z-index: 1;
+ position: fixed;
+ bottom: 5px;
+ right: 5px;
+ }
h2 {
margin: 0 5px;
background: white;
@@ -198,7 +286,7 @@
height: 100vh;
background: salmon;
}
- #sliders {
+ #units {
position: absolute;
left: 10px;
top: 10px;