import { numericGridSize, seededRandom } from './caRuntime.js' import type { CaClass, JsonObject, SceneParams } from './types.js' export interface InitialConditionResult { type: 'cells' cells: number[][] generator: { kind: string } & JsonObject } export interface InitialConditionRuntimeInput { settings: SceneParams caClass: CaClass params: JsonObject } export interface InitialConditionRuntime { id: string label: string generate: (input: InitialConditionRuntimeInput) => InitialConditionResult } const initialConditionRuntimes = new Map() function clampDensity(value: unknown) { return Math.max(0, Math.min(1, typeof value === 'number' ? value : 0.28)) } function randomSoup({ settings, caClass, params }: InitialConditionRuntimeInput): InitialConditionResult { const density = clampDensity(params.density) const seed = typeof params.seed === 'string' ? params.seed : 'studio-seed' const random = seededRandom(seed) const [width, height, depth] = numericGridSize(settings.simulation?.grid, caClass.dimensions) const cells: number[][] = [] for (let z = 0; z < depth; z += 1) { for (let y = 0; y < height; y += 1) { for (let x = 0; x < width; x += 1) { if (random() < density) cells.push([x, y, z]) } } } return { type: 'cells', cells, generator: { kind: 'random-soup', density, seed } } } export function registerInitialConditionRuntime( runtime: InitialConditionRuntime, options: { replace?: boolean } = {} ) { if (!runtime.id.trim()) throw new Error('Initial condition runtime id is required') if (initialConditionRuntimes.has(runtime.id) && !options.replace) { throw new Error(`Initial condition runtime already registered: ${runtime.id}`) } initialConditionRuntimes.set(runtime.id, runtime) } export function listInitialConditionRuntimes() { return [...initialConditionRuntimes.values()] } export function getInitialConditionRuntime(runtimeId: string) { return initialConditionRuntimes.get(runtimeId) ?? null } export function generateInitialCondition(runtimeId: string, input: InitialConditionRuntimeInput) { const runtime = getInitialConditionRuntime(runtimeId) if (!runtime) throw new Error(`Initial condition runtime unavailable: ${runtimeId}`) const result = runtime.generate(input) if (result.type !== 'cells' || !Array.isArray(result.cells)) { throw new Error(`Initial condition runtime returned an invalid result: ${runtimeId}`) } return result } registerInitialConditionRuntime( { id: 'random-soup', label: 'Random soup', generate: randomSoup }, { replace: true } )