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;