[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.

fix deploy?

+204 -127
+11 -7
docs-builder/astro.config.ts
··· 32 32 export default defineConfig({ 33 33 vite: { 34 34 resolve: { 35 - alias: { 36 - $components: resolve("./src/components"), 37 - $layouts: resolve("./src/layouts"), 38 - $pages: resolve("./src/pages"), 39 - $assets: resolve("./src/assets"), 40 - $content: resolve("./src/content"), 41 - }, 35 + alias: [ 36 + { find: "$components", replacement: resolve("./src/components") }, 37 + { find: "$layouts", replacement: resolve("./src/layouts") }, 38 + { find: "$pages", replacement: resolve("./src/pages") }, 39 + { find: "$assets", replacement: resolve("./src/assets") }, 40 + { find: "$content", replacement: resolve("./src/content") }, 41 + { find: "shapecraft/three", replacement: resolve("../src/three/index.ts") }, 42 + { find: "shapecraft/noise", replacement: resolve("../src/noise/index.ts") }, 43 + { find: "shapecraft/color", replacement: resolve("../src/color/index.ts") }, 44 + { find: "shapecraft", replacement: resolve("../src/index.ts") }, 45 + ], 42 46 }, 43 47 define: { 44 48 __PUBLIC_CONFIG__: JSON.stringify(config),
+34 -13
docs/components/DisplaceDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { icosphere } = await import('../../src') 9 - const { fbm } = await import('../../src/noise') 10 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { icosphere } = await import('shapecraft') 10 + const { fbm } = await import('shapecraft/noise') 11 + const { toThreeMesh } = await import('shapecraft/three') 12 + 13 + const canvas = document.getElementById('displace-demo') as HTMLCanvasElement 14 + if (!canvas) return 11 15 12 - const ctx = await setupScene('displace-demo', { cameraPos: [3, 2, 3], target: [0, 0.5, 0] }) 13 - if (!ctx) return 16 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 17 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 18 + const scene = new THREE.Scene() 19 + scene.background = new THREE.Color(0x2a2a3a) 20 + const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 21 + camera.position.set(3, 2, 3) 22 + const controls = new OrbitControls(camera, canvas) 23 + controls.target.set(0, 0.5, 0) 24 + controls.enableDamping = true 25 + controls.update() 26 + 27 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 28 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 29 + sun.position.set(5, 8, 5) 30 + scene.add(sun) 14 31 15 32 const noise = fbm({ seed: 42, scale: 2, octaves: 3 }) 16 33 17 - ctx.scene.add(toThreeMesh( 18 - icosphere({ radius: 0.5, subdivisions: 2 }) 19 - .translate(-1.5, 0.5, 0).vertexColor('#4488cc') 34 + scene.add(toThreeMesh( 35 + icosphere({ radius: 0.5, subdivisions: 2 }).translate(-1.5, 0.5, 0).vertexColor('#4488cc') 20 36 )) 21 - ctx.scene.add(toThreeMesh( 37 + scene.add(toThreeMesh( 22 38 icosphere({ radius: 0.5, subdivisions: 2 }) 23 39 .displaceNoise(noise, 0.15, { direction: 'radial' }) 24 40 .translate(0, 0.5, 0).vertexColor('#cc8844') 25 41 )) 26 - ctx.scene.add(toThreeMesh( 42 + scene.add(toThreeMesh( 27 43 icosphere({ radius: 0.5, subdivisions: 2 }) 28 - .jitter(0.06, { seed: 7 }) 29 - .translate(1.5, 0.5, 0).vertexColor('#88cc44') 44 + .jitter(0.06, { seed: 7 }).translate(1.5, 0.5, 0).vertexColor('#88cc44') 30 45 )) 46 + 47 + ;(function animate() { 48 + requestAnimationFrame(animate) 49 + controls.update() 50 + renderer.render(scene, camera) 51 + })() 31 52 } 32 53 init() 33 54 </script>
+36 -18
docs/components/LoftDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { tube, loft, thicken } = await import('../../src') 9 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { tube, loft, thicken } = await import('shapecraft') 10 + const { toThreeMesh } = await import('shapecraft/three') 10 11 11 - const ctx = await setupScene('loft-demo', { cameraPos: [3, 3, 5], target: [0, 1, 0] }) 12 - if (!ctx) return 12 + const canvas = document.getElementById('loft-demo') as HTMLCanvasElement 13 + if (!canvas) return 14 + 15 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 16 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 17 + const scene = new THREE.Scene() 18 + scene.background = new THREE.Color(0x2a2a3a) 19 + const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 20 + camera.position.set(3, 3, 5) 21 + const controls = new OrbitControls(camera, canvas) 22 + controls.target.set(0, 1, 0) 23 + controls.enableDamping = true 24 + controls.update() 13 25 14 - // Tapered branch 26 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 27 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 28 + sun.position.set(5, 8, 5) 29 + scene.add(sun) 30 + 15 31 const branchPath = Array.from({ length: 10 }, (_, i) => { 16 32 const t = i / 9 17 33 return [Math.sin(t * 2) * 0.5, t * 2, Math.cos(t * 3) * 0.3] as [number, number, number] 18 34 }) 19 - ctx.scene.add(toThreeMesh( 20 - tube(branchPath, (t) => 0.08 * (1 - t * 0.8), 5) 35 + scene.add(toThreeMesh( 36 + tube(branchPath, (t: number) => 0.08 * (1 - t * 0.8), 5) 21 37 .translate(-1.5, 0, 0).vertexColor('#8B6B4A') 22 38 )) 23 39 24 - // Lofted leaf 25 40 const leafPath = Array.from({ length: 8 }, (_, i) => { 26 41 const t = i / 7 27 42 return [0, t * 1.5 + 0.3, -t * 0.3] as [number, number, number] 28 43 }) 29 44 const leaf = loft({ 30 45 path: leafPath, 31 - shape: (t) => { 46 + shape: (t: number) => { 32 47 const w = 0.25 * Math.sin(t * Math.PI) * (1 - t * 0.3) 33 - return [[w, 0.02], [0, 0.06], [-w, 0.02], [-w, -0.01], [0, -0.02], [w, -0.01]] 48 + return [[w, 0.02], [0, 0.06], [-w, 0.02], [-w, -0.01], [0, -0.02], [w, -0.01]] as [number,number][] 34 49 }, 35 - closedShape: true, 36 - closed: false, 37 - up: [0, 0, 1], 50 + closedShape: true, closed: false, up: [0, 0, 1], 38 51 }).vertexColor('#3a7a2a') 39 - ctx.scene.add(toThreeMesh(thicken(leaf, 0.01))) 52 + scene.add(toThreeMesh(thicken(leaf, 0.01))) 40 53 41 - // Wavy trunk 42 54 const trunkPath = Array.from({ length: 15 }, (_, i) => { 43 55 const t = i / 14 44 56 return [1.5 + Math.sin(t * 4) * 0.15, t * 2, 0] as [number, number, number] 45 57 }) 46 - ctx.scene.add(toThreeMesh( 47 - tube(trunkPath, (t) => { 58 + scene.add(toThreeMesh( 59 + tube(trunkPath, (t: number) => { 48 60 const taper = 0.1 * (1 - t * 0.5) 49 61 const wave = 1 + Math.sin(t * Math.PI * 10) * 0.12 50 62 return taper * wave 51 63 }, 5).vertexColor('#6a5030') 52 64 )) 65 + 66 + ;(function animate() { 67 + requestAnimationFrame(animate) 68 + controls.update() 69 + renderer.render(scene, camera) 70 + })() 53 71 } 54 72 init() 55 73 </script>
+33 -14
docs/components/PatternsDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { plane, icosphere, merge, tube, createRng } = await import('../../src') 9 - const { fbm } = await import('../../src/noise') 10 - const { heightGradient, normalGradient } = await import('../../src/color') 11 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { plane, icosphere, merge, tube, createRng } = await import('shapecraft') 10 + const { fbm } = await import('shapecraft/noise') 11 + const { heightGradient, normalGradient } = await import('shapecraft/color') 12 + const { toThreeMesh } = await import('shapecraft/three') 13 + 14 + const canvas = document.getElementById('patterns-demo') as HTMLCanvasElement 15 + if (!canvas) return 16 + 17 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 18 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 19 + const scene = new THREE.Scene() 20 + scene.background = new THREE.Color(0x2a2a3a) 21 + const camera = new THREE.PerspectiveCamera(55, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 22 + camera.position.set(6, 5, 8) 23 + const controls = new OrbitControls(camera, canvas) 24 + controls.target.set(0, 0.5, 0) 25 + controls.enableDamping = true 26 + controls.update() 12 27 13 - const ctx = await setupScene('patterns-demo', { cameraPos: [6, 5, 8], target: [0, 0.5, 0] }) 14 - if (!ctx) return 28 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 29 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 30 + sun.position.set(5, 8, 5) 31 + scene.add(sun) 15 32 16 33 const terrainNoise = fbm({ seed: 7, scale: 0.2, octaves: 3 }) 17 - ctx.scene.add(toThreeMesh( 34 + scene.add(toThreeMesh( 18 35 plane({ size: 12, segments: 40 }) 19 36 .displaceNoise(terrainNoise, 0.5) 20 37 .vertexColor(heightGradient([ ··· 30 47 const z = (rand() - 0.5) * 8 31 48 const h = 1 + rand() * 1.5 32 49 const r = 0.3 + rand() * 0.3 33 - 34 50 const trunk = tube( 35 - [[0,0,0],[0,h*0.4,0]], (t) => 0.04 * (1 - t * 0.5), 5 51 + [[0,0,0],[0,h*0.4,0]], (t: number) => 0.04 * (1 - t * 0.5), 5 36 52 ).vertexColor('#4a2815') 37 - 38 53 const canopy = icosphere({ radius: r, subdivisions: 1 }) 39 54 .scale(1, 0.8, 1).jitter(r * 0.08, { seed: i }) 40 55 .translate(0, h * 0.5, 0) 41 56 .faceColor(normalGradient([0.2, 0.5, 0.1], [0.1, 0.3, 0.05])) 42 - 43 57 const obj = toThreeMesh(merge(trunk, canopy)) 44 58 obj.position.set(x, terrainNoise.get(x, z), z) 45 - obj.castShadow = true 46 - ctx.scene.add(obj) 59 + scene.add(obj) 47 60 } 61 + 62 + ;(function animate() { 63 + requestAnimationFrame(animate) 64 + controls.update() 65 + renderer.render(scene, camera) 66 + })() 48 67 } 49 68 init() 50 69 </script>
+29 -6
docs/components/PrimitivesDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { box, sphere, cylinder, cone, icosphere, torus } = await import('../../src') 9 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { box, sphere, cylinder, cone, icosphere, torus } = await import('shapecraft') 10 + const { toThreeMesh } = await import('shapecraft/three') 11 + 12 + const canvas = document.getElementById('primitives-demo') as HTMLCanvasElement 13 + if (!canvas) return 14 + 15 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 16 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 17 + const scene = new THREE.Scene() 18 + scene.background = new THREE.Color(0x2a2a3a) 19 + const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 20 + camera.position.set(6, 4, 8) 21 + const controls = new OrbitControls(camera, canvas) 22 + controls.target.set(3, 0.5, 0) 23 + controls.enableDamping = true 24 + controls.update() 10 25 11 - const ctx = await setupScene('primitives-demo', { cameraPos: [6, 4, 8], target: [3, 0.5, 0] }) 12 - if (!ctx) return 26 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 27 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 28 + sun.position.set(5, 8, 5) 29 + scene.add(sun) 13 30 14 31 const primitives = [ 15 32 box().vertexColor('#cc8844'), ··· 20 37 torus().vertexColor('#aa44cc'), 21 38 ] 22 39 primitives.forEach((m, i) => { 23 - ctx.scene.add(toThreeMesh(m.translate(i * 1.5, 0.5, 0))) 40 + scene.add(toThreeMesh(m.translate(i * 1.5, 0.5, 0))) 24 41 }) 42 + 43 + ;(function animate() { 44 + requestAnimationFrame(animate) 45 + controls.update() 46 + renderer.render(scene, camera) 47 + })() 25 48 } 26 49 init() 27 50 </script>
+29 -7
docs/components/QuickstartDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { box, sphere, merge } = await import('../../src') 9 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { box, sphere, merge } = await import('shapecraft') 10 + const { toThreeMesh } = await import('shapecraft/three') 10 11 11 - const ctx = await setupScene('quickstart-demo') 12 - if (!ctx) return 12 + const canvas = document.getElementById('quickstart-demo') as HTMLCanvasElement 13 + if (!canvas) return 14 + 15 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 16 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 17 + const scene = new THREE.Scene() 18 + scene.background = new THREE.Color(0x2a2a3a) 19 + const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 20 + camera.position.set(3, 2, 4) 21 + const controls = new OrbitControls(camera, canvas) 22 + controls.target.set(0, 0.5, 0) 23 + controls.enableDamping = true 24 + controls.update() 25 + 26 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 27 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 28 + sun.position.set(5, 8, 5) 29 + scene.add(sun) 13 30 14 31 const b = box({ size: 1 }).vertexColor('#cc8844') 15 32 const s = sphere({ radius: 0.5 }).translate(2, 0, 0).vertexColor('#4488cc') 16 - const combined = merge(b, s).translate(-1, 0.5, 0) 17 - ctx.scene.add(toThreeMesh(combined)) 33 + scene.add(toThreeMesh(merge(b, s).translate(-1, 0.5, 0))) 34 + 35 + ;(function animate() { 36 + requestAnimationFrame(animate) 37 + controls.update() 38 + renderer.render(scene, camera) 39 + })() 18 40 } 19 41 init() 20 42 </script>
+32 -10
docs/components/SubdivideDemo.astro
··· 4 4 5 5 <script> 6 6 async function init() { 7 - const { setupScene } = await import('./scene-setup') 8 - const { icosphere } = await import('../../src') 9 - const { toThreeMesh } = await import('../../src/three') 7 + const THREE = await import('three') 8 + const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 9 + const { icosphere } = await import('shapecraft') 10 + const { toThreeMesh } = await import('shapecraft/three') 10 11 11 - const ctx = await setupScene('subdivide-demo', { cameraPos: [4, 2, 4], target: [0, 0.5, 0] }) 12 - if (!ctx) return 12 + const canvas = document.getElementById('subdivide-demo') as HTMLCanvasElement 13 + if (!canvas) return 13 14 14 - ctx.scene.add(toThreeMesh( 15 - icosphere({ radius: 0.5, subdivisions: 0 }) 16 - .translate(-2, 0.5, 0).vertexColor('#cc4444') 15 + const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 16 + renderer.setSize(canvas.clientWidth, canvas.clientHeight) 17 + const scene = new THREE.Scene() 18 + scene.background = new THREE.Color(0x2a2a3a) 19 + const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 20 + camera.position.set(4, 2, 4) 21 + const controls = new OrbitControls(camera, canvas) 22 + controls.target.set(0, 0.5, 0) 23 + controls.enableDamping = true 24 + controls.update() 25 + 26 + scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 27 + const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 28 + sun.position.set(5, 8, 5) 29 + scene.add(sun) 30 + 31 + scene.add(toThreeMesh( 32 + icosphere({ radius: 0.5, subdivisions: 0 }).translate(-2, 0.5, 0).vertexColor('#cc4444') 17 33 )) 18 - ctx.scene.add(toThreeMesh( 34 + scene.add(toThreeMesh( 19 35 icosphere({ radius: 0.5, subdivisions: 0 }) 20 36 .subdivideAdaptive(0.3).spherize(0.5) 21 37 .translate(0, 0.5, 0).vertexColor('#44cc44') 22 38 )) 23 - ctx.scene.add(toThreeMesh( 39 + scene.add(toThreeMesh( 24 40 icosphere({ radius: 0.5, subdivisions: 0 }) 25 41 .subdivideAdaptive(0.3).spherize(0.5) 26 42 .jitter(0.04, { seed: 3 }) 27 43 .translate(2, 0.5, 0).vertexColor('#4444cc') 28 44 )) 45 + 46 + ;(function animate() { 47 + requestAnimationFrame(animate) 48 + controls.update() 49 + renderer.render(scene, camera) 50 + })() 29 51 } 30 52 init() 31 53 </script>
-52
docs/components/scene-setup.ts
··· 1 - export async function setupScene(canvasId: string, options?: { 2 - cameraPos?: [number, number, number] 3 - target?: [number, number, number] 4 - background?: number 5 - }) { 6 - const canvas = document.getElementById(canvasId) as HTMLCanvasElement 7 - if (!canvas) return null 8 - 9 - const THREE = await import('three') 10 - const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js') 11 - 12 - const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) 13 - renderer.setSize(canvas.clientWidth, canvas.clientHeight) 14 - renderer.setPixelRatio(window.devicePixelRatio) 15 - renderer.toneMapping = THREE.ACESFilmicToneMapping 16 - 17 - const scene = new THREE.Scene() 18 - scene.background = new THREE.Color(options?.background ?? 0x2a2a3a) 19 - 20 - const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100) 21 - const cp = options?.cameraPos ?? [3, 2, 4] 22 - camera.position.set(cp[0], cp[1], cp[2]) 23 - 24 - const controls = new OrbitControls(camera, canvas) 25 - const t = options?.target ?? [0, 0.5, 0] 26 - controls.target.set(t[0], t[1], t[2]) 27 - controls.enableDamping = true 28 - controls.update() 29 - 30 - // Lights 31 - scene.add(new THREE.HemisphereLight(0x87ceeb, 0x3a5a2a, 0.6)) 32 - const sun = new THREE.DirectionalLight(0xfff4e0, 1.2) 33 - sun.position.set(5, 8, 5) 34 - scene.add(sun) 35 - 36 - 37 - function animate() { 38 - requestAnimationFrame(animate) 39 - controls.update() 40 - renderer.render(scene, camera) 41 - } 42 - animate() 43 - 44 - const ro = new ResizeObserver(() => { 45 - camera.aspect = canvas.clientWidth / canvas.clientHeight 46 - camera.updateProjectionMatrix() 47 - renderer.setSize(canvas.clientWidth, canvas.clientHeight) 48 - }) 49 - ro.observe(canvas) 50 - 51 - return { scene, camera, renderer, controls } 52 - }