[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 / docs / 10-patterns.mdx
3.7 kB 137 lines
1import PatternsDemo from './components/PatternsDemo.astro' 2 3# Patterns 4 5<PatternsDemo /> 6 7## Generator functions 8 9A generator is just a function that returns a Mesh: 10 11```ts 12function tree(options?: { height?: number; seed?: number }): Mesh { 13 const rand = createRng(options?.seed ?? 1) 14 const trunk = cylinder({ radius: 0.05, height: height * 0.4 }) 15 .translate(0, height * 0.2, 0) 16 .vertexColor([0.35, 0.22, 0.1]) 17 const canopy = icosphere({ radius: 0.5 }) 18 .translate(0, height * 0.6, 0) 19 .faceColor(...) 20 return merge(trunk, canopy) 21} 22``` 23 24## Seeded randomness 25 26Use `createRng` (wraps alea PRNG) for deterministic generation: 27 28```ts 29import { createRng } from 'shapecraft' 30 31const rand = createRng(42) // seed deterministic sequence 32rand() // 0-1 33rand() // next value 34 35// Derive sub-seeds for noise, jitter, etc. 36function subSeed() { return Math.floor(rand() * 2147483647) } 37const noise = new UberNoise({ seed: subSeed() }) 38mesh.jitter(0.05, { seed: subSeed() }) 39``` 40 41**Important:** always consume `rand()` / `subSeed()` calls in the same order regardless of options, so changing one parameter doesn't shift the entire random sequence. 42 43## Option schemas & presets 44 45Define configurable generators with typed schemas: 46 47```ts 48import { resolveOptions, type OptionSchema } from 'shapecraft' 49 50const treeSchema = { 51 height: { type: 'range', default: 2.5, min: 0.5, max: 6, step: 0.1 }, 52 seed: { type: 'integer', default: 1, min: 1, max: 100 }, 53 lean: { type: 'range', default: [0.1, 0.4], min: 0, max: 1 }, // randomized range 54 showCanopy: { type: 'boolean', default: true }, 55 colors: { type: 'color-array', default: ['#1a5a10', '#2a7518'] }, 56} satisfies OptionSchema 57 58const presets = { 59 default: {}, 60 autumn: { colors: ['#c44422', '#d48825'] }, 61 winter: { colors: ['#4a4a4a'], snowColors: ['#e8e8f0'] }, 62} 63 64function tree(options = {}) { 65 const rand = createRng(options.seed ?? 1) 66 const o = resolveOptions(treeSchema, options, presets, rand) 67 // o.height is a number, o.lean is resolved from [min,max] via rand 68} 69``` 70 71Numeric defaults can be `[min, max]` resolved deterministically from the seed. The editor shows the midpoint. 72 73## Scatter points 74 75```ts 76import { scatterOnSphere } from 'shapecraft' 77 78// N random points on a sphere surface 79const points = scatterOnSphere(5, seed, { 80 radius: 1, 81 polarMin: Math.PI * 0.3, // avoid top 82 polarMax: Math.PI * 0.7, // avoid bottom 83}) 84// [[x,y,z], [x,y,z], ...] 85``` 86 87## Terrain workflow 88 89```ts 90const terrain = plane({ size: 20, segments: 100 }) 91 .displaceNoise(fbm({ seed: 42, scale: 0.2, octaves: 5 }), 3) 92 .vertexColor(heightGradient([ 93 [0, [0.2, 0.5, 0.1]], 94 [1.5, [0.5, 0.35, 0.2]], 95 [3, [1, 1, 1]], 96 ])) 97``` 98 99## Organic shapes with icosphere 100 101```ts 102// Subdivide spherize displace radially (no gaps) 103icosphere({ radius: r, subdivisions: 0 }) 104 .subdivideAdaptive(edgeLen) 105 .spherize(r) 106 .displaceNoise(noise, r * 0.5, { direction: 'radial' }) 107 .jitter(r * 0.04, { seed }) 108``` 109 110Use `direction: 'radial'` instead of `'normal'` on non-indexed geometry to avoid gaps between faces. 111 112## Branches & tubes 113 114```ts 115import { tube } from 'shapecraft' 116 117// Tapered branch along a curved path 118const path = [[0,0,0], [0.1,0.5,0], [0.3,1,0.1], [0.2,1.5,0]] 119const branch = tube(path, (t) => 0.05 * (1 - t * 0.8), 5) 120``` 121 122## Leaves with loft + thicken 123 124```ts 125import { loft, thicken } from 'shapecraft' 126 127const leaf = loft({ 128 path: leafPath, 129 shape: (t) => { 130 const w = 0.2 * Math.sin(t * Math.PI) // wide in middle 131 return [[-w, 0], [0, w*0.2], [w, 0]] 132 }, 133 closedShape: false, 134 up: [0, 1, 0], 135}) 136const solidLeaf = thicken(leaf, 0.01) 137```