[READ-ONLY] Mirror of https://github.com/flo-bit/shapecraft. flo-bit.dev/shapecraft/
0

Configure Feed

Select the types of activity you want to include in your feed.

shapecraft / tests / generators.test.ts
4.0 kB 103 lines
1import { describe, it, expect } from 'vitest' 2import { tree, pine, palm, bush, grass, fern, flower, deadTree, leafyTree, rock, sharpRock, blockRock, mushroom } from '../src/generators' 3import type { Mesh } from '../src/core/mesh' 4import type { Asset } from '../src/core/asset' 5 6const generators = [ 7 { name: 'tree', gen: tree }, 8 { name: 'pine', gen: pine }, 9 { name: 'palm', gen: palm }, 10 { name: 'bush', gen: bush }, 11 { name: 'grass', gen: grass }, 12 { name: 'fern', gen: fern }, 13 { name: 'flower', gen: flower }, 14 { name: 'dead', gen: deadTree }, 15 { name: 'leafy', gen: leafyTree }, 16 { name: 'rock', gen: rock }, 17 { name: 'sharp', gen: sharpRock }, 18 { name: 'block', gen: blockRock }, 19 { name: 'mushroom', gen: mushroom }, 20] as const 21 22// Generators that support snow (trees, shrubs, rocks). Grass/ferns don't take snow options. 23const snowGenerators = generators.filter((g) => ['tree', 'pine', 'palm', 'bush', 'leafy', 'rock', 'sharp', 'block'].includes(g.name)) 24 25// Golden snapshots captured after the named-stream RNG migration. A change here means 26// generator output shifted — intentional changes should update these numbers deliberately. 27const golden: Record<string, { verts: number }> = { 28 tree: { verts: 1830 }, 29 pine: { verts: 840 }, 30 palm: { verts: 3630 }, 31 bush: { verts: 3600 }, 32 grass: { verts: 1632 }, 33 fern: { verts: 13932 }, 34 flower: { verts: 1050 }, 35 dead: { verts: 1400 }, 36 leafy: { verts: 7020 }, 37 rock: { verts: 1440 }, 38 sharp: { verts: 696 }, 39 block: { verts: 432 }, 40 mushroom: { verts: 1584 }, 41} 42 43function allFinite(m: Mesh | Asset): boolean { 44 const p = m.positions 45 for (let i = 0; i < p.length; i++) if (!Number.isFinite(p[i])) return false 46 return true 47} 48 49describe.each(generators)('$name generator', ({ name, gen }) => { 50 it('produces a valid, colored, finite mesh', () => { 51 const m = gen({ seed: 1 }) 52 expect(m.vertexCount).toBeGreaterThan(0) 53 expect(m.colors).not.toBeNull() 54 expect(allFinite(m)).toBe(true) 55 }) 56 57 it('matches the golden vertex count for seed 1', () => { 58 expect(gen({ seed: 1 }).vertexCount).toBe(golden[name].verts) 59 }) 60 61 it('is deterministic: same seed → identical positions', () => { 62 const a = gen({ seed: 7 }).positions 63 const b = gen({ seed: 7 }).positions 64 expect(Array.from(a)).toEqual(Array.from(b)) 65 }) 66 67 it('different seeds produce different geometry', () => { 68 const a = gen({ seed: 1 }).positions 69 const b = gen({ seed: 2 }).positions 70 expect(Array.from(a)).not.toEqual(Array.from(b)) 71 }) 72}) 73 74describe('stream independence at the model level', () => { 75 // The headline benefit of named streams: a feature that only affects color (snow) 76 // must not perturb geometry, because positions come from independent streams. 77 it.each(snowGenerators)('$name: painted snow (depth 0) leaves geometry byte-identical', ({ gen }) => { 78 const bare = gen({ seed: 3, snowColors: [] }) 79 const snowy = gen({ seed: 3, snowColors: ['#ffffff', '#eeeeee'] }) 80 81 // Same geometry... 82 expect(Array.from(snowy.positions)).toEqual(Array.from(bare.positions)) 83 // ...but different coloring. 84 expect(Array.from(snowy.colors!)).not.toEqual(Array.from(bare.colors!)) 85 }) 86}) 87 88describe('geometric snow (snowDepth > 0)', () => { 89 it.each(snowGenerators)('$name: adds a snow layer of real geometry', ({ gen }) => { 90 const bare = gen({ seed: 4, snowColors: [] }) 91 const snowy = gen({ seed: 4, snowColors: ['#eef0f5'], snowDepth: 0.08 }) 92 // The snow shell adds vertices on top of the model. 93 expect(snowy.vertexCount).toBeGreaterThan(bare.vertexCount) 94 // And reaches higher than the bare model (snow sits on top). 95 expect(snowy.boundingBox.max.y).toBeGreaterThan(bare.boundingBox.max.y) 96 }) 97 98 it.each(snowGenerators)('$name: geometric snow is deterministic', ({ gen }) => { 99 const a = gen({ seed: 4, snowColors: ['#eef0f5'], snowDepth: 0.08 }).positions 100 const b = gen({ seed: 4, snowColors: ['#eef0f5'], snowDepth: 0.08 }).positions 101 expect(Array.from(a)).toEqual(Array.from(b)) 102 }) 103})