commit 305848b3f608989ba3289a49e5911aa375e2319f
parent 0b6136b99e5974b174908de9a3bd00a8948bca23
Author: massi <mdsiboldi@gmail.com>
Date: Sat, 22 Jul 2023 00:13:04 -0700
drag n drop
Diffstat:
7 files changed, 148 insertions(+), 48 deletions(-)
diff --git a/src/lib/InputDragger.svelte b/src/lib/InputDragger.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ export let onConnect: (() => void) | null | undefined;
+ export let connected: boolean;
+</script>
+
+<div class="input-dragger">
+ {#if onConnect}
+ <div
+ class={['connect', connected && 'connected'].filter(Boolean).join(' ')}
+ on:mouseup={onConnect}
+ />
+ {/if}
+</div>
+
+<style>
+ .connect {
+ position: absolute;
+ background: green;
+ opacity: 0.5;
+ top: -5px;
+ right: -5px;
+ bottom: -5px;
+ left: -5px;
+ }
+ .input-dragger {
+ background: lightgreen;
+ width: 5px;
+ position: absolute;
+ top: 2px;
+ bottom: 2px;
+ left: -10px;
+ }
+ .connect.connected {
+ background: red;
+ }
+</style>
diff --git a/src/lib/NumberSelector.svelte b/src/lib/NumberSelector.svelte
@@ -1,11 +1,8 @@
<script lang="ts">
- import { range, clamp } from '$lib/types';
+ import { clamp } from '$lib/types';
export let value: number;
export let updateValue: (n: number) => void;
- const MIN = range.slider.min;
- const MAX = range.slider.max;
-
let pos:
| {
start: { x: number; y: number };
@@ -69,9 +66,8 @@
let ticker: Ticker | undefined;
- $: distance = pos ? Math.sqrt(pos.delta.x * pos.delta.x + pos.delta.y * pos.delta.y) : 0;
-
const handleMouseDown = async (evt: MouseEvent) => {
+ //@ts-ignore TODO figure out if this is true cuz mdn says to do this.
await el?.requestPointerLock();
pos = {
start: {
diff --git a/src/lib/Osc.svelte b/src/lib/Osc.svelte
@@ -1,10 +1,13 @@
<script lang="ts">
- import type { OscUnit, WithTarget } from '$lib/types';
- import { wrangle, rescale, range } from '$lib/types';
+ import type { OscUnit, OscUnitInputs, Output } from '$lib/types';
+ import { unitInputs, wrangle, rescale, range } from '$lib/types';
import NumberSelector from '$lib/NumberSelector.svelte';
+ import InputDragger from '$lib/InputDragger.svelte';
export let unit: OscUnit;
+ export let signalDragging: false | Output;
export let updateOsc: (osc: OscUnit) => void;
+ export let onConnect: ((k: string) => void) | null;
$: vals = {
coarse:
@@ -18,22 +21,34 @@
typeof unit.amount === 'number' ? wrangle(unit.amount, range.signal, range.osc.amount) : null
};
- console.log({ vals });
-
- const updateValue = (k: string, n: number) => {
+ const updateValue = (k: keyof OscUnitInputs, n: number) => {
updateOsc({ ...unit, [k]: Math.round(rescale(n, range.osc[k], range.signal)) });
};
+
+ const isConnected = (k: keyof OscUnitInputs, signalDragging: false | Output) => {
+ const inputs = unit[k];
+ if (signalDragging && Array.isArray(inputs)) {
+ const { id: sigId } = signalDragging;
+ return Boolean(inputs.find(({ id }) => id === sigId));
+ }
+ return false;
+ };
</script>
<div class="unit-container">
<h3>{unit.kind}</h3>
- {#each [...Object.entries(vals)] as [k, v]}
+ {#each unitInputs.osc as k}
+ {@const v = vals[k]}
+ {@const inputs = unit[k]}
<div class="sect">
+ <InputDragger
+ connected={isConnected(k, signalDragging)}
+ onConnect={onConnect ? onConnect.bind(null, k) : null}
+ />
{#if v !== null}
<h3>{k}</h3>
<NumberSelector value={v} updateValue={updateValue.bind(undefined, k)} />
<input
- name={unit.id}
type="range"
min={range.osc[k].min}
max={range.osc[k].max}
@@ -41,6 +56,8 @@
value={v}
on:input={(e) => updateValue(k, Number(e.currentTarget?.value))}
/>
+ {:else}
+ <div>{Array.isArray(inputs) ? inputs.map((o) => o.id).join(' ') : ''}</div>
{/if}
</div>
{/each}
@@ -48,13 +65,10 @@
<style>
.sect {
+ position: relative;
width: 100%;
height: 80px;
}
- .disabled {
- opacity: 0.5;
- pointer-events: none;
- }
.unit-container {
background: rgba(255, 255, 255, 0.3);
padding: 10px;
diff --git a/src/lib/Sink.svelte b/src/lib/Sink.svelte
@@ -1,22 +1,33 @@
<script lang="ts">
+ import type { Input } from '$lib/types';
export let channel: 'l' | 'c' | 'h';
export let onSinkConnect: null | ((ch: 'l' | 'c' | 'h') => void);
+ export let connected: boolean;
+ export let input: Input | null;
let _onSinkConnect: null | (() => void) = null;
- $: _onSinkConnect = onSinkConnect ? () => onSinkConnect && onSinkConnect(channel) : null;
+ $: _onSinkConnect = onSinkConnect ? onSinkConnect.bind(null, channel) : null;
+ $: classes = ['connect', _onSinkConnect && 'hl', connected && 'connected']
+ .filter(Boolean)
+ .join(' ');
</script>
<div class="sink">
{#if onSinkConnect}
- <div class="connect" on:mouseup={_onSinkConnect} />
+ <div class={classes} on:mouseup={_onSinkConnect} />
{/if}
- <button class={Boolean(_onSinkConnect) ? 'hl' : ''}>{channel}</button>
+ <button class={Boolean(_onSinkConnect) ? 'hl' : ''}>
+ {channel}: {Array.isArray(input) ? input.map((o) => o.id).join(' ') : 'none'}
+ </button>
</div>
<style>
.hl {
background: lightgreen;
}
+ .hl.connected {
+ background: red;
+ }
.sink {
position: relative;
}
@@ -31,12 +42,15 @@
.connect:hover {
background: green;
}
+ .connect.connected:hover {
+ background: red;
+ }
button {
margin: 5px;
- width: 40px;
+ width: 150px;
height: 40px;
padding: 10px 15px;
font-size: 16px;
- border-radius: 100%;
+ border-radius: 50%;
}
</style>
diff --git a/src/lib/engine.worker.ts b/src/lib/engine.worker.ts
@@ -70,14 +70,6 @@ function vUnit(x: { id: UnitId }): number {
const position = getUnitState(id);
return Math.sin(2 * Math.PI * (position / LOOP_CYCLES)) *
v(unit.amount);
- //if (id % 7 === 0) {
- // return (position < LOOP_CYCLES / 2 ? -1 : 1) * v(unit.amount);
- //} else if (id % 5 === 0) {
- // return (position / LOOP_CYCLES) * v(unit.amount);
- //} else {
- // return Math.sin(2 * Math.PI * (position / LOOP_CYCLES)) *
- // v(unit.amount);
- //}
}
case "const": {
// range is whatever the control for it is???
diff --git a/src/lib/types.ts b/src/lib/types.ts
@@ -24,13 +24,10 @@ export type EngineMessage = {
content: OffscreenCanvas;
};
-export type HigherUnit =
- | OscUnit
- | NoiseUnit;
-
export type Unit =
| ConstUnit
- | HigherUnit;
+ | OscUnit
+ | NoiseUnit;
export type UnitId = string;
@@ -41,12 +38,25 @@ export type Pos = { x: number; y: number };
type WithPos = { pos: Pos };
-export type OscUnit = WithPos & {
- kind: "osc";
+export type OscUnitInputs = {
coarse: Input;
fine: Input;
superfine: Input;
amount: Input;
+}
+export type NoiseUnitInputs = {
+ amount: Input;
+}
+type UnitInputs = {
+ osc: (keyof OscUnitInputs)[],
+ noise: (keyof NoiseUnitInputs)[]
+}
+export const unitInputs: UnitInputs = {
+ osc: ['coarse', 'fine', 'superfine', 'amount'],
+ noise: ['amount']
+}
+export type OscUnit = WithPos & OscUnitInputs & {
+ kind: "osc";
};
export type NoiseUnit = WithPos & {
@@ -228,8 +238,15 @@ export const inp = {
}
else {
// it's there and we removed it
- return deduped;
+ // if the last input signal was removed, we're back to a value.
+ return deduped.length === 0 ? 0 : deduped;
}
}
+ },
+ connected: (input: Input, output: Output): boolean => {
+ if (Array.isArray(input)) {
+ return Boolean(input.find((o) => o.id === output.id));
+ }
+ return false;
}
}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
@@ -4,7 +4,7 @@
import Sink from '$lib/Sink.svelte';
import Osc from '$lib/Osc.svelte';
import Slider from '$lib/Slider.svelte';
- import { inp, ensure, rescale, range, is, getUnit as _getUnit } from '$lib/types';
+ import { inp, rescale, range, is, getUnit as _getUnit } from '$lib/types';
import type { Output, Input, Unit, UnitId, ConstUnit, UnitMap, Sinks } from '$lib/types';
let cvs: HTMLCanvasElement | undefined;
@@ -68,6 +68,7 @@
const doc: SerializedState = fromUrl();
if (doc) {
units = new Map(Object.entries(doc.units));
+ uid = units.size;
sinks = doc.sinks;
}
});
@@ -177,9 +178,15 @@
let signalDragging: false | Output = false;
- const handleSignalStart = (obj: Output) => {
- console.log('signal start', obj);
- signalDragging = obj;
+ const handleSignalStart = (o: Output) => {
+ signalDragging = o;
+ const h = () => {
+ signalDragging = false;
+ document.removeEventListener('mouseup', h);
+ };
+ setTimeout(() => {
+ document.addEventListener('mouseup', h);
+ }, 0);
};
const _onSinkConnect = (ch: 'l' | 'c' | 'h'): void => {
@@ -198,11 +205,26 @@
$: onSinkConnect = signalDragging ? _onSinkConnect : null;
- $: unitEntries = units.entries();
type SinkEntries = [keyof Sinks, Input | null][];
// Object.entries COULD have extra stuff, so it doesn't assume keys are keyof Sinks exactly.
// see https://stackoverflow.com/questions/60141960/typescript-key-value-relation-preserving-object-entries-type
$: sinkEntries = [...Object.entries(sinks)] as SinkEntries;
+
+ const getConnectHandler = (sdrag: false | Output, input: UnitId) => {
+ if (!sdrag) return null;
+ if (sdrag.id === input) return null;
+ return connectHandler.bind(null, sdrag.id, input);
+ };
+
+ const connectHandler = (output: UnitId, input: UnitId, k: string) => {
+ const unit = getUnit(input);
+ // @ts-ignore unit[k] is a channel but i don't wanna prove it.
+ units.set(input, { ...unit, [k]: inp.toggle(unit[k], { id: output }) });
+ engineWorker && engineWorker.postMessage({ kind: 'config', content: { sinks, units } });
+ console.log(sinks);
+ signalDragging = false;
+ units = units;
+ };
</script>
<canvas bind:this={cvs} />
@@ -212,20 +234,29 @@
<button on:click={mk.osc}>add osc</button>
</div>
-<h1>HI {unitEntries}</h1>
<div id="sinks">
- {#each sinkEntries as [channel]}
- <Sink {channel} {onSinkConnect} />
+ {#each sinkEntries as [channel, input]}
+ <Sink
+ {input}
+ {channel}
+ {onSinkConnect}
+ connected={signalDragging && input ? inp.connected(input, signalDragging) : false}
+ />
{/each}
</div>
<div id="units">
- {#each [...unitEntries] as [id, unit]}
+ {#each [...units.entries()] as [id, unit]}
<div class="unit" style={`top: ${unit.pos.y}px; left: ${unit.pos.x}px`}>
<h2 on:mousedown={handleMouseDown.bind(null, id)}>{id} {unit.kind}</h2>
{#if unit.kind === 'const'}
<Slider {id} {units} handleInput={updateUnit} handleChange={updateUrl} />
{:else if unit.kind === 'osc'}
- <Osc {unit} updateOsc={updateEntireUnit.bind(null, id)} />
+ <Osc
+ {unit}
+ {signalDragging}
+ onConnect={getConnectHandler(signalDragging, id)}
+ updateOsc={updateEntireUnit.bind(null, id)}
+ />
{/if}
<div
class="output-dragger"