From f93bcfb8d4458aab55b9ca28468696e85e622c83 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Mon, 6 Jul 2026 23:11:42 +0200 Subject: [PATCH] Working but slow --- backend/admin/src/caEngineSteps/naga.ts | 4 +- backend/admin/src/caEngineSteps/wildfire2d.ts | 4 +- backend/admin/src/caEngines.ts | 2 +- .../admin/src/caInitialConditionGenerators.ts | 37 +- backend/admin/src/caRuntime.ts | 6 + backend/admin/src/main.tsx | 61 +- .../admin/src/voxel3d/voxelRendererCore.ts | 96 +- backend/public/admin/assets/index-COp4VUl7.js | 4116 ---------------- backend/public/admin/assets/index-DowkWVYj.js | 4155 +++++++++++++++++ backend/public/admin/index.html | 2 +- .../013_add_engine_rng_seed_setting.sql | 17 + ..._icg_seed_and_naga_length_distribution.sql | 55 + backend/test/caEngines.test.ts | 29 + .../test/caInitialConditionGenerators.test.ts | 38 +- backend/test/caStudioApi.test.ts | 26 +- 15 files changed, 4443 insertions(+), 4205 deletions(-) delete mode 100644 backend/public/admin/assets/index-COp4VUl7.js create mode 100644 backend/public/admin/assets/index-DowkWVYj.js create mode 100644 backend/src/migrations/013_add_engine_rng_seed_setting.sql create mode 100644 backend/src/migrations/014_rename_icg_seed_and_naga_length_distribution.sql diff --git a/backend/admin/src/caEngineSteps/naga.ts b/backend/admin/src/caEngineSteps/naga.ts index b7f4b79..f94ee5e 100644 --- a/backend/admin/src/caEngineSteps/naga.ts +++ b/backend/admin/src/caEngineSteps/naga.ts @@ -1,4 +1,4 @@ -import { resolveBoundaryCondition, resolveBoundaryIndex, seededRandom } from '../caRuntime.js' +import { resolveBoundaryCondition, resolveBoundaryIndex, resolveRngSeed, seededRandom } from '../caRuntime.js' import type { BoundaryCondition, Cells, JsonObject, SceneParams } from '../types.js' export type NagaArrow = 1 | 2 | 3 | 4 | 5 | 6 @@ -389,7 +389,7 @@ export function stepNaga3d(cells: Cells, settings: SceneParams) { ? universeFromCoordinates(coordinates) : seedUniverseFromCells(cells) const tick = typeof simulation.nagaTick === 'number' ? simulation.nagaTick : 0 - const seed = typeof simulation.seed === 'string' ? simulation.seed : 'naga' + const seed = resolveRngSeed(simulation, 'naga') const topology: NagaTopology = { boundary: resolveBoundaryCondition(simulation.grid), size diff --git a/backend/admin/src/caEngineSteps/wildfire2d.ts b/backend/admin/src/caEngineSteps/wildfire2d.ts index 46feb01..ac0c13d 100644 --- a/backend/admin/src/caEngineSteps/wildfire2d.ts +++ b/backend/admin/src/caEngineSteps/wildfire2d.ts @@ -1,4 +1,4 @@ -import { resolveBoundaryCondition, resolveBoundaryIndex, seededRandom } from '../caRuntime.js' +import { resolveBoundaryCondition, resolveBoundaryIndex, resolveRngSeed, seededRandom } from '../caRuntime.js' import type { Cells, SceneParams } from '../types.js' import { emptyCellsLike } from './cellGrid.js' @@ -17,7 +17,7 @@ export function stepWildfire2d(cells: Cells, settings: SceneParams) { const width = cells[0]?.length ?? 0 const boundary = resolveBoundaryCondition(settings.simulation?.grid) const spreadProbability = parseProbability(settings.simulation?.spreadProbability, 1) - const seed = typeof settings.simulation?.seed === 'string' ? settings.simulation.seed : 'wildfire' + const seed = resolveRngSeed(settings.simulation, 'wildfire') for (let y = 0; y < height; y += 1) { for (let x = 0; x < width; x += 1) { diff --git a/backend/admin/src/caEngines.ts b/backend/admin/src/caEngines.ts index 34633bc..d308870 100644 --- a/backend/admin/src/caEngines.ts +++ b/backend/admin/src/caEngines.ts @@ -95,7 +95,7 @@ registerCaEngineRuntime( id: 'naga-3d', label: 'Naga 3D', supportedClasses: [{ dimensions: 3, states: 7 }], - defaultParams: { ruleId: 'naga-3d', rendererId: 'voxel-3d', seed: 'naga', edgeWrap: true }, + defaultParams: { ruleId: 'naga-3d', rendererId: 'voxel-3d', rng_seed: 'naga', edgeWrap: true }, step: stepNaga3d }, { replace: true } diff --git a/backend/admin/src/caInitialConditionGenerators.ts b/backend/admin/src/caInitialConditionGenerators.ts index 3d79651..b56da67 100644 --- a/backend/admin/src/caInitialConditionGenerators.ts +++ b/backend/admin/src/caInitialConditionGenerators.ts @@ -36,10 +36,27 @@ function clampProbability(value: unknown, fallback: number) { return Math.max(0, Math.min(1, typeof value === 'number' && Number.isFinite(value) ? value : fallback)) } +function resolveIcSeed(params: JsonObject, fallback: string) { + if (typeof params.ic_seed === 'string' && params.ic_seed.trim()) return params.ic_seed + if (typeof params.seed === 'string' && params.seed.trim()) return params.seed + return fallback +} + +function randomNormal(random: () => number) { + const left = Math.max(Number.EPSILON, random()) + const right = Math.max(Number.EPSILON, random()) + return Math.sqrt(-2 * Math.log(left)) * Math.cos(2 * Math.PI * right) +} + +function sampleNagaLength(meanLength: number, lengthStdDev: number, maxLength: number, random: () => number) { + if (lengthStdDev <= 0) return clampInteger(meanLength, 5, 1, maxLength) + return clampInteger(meanLength + randomNormal(random) * lengthStdDev, meanLength, 1, maxLength) +} + 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 icSeed = resolveIcSeed(params, 'studio-seed') + const random = seededRandom(icSeed) const [width, height, depth] = numericGridSize(settings.simulation?.grid, caClass.dimensions) const cells: number[][] = [] @@ -57,7 +74,7 @@ function randomSoup({ settings, caClass, params }: InitialConditionRuntimeInput) generator: { kind: 'random-soup', density, - seed + ic_seed: icSeed } } } @@ -83,11 +100,13 @@ function inBounds(x: number, y: number, z: number, width: number, height: number function nagaMarkov({ settings, caClass, params }: InitialConditionRuntimeInput): InitialConditionResult { const [width, height, depth] = numericGridSize(settings.simulation?.grid, caClass.dimensions) + const maxLength = Math.max(1, width * height * depth) const count = clampInteger(params.count, 3, 1, Math.max(1, width * height * depth)) - const length = clampInteger(params.length, 5, 1, Math.max(1, width * height * depth)) + const meanLength = clampInteger(params.meanLength ?? params.length, 5, 1, maxLength) + const lengthStdDev = Math.max(0, Math.min(maxLength, typeof params.lengthStdDev === 'number' && Number.isFinite(params.lengthStdDev) ? params.lengthStdDev : 0)) const sameTypeProbability = clampProbability(params.sameTypeProbability, 0.8) - const seed = typeof params.seed === 'string' ? params.seed : 'naga-markov' - const random = seededRandom(seed) + const icSeed = resolveIcSeed(params, 'naga-markov') + const random = seededRandom(icSeed) const occupied = new Set() const cells: number[][] = [] const maxAttempts = count * 300 @@ -100,6 +119,7 @@ function nagaMarkov({ settings, caClass, params }: InitialConditionRuntimeInput) let y = Math.floor(random() * height) let z = Math.floor(random() * depth) let arrow = randomNagaArrow(random) + const length = sampleNagaLength(meanLength, lengthStdDev, maxLength, random) const proposed: number[][] = [] const proposedKeys = new Set() let valid = true @@ -164,9 +184,10 @@ function nagaMarkov({ settings, caClass, params }: InitialConditionRuntimeInput) generator: { kind: 'naga-markov', count, - length, + meanLength, + lengthStdDev, sameTypeProbability, - seed, + ic_seed: icSeed, created } } diff --git a/backend/admin/src/caRuntime.ts b/backend/admin/src/caRuntime.ts index d516c67..7310027 100644 --- a/backend/admin/src/caRuntime.ts +++ b/backend/admin/src/caRuntime.ts @@ -72,6 +72,12 @@ export function seededRandom(seed: string) { } } +export function resolveRngSeed(simulation: JsonObject | undefined, fallback: string) { + if (typeof simulation?.rng_seed === 'string' && simulation.rng_seed.trim()) return simulation.rng_seed + if (typeof simulation?.seed === 'string' && simulation.seed.trim()) return simulation.seed + return fallback +} + export function isBoundaryCondition(value: unknown): value is BoundaryCondition { return value === 'wrap' || value === 'mirror' || value === 'fixed' } diff --git a/backend/admin/src/main.tsx b/backend/admin/src/main.tsx index 7f95eaa..4b3fe2e 100644 --- a/backend/admin/src/main.tsx +++ b/backend/admin/src/main.tsx @@ -72,7 +72,7 @@ interface IcgConfig { dimensions: number states: number density: number - seed: string + icSeed: string } interface EngineConfig { @@ -324,7 +324,7 @@ function defaultIcgConfig(): IcgConfig { dimensions: 2, states: 2, density: 0.28, - seed: 'studio-seed' + icSeed: 'studio-seed' } } @@ -353,16 +353,16 @@ function icgParamsSchema(config: IcgConfig): JsonObject { max: 1, step: 0.01 }, - seed: { + ic_seed: { type: 'string', - label: 'Seed', - default: config.seed + label: 'IC seed', + default: config.icSeed } } } function icgDefaultParams(config: IcgConfig): JsonObject { - return { density: config.density, seed: config.seed } + return { density: config.density, ic_seed: config.icSeed } } function ruleLabel(ruleId: string) { @@ -1644,8 +1644,8 @@ function IcgConfigFields({ ) @@ -1817,7 +1817,11 @@ function configFromIcg(generator: InitialConditionGenerator): IcgConfig { dimensions: supportedClass?.dimensions ?? 2, states: typeof supportedClass?.states === 'number' ? supportedClass.states : 2, density: typeof generator.default_params.density === 'number' ? generator.default_params.density : 0.28, - seed: typeof generator.default_params.seed === 'string' ? generator.default_params.seed : 'studio-seed' + icSeed: typeof generator.default_params.ic_seed === 'string' + ? generator.default_params.ic_seed + : typeof generator.default_params.seed === 'string' + ? generator.default_params.seed + : 'studio-seed' } } @@ -2168,7 +2172,8 @@ function LibraryEditor({ const [selectedEngineId, setSelectedEngineId] = React.useState('') const [icgDensity, setIcgDensity] = React.useState(0.28) const [icgNagaCount, setIcgNagaCount] = React.useState(3) - const [icgNagaLength, setIcgNagaLength] = React.useState(5) + const [icgNagaMeanLength, setIcgNagaMeanLength] = React.useState(5) + const [icgNagaLengthStdDev, setIcgNagaLengthStdDev] = React.useState(0) const [icgNagaSameTypeProbability, setIcgNagaSameTypeProbability] = React.useState(0.8) const [icgSeed, setIcgSeed] = React.useState('studio-seed') const [nodeName, setNodeName] = React.useState('Untitled preset') @@ -2664,24 +2669,28 @@ function LibraryEditor({ function applyIcgDefaults(generator: InitialConditionGenerator | undefined) { if (typeof generator?.default_params.density === 'number') setIcgDensity(generator.default_params.density) if (typeof generator?.default_params.count === 'number') setIcgNagaCount(generator.default_params.count) - if (typeof generator?.default_params.length === 'number') setIcgNagaLength(generator.default_params.length) + if (typeof generator?.default_params.meanLength === 'number') setIcgNagaMeanLength(generator.default_params.meanLength) + else if (typeof generator?.default_params.length === 'number') setIcgNagaMeanLength(generator.default_params.length) + if (typeof generator?.default_params.lengthStdDev === 'number') setIcgNagaLengthStdDev(generator.default_params.lengthStdDev) if (typeof generator?.default_params.sameTypeProbability === 'number') { setIcgNagaSameTypeProbability(generator.default_params.sameTypeProbability) } - if (typeof generator?.default_params.seed === 'string') setIcgSeed(generator.default_params.seed) + if (typeof generator?.default_params.ic_seed === 'string') setIcgSeed(generator.default_params.ic_seed) + else if (typeof generator?.default_params.seed === 'string') setIcgSeed(generator.default_params.seed) } function selectedIcgParams(generator: InitialConditionGenerator) { if (generator.generator_kind === 'naga-markov') { return { count: icgNagaCount, - length: icgNagaLength, + meanLength: icgNagaMeanLength, + lengthStdDev: icgNagaLengthStdDev, sameTypeProbability: icgNagaSameTypeProbability, - seed: icgSeed + ic_seed: icgSeed } } - return { density: Math.max(0, Math.min(1, icgDensity)), seed: icgSeed } + return { density: Math.max(0, Math.min(1, icgDensity)), ic_seed: icgSeed } } function applySelectedIcg() { @@ -3204,12 +3213,6 @@ function LibraryEditor({ {selectedIcg?.generator_kind === 'naga-markov' ? ( <> -
- -
{icgNagaCount}
+
+ + {icgNagaMeanLength} +
+
+ + {icgNagaLengthStdDev} +
)}