[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 / src / generators / vegetation / plants / flower.ts
5.1 kB 122 lines
1import { sphere } from '../../../primitives' 2import { merge } from '../../../ops' 3import { setup, trunk, blade } from '../../../build' 4import type { Mesh } from '../../../core/mesh' 5import type { Vec3 } from '../../../core/types' 6import type { OptionSchema, OptionInput } from '../../../core/schema' 7 8export const flowerSchema = { 9 seed: { type: 'integer', default: 1, min: 1, max: 100, label: 'Seed' }, 10 height: { type: 'range', default: 0.5, min: 0.15, max: 1.2, step: 0.05, label: 'Stem Height' }, 11 stemRadius: { type: 'range', default: 0.012,min: 0.004,max: 0.03, step: 0.002, label: 'Stem Radius' }, 12 lean: { type: 'range', default: 0.12, min: 0, max: 0.4, step: 0.02, label: 'Lean' }, 13 petals: { type: 'integer', default: 9, min: 3, max: 18, label: 'Petals' }, 14 petalLength: { type: 'range', default: 0.13, min: 0.05, max: 0.3, step: 0.01, label: 'Petal Length' }, 15 petalWidth: { type: 'range', default: 0.06, min: 0.02, max: 0.14, step: 0.005, label: 'Petal Width' }, 16 petalLift: { type: 'range', default: 0.45, min: 0, max: 1.2, step: 0.05, label: 'Petal Lift' }, 17 centerSize: { type: 'range', default: 0.045,min: 0.02, max: 0.1, step: 0.005, label: 'Center Size' }, 18 leaves: { type: 'integer', default: 2, min: 0, max: 4, label: 'Leaves' }, 19 leafLength: { type: 'range', default: 0.16, min: 0.05, max: 0.35, step: 0.01, label: 'Leaf Length' }, 20 petalColor: { type: 'color', default: '#e0556b', label: 'Petal Color' }, 21 centerColor: { type: 'color', default: '#f0c040', label: 'Center Color' }, 22 stemColors: { type: 'color-array', default: ['#2f6a1e', '#3f8526'], min: 1, max: 4, label: 'Stem Colors' }, 23} satisfies OptionSchema 24 25export type FlowerOptions = Partial<OptionInput<typeof flowerSchema>> & { preset?: string } 26 27export const flowerPresets: Record<string, Partial<FlowerOptions>> = { 28 default: {}, 29 daisy: { petals: 14, petalColor: '#f3f0ee', centerColor: '#f0b830', petalWidth: 0.04, petalLength: 0.16 }, 30 tulip: { petals: 6, petalLift: 1.0, petalColor: '#d23a52', petalWidth: 0.1, petalLength: 0.18, centerSize: 0.03 }, 31 poppy: { petals: 5, petalColor: '#d8392b', petalWidth: 0.12, petalLift: 0.6 }, 32 dandelion: { petals: 18, petalColor: '#f2cf33', petalWidth: 0.025, petalLength: 0.1, petalLift: 0.7 }, 33} 34 35function norm(x: number, y: number, z: number): Vec3 { 36 const l = Math.hypot(x, y, z) || 1 37 return [x / l, y / l, z / l] 38} 39 40export function flower(options: FlowerOptions = {}): Mesh { 41 const { o, rng } = setup(flowerSchema, options, flowerPresets) 42 const shapeRng = rng.stream('shape') 43 44 const leanAngle = shapeRng() * Math.PI * 2 45 const leanX = Math.cos(leanAngle) * o.lean 46 const leanZ = Math.sin(leanAngle) * o.lean 47 48 const parts: Mesh[] = [] 49 50 // Stem. 51 parts.push(trunk({ 52 height: o.height, 53 baseRadius: o.stemRadius, 54 topRadius: o.stemRadius * 0.7, 55 taper: 1, 56 lean: [leanX, leanZ], 57 noiseSeed: shapeRng.seed(), 58 noiseScale: 6, 59 noiseAmount: 0.06, 60 segments: 5, 61 heightSegments: 4, 62 colors: o.stemColors, 63 })) 64 65 const head: Vec3 = [leanX, o.height, leanZ] 66 67 // Flower center. 68 parts.push( 69 sphere({ radius: o.centerSize, widthSegments: 6, heightSegments: 4 }) 70 .scale(1, 0.6, 1) 71 .translate(head[0], head[1], head[2]) 72 .vertexColor(o.centerColor), 73 ) 74 75 // Petals arranged in a ring, lifting up into a cup. 76 for (let i = 0; i < o.petals; i++) { 77 const angle = (i / o.petals) * Math.PI * 2 + (shapeRng() - 0.5) * 0.15 78 const ca = Math.cos(angle), sa = Math.sin(angle) 79 const dir = norm(ca, o.petalLift * 1.4, sa) 80 const start: Vec3 = [head[0] + ca * o.centerSize, head[1], head[2] + sa * o.centerSize] 81 const segs = 3 82 const path: Vec3[] = [] 83 for (let j = 0; j <= segs; j++) { 84 const t = j / segs 85 path.push([ 86 start[0] + dir[0] * o.petalLength * t, 87 start[1] + dir[1] * o.petalLength * t, 88 start[2] + dir[2] * o.petalLength * t, 89 ]) 90 } 91 parts.push( 92 blade(path, { width: (t) => o.petalWidth * Math.sin(Math.min(1, t) * Math.PI) }) 93 .vertexColor(o.petalColor), 94 ) 95 } 96 97 // Leaves partway up the stem. 98 for (let i = 0; i < o.leaves; i++) { 99 const t0 = 0.3 + (i / Math.max(1, o.leaves)) * 0.4 100 const angle = shapeRng() * Math.PI * 2 101 const ca = Math.cos(angle), sa = Math.sin(angle) 102 const dir = norm(ca, 0.35, sa) 103 const base: Vec3 = [leanX * t0 * t0, o.height * t0, leanZ * t0 * t0] 104 const segs = 3 105 const path: Vec3[] = [] 106 for (let j = 0; j <= segs; j++) { 107 const t = j / segs 108 const droop = 0.25 * o.leafLength * t * t 109 path.push([ 110 base[0] + dir[0] * o.leafLength * t, 111 base[1] + dir[1] * o.leafLength * t - droop, 112 base[2] + dir[2] * o.leafLength * t, 113 ]) 114 } 115 parts.push( 116 blade(path, { width: (t) => o.leafLength * 0.3 * Math.sin(Math.min(1, t) * Math.PI) }) 117 .vertexColor(o.stemColors[o.stemColors.length - 1]), 118 ) 119 } 120 121 return merge(...parts) 122}