@developerz.ai/ts23d 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (524) hide show
  1. package/.claude/agents/bug-hunter.md +21 -0
  2. package/.claude/agents/docs-author.md +13 -0
  3. package/.claude/agents/dx-auditor.md +14 -0
  4. package/.claude/agents/primitive-author.md +22 -0
  5. package/.claude/agents/recipe-author.md +36 -0
  6. package/.claude/agents/visual-reviewer.md +23 -0
  7. package/.claude/commands/feature.md +28 -0
  8. package/.claude/commands/new-recipe.md +19 -0
  9. package/.claude/commands/release.md +14 -0
  10. package/.claude/commands/verify.md +9 -0
  11. package/.claude/skills/author-recipe/SKILL.md +93 -0
  12. package/CHANGELOG.md +79 -0
  13. package/CLAUDE.md +85 -0
  14. package/LICENSE +21 -0
  15. package/README.md +380 -2
  16. package/dist/chunk-11v28977.js +1575 -0
  17. package/dist/chunk-11v28977.js.map +30 -0
  18. package/dist/chunk-1ypfs7ga.js +3571 -0
  19. package/dist/chunk-1ypfs7ga.js.map +42 -0
  20. package/dist/chunk-4ddzsqtc.js +464 -0
  21. package/dist/chunk-4ddzsqtc.js.map +19 -0
  22. package/dist/chunk-a8fxakcj.js +19 -0
  23. package/dist/chunk-a8fxakcj.js.map +11 -0
  24. package/dist/chunk-gcyp9b4d.js +755 -0
  25. package/dist/chunk-gcyp9b4d.js.map +22 -0
  26. package/dist/chunk-kgvbpqqw.js +44 -0
  27. package/dist/chunk-kgvbpqqw.js.map +10 -0
  28. package/dist/chunk-kvcep7ft.js +1919 -0
  29. package/dist/chunk-kvcep7ft.js.map +43 -0
  30. package/dist/chunk-ravbb06q.js +89 -0
  31. package/dist/chunk-ravbb06q.js.map +11 -0
  32. package/dist/chunk-w1zyhj40.js +1031 -0
  33. package/dist/chunk-w1zyhj40.js.map +21 -0
  34. package/dist/chunk-wtqhq529.js +2532 -0
  35. package/dist/chunk-wtqhq529.js.map +43 -0
  36. package/dist/chunk-xnt2sdhf.js +836 -0
  37. package/dist/chunk-xnt2sdhf.js.map +17 -0
  38. package/dist/cli/args.d.ts +24 -0
  39. package/dist/cli/bin.d.ts +2 -0
  40. package/dist/cli/bin.js +307 -0
  41. package/dist/cli/bin.js.map +14 -0
  42. package/dist/cli/load.d.ts +2 -0
  43. package/dist/cli/manifest.d.ts +49 -0
  44. package/dist/cli/render-cmds.d.ts +12 -0
  45. package/dist/cli/run.d.ts +1 -0
  46. package/dist/core/asset.d.ts +10 -0
  47. package/dist/core/definition.d.ts +148 -0
  48. package/dist/core/errors.d.ts +17 -0
  49. package/dist/core/family.d.ts +23 -0
  50. package/dist/core/index.d.ts +9 -0
  51. package/dist/core/index.js +69 -0
  52. package/dist/core/index.js.map +9 -0
  53. package/dist/core/mesh.d.ts +35 -0
  54. package/dist/core/part.d.ts +114 -0
  55. package/dist/core/png.d.ts +1 -0
  56. package/dist/core/rng.d.ts +12 -0
  57. package/dist/core/transform.d.ts +20 -0
  58. package/dist/core/vec.d.ts +11 -0
  59. package/dist/core/world.d.ts +16 -0
  60. package/dist/export/bin-writer.d.ts +16 -0
  61. package/dist/export/container.d.ts +3 -0
  62. package/dist/export/glb.d.ts +38 -0
  63. package/dist/export/index.d.ts +4 -0
  64. package/dist/export/index.js +26 -0
  65. package/dist/export/index.js.map +9 -0
  66. package/dist/export/lights.d.ts +8 -0
  67. package/dist/export/materials.d.ts +23 -0
  68. package/dist/export/merge-pivots.d.ts +6 -0
  69. package/dist/export/metadata.d.ts +19 -0
  70. package/dist/export/motion.d.ts +9 -0
  71. package/dist/export/optimize-parts.d.ts +26 -0
  72. package/dist/export/primitive.d.ts +3 -0
  73. package/dist/export/tangents.d.ts +2 -0
  74. package/dist/export/textures.d.ts +22 -0
  75. package/dist/export/version.d.ts +2 -0
  76. package/dist/grammar/index.d.ts +2 -0
  77. package/dist/grammar/index.js +16 -0
  78. package/dist/grammar/index.js.map +9 -0
  79. package/dist/grammar/split.d.ts +21 -0
  80. package/dist/grammar/whyte.d.ts +5 -0
  81. package/dist/index.d.ts +11 -0
  82. package/dist/index.js +411 -0
  83. package/dist/index.js.map +9 -0
  84. package/dist/kinds/building.d.ts +44 -0
  85. package/dist/kinds/faces.d.ts +27 -0
  86. package/dist/kinds/head-armour.d.ts +9 -0
  87. package/dist/kinds/head-beard.d.ts +6 -0
  88. package/dist/kinds/head-bust.d.ts +4 -0
  89. package/dist/kinds/head-extras.d.ts +15 -0
  90. package/dist/kinds/head-face.d.ts +6 -0
  91. package/dist/kinds/head-geom.d.ts +7 -0
  92. package/dist/kinds/head-hair.d.ts +4 -0
  93. package/dist/kinds/head-layout.d.ts +29 -0
  94. package/dist/kinds/head-mesh.d.ts +27 -0
  95. package/dist/kinds/head-params.d.ts +108 -0
  96. package/dist/kinds/head-presets.d.ts +19 -0
  97. package/dist/kinds/head-rim.d.ts +10 -0
  98. package/dist/kinds/head.d.ts +39 -0
  99. package/dist/kinds/hull-lines.d.ts +65 -0
  100. package/dist/kinds/hull-skin.d.ts +6 -0
  101. package/dist/kinds/hull.d.ts +14 -0
  102. package/dist/kinds/index.d.ts +16 -0
  103. package/dist/kinds/index.js +78 -0
  104. package/dist/kinds/index.js.map +9 -0
  105. package/dist/kinds/mass.d.ts +15 -0
  106. package/dist/kinds/openings.d.ts +30 -0
  107. package/dist/kinds/planned.d.ts +6 -0
  108. package/dist/kinds/roof-chimney.d.ts +16 -0
  109. package/dist/kinds/roof-shape.d.ts +64 -0
  110. package/dist/kinds/roof.d.ts +31 -0
  111. package/dist/kinds/vegetation-fronds.d.ts +15 -0
  112. package/dist/kinds/vegetation-impostor.d.ts +9 -0
  113. package/dist/kinds/vegetation-leaves.d.ts +15 -0
  114. package/dist/kinds/vegetation-mesh.d.ts +21 -0
  115. package/dist/kinds/vegetation-params.d.ts +90 -0
  116. package/dist/kinds/vegetation-presets.d.ts +20 -0
  117. package/dist/kinds/vegetation-skeleton.d.ts +20 -0
  118. package/dist/kinds/vegetation.d.ts +42 -0
  119. package/dist/kinds/vehicle.d.ts +42 -0
  120. package/dist/material/generator.d.ts +4 -0
  121. package/dist/material/index.d.ts +4 -0
  122. package/dist/material/index.js +38 -0
  123. package/dist/material/index.js.map +9 -0
  124. package/dist/material/paint/bond.d.ts +3 -0
  125. package/dist/material/paint/canvas.d.ts +32 -0
  126. package/dist/material/paint/cell.d.ts +4 -0
  127. package/dist/material/paint/cells-round.d.ts +5 -0
  128. package/dist/material/paint/cells.d.ts +7 -0
  129. package/dist/material/paint/compose.d.ts +5 -0
  130. package/dist/material/paint/concrete.d.ts +3 -0
  131. package/dist/material/paint/courses.d.ts +31 -0
  132. package/dist/material/paint/decode-png.d.ts +6 -0
  133. package/dist/material/paint/default-sheet.d.ts +2 -0
  134. package/dist/material/paint/foliage-broad.d.ts +5 -0
  135. package/dist/material/paint/foliage-common.d.ts +35 -0
  136. package/dist/material/paint/foliage-compound.d.ts +4 -0
  137. package/dist/material/paint/foliage-conifer.d.ts +5 -0
  138. package/dist/material/paint/foliage-raster.d.ts +26 -0
  139. package/dist/material/paint/foliage.d.ts +3 -0
  140. package/dist/material/paint/masks.d.ts +3 -0
  141. package/dist/material/paint/noise.d.ts +9 -0
  142. package/dist/material/paint/normal.d.ts +8 -0
  143. package/dist/material/paint/orm.d.ts +8 -0
  144. package/dist/material/paint/planks.d.ts +3 -0
  145. package/dist/material/paint/plates.d.ts +3 -0
  146. package/dist/material/paint/shapes.d.ts +8 -0
  147. package/dist/material/paint/slate.d.ts +3 -0
  148. package/dist/material/paint/stamp.d.ts +3 -0
  149. package/dist/material/paint/strip.d.ts +3 -0
  150. package/dist/material/paint/surface.d.ts +5 -0
  151. package/dist/material/paint/types.d.ts +35 -0
  152. package/dist/material/paint/validate.d.ts +3 -0
  153. package/dist/material/palette.d.ts +12 -0
  154. package/dist/material/shader-godot.d.ts +2 -0
  155. package/dist/material/shader-three.d.ts +2 -0
  156. package/dist/material/shader.d.ts +46 -0
  157. package/dist/material/trim-sheet.d.ts +22 -0
  158. package/dist/neural/generator.d.ts +25 -0
  159. package/dist/neural/images.d.ts +17 -0
  160. package/dist/neural/index.d.ts +2 -0
  161. package/dist/neural/index.js +14 -0
  162. package/dist/neural/index.js.map +9 -0
  163. package/dist/passes/bake-ao.d.ts +17 -0
  164. package/dist/passes/grime.d.ts +15 -0
  165. package/dist/passes/index.d.ts +6 -0
  166. package/dist/passes/index.js +22 -0
  167. package/dist/passes/index.js.map +9 -0
  168. package/dist/passes/livery.d.ts +19 -0
  169. package/dist/passes/optimize-cache.d.ts +15 -0
  170. package/dist/passes/optimize-weld.d.ts +15 -0
  171. package/dist/passes/optimize.d.ts +28 -0
  172. package/dist/passes/ray-grid.d.ts +23 -0
  173. package/dist/passes/weighted-normals.d.ts +8 -0
  174. package/dist/primitives/beam.d.ts +17 -0
  175. package/dist/primitives/bevel-box.d.ts +13 -0
  176. package/dist/primitives/box.d.ts +16 -0
  177. package/dist/primitives/cell.d.ts +20 -0
  178. package/dist/primitives/check.d.ts +12 -0
  179. package/dist/primitives/closed.d.ts +11 -0
  180. package/dist/primitives/erosion.d.ts +22 -0
  181. package/dist/primitives/font-5x7.d.ts +3 -0
  182. package/dist/primitives/heightfield.d.ts +16 -0
  183. package/dist/primitives/index.d.ts +23 -0
  184. package/dist/primitives/index.js +81 -0
  185. package/dist/primitives/index.js.map +9 -0
  186. package/dist/primitives/lathe-facets.d.ts +8 -0
  187. package/dist/primitives/lathe.d.ts +26 -0
  188. package/dist/primitives/lattice.d.ts +30 -0
  189. package/dist/primitives/loft.d.ts +15 -0
  190. package/dist/primitives/mc-table.d.ts +2 -0
  191. package/dist/primitives/member.d.ts +19 -0
  192. package/dist/primitives/noise.d.ts +34 -0
  193. package/dist/primitives/planar.d.ts +10 -0
  194. package/dist/primitives/prism.d.ts +13 -0
  195. package/dist/primitives/sculpt-edit.d.ts +56 -0
  196. package/dist/primitives/sculpt-tree.d.ts +23 -0
  197. package/dist/primitives/sculpt.d.ts +27 -0
  198. package/dist/primitives/sdf-grid.d.ts +17 -0
  199. package/dist/primitives/sdf-ops.d.ts +37 -0
  200. package/dist/primitives/sdf.d.ts +21 -0
  201. package/dist/primitives/strip-uv.d.ts +11 -0
  202. package/dist/primitives/surface-nets.d.ts +7 -0
  203. package/dist/primitives/sweep.d.ts +19 -0
  204. package/dist/primitives/terrain-color.d.ts +30 -0
  205. package/dist/primitives/terrain-grid.d.ts +36 -0
  206. package/dist/primitives/text-layout.d.ts +22 -0
  207. package/dist/primitives/text.d.ts +27 -0
  208. package/dist/primitives/tiled.d.ts +22 -0
  209. package/dist/primitives/transport.d.ts +17 -0
  210. package/dist/primitives/triangulate.d.ts +2 -0
  211. package/dist/primitives/value-noise.d.ts +10 -0
  212. package/dist/qa/checks.d.ts +11 -0
  213. package/dist/qa/clash.d.ts +2 -0
  214. package/dist/qa/floating.d.ts +5 -0
  215. package/dist/qa/gates.d.ts +12 -0
  216. package/dist/qa/geometry.d.ts +21 -0
  217. package/dist/qa/index.d.ts +12 -0
  218. package/dist/qa/index.js +78 -0
  219. package/dist/qa/index.js.map +9 -0
  220. package/dist/qa/inside.d.ts +12 -0
  221. package/dist/qa/inspect.d.ts +18 -0
  222. package/dist/qa/lods.d.ts +12 -0
  223. package/dist/qa/measures.d.ts +4 -0
  224. package/dist/qa/occlusion.d.ts +3 -0
  225. package/dist/qa/sanity.d.ts +7 -0
  226. package/dist/qa/types.d.ts +12 -0
  227. package/dist/render/beauty.d.ts +40 -0
  228. package/dist/render/camera.d.ts +33 -0
  229. package/dist/render/depth-map.d.ts +38 -0
  230. package/dist/render/font.d.ts +1 -0
  231. package/dist/render/ground.d.ts +13 -0
  232. package/dist/render/index.d.ts +10 -0
  233. package/dist/render/index.js +47 -0
  234. package/dist/render/index.js.map +9 -0
  235. package/dist/render/lighting.d.ts +20 -0
  236. package/dist/render/lod-strip.d.ts +5 -0
  237. package/dist/render/metrics.d.ts +37 -0
  238. package/dist/render/options.d.ts +28 -0
  239. package/dist/render/photo-match.d.ts +19 -0
  240. package/dist/render/raster.d.ts +25 -0
  241. package/dist/render/resolve.d.ts +11 -0
  242. package/dist/render/review-prompt.d.ts +2 -0
  243. package/dist/render/review-warnings.d.ts +34 -0
  244. package/dist/render/review.d.ts +24 -0
  245. package/dist/render/scene.d.ts +18 -0
  246. package/dist/render/shade.d.ts +22 -0
  247. package/dist/render/sheet.d.ts +15 -0
  248. package/dist/render/texture.d.ts +15 -0
  249. package/dist/render/thumbnail.d.ts +8 -0
  250. package/dist/render/vis-buffer.d.ts +18 -0
  251. package/dist/testing/assert.d.ts +21 -0
  252. package/dist/testing/compiled.d.ts +15 -0
  253. package/dist/testing/hash.d.ts +1 -0
  254. package/dist/testing/index.d.ts +4 -0
  255. package/dist/testing/index.js +134 -0
  256. package/dist/testing/index.js.map +13 -0
  257. package/dist/testing/snapshot.d.ts +13 -0
  258. package/docs/README.md +52 -0
  259. package/docs/architecture/README.md +68 -0
  260. package/docs/architecture/adding-a-kind.md +44 -0
  261. package/docs/architecture/adding-a-primitive.md +49 -0
  262. package/docs/architecture/errors.md +47 -0
  263. package/docs/architecture/glb-production.md +214 -0
  264. package/docs/architecture/gltf-capabilities.md +80 -0
  265. package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
  266. package/docs/decisions/002-typescript-not-json.md +22 -0
  267. package/docs/decisions/003-shared-trim-sheet.md +21 -0
  268. package/docs/decisions/004-z-up-metres.md +21 -0
  269. package/docs/decisions/005-zero-runtime-deps.md +21 -0
  270. package/docs/decisions/006-single-package-subpath-exports.md +20 -0
  271. package/docs/decisions/007-cpu-contact-sheets.md +21 -0
  272. package/docs/decisions/008-surface-materials.md +21 -0
  273. package/docs/decisions/009-gltf-front-axis.md +21 -0
  274. package/docs/decisions/README.md +15 -0
  275. package/docs/idea/01-vision.md +117 -0
  276. package/docs/idea/02-dsl.md +343 -0
  277. package/docs/idea/03-ir.md +97 -0
  278. package/docs/idea/04-materials.md +114 -0
  279. package/docs/idea/05-agent-loop.md +141 -0
  280. package/docs/idea/06-qa.md +101 -0
  281. package/docs/idea/07-what-fits.md +45 -0
  282. package/docs/idea/08-neural-adapters.md +78 -0
  283. package/docs/idea/09-kinds.md +105 -0
  284. package/docs/landscape.md +49 -0
  285. package/docs/plans/roadmap.md +127 -0
  286. package/examples/build-with-bun.ts +41 -0
  287. package/llms.txt +47 -0
  288. package/package.json +99 -4
  289. package/recipes/README.md +38 -0
  290. package/recipes/asteroid/README.md +46 -0
  291. package/recipes/asteroid/asteroid.ts +336 -0
  292. package/recipes/boulders/README.md +28 -0
  293. package/recipes/boulders/boulders.ts +117 -0
  294. package/recipes/boxcar/README.md +46 -0
  295. package/recipes/boxcar/boxcar.ts +916 -0
  296. package/recipes/cargo_container_scifi/README.md +52 -0
  297. package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
  298. package/recipes/dropship/README.md +84 -0
  299. package/recipes/dropship/dropship.ts +1144 -0
  300. package/recipes/eiffel_tower/README.md +110 -0
  301. package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
  302. package/recipes/goblin_head/README.md +62 -0
  303. package/recipes/goblin_head/goblin_head.ts +36 -0
  304. package/recipes/human_head/README.md +64 -0
  305. package/recipes/human_head/human_head.ts +37 -0
  306. package/recipes/loco_440/README.md +32 -0
  307. package/recipes/loco_440/loco_440.ts +1404 -0
  308. package/recipes/material_swatches/README.md +21 -0
  309. package/recipes/material_swatches/material_swatches.ts +183 -0
  310. package/recipes/mountain_range/README.md +45 -0
  311. package/recipes/mountain_range/mountain_range.ts +128 -0
  312. package/recipes/oak/README.md +30 -0
  313. package/recipes/oak/oak.ts +19 -0
  314. package/recipes/orc_head/README.md +66 -0
  315. package/recipes/orc_head/orc_head.ts +39 -0
  316. package/recipes/palm/README.md +30 -0
  317. package/recipes/palm/palm.ts +19 -0
  318. package/recipes/pine/README.md +29 -0
  319. package/recipes/pine/pine.ts +19 -0
  320. package/recipes/signal_gantry/README.md +36 -0
  321. package/recipes/signal_gantry/signal_gantry.ts +522 -0
  322. package/recipes/station_halt/README.md +39 -0
  323. package/recipes/station_halt/station_halt.ts +722 -0
  324. package/recipes/t34/README.md +44 -0
  325. package/recipes/t34/t34.ts +1068 -0
  326. package/recipes/tiger_1/README.md +75 -0
  327. package/recipes/tiger_1/tiger_1.ts +1370 -0
  328. package/recipes/water_tower/README.md +40 -0
  329. package/recipes/water_tower/water_tower.ts +542 -0
  330. package/src/cli/args.ts +112 -0
  331. package/src/cli/bin.ts +7 -0
  332. package/src/cli/load.ts +17 -0
  333. package/src/cli/manifest.ts +89 -0
  334. package/src/cli/render-cmds.ts +66 -0
  335. package/src/cli/run.ts +168 -0
  336. package/src/core/asset.ts +128 -0
  337. package/src/core/definition.ts +149 -0
  338. package/src/core/errors.ts +45 -0
  339. package/src/core/family.ts +50 -0
  340. package/src/core/index.ts +66 -0
  341. package/src/core/mesh.ts +114 -0
  342. package/src/core/part.ts +130 -0
  343. package/src/core/png.ts +49 -0
  344. package/src/core/rng.ts +49 -0
  345. package/src/core/transform.ts +90 -0
  346. package/src/core/vec.ts +26 -0
  347. package/src/core/world.ts +80 -0
  348. package/src/export/bin-writer.ts +79 -0
  349. package/src/export/container.ts +35 -0
  350. package/src/export/glb.ts +200 -0
  351. package/src/export/index.ts +18 -0
  352. package/src/export/lights.ts +49 -0
  353. package/src/export/materials.ts +126 -0
  354. package/src/export/merge-pivots.ts +146 -0
  355. package/src/export/metadata.ts +59 -0
  356. package/src/export/motion.ts +70 -0
  357. package/src/export/optimize-parts.ts +80 -0
  358. package/src/export/primitive.ts +35 -0
  359. package/src/export/tangents.ts +75 -0
  360. package/src/export/textures.ts +88 -0
  361. package/src/export/version.ts +6 -0
  362. package/src/grammar/index.ts +4 -0
  363. package/src/grammar/split.ts +87 -0
  364. package/src/grammar/whyte.ts +20 -0
  365. package/src/index.ts +14 -0
  366. package/src/kinds/building.ts +153 -0
  367. package/src/kinds/faces.ts +138 -0
  368. package/src/kinds/head-armour.ts +151 -0
  369. package/src/kinds/head-beard.ts +133 -0
  370. package/src/kinds/head-bust.ts +221 -0
  371. package/src/kinds/head-extras.ts +150 -0
  372. package/src/kinds/head-face.ts +222 -0
  373. package/src/kinds/head-geom.ts +31 -0
  374. package/src/kinds/head-hair.ts +152 -0
  375. package/src/kinds/head-layout.ts +63 -0
  376. package/src/kinds/head-mesh.ts +157 -0
  377. package/src/kinds/head-params.ts +153 -0
  378. package/src/kinds/head-presets.ts +152 -0
  379. package/src/kinds/head-rim.ts +128 -0
  380. package/src/kinds/head.ts +171 -0
  381. package/src/kinds/hull-lines.ts +160 -0
  382. package/src/kinds/hull-skin.ts +83 -0
  383. package/src/kinds/hull.ts +209 -0
  384. package/src/kinds/index.ts +63 -0
  385. package/src/kinds/mass.ts +110 -0
  386. package/src/kinds/openings.ts +141 -0
  387. package/src/kinds/planned.ts +12 -0
  388. package/src/kinds/roof-chimney.ts +100 -0
  389. package/src/kinds/roof-shape.ts +230 -0
  390. package/src/kinds/roof.ts +250 -0
  391. package/src/kinds/vegetation-fronds.ts +126 -0
  392. package/src/kinds/vegetation-impostor.ts +118 -0
  393. package/src/kinds/vegetation-leaves.ts +168 -0
  394. package/src/kinds/vegetation-mesh.ts +174 -0
  395. package/src/kinds/vegetation-params.ts +147 -0
  396. package/src/kinds/vegetation-presets.ts +184 -0
  397. package/src/kinds/vegetation-skeleton.ts +168 -0
  398. package/src/kinds/vegetation.ts +199 -0
  399. package/src/kinds/vehicle.ts +144 -0
  400. package/src/material/generator.ts +56 -0
  401. package/src/material/index.ts +29 -0
  402. package/src/material/paint/bond.ts +60 -0
  403. package/src/material/paint/canvas.ts +91 -0
  404. package/src/material/paint/cell.ts +39 -0
  405. package/src/material/paint/cells-round.ts +119 -0
  406. package/src/material/paint/cells.ts +111 -0
  407. package/src/material/paint/compose.ts +53 -0
  408. package/src/material/paint/concrete.ts +62 -0
  409. package/src/material/paint/courses.ts +85 -0
  410. package/src/material/paint/decode-png.ts +100 -0
  411. package/src/material/paint/default-sheet.ts +61 -0
  412. package/src/material/paint/foliage-broad.ts +126 -0
  413. package/src/material/paint/foliage-common.ts +93 -0
  414. package/src/material/paint/foliage-compound.ts +135 -0
  415. package/src/material/paint/foliage-conifer.ts +143 -0
  416. package/src/material/paint/foliage-raster.ts +196 -0
  417. package/src/material/paint/foliage.ts +23 -0
  418. package/src/material/paint/masks.ts +44 -0
  419. package/src/material/paint/noise.ts +52 -0
  420. package/src/material/paint/normal.ts +52 -0
  421. package/src/material/paint/orm.ts +95 -0
  422. package/src/material/paint/planks.ts +56 -0
  423. package/src/material/paint/plates.ts +72 -0
  424. package/src/material/paint/shapes.ts +28 -0
  425. package/src/material/paint/slate.ts +52 -0
  426. package/src/material/paint/stamp.ts +40 -0
  427. package/src/material/paint/strip.ts +42 -0
  428. package/src/material/paint/surface.ts +60 -0
  429. package/src/material/paint/types.ts +51 -0
  430. package/src/material/paint/validate.ts +58 -0
  431. package/src/material/palette.ts +43 -0
  432. package/src/material/shader-godot.ts +77 -0
  433. package/src/material/shader-three.ts +103 -0
  434. package/src/material/shader.ts +100 -0
  435. package/src/material/trim-sheet.ts +100 -0
  436. package/src/neural/generator.ts +37 -0
  437. package/src/neural/images.ts +27 -0
  438. package/src/neural/index.ts +10 -0
  439. package/src/passes/bake-ao.ts +183 -0
  440. package/src/passes/grime.ts +37 -0
  441. package/src/passes/index.ts +8 -0
  442. package/src/passes/livery.ts +66 -0
  443. package/src/passes/optimize-cache.ts +175 -0
  444. package/src/passes/optimize-weld.ts +147 -0
  445. package/src/passes/optimize.ts +85 -0
  446. package/src/passes/ray-grid.ts +237 -0
  447. package/src/passes/weighted-normals.ts +125 -0
  448. package/src/primitives/beam.ts +60 -0
  449. package/src/primitives/bevel-box.ts +140 -0
  450. package/src/primitives/box.ts +90 -0
  451. package/src/primitives/cell.ts +115 -0
  452. package/src/primitives/check.ts +49 -0
  453. package/src/primitives/closed.ts +74 -0
  454. package/src/primitives/erosion.ts +183 -0
  455. package/src/primitives/font-5x7.ts +117 -0
  456. package/src/primitives/heightfield.ts +101 -0
  457. package/src/primitives/index.ts +56 -0
  458. package/src/primitives/lathe-facets.ts +45 -0
  459. package/src/primitives/lathe.ts +174 -0
  460. package/src/primitives/lattice.ts +131 -0
  461. package/src/primitives/loft.ts +174 -0
  462. package/src/primitives/mc-table.ts +41 -0
  463. package/src/primitives/member.ts +130 -0
  464. package/src/primitives/noise.ts +157 -0
  465. package/src/primitives/planar.ts +55 -0
  466. package/src/primitives/prism.ts +85 -0
  467. package/src/primitives/sculpt-edit.ts +212 -0
  468. package/src/primitives/sculpt-tree.ts +226 -0
  469. package/src/primitives/sculpt.ts +80 -0
  470. package/src/primitives/sdf-grid.ts +131 -0
  471. package/src/primitives/sdf-ops.ts +145 -0
  472. package/src/primitives/sdf.ts +83 -0
  473. package/src/primitives/strip-uv.ts +26 -0
  474. package/src/primitives/surface-nets.ts +161 -0
  475. package/src/primitives/sweep.ts +162 -0
  476. package/src/primitives/terrain-color.ts +85 -0
  477. package/src/primitives/terrain-grid.ts +137 -0
  478. package/src/primitives/text-layout.ts +85 -0
  479. package/src/primitives/text.ts +197 -0
  480. package/src/primitives/tiled.ts +93 -0
  481. package/src/primitives/transport.ts +85 -0
  482. package/src/primitives/triangulate.ts +42 -0
  483. package/src/primitives/value-noise.ts +62 -0
  484. package/src/qa/checks.ts +110 -0
  485. package/src/qa/clash.ts +177 -0
  486. package/src/qa/floating.ts +63 -0
  487. package/src/qa/gates.ts +126 -0
  488. package/src/qa/geometry.ts +187 -0
  489. package/src/qa/index.ts +28 -0
  490. package/src/qa/inside.ts +114 -0
  491. package/src/qa/inspect.ts +107 -0
  492. package/src/qa/lods.ts +97 -0
  493. package/src/qa/measures.ts +67 -0
  494. package/src/qa/occlusion.ts +166 -0
  495. package/src/qa/sanity.ts +137 -0
  496. package/src/qa/types.ts +17 -0
  497. package/src/render/beauty.ts +115 -0
  498. package/src/render/camera.ts +142 -0
  499. package/src/render/depth-map.ts +191 -0
  500. package/src/render/font.ts +76 -0
  501. package/src/render/ground.ts +87 -0
  502. package/src/render/index.ts +34 -0
  503. package/src/render/lighting.ts +95 -0
  504. package/src/render/lod-strip.ts +34 -0
  505. package/src/render/metrics.ts +147 -0
  506. package/src/render/options.ts +81 -0
  507. package/src/render/photo-match.ts +28 -0
  508. package/src/render/raster.ts +143 -0
  509. package/src/render/resolve.ts +109 -0
  510. package/src/render/review-prompt.ts +59 -0
  511. package/src/render/review-warnings.ts +92 -0
  512. package/src/render/review.ts +103 -0
  513. package/src/render/scene.ts +119 -0
  514. package/src/render/shade.ts +199 -0
  515. package/src/render/sheet.ts +88 -0
  516. package/src/render/texture.ts +97 -0
  517. package/src/render/thumbnail.ts +75 -0
  518. package/src/render/vis-buffer.ts +119 -0
  519. package/src/testing/assert.ts +115 -0
  520. package/src/testing/compiled.ts +31 -0
  521. package/src/testing/hash.ts +14 -0
  522. package/src/testing/index.ts +14 -0
  523. package/src/testing/snapshot.ts +37 -0
  524. package/src/types/gltf-validator.d.ts +13 -0
@@ -0,0 +1,85 @@
1
+ // Running-bond layout shared by bricks, stone, planks, slates and plates: horizontal courses
2
+ // (rows along V) split into tiles by vertical joints, staggered per course and periodic along U.
3
+ // Per-course column tables are filled once per course, so the per-pixel loop only reads arrays.
4
+
5
+ import { createRng } from '../../core/index.ts';
6
+ import type { Canvas } from './canvas.ts';
7
+
8
+ export interface CourseSpec {
9
+ /** Course height, metres (along V). */
10
+ readonly course: number;
11
+ /** Mean tile length, metres (along U). */
12
+ readonly length: number;
13
+ /** 0 = regular; 0.5 = sizes vary ±25 %. */
14
+ readonly irregular: number;
15
+ /** Fraction of a tile each odd course shifts; random offsets when irregular. */
16
+ readonly stagger: number;
17
+ readonly seed: number;
18
+ }
19
+
20
+ export class Courses {
21
+ /** Tile index within the current course, per column. */
22
+ readonly id: Int32Array;
23
+ /** Distance to the nearest vertical joint, metres, per column. */
24
+ readonly dx: Float32Array;
25
+ /** Distance from the tile's left joint, metres, per column. */
26
+ readonly ux: Float32Array;
27
+ /** The current course: index and its bottom / top in metres. */
28
+ index = -1;
29
+ y0 = 0;
30
+ y1 = 0;
31
+ private readonly bounds: number[] = [0];
32
+ private readonly c: Canvas;
33
+ private readonly spec: CourseSpec;
34
+
35
+ constructor(c: Canvas, spec: CourseSpec) {
36
+ this.c = c;
37
+ this.spec = spec;
38
+ this.id = new Int32Array(c.w);
39
+ this.dx = new Float32Array(c.w);
40
+ this.ux = new Float32Array(c.w);
41
+ const rng = createRng(spec.seed).fork('rows');
42
+ let y = 0;
43
+ while (y < c.metresV) {
44
+ y += spec.course * (1 + spec.irregular * (rng.next() - 0.5));
45
+ this.bounds.push(y);
46
+ }
47
+ }
48
+
49
+ /** Point the tables at the course containing metres `ym`; returns the course index. */
50
+ at(ym: number): number {
51
+ if (this.index >= 0 && ym >= this.y0 && ym < this.y1) return this.index;
52
+ let k = 0;
53
+ while (k < this.bounds.length - 2 && ym >= (this.bounds[k + 1] ?? 0)) k++;
54
+ this.index = k;
55
+ this.y0 = this.bounds[k] ?? 0;
56
+ this.y1 = this.bounds[k + 1] ?? this.c.metresV;
57
+ this.fill(k);
58
+ return k;
59
+ }
60
+
61
+ private fill(k: number): void {
62
+ const { w, metresU } = this.c;
63
+ const s = this.spec;
64
+ const rng = createRng(s.seed).fork(`course${k}`);
65
+ const n = Math.max(1, Math.round(metresU / s.length));
66
+ const lens: number[] = [];
67
+ for (let i = 0; i < n; i++) lens.push(1 + s.irregular * (rng.next() - 0.5));
68
+ const total = lens.reduce((a, b) => a + b, 0);
69
+ const joints = [0];
70
+ for (const l of lens) joints.push((joints[joints.length - 1] ?? 0) + (l / total) * metresU);
71
+ const shift = s.irregular > 0 ? rng.next() * metresU : (k % 2) * s.stagger * (metresU / n);
72
+ const px = metresU / w;
73
+ for (let x = 0; x < w; x++) {
74
+ let u = ((x + 0.5) * px - shift) % metresU;
75
+ if (u < 0) u += metresU;
76
+ let t = 0;
77
+ while (t < n - 1 && u >= (joints[t + 1] ?? metresU)) t++;
78
+ const a = joints[t] ?? 0;
79
+ const b = joints[t + 1] ?? metresU;
80
+ this.id[x] = t;
81
+ this.ux[x] = u - a;
82
+ this.dx[x] = Math.min(u - a, b - u);
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,100 @@
1
+ // Minimal PNG decoder for stamping supplied images into cells: 8-bit grey, grey+alpha, RGB, RGBA
2
+ // and palette (with tRNS), non-interlaced. Zero dependencies beyond node:zlib. Anything else
3
+ // throws T3D_RECIPE_INVALID with the conversion to make.
4
+
5
+ import { inflateSync } from 'node:zlib';
6
+ import { Ts23dError } from '../../core/index.ts';
7
+
8
+ export interface Rgba {
9
+ readonly width: number;
10
+ readonly height: number;
11
+ readonly data: Uint8Array;
12
+ }
13
+
14
+ const SIG = [137, 80, 78, 71, 13, 10, 26, 10];
15
+ const CHANNELS: Readonly<Record<number, number>> = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
16
+
17
+ function bad(cause: string): never {
18
+ throw new Ts23dError('T3D_RECIPE_INVALID', {
19
+ cause: `cell image: ${cause}`,
20
+ fix: 'pass PNG bytes, 8 bits per channel, non-interlaced (e.g. re-save as RGBA8 PNG)',
21
+ });
22
+ }
23
+
24
+ function paeth(a: number, b: number, c: number): number {
25
+ const p = a + b - c;
26
+ const pa = Math.abs(p - a);
27
+ const pb = Math.abs(p - b);
28
+ const pc = Math.abs(p - c);
29
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
30
+ }
31
+
32
+ export function decodePng(bytes: Uint8Array): Rgba {
33
+ if (bytes.length < 33 || SIG.some((s, i) => bytes[i] !== s)) bad('not a PNG (bad signature)');
34
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
35
+ let width = 0;
36
+ let height = 0;
37
+ let type = -1;
38
+ let palette: Uint8Array = new Uint8Array();
39
+ let trns: Uint8Array = new Uint8Array();
40
+ const idat: Uint8Array[] = [];
41
+ for (let o = 8; o + 8 <= bytes.length; ) {
42
+ const len = dv.getUint32(o);
43
+ const name = String.fromCharCode(...bytes.subarray(o + 4, o + 8));
44
+ const body = bytes.subarray(o + 8, o + 8 + len);
45
+ if (name === 'IHDR') {
46
+ width = dv.getUint32(o + 8);
47
+ height = dv.getUint32(o + 12);
48
+ type = body[9] ?? -1;
49
+ if (body[8] !== 8) bad(`bit depth ${body[8]} (only 8 is supported)`);
50
+ if (body[12] !== 0) bad('interlaced');
51
+ } else if (name === 'PLTE') palette = body;
52
+ else if (name === 'tRNS') trns = body;
53
+ else if (name === 'IDAT') idat.push(body);
54
+ else if (name === 'IEND') break;
55
+ o += 12 + len;
56
+ }
57
+ const ch = CHANNELS[type];
58
+ if (!ch || width === 0 || height === 0) bad(`colour type ${type} or empty image`);
59
+ const raw = new Uint8Array(inflateSync(Buffer.concat(idat)));
60
+ const stride = width * ch;
61
+ if (raw.length < height * (stride + 1)) bad('truncated image data');
62
+ const px = new Uint8Array(height * stride);
63
+ for (let y = 0; y < height; y++) {
64
+ const f = raw[y * (stride + 1)] ?? 0;
65
+ const src = y * (stride + 1) + 1;
66
+ const row = y * stride;
67
+ for (let x = 0; x < stride; x++) {
68
+ const a = x >= ch ? (px[row + x - ch] ?? 0) : 0;
69
+ const b = y > 0 ? (px[row - stride + x] ?? 0) : 0;
70
+ const c = x >= ch && y > 0 ? (px[row - stride + x - ch] ?? 0) : 0;
71
+ const v = raw[src + x] ?? 0;
72
+ const pred =
73
+ f === 1 ? a : f === 2 ? b : f === 3 ? (a + b) >> 1 : f === 4 ? paeth(a, b, c) : 0;
74
+ px[row + x] = (v + pred) & 255;
75
+ }
76
+ }
77
+ const data = new Uint8Array(width * height * 4);
78
+ for (let i = 0; i < width * height; i++) {
79
+ const s = i * ch;
80
+ const g = px[s] ?? 0;
81
+ const o = i * 4;
82
+ if (type === 3) {
83
+ data[o] = palette[g * 3] ?? 0;
84
+ data[o + 1] = palette[g * 3 + 1] ?? 0;
85
+ data[o + 2] = palette[g * 3 + 2] ?? 0;
86
+ data[o + 3] = trns[g] ?? 255;
87
+ } else if (ch >= 3) {
88
+ data[o] = g;
89
+ data[o + 1] = px[s + 1] ?? 0;
90
+ data[o + 2] = px[s + 2] ?? 0;
91
+ data[o + 3] = ch === 4 ? (px[s + 3] ?? 255) : 255;
92
+ } else {
93
+ data[o] = g;
94
+ data[o + 1] = g;
95
+ data[o + 2] = g;
96
+ data[o + 3] = ch === 2 ? (px[s + 1] ?? 255) : 255;
97
+ }
98
+ }
99
+ return { width, height, data };
100
+ }
@@ -0,0 +1,61 @@
1
+ // The house sheet: every strip and cell, tuned so albedo stays near-greyscale and bright (vertex
2
+ // colour carries hue) and each strip reads at its typical face height (`scaleMetres`).
3
+
4
+ import type { SheetRecipe, StripRecipe } from './types.ts';
5
+
6
+ const leaf: StripRecipe = { kind: 'flat', scaleMetres: 1, roughness: 0.6 };
7
+
8
+ export const DEFAULT_SHEET: SheetRecipe = {
9
+ seed: 1,
10
+ size: 1024,
11
+ strips: {
12
+ plain: { kind: 'noise', scaleMetres: 1, roughness: 0.6 },
13
+ brick: { kind: 'bond', scaleMetres: 0.45, roughness: 0.85, params: { grime: 0.2 } },
14
+ stone: {
15
+ kind: 'bond',
16
+ scaleMetres: 0.9,
17
+ roughness: 0.9,
18
+ params: {
19
+ course: 0.22,
20
+ length: 0.4,
21
+ mortar: 0.016,
22
+ depth: 0.012,
23
+ bevel: 0.025,
24
+ irregular: 0.5,
25
+ relief: 0.004,
26
+ variation: 0.3,
27
+ grime: 0.2,
28
+ },
29
+ },
30
+ slate: { kind: 'slate', scaleMetres: 0.66, roughness: 0.7 },
31
+ iron: { kind: 'plates', scaleMetres: 0.5, roughness: 0.55, metalness: 0 },
32
+ wood: { kind: 'planks', scaleMetres: 0.6, roughness: 0.75 },
33
+ glass: { kind: 'glass', scaleMetres: 1, roughness: 0.05 },
34
+ concrete: { kind: 'concrete', scaleMetres: 1, roughness: 0.9, params: { grime: 0.3 } },
35
+ },
36
+ cells: {
37
+ window_sash: { kind: 'noise', scaleMetres: 1, roughness: 0.5 },
38
+ door_panel: { kind: 'noise', scaleMetres: 1, roughness: 0.6 },
39
+ clock: { kind: 'noise', scaleMetres: 1, roughness: 0.3 },
40
+ smokebox: {
41
+ kind: 'plates',
42
+ scaleMetres: 1,
43
+ roughness: 0.6,
44
+ metalness: 0.2,
45
+ params: { rust: 0.3 },
46
+ },
47
+ sign: { kind: 'noise', scaleMetres: 1, roughness: 0.5 },
48
+ porthole: { kind: 'plates', scaleMetres: 1, roughness: 0.5 },
49
+ foliage_broadleaf: leaf,
50
+ foliage_maple: leaf,
51
+ foliage_maple_autumn: leaf,
52
+ foliage_birch: leaf,
53
+ foliage_acacia: leaf,
54
+ foliage_palm: leaf,
55
+ foliage_spruce: leaf,
56
+ foliage_spruce_snow: leaf,
57
+ foliage_willow: leaf,
58
+ foliage_reeds: leaf,
59
+ foliage_needles: leaf,
60
+ },
61
+ };
@@ -0,0 +1,126 @@
1
+ // Broadleaf foliage cells: a twig grows from the bottom-centre anchor toward +V, side twigs
2
+ // alternate off it, leaves sit at the nodes. A darker back layer of leaves fills the crown first
3
+ // for depth. Variants: beech/oak cluster (ovate, fine teeth), palmate maple, autumn maple
4
+ // (red/orange/yellow per leaf), birch (small serrated leaves on thin drooping twigs).
5
+
6
+ import { createRng, type Rng } from '../../core/index.ts';
7
+ import type { Canvas } from './canvas.ts';
8
+ import { AUTUMN, at, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
9
+ import { Foliage, type LeafShape, type Rgb } from './foliage-raster.ts';
10
+
11
+ interface ClusterSpec {
12
+ readonly shape: LeafShape;
13
+ readonly len: number;
14
+ readonly wid: number;
15
+ readonly serrate: number;
16
+ readonly twigs: number;
17
+ readonly perTwig: number;
18
+ readonly back: number;
19
+ /** Side-twig droop: 0 = straight out, 1 = bends back down. */
20
+ readonly droop: number;
21
+ readonly colour: (rng: Rng) => Rgb;
22
+ }
23
+
24
+ function cluster(c: Canvas, seed: number, s: ClusterSpec): void {
25
+ const rng = createRng(seed);
26
+ const f = new Foliage(c);
27
+ const leafAt = (x: number, y: number, a: number, k: number, lift: number): void => {
28
+ const len = s.len * rng.range(0.8, 1.15) * k;
29
+ f.leaf(s.shape, x, y, a, len, s.wid * (len / s.len), s.colour(rng), s.serrate, lift);
30
+ };
31
+ // Back layer: darker, unattached leaves filling the crown silhouette.
32
+ for (let i = 0; i < s.back; i++) {
33
+ const x = 0.5 + rng.range(-0.33, 0.33);
34
+ const y = rng.range(0.3, 0.85);
35
+ const col = dim(s.colour(rng), 0.5);
36
+ const len = s.len * rng.range(0.8, 1.1);
37
+ f.leaf(s.shape, x, y, rng.range(-1.4, 1.4), len, s.wid * (len / s.len), col, s.serrate);
38
+ }
39
+ const main = curve(
40
+ 0.5,
41
+ 0.02,
42
+ 0.5 + rng.range(-0.1, 0.1),
43
+ 0.45,
44
+ 0.5 + rng.range(-0.08, 0.08),
45
+ 0.8,
46
+ );
47
+ drawStem(f, main, 0.016, 0.004, LEAF.twig);
48
+ for (let k = 0; k < s.twigs; k++) {
49
+ const t = 0.22 + (k / Math.max(1, s.twigs - 1)) * 0.6;
50
+ const side = k % 2 === 0 ? 1 : -1;
51
+ const i = at(main, t);
52
+ const bx = main.x[i] ?? 0.5;
53
+ const by = main.y[i] ?? 0.5;
54
+ const a = (main.a[i] ?? 0) + side * rng.range(0.7, 1.1);
55
+ const len = 0.3 * (1 - t * 0.45);
56
+ const ex = bx + Math.sin(a) * len;
57
+ const ey = by + Math.cos(a) * len - s.droop * len * 0.4;
58
+ const twig = curve(bx, by, (bx + ex) / 2, (by + ey) / 2 + len * 0.15, ex, ey, 8);
59
+ drawStem(f, twig, 0.007, 0.002, LEAF.twig);
60
+ for (let q = 1; q <= s.perTwig; q++) {
61
+ const j = at(twig, q / s.perTwig);
62
+ const alt = q % 2 === 0 ? 1 : -1;
63
+ const ta = (twig.a[j] ?? 0) + (q === s.perTwig ? 0 : alt * rng.range(0.5, 0.9));
64
+ leafAt(twig.x[j] ?? 0, twig.y[j] ?? 0, ta, 1 - 0.15 * (1 - q / s.perTwig), 0.002);
65
+ }
66
+ }
67
+ const tip = main.x.length - 1;
68
+ leafAt(main.x[tip] ?? 0.5, main.y[tip] ?? 0.8, main.a[tip] ?? 0, 1.1, 0.003);
69
+ f.finish(0.5, 0.35);
70
+ }
71
+
72
+ export function paintBroadleaf(c: Canvas, seed: number): void {
73
+ cluster(c, seed, {
74
+ shape: 'ovate',
75
+ len: 0.15,
76
+ wid: 0.085,
77
+ serrate: 0.03,
78
+ twigs: 6,
79
+ perTwig: 3,
80
+ back: 16,
81
+ droop: 0.1,
82
+ colour: (rng) => vary(rng, LEAF.beech, 0.18),
83
+ });
84
+ }
85
+
86
+ export function paintMaple(c: Canvas, seed: number): void {
87
+ cluster(c, seed, {
88
+ shape: 'palmate',
89
+ len: 0.24,
90
+ wid: 0.24,
91
+ serrate: 0.04,
92
+ twigs: 4,
93
+ perTwig: 2,
94
+ back: 7,
95
+ droop: 0,
96
+ colour: (rng) => vary(rng, LEAF.maple, 0.15),
97
+ });
98
+ }
99
+
100
+ export function paintMapleAutumn(c: Canvas, seed: number): void {
101
+ cluster(c, seed, {
102
+ shape: 'palmate',
103
+ len: 0.24,
104
+ wid: 0.24,
105
+ serrate: 0.04,
106
+ twigs: 4,
107
+ perTwig: 2,
108
+ back: 7,
109
+ droop: 0,
110
+ colour: (rng) => vary(rng, rng.pick(AUTUMN), 0.12),
111
+ });
112
+ }
113
+
114
+ export function paintBirch(c: Canvas, seed: number): void {
115
+ cluster(c, seed, {
116
+ shape: 'ovate',
117
+ len: 0.085,
118
+ wid: 0.06,
119
+ serrate: 0.12,
120
+ twigs: 8,
121
+ perTwig: 5,
122
+ back: 22,
123
+ droop: 0.6,
124
+ colour: (rng) => vary(rng, LEAF.birch, 0.2),
125
+ });
126
+ }
@@ -0,0 +1,93 @@
1
+ // Shared growth helpers for foliage cells: quadratic stem curves sampled into points, per-leaf
2
+ // colour jitter and the leaf palettes (linear RGB). Allocation is per leaf/stem, never per pixel.
3
+
4
+ import type { Rng } from '../../core/index.ts';
5
+ import type { Foliage, Rgb } from './foliage-raster.ts';
6
+
7
+ /** A quadratic Bézier from p0 via p1 to p2, metres in the cell (anchor usually 0.5, 0.02). */
8
+ export interface Curve {
9
+ readonly x: readonly number[];
10
+ readonly y: readonly number[];
11
+ /** Tangent angle per sample (0 = +V, + toward +U), the convention `Foliage.leaf` takes. */
12
+ readonly a: readonly number[];
13
+ }
14
+
15
+ export function curve(
16
+ x0: number,
17
+ y0: number,
18
+ x1: number,
19
+ y1: number,
20
+ x2: number,
21
+ y2: number,
22
+ n = 16,
23
+ ): Curve {
24
+ const x: number[] = [];
25
+ const y: number[] = [];
26
+ const a: number[] = [];
27
+ for (let i = 0; i <= n; i++) {
28
+ const t = i / n;
29
+ const u = 1 - t;
30
+ x.push(u * u * x0 + 2 * u * t * x1 + t * t * x2);
31
+ y.push(u * u * y0 + 2 * u * t * y1 + t * t * y2);
32
+ const dx = 2 * u * (x1 - x0) + 2 * t * (x2 - x1);
33
+ const dy = 2 * u * (y1 - y0) + 2 * t * (y2 - y1);
34
+ a.push(Math.atan2(dx, dy));
35
+ }
36
+ return { x, y, a };
37
+ }
38
+
39
+ /** Sample index nearest parameter t ∈ [0, 1]. */
40
+ export const at = (c: Curve, t: number): number => Math.round(t * (c.x.length - 1));
41
+
42
+ /** Draw a curve as a tapered stem. */
43
+ export function drawStem(f: Foliage, c: Curve, w0: number, w1: number, col: Rgb): void {
44
+ const n = c.x.length - 1;
45
+ for (let i = 0; i < n; i++) {
46
+ const t0 = i / n;
47
+ const t1 = (i + 1) / n;
48
+ f.stem(
49
+ c.x[i] ?? 0,
50
+ c.y[i] ?? 0,
51
+ c.x[i + 1] ?? 0,
52
+ c.y[i + 1] ?? 0,
53
+ w0 + (w1 - w0) * t0,
54
+ w0 + (w1 - w0) * t1,
55
+ col,
56
+ );
57
+ }
58
+ }
59
+
60
+ /** Jitter a colour per leaf: brightness ±amt and a small hue drift between R and G. */
61
+ export function vary(rng: Rng, col: Rgb, amt: number): Rgb {
62
+ const k = 1 + amt * (rng.next() * 2 - 1);
63
+ const drift = 1 + amt * 0.6 * (rng.next() * 2 - 1);
64
+ return [col[0] * k * drift, col[1] * k, (col[2] * k) / drift];
65
+ }
66
+
67
+ export const dim = (col: Rgb, k: number): Rgb => [col[0] * k, col[1] * k, col[2] * k];
68
+
69
+ /** Leaf palettes, linear RGB (multiply by a near-white vertex colour on the card). */
70
+ export const LEAF = {
71
+ beech: [0.09, 0.2, 0.035] as Rgb,
72
+ maple: [0.1, 0.24, 0.04] as Rgb,
73
+ birch: [0.16, 0.3, 0.05] as Rgb,
74
+ acacia: [0.11, 0.2, 0.06] as Rgb,
75
+ palm: [0.12, 0.22, 0.05] as Rgb,
76
+ dry: [0.3, 0.2, 0.08] as Rgb,
77
+ spruce: [0.035, 0.09, 0.04] as Rgb,
78
+ pine: [0.06, 0.13, 0.04] as Rgb,
79
+ willow: [0.2, 0.3, 0.08] as Rgb,
80
+ reed: [0.2, 0.28, 0.08] as Rgb,
81
+ snow: [0.85, 0.88, 0.92] as Rgb,
82
+ bark: [0.08, 0.055, 0.035] as Rgb,
83
+ twig: [0.12, 0.09, 0.05] as Rgb,
84
+ } as const;
85
+
86
+ /** Autumn maple: each leaf picks one of these, then gets jittered. */
87
+ export const AUTUMN: readonly Rgb[] = [
88
+ [0.6, 0.08, 0.02],
89
+ [0.75, 0.25, 0.02],
90
+ [0.8, 0.45, 0.03],
91
+ [0.45, 0.04, 0.03],
92
+ [0.55, 0.35, 0.05],
93
+ ];
@@ -0,0 +1,135 @@
1
+ // Compound and strand foliage cells, all growing from the bottom-centre anchor toward +V:
2
+ // pinnate acacia (rachis, paired pinnae, tiny leaflets), palm frond (arched rachis, lance
3
+ // leaflets both sides, some dry brown tips), willow curtain (long fine strands of narrow leaves;
4
+ // hang it by mapping the card with V down). A dimmed back layer goes down first.
5
+
6
+ import { createRng } from '../../core/index.ts';
7
+ import type { Canvas } from './canvas.ts';
8
+ import { at, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
9
+ import { Foliage } from './foliage-raster.ts';
10
+
11
+ export function paintAcacia(c: Canvas, seed: number): void {
12
+ const rng = createRng(seed);
13
+ const f = new Foliage(c);
14
+ const rachis = curve(0.5, 0.02, 0.48 + rng.range(-0.05, 0.05), 0.5, 0.5, 0.95);
15
+ for (let layer = 0; layer < 2; layer++) {
16
+ const k = layer === 0 ? 0.5 : 1;
17
+ if (layer === 1) drawStem(f, rachis, 0.01, 0.003, LEAF.twig);
18
+ const pairs = layer === 0 ? 6 : 8;
19
+ for (let p = 0; p < pairs; p++) {
20
+ const t = 0.15 + (p / pairs) * 0.8 + (layer === 0 ? 0.05 : 0);
21
+ const i = at(rachis, t);
22
+ for (const side of [-1, 1]) {
23
+ const a = (rachis.a[i] ?? 0) + side * rng.range(0.9, 1.2);
24
+ const len = 0.22 * (1 - t * 0.5);
25
+ const bx = rachis.x[i] ?? 0.5;
26
+ const by = rachis.y[i] ?? 0.5;
27
+ const pinna = curve(
28
+ bx,
29
+ by,
30
+ bx + Math.sin(a) * len * 0.5,
31
+ by + Math.cos(a) * len * 0.5 + 0.02,
32
+ bx + Math.sin(a) * len,
33
+ by + Math.cos(a) * len,
34
+ 6,
35
+ );
36
+ drawStem(f, pinna, 0.003, 0.0015, dim(LEAF.twig, k));
37
+ for (let q = 1; q <= 12; q++) {
38
+ const j = at(pinna, q / 12.5);
39
+ const col = dim(vary(rng, LEAF.acacia, 0.15), k);
40
+ for (const s2 of [-1, 1])
41
+ f.leaf(
42
+ 'ovate',
43
+ pinna.x[j] ?? 0,
44
+ pinna.y[j] ?? 0,
45
+ (pinna.a[j] ?? 0) + s2 * 1.1,
46
+ 0.03,
47
+ 0.011,
48
+ col,
49
+ 0,
50
+ layer * 0.002,
51
+ );
52
+ }
53
+ }
54
+ }
55
+ }
56
+ f.finish(0.5, 0.25);
57
+ }
58
+
59
+ export function paintPalm(c: Canvas, seed: number): void {
60
+ const rng = createRng(seed);
61
+ const f = new Foliage(c);
62
+ const rachis = curve(0.5, 0.02, 0.3, 0.7, 0.78, 0.96, 24);
63
+ for (let layer = 0; layer < 2; layer++) {
64
+ const k = layer === 0 ? 0.55 : 1;
65
+ const n = 26;
66
+ for (let q = 0; q < n; q++) {
67
+ const t = 0.1 + (q / n) * 0.9 + (layer === 0 ? 0.015 : 0);
68
+ const i = at(rachis, Math.min(1, t));
69
+ const bx = rachis.x[i] ?? 0.5;
70
+ const by = rachis.y[i] ?? 0.5;
71
+ for (const side of [-1, 1]) {
72
+ const a = (rachis.a[i] ?? 0) + side * rng.range(0.75, 1.0);
73
+ const len = 0.3 * Math.sin(Math.PI * (0.2 + 0.75 * t)) * rng.range(0.85, 1.05);
74
+ const wid = 0.03;
75
+ f.leaf('lance', bx, by, a, len, wid, dim(vary(rng, LEAF.palm, 0.15), k), 0, layer * 0.002);
76
+ if (rng.next() < 0.3) {
77
+ const tx = bx + Math.sin(a) * len * 0.72;
78
+ const ty = by + Math.cos(a) * len * 0.72;
79
+ f.leaf(
80
+ 'blade',
81
+ tx,
82
+ ty,
83
+ a,
84
+ len * 0.28,
85
+ wid * 0.55,
86
+ dim(vary(rng, LEAF.dry, 0.2), k),
87
+ 0,
88
+ layer * 0.002 + 0.0002,
89
+ );
90
+ }
91
+ }
92
+ }
93
+ if (layer === 0) drawStem(f, rachis, 0.018, 0.004, dim(LEAF.palm, 0.8));
94
+ }
95
+ f.finish(0.5, 0.2);
96
+ }
97
+
98
+ export function paintWillow(c: Canvas, seed: number): void {
99
+ const rng = createRng(seed);
100
+ const f = new Foliage(c);
101
+ const strands = 9;
102
+ for (let s = 0; s < strands; s++) {
103
+ const back = s < 3;
104
+ const k = back ? 0.55 : 1;
105
+ const ex = 0.5 + rng.range(-0.4, 0.4);
106
+ const strand = curve(
107
+ 0.5 + rng.range(-0.03, 0.03),
108
+ 0.02,
109
+ (0.5 + ex) / 2 + rng.range(-0.1, 0.1),
110
+ 0.5,
111
+ ex,
112
+ rng.range(0.85, 0.97),
113
+ 24,
114
+ );
115
+ drawStem(f, strand, 0.006, 0.0015, dim(LEAF.twig, k));
116
+ for (let q = 2; q < 30; q++) {
117
+ const j = at(strand, q / 30);
118
+ const side = q % 2 === 0 ? 1 : -1;
119
+ const a = (strand.a[j] ?? 0) + side * rng.range(0.2, 0.45);
120
+ const col = dim(vary(rng, LEAF.willow, 0.18), k);
121
+ f.leaf(
122
+ 'lance',
123
+ strand.x[j] ?? 0,
124
+ strand.y[j] ?? 0,
125
+ a,
126
+ rng.range(0.07, 0.1),
127
+ 0.014,
128
+ col,
129
+ 0,
130
+ back ? 0 : 0.002,
131
+ );
132
+ }
133
+ }
134
+ f.finish(0.5, 0.2);
135
+ }