Add unit tests for spline corridor junction mesh generation
This commit is contained in:
189
tests/geometry/spline-corridor-junction-mesh.test.ts
Normal file
189
tests/geometry/spline-corridor-junction-mesh.test.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createScenePath } from "../../src/document/paths";
|
||||
import { createSplineCorridorJunction } from "../../src/document/spline-corridor-junctions";
|
||||
import {
|
||||
buildSplineCorridorJunctionFootprint,
|
||||
sampleSplineCorridorJunctionFootprintInfluence
|
||||
} from "../../src/geometry/spline-corridor-junction-footprint";
|
||||
import { buildSplineCorridorJunctionMeshGeometry } from "../../src/geometry/spline-corridor-junction-mesh";
|
||||
|
||||
function createRoadPath(
|
||||
id: string,
|
||||
points: Array<{ x: number; y?: number; z: number }>
|
||||
) {
|
||||
return createScenePath({
|
||||
id,
|
||||
road: {
|
||||
enabled: true,
|
||||
width: 2,
|
||||
shoulderWidth: 1,
|
||||
heightOffset: 0.1
|
||||
},
|
||||
points: points.map((point, index) => ({
|
||||
id: `${id}-point-${index}`,
|
||||
position: {
|
||||
x: point.x,
|
||||
y: point.y ?? 0,
|
||||
z: point.z
|
||||
}
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
describe("spline corridor junction mesh generation", () => {
|
||||
it("builds a connection-shaped footprint from clipped corridor boundaries", () => {
|
||||
const pathA = createRoadPath("path-junction-footprint-a", [
|
||||
{ x: 0, z: 0 },
|
||||
{ x: 10, z: 0 }
|
||||
]);
|
||||
const pathB = createRoadPath("path-junction-footprint-b", [
|
||||
{ x: 5, z: -5 },
|
||||
{ x: 5, z: 5 }
|
||||
]);
|
||||
const junction = createSplineCorridorJunction({
|
||||
id: "junction-footprint",
|
||||
center: { x: 5, y: 0, z: 0 },
|
||||
radius: 3,
|
||||
connections: [
|
||||
{
|
||||
pathId: pathA.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
},
|
||||
{
|
||||
pathId: pathB.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const footprint = buildSplineCorridorJunctionFootprint({
|
||||
junction,
|
||||
paths: [pathA, pathB]
|
||||
});
|
||||
|
||||
expect(footprint).not.toBeNull();
|
||||
expect(footprint?.points).toHaveLength(8);
|
||||
expect(footprint?.bounds).toEqual({
|
||||
minX: 3,
|
||||
maxX: 7,
|
||||
minZ: -2,
|
||||
maxZ: 2
|
||||
});
|
||||
});
|
||||
|
||||
it("builds junction fill geometry from the connection footprint instead of a circle", () => {
|
||||
const pathA = createRoadPath("path-junction-mesh-a", [
|
||||
{ x: 0, z: 0 },
|
||||
{ x: 10, z: 0 }
|
||||
]);
|
||||
const pathB = createRoadPath("path-junction-mesh-b", [
|
||||
{ x: 5, z: -5 },
|
||||
{ x: 5, z: 5 }
|
||||
]);
|
||||
const junction = createSplineCorridorJunction({
|
||||
id: "junction-mesh",
|
||||
center: { x: 5, y: 0, z: 0 },
|
||||
radius: 3,
|
||||
connections: [
|
||||
{
|
||||
pathId: pathA.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
},
|
||||
{
|
||||
pathId: pathB.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const geometry = buildSplineCorridorJunctionMeshGeometry({
|
||||
junction,
|
||||
paths: [pathA, pathB]
|
||||
});
|
||||
const positionAttribute = geometry?.getAttribute("position");
|
||||
|
||||
expect(positionAttribute?.count).toBe(9);
|
||||
expect(Array.from(positionAttribute?.array ?? [])).toEqual(
|
||||
expect.arrayContaining([
|
||||
3,
|
||||
expect.closeTo(0.1, 5),
|
||||
-1,
|
||||
7,
|
||||
expect.closeTo(0.1, 5),
|
||||
1,
|
||||
4,
|
||||
expect.closeTo(0.1, 5),
|
||||
-2,
|
||||
6,
|
||||
expect.closeTo(0.1, 5),
|
||||
2
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("samples full terrain influence inside the connection footprint and falloff outside it", () => {
|
||||
const pathA = createRoadPath("path-junction-influence-a", [
|
||||
{ x: 0, z: 0 },
|
||||
{ x: 10, z: 0 }
|
||||
]);
|
||||
const pathB = createRoadPath("path-junction-influence-b", [
|
||||
{ x: 5, z: -5 },
|
||||
{ x: 5, z: 5 }
|
||||
]);
|
||||
const junction = createSplineCorridorJunction({
|
||||
id: "junction-influence",
|
||||
center: { x: 5, y: 0, z: 0 },
|
||||
radius: 3,
|
||||
connections: [
|
||||
{
|
||||
pathId: pathA.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
},
|
||||
{
|
||||
pathId: pathB.id,
|
||||
progress: 0.5,
|
||||
clipDistance: 2
|
||||
}
|
||||
]
|
||||
});
|
||||
const footprint = buildSplineCorridorJunctionFootprint({
|
||||
junction,
|
||||
paths: [pathA, pathB]
|
||||
});
|
||||
|
||||
if (footprint === null) {
|
||||
throw new Error("Expected a junction footprint.");
|
||||
}
|
||||
|
||||
expect(
|
||||
sampleSplineCorridorJunctionFootprintInfluence({
|
||||
footprint,
|
||||
x: 5,
|
||||
z: 0,
|
||||
falloffDistance: 1
|
||||
})
|
||||
).toBe(1);
|
||||
expect(
|
||||
sampleSplineCorridorJunctionFootprintInfluence({
|
||||
footprint,
|
||||
x: 2.5,
|
||||
z: 0,
|
||||
falloffDistance: 1
|
||||
})
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
sampleSplineCorridorJunctionFootprintInfluence({
|
||||
footprint,
|
||||
x: 1,
|
||||
z: 0,
|
||||
falloffDistance: 1
|
||||
})
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user