commit 5c9284a6084c232c1c47be8539b15e81f2c2afc2
parent 9a1882ee9f108571b74e38371612467f7492a3f7
Author: massi <mdsiboldi@gmail.com>
Date: Mon, 15 Jul 2024 11:43:07 -0700
initial image support, modify build config
Diffstat:
6 files changed, 130 insertions(+), 30 deletions(-)
diff --git a/src/lib/ImgUnit.svelte b/src/lib/ImgUnit.svelte
@@ -12,6 +12,7 @@
<div>
<h1>image of a turtle</h1>
+ <UnitControl {...common} controlName="speed" />
</div>
<style>
diff --git a/src/lib/engine.worker.ts b/src/lib/engine.worker.ts
@@ -18,7 +18,8 @@ import {
wrangle,
unitRange,
oscShapes,
- mathOps
+ mathOps,
+ CELLS
} from '$lib/types';
import _ from 'lodash';
@@ -62,6 +63,44 @@ function getUnitState(id: UnitId): UnitState {
case 'lag': {
return theState === undefined ? [] : theState;
}
+ case 'img': {
+ if (theState === undefined) {
+ // NOW: make img sample here. should also store position
+ console.log('fetching...');
+ const fetcher = async () => {
+ try {
+ const img = await fetch(
+ new Request(
+ 'https://t4.ftcdn.net/jpg/01/36/70/67/360_F_136706734_KWhNBhLvY5XTlZVocpxFQK1FfKNOYbMj.jpg',
+ {
+ mode: 'cors'
+ }
+ )
+ );
+ const bmp = await createImageBitmap(await img.blob(), {
+ resizeHeight: ROWS,
+ resizeWidth: COLS,
+ resizeQuality: 'pixelated'
+ });
+ const canvas = new OffscreenCanvas(bmp.width, bmp.height);
+ const ctx = canvas.getContext('2d');
+ ctx?.drawImage(bmp, 0, 0, COLS, ROWS);
+ const imgData = ctx?.getImageData(0, 0, COLS, ROWS);
+ const newState = {
+ position: 0,
+ data: imgData!.data
+ };
+ console.log(newState);
+ setUnitState(id, newState);
+ } catch (e) {
+ console.error(e);
+ }
+ };
+ fetcher();
+ return 'loading';
+ }
+ return theState;
+ }
default: {
throw new Error('state for this invalid or NYI');
}
@@ -79,11 +118,18 @@ function vUnit(x: { id: UnitId }): number {
const unit: Unit = getUnit(null, config!.units, id);
switch (unit.kind) {
case 'osc': {
- const position = getUnitState(id);
+ const position = getUnitState(id) / LOOP_CYCLES;
const shapeIdx = Math.round(
wrangle(v(unit.controls.waveshape), RANGE.signal, unitRange.osc.waveshape)
);
- return oscShapes[shapeIdx](position / LOOP_CYCLES, v(unit.controls.amount));
+ // to calculate amplitude at position:
+ // take the position as % progress through the wave.
+ // find the osc shape's "value" at that point, range being pmone.
+ // multiply that by the amount control, scaled to pmone,
+ // finally, scale this value to a signal.
+ const scalar = wrangle(v(unit.controls.amount), RANGE.signal, RANGE.pmone);
+ const waveAmp = oscShapes[shapeIdx](position);
+ return wrangle(waveAmp * scalar, RANGE.pmone, RANGE.signal);
}
case 'const': {
return v(unit.controls.value);
@@ -109,6 +155,22 @@ function vUnit(x: { id: UnitId }): number {
case 'lag': {
return getUnitState(id)[0];
}
+ case 'img': {
+ // NOW: make offscreen canvas of the same resolution as the screen
+ // take color measurements of each pixel
+ // store position and increase by one mod cells each frame
+ const state = getUnitState(id);
+ logMap.img = state;
+ if (state instanceof Object) {
+ logMap.test = state.data[0];
+ return rescale(
+ state.data[Math.abs(Math.round(state.position)) * 4],
+ { min: 0, max: 255 },
+ RANGE.signal
+ );
+ }
+ return 0;
+ }
}
}
@@ -168,7 +230,7 @@ function update() {
} else {
setUnitState(
id,
- (position + (coarse * LOOP_CYCLES) / 100 + fine * 20000 + superfine) % LOOP_CYCLES
+ (position + (coarse * LOOP_CYCLES) / 100 + fine * 10000 + superfine) % LOOP_CYCLES
);
}
break;
@@ -192,7 +254,7 @@ function update() {
let arr = getUnitState(id) as number[];
arr.push(v(unit.controls.signal));
if (arr.length > amt) {
- if (arr.length - 1 === amt) {
+ if (arr.length - 2 === amt) {
arr.pop();
} else {
arr = arr.slice(arr.length - amt);
@@ -203,6 +265,15 @@ function update() {
}
break;
}
+ case 'img': {
+ const state = getUnitState(id);
+ if (state instanceof Object) {
+ const speed = wrangle(v(unit.controls.speed), RANGE.signal, unitRange.img.speed);
+ state.position = (state.position + speed / 100000) % CELLS;
+ } else {
+ setUnitState(id, state);
+ }
+ }
}
}
}
diff --git a/src/lib/stores.ts b/src/lib/stores.ts
@@ -115,6 +115,15 @@ const mkUnitStore = () => {
};
break;
}
+ case 'img': {
+ unit = {
+ kind: 'img',
+ controls: {
+ speed: mkInput(rescale(100000, unitRange.img.speed, RANGE.signal))
+ },
+ pos: { x: 0, y: 0 }
+ };
+ }
}
const uuid = uuidv4();
diff --git a/src/lib/types.ts b/src/lib/types.ts
@@ -40,7 +40,7 @@ export type Pos = {
y: number;
};
-export const unitKinds = ['const', 'osc', 'noise', 'smooth', 'math', 'lag'] as const;
+export const unitKinds = ['const', 'osc', 'noise', 'smooth', 'math', 'lag', 'img'] as const;
export type UnitKind = (typeof unitKinds)[number];
type Corr<X, K extends keyof X> = { [P in K]: X[P] }[K];
export type Unit<K extends UnitKind = UnitKind> = Corr<UnitMap, K>;
@@ -52,6 +52,7 @@ type UnitMap = {
smooth: SmoothUnit;
math: MathUnit;
lag: LagUnit;
+ img: ImgUnit;
};
// TODO: can we error when keys don't exhaust UnitKind? Record can't infer the tuples correctly.
@@ -61,7 +62,8 @@ const controlNames = {
const: ['value'] as const,
smooth: ['signal', 'frames'] as const,
math: ['a', 'op', 'b'] as const,
- lag: ['signal', 'amount'] as const
+ lag: ['signal', 'amount'] as const,
+ img: ['speed'] as const
};
type ControlNames = typeof controlNames;
export type ControlName<K extends UnitKind> = ControlNames[K][number];
@@ -80,6 +82,7 @@ export type NoiseUnit = GenericUnit<'noise'>;
export type SmoothUnit = GenericUnit<'smooth'>;
export type MathUnit = GenericUnit<'math'>;
export type LagUnit = GenericUnit<'lag'>;
+export type ImgUnit = GenericUnit<'img'>;
export type UnitId = string;
@@ -120,16 +123,18 @@ export const RANGE = {
}
} as const;
-type OscShapes = Array<(p: number, a: number) => number>;
+type OscShapes = Array<(p: number) => number>;
+// position from 0.0 to 1.0, from start to end of shape.
+// returns value within the input range.
export const oscShapes: OscShapes = [
- (position: number, amount: number) => {
- return Math.sin(2 * Math.PI * position) * amount;
+ (p) => {
+ return Math.sin(2 * Math.PI * p);
},
- (p, a) => {
- return (p < 0.5 ? -1 : 1) * a;
+ (p) => {
+ return p < 0.5 ? -1 : 1;
},
- (p, a) => p * a
+ (p) => p
];
type MathOp = (a: number, b: number) => number;
@@ -157,8 +162,8 @@ export const unitRange: {
max: 10_000
},
amount: {
- min: -50,
- max: 50
+ min: -100_000,
+ max: 100_000
},
waveshape: {
min: 0,
@@ -195,6 +200,12 @@ export const unitRange: {
min: 0,
max: 10_000
}
+ },
+ img: {
+ speed: {
+ min: -10_000_000,
+ max: 10_000_000
+ }
}
} as const;
@@ -235,7 +246,8 @@ export const is = {
noise: isUnit('noise'),
smooth: isUnit('smooth'),
math: isUnit('math'),
- lag: isUnit('lag')
+ lag: isUnit('lag'),
+ img: isUnit('img')
}
};
@@ -255,7 +267,8 @@ export const ensure = {
noise: ensureUnit('noise'),
smooth: ensureUnit('smooth'),
math: ensureUnit('math'),
- lag: ensureUnit('lag')
+ lag: ensureUnit('lag'),
+ img: ensureUnit('img')
}
};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
@@ -21,6 +21,7 @@
import SmoothUnitComponent from '$lib/SmoothUnit.svelte';
import MathUnitComponent from '$lib/MathUnit.svelte';
import LagUnitComponent from '$lib/LagUnit.svelte';
+ import ImgUnitComponent from '$lib/ImgUnit.svelte';
import UnitDrag from '$lib/UnitDrag.svelte';
import Wires from '$lib/Wires.svelte';
@@ -229,6 +230,8 @@
<MathUnitComponent {id} />
{:else if unit.kind === 'lag'}
<LagUnitComponent {id} />
+ {:else if unit.kind === 'img'}
+ <ImgUnitComponent {id} />
{/if}
</div>
</div>
diff --git a/svelte.config.js b/svelte.config.js
@@ -1,20 +1,23 @@
-import adapter from "@sveltejs/adapter-static";
-import { vitePreprocess } from "@sveltejs/kit/vite";
+import adapter from '@sveltejs/adapter-static';
+import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
- // Consult https://kit.svelte.dev/docs/integrations#preprocessors
- // for more information about preprocessors
- preprocess: vitePreprocess(),
+ // Consult https://kit.svelte.dev/docs/integrations#preprocessors
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
- kit: {
- // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
- // If your environment is not supported or you settled on a specific environment, switch out the adapter.
- // See https://kit.svelte.dev/docs/adapters for more information about adapters.
- adapter: adapter({
- fallback: "index.html", // may differ from host to host
- }),
- },
+ kit: {
+ // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
+ // If your environment is not supported or you settled on a specific environment, switch out the adapter.
+ // See https://kit.svelte.dev/docs/adapters for more information about adapters.
+ adapter: adapter({
+ fallback: 'index.html' // may differ from host to host
+ }),
+ paths: {
+ base: '/color-synth'
+ }
+ }
};
export default config;