From 214da6319bbbe9f56d73d393693066071b392d25 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Wed, 13 May 2026 03:48:56 +0200 Subject: [PATCH] Add tests for spline repeater asset placement and terrain conforming --- tests/domain/spline-repeaters.test.ts | 123 ++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tests/domain/spline-repeaters.test.ts diff --git a/tests/domain/spline-repeaters.test.ts b/tests/domain/spline-repeaters.test.ts new file mode 100644 index 00000000..f9d03886 --- /dev/null +++ b/tests/domain/spline-repeaters.test.ts @@ -0,0 +1,123 @@ +import { describe, expect, it } from "vitest"; + +import { createScenePath } from "../../src/document/paths"; +import { createTerrain } from "../../src/document/terrains"; +import { deriveSplineRepeaterInstances } from "../../src/spline-corridor/spline-repeaters"; + +describe("spline repeaters", () => { + it("places bundled corridor assets along paths without requiring roads", () => { + const path = createScenePath({ + id: "path-repeater-no-road", + road: { + enabled: false + }, + repeaters: [ + { + id: "repeater-fence", + assetId: "fence_segment_wood_2m", + placement: "left", + offset: 1, + spacing: 2, + startInset: 0, + endInset: 0, + scale: 1, + randomScale: 0, + randomYawDegrees: 0, + yawOffsetDegrees: 0, + terrainConform: false, + heightOffset: 0, + alignToSpline: true, + seed: 7 + } + ], + points: [ + { + id: "point-a", + position: { x: 0, y: 0, z: 0 } + }, + { + id: "point-b", + position: { x: 4, y: 0, z: 0 } + } + ] + }); + + const instances = deriveSplineRepeaterInstances({ path }); + + expect(instances).toHaveLength(3); + expect(instances.map((instance) => instance.position)).toEqual([ + { x: 0, y: 0, z: 1 }, + { x: 2, y: 0, z: 1 }, + { x: 4, y: 0, z: 1 } + ]); + expect(instances.map((instance) => instance.yawDegrees)).toEqual([ + 90, + 90, + 90 + ]); + }); + + it("deterministically applies terrain conforming and random variation", () => { + const terrain = createTerrain({ + id: "terrain-repeater", + position: { x: 0, y: 1, z: 0 }, + sampleCountX: 3, + sampleCountZ: 3, + cellSize: 1, + heights: [ + 0, + 0, + 0, + 1, + 1, + 1, + 2, + 2, + 2 + ] + }); + const path = createScenePath({ + id: "path-repeater-terrain", + repeaters: [ + { + id: "repeater-rocks", + assetId: "edge_small_rocks_a_2m", + placement: "right", + offset: 1, + spacing: 2, + randomScale: 0.2, + randomYawDegrees: 10, + terrainConform: true, + heightOffset: 0.25, + seed: 3 + } + ], + points: [ + { + id: "point-a", + position: { x: 0, y: 10, z: 1 } + }, + { + id: "point-b", + position: { x: 2, y: 10, z: 1 } + } + ] + }); + + const first = deriveSplineRepeaterInstances({ + path, + terrains: [terrain] + }); + const second = deriveSplineRepeaterInstances({ + path, + terrains: [terrain] + }); + + expect(first).toEqual(second); + expect(first).toHaveLength(2); + expect(first[0]?.position.y).toBe(1.25); + expect(first[1]?.position.y).toBe(1.25); + expect(first[0]?.scale).not.toBe(1); + expect(first[0]?.yawDegrees).not.toBe(90); + }); +});