feat(mesh): add tile mesh generation package

Add @tts/mesh, a package for generating extruded tile meshes from 2D
shapes. It provides shape generators (rect, hex, circle, rounded rect,
frame), earcut-based tessellation of top/bottom faces with UVs, and
outward-normal wall generation, combined into raw BufferGeometry-ready
typed arrays.
This commit is contained in:
2026-08-08 13:42:32 +08:00
parent 3b86a641aa
commit 14f83f476c
13 changed files with 782 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest';
import { wallFaces } from './walls.js';
import { rectShape } from './shapes.js';
describe('wallFaces', () => {
it('builds a quad strip around a rectangle', () => {
const wall = wallFaces(rectShape(2, 2), 1);
// 4 outline points * 2 vertices each.
expect(wall.positions.length / 3).toBe(8);
expect(wall.indices.length).toBe(4 * 6); // 4 quads * 2 triangles
});
it('has outward-facing normals', () => {
const wall = wallFaces(rectShape(2, 2), 1);
// Bottom edge (from (-1,-1) to (1,-1)) normal should point -Y.
// First two vertices belong to the first outline point (-1,-1).
const n = wall.normals.slice(0, 3);
expect(n[0]).toBeCloseTo(0, 5);
expect(n[1]).toBeCloseTo(-1, 5);
expect(n[2]).toBeCloseTo(0, 5);
});
it('spans from z=0 to z=height', () => {
const wall = wallFaces(rectShape(2, 2), 3);
// First vertex is bottom (z=0), second is top (z=3).
expect(wall.positions[2]).toBe(0);
expect(wall.positions[5]).toBe(3);
});
it('maps u across the perimeter and v up the height', () => {
const wall = wallFaces(rectShape(2, 2), 2);
// u starts at 0 for the first outline point.
expect(wall.uvs[0]).toBeCloseTo(0, 5);
// v goes 0 -> 1 from bottom to top.
expect(wall.uvs[1]).toBeCloseTo(0, 5);
expect(wall.uvs[3]).toBeCloseTo(1, 5);
});
});