SOURCE / PINNED RELEASE
Made of little things.
Safe Sand · Material studies
- Release
- 9101a9cd9fb4…
- Author-recorded commit
- 75d98e1b8edb…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-7682e55a791
Archive hash verified: 6c8ddabeb31100e1…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
export interface Setup {
version: 1;
material: string;
surface: 'light' | 'slate' | 'dark';
radius: number;
amount: number;
tool?: string;
speed?: number;
pitch?: number;
yaw?: number;
flowRate?: number;
pourHeight?: number;
}
export function parseSetup(raw: string, materialKeys: readonly string[]): Setup {
const value: unknown = JSON.parse(raw);
if (!value || typeof value !== 'object') throw new Error('Invalid saved setup.');
const s = value as Record<string, unknown>;
if (s.version !== 1 || typeof s.material !== 'string' || !materialKeys.includes(s.material)
|| !['light', 'slate', 'dark'].includes(String(s.surface))
|| typeof s.radius !== 'number' || !Number.isFinite(s.radius) || s.radius < 2 || s.radius > 10
|| ![0.35, 0.8, 1].includes(Number(s.amount)) || typeof s.amount !== 'number') {
throw new Error('This saved setup is not compatible.');
}
const optional: Partial<Setup> = {};
const ranges = { pitch: [-35, 35], yaw: [-35, 35], flowRate: [2000, 24000], pourHeight: [2, 30] };
for (const [key, [min, max]] of Object.entries(ranges)) {
if (s[key] === undefined) continue;
if (typeof s[key] !== 'number' || !Number.isFinite(s[key]) || s[key] < min || s[key] > max) throw new Error('Invalid saved control value.');
Object.assign(optional, { [key]: s[key] });
}
if (s.speed !== undefined) {
if (typeof s.speed !== 'number' || ![0.25, 0.5, 1].includes(s.speed)) throw new Error('Invalid saved speed.');
optional.speed = s.speed;
}
if (s.tool !== undefined) {
if (typeof s.tool !== 'string' || !['push', 'pour', 'rake', 'erase', 'magnet', 'repel', 'camera'].includes(s.tool)) throw new Error('Invalid saved tool.');
optional.tool = s.tool;
}
return { version: 1, material: s.material, surface: s.surface as Setup['surface'], radius: s.radius, amount: s.amount, ...optional };
}
