@developerz.ai/ts23d 0.0.0-stage → 0.2.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 (567) 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 +90 -0
  13. package/CLAUDE.md +85 -0
  14. package/LICENSE +21 -0
  15. package/README.md +385 -2
  16. package/dist/chunk-6b05mnmx.js +19 -0
  17. package/dist/chunk-6b05mnmx.js.map +11 -0
  18. package/dist/chunk-7rtfjh9f.js +45 -0
  19. package/dist/chunk-7rtfjh9f.js.map +10 -0
  20. package/dist/chunk-8j4c28jt.js +4085 -0
  21. package/dist/chunk-8j4c28jt.js.map +46 -0
  22. package/dist/chunk-d1fv6hrm.js +2532 -0
  23. package/dist/chunk-d1fv6hrm.js.map +43 -0
  24. package/dist/chunk-dbq4qf52.js +902 -0
  25. package/dist/chunk-dbq4qf52.js.map +25 -0
  26. package/dist/chunk-drna2rny.js +836 -0
  27. package/dist/chunk-drna2rny.js.map +17 -0
  28. package/dist/chunk-dxvypr7y.js +89 -0
  29. package/dist/chunk-dxvypr7y.js.map +11 -0
  30. package/dist/chunk-f6yme003.js +1589 -0
  31. package/dist/chunk-f6yme003.js.map +30 -0
  32. package/dist/chunk-hg7pf34j.js +465 -0
  33. package/dist/chunk-hg7pf34j.js.map +19 -0
  34. package/dist/chunk-jq4e17tj.js +1031 -0
  35. package/dist/chunk-jq4e17tj.js.map +21 -0
  36. package/dist/chunk-y63c6zcv.js +2668 -0
  37. package/dist/chunk-y63c6zcv.js.map +53 -0
  38. package/dist/cli/args.d.ts +30 -0
  39. package/dist/cli/bin.d.ts +2 -0
  40. package/dist/cli/bin.js +348 -0
  41. package/dist/cli/bin.js.map +15 -0
  42. package/dist/cli/load.d.ts +2 -0
  43. package/dist/cli/manifest.d.ts +53 -0
  44. package/dist/cli/render-cmds.d.ts +12 -0
  45. package/dist/cli/run.d.ts +1 -0
  46. package/dist/cli/texture-cache.d.ts +4 -0
  47. package/dist/core/asset.d.ts +10 -0
  48. package/dist/core/definition.d.ts +155 -0
  49. package/dist/core/errors.d.ts +17 -0
  50. package/dist/core/family.d.ts +23 -0
  51. package/dist/core/index.d.ts +9 -0
  52. package/dist/core/index.js +69 -0
  53. package/dist/core/index.js.map +9 -0
  54. package/dist/core/mesh.d.ts +35 -0
  55. package/dist/core/part.d.ts +114 -0
  56. package/dist/core/png.d.ts +1 -0
  57. package/dist/core/rng.d.ts +12 -0
  58. package/dist/core/transform.d.ts +20 -0
  59. package/dist/core/vec.d.ts +11 -0
  60. package/dist/core/world.d.ts +16 -0
  61. package/dist/export/bin-writer.d.ts +16 -0
  62. package/dist/export/container.d.ts +3 -0
  63. package/dist/export/credits.d.ts +4 -0
  64. package/dist/export/glb-options.d.ts +48 -0
  65. package/dist/export/glb.d.ts +8 -0
  66. package/dist/export/hierarchy.d.ts +23 -0
  67. package/dist/export/index.d.ts +5 -0
  68. package/dist/export/index.js +40 -0
  69. package/dist/export/index.js.map +9 -0
  70. package/dist/export/lights.d.ts +8 -0
  71. package/dist/export/lod-coverage.d.ts +12 -0
  72. package/dist/export/materials.d.ts +23 -0
  73. package/dist/export/merge-pivots.d.ts +6 -0
  74. package/dist/export/metadata.d.ts +21 -0
  75. package/dist/export/motion.d.ts +9 -0
  76. package/dist/export/optimize-parts.d.ts +26 -0
  77. package/dist/export/primitive.d.ts +3 -0
  78. package/dist/export/tangents.d.ts +2 -0
  79. package/dist/export/textures.d.ts +22 -0
  80. package/dist/export/version.d.ts +2 -0
  81. package/dist/grammar/index.d.ts +2 -0
  82. package/dist/grammar/index.js +16 -0
  83. package/dist/grammar/index.js.map +9 -0
  84. package/dist/grammar/split.d.ts +21 -0
  85. package/dist/grammar/whyte.d.ts +5 -0
  86. package/dist/index.d.ts +11 -0
  87. package/dist/index.js +457 -0
  88. package/dist/index.js.map +9 -0
  89. package/dist/kinds/building.d.ts +44 -0
  90. package/dist/kinds/faces.d.ts +27 -0
  91. package/dist/kinds/head-armour.d.ts +5 -0
  92. package/dist/kinds/head-beard.d.ts +6 -0
  93. package/dist/kinds/head-bust.d.ts +5 -0
  94. package/dist/kinds/head-extras.d.ts +15 -0
  95. package/dist/kinds/head-face.d.ts +8 -0
  96. package/dist/kinds/head-garb.d.ts +4 -0
  97. package/dist/kinds/head-geom.d.ts +7 -0
  98. package/dist/kinds/head-hair.d.ts +4 -0
  99. package/dist/kinds/head-layout.d.ts +29 -0
  100. package/dist/kinds/head-mesh.d.ts +31 -0
  101. package/dist/kinds/head-mouth.d.ts +4 -0
  102. package/dist/kinds/head-params.d.ts +116 -0
  103. package/dist/kinds/head-presets.d.ts +19 -0
  104. package/dist/kinds/head-rim.d.ts +10 -0
  105. package/dist/kinds/head-tone.d.ts +10 -0
  106. package/dist/kinds/head-torso.d.ts +35 -0
  107. package/dist/kinds/head.d.ts +39 -0
  108. package/dist/kinds/hull-lines.d.ts +65 -0
  109. package/dist/kinds/hull-skin.d.ts +6 -0
  110. package/dist/kinds/hull.d.ts +14 -0
  111. package/dist/kinds/index.d.ts +16 -0
  112. package/dist/kinds/index.js +78 -0
  113. package/dist/kinds/index.js.map +9 -0
  114. package/dist/kinds/mass.d.ts +15 -0
  115. package/dist/kinds/openings.d.ts +30 -0
  116. package/dist/kinds/planned.d.ts +6 -0
  117. package/dist/kinds/roof-chimney.d.ts +16 -0
  118. package/dist/kinds/roof-shape.d.ts +64 -0
  119. package/dist/kinds/roof.d.ts +31 -0
  120. package/dist/kinds/vegetation-fronds.d.ts +15 -0
  121. package/dist/kinds/vegetation-impostor.d.ts +9 -0
  122. package/dist/kinds/vegetation-leaves.d.ts +15 -0
  123. package/dist/kinds/vegetation-mesh.d.ts +21 -0
  124. package/dist/kinds/vegetation-params.d.ts +90 -0
  125. package/dist/kinds/vegetation-presets.d.ts +20 -0
  126. package/dist/kinds/vegetation-skeleton.d.ts +20 -0
  127. package/dist/kinds/vegetation.d.ts +42 -0
  128. package/dist/kinds/vehicle.d.ts +42 -0
  129. package/dist/material/cc0.d.ts +4 -0
  130. package/dist/material/define.d.ts +34 -0
  131. package/dist/material/generator.d.ts +4 -0
  132. package/dist/material/index.d.ts +9 -0
  133. package/dist/material/index.js +70 -0
  134. package/dist/material/index.js.map +9 -0
  135. package/dist/material/library.d.ts +7 -0
  136. package/dist/material/material-def.d.ts +30 -0
  137. package/dist/material/paint/bond.d.ts +3 -0
  138. package/dist/material/paint/canvas.d.ts +39 -0
  139. package/dist/material/paint/cell.d.ts +4 -0
  140. package/dist/material/paint/cells-round.d.ts +5 -0
  141. package/dist/material/paint/cells.d.ts +7 -0
  142. package/dist/material/paint/compose.d.ts +5 -0
  143. package/dist/material/paint/concrete.d.ts +3 -0
  144. package/dist/material/paint/courses.d.ts +31 -0
  145. package/dist/material/paint/decode-png.d.ts +6 -0
  146. package/dist/material/paint/default-sheet.d.ts +4 -0
  147. package/dist/material/paint/fabric.d.ts +4 -0
  148. package/dist/material/paint/foliage-broad.d.ts +5 -0
  149. package/dist/material/paint/foliage-common.d.ts +35 -0
  150. package/dist/material/paint/foliage-compound.d.ts +4 -0
  151. package/dist/material/paint/foliage-conifer.d.ts +5 -0
  152. package/dist/material/paint/foliage-raster.d.ts +26 -0
  153. package/dist/material/paint/foliage.d.ts +3 -0
  154. package/dist/material/paint/image-strip.d.ts +4 -0
  155. package/dist/material/paint/kit.d.ts +5 -0
  156. package/dist/material/paint/masks.d.ts +3 -0
  157. package/dist/material/paint/metal.d.ts +3 -0
  158. package/dist/material/paint/noise.d.ts +9 -0
  159. package/dist/material/paint/normal.d.ts +8 -0
  160. package/dist/material/paint/orm.d.ts +8 -0
  161. package/dist/material/paint/planks.d.ts +3 -0
  162. package/dist/material/paint/plates.d.ts +3 -0
  163. package/dist/material/paint/shapes.d.ts +8 -0
  164. package/dist/material/paint/slate.d.ts +3 -0
  165. package/dist/material/paint/stamp.d.ts +3 -0
  166. package/dist/material/paint/strip.d.ts +3 -0
  167. package/dist/material/paint/surface.d.ts +5 -0
  168. package/dist/material/paint/types.d.ts +65 -0
  169. package/dist/material/paint/validate.d.ts +3 -0
  170. package/dist/material/palette.d.ts +14 -0
  171. package/dist/material/shader-godot.d.ts +2 -0
  172. package/dist/material/shader-three.d.ts +2 -0
  173. package/dist/material/shader.d.ts +46 -0
  174. package/dist/material/slots.d.ts +5 -0
  175. package/dist/material/texture-source.d.ts +34 -0
  176. package/dist/material/trim-sheet.d.ts +38 -0
  177. package/dist/neural/generator.d.ts +25 -0
  178. package/dist/neural/images.d.ts +17 -0
  179. package/dist/neural/index.d.ts +2 -0
  180. package/dist/neural/index.js +14 -0
  181. package/dist/neural/index.js.map +9 -0
  182. package/dist/passes/bake-ao.d.ts +17 -0
  183. package/dist/passes/grime.d.ts +15 -0
  184. package/dist/passes/index.d.ts +6 -0
  185. package/dist/passes/index.js +22 -0
  186. package/dist/passes/index.js.map +9 -0
  187. package/dist/passes/livery.d.ts +19 -0
  188. package/dist/passes/optimize-cache.d.ts +15 -0
  189. package/dist/passes/optimize-weld.d.ts +15 -0
  190. package/dist/passes/optimize.d.ts +28 -0
  191. package/dist/passes/ray-grid.d.ts +23 -0
  192. package/dist/passes/weighted-normals.d.ts +8 -0
  193. package/dist/primitives/beam.d.ts +17 -0
  194. package/dist/primitives/bevel-box.d.ts +13 -0
  195. package/dist/primitives/box.d.ts +16 -0
  196. package/dist/primitives/cell.d.ts +20 -0
  197. package/dist/primitives/check.d.ts +12 -0
  198. package/dist/primitives/closed.d.ts +11 -0
  199. package/dist/primitives/erosion.d.ts +22 -0
  200. package/dist/primitives/font-5x7.d.ts +3 -0
  201. package/dist/primitives/heightfield.d.ts +16 -0
  202. package/dist/primitives/index.d.ts +23 -0
  203. package/dist/primitives/index.js +81 -0
  204. package/dist/primitives/index.js.map +9 -0
  205. package/dist/primitives/lathe-facets.d.ts +8 -0
  206. package/dist/primitives/lathe.d.ts +26 -0
  207. package/dist/primitives/lattice.d.ts +30 -0
  208. package/dist/primitives/loft.d.ts +15 -0
  209. package/dist/primitives/mc-table.d.ts +2 -0
  210. package/dist/primitives/member.d.ts +19 -0
  211. package/dist/primitives/noise.d.ts +34 -0
  212. package/dist/primitives/planar.d.ts +10 -0
  213. package/dist/primitives/prism.d.ts +13 -0
  214. package/dist/primitives/sculpt-edit.d.ts +56 -0
  215. package/dist/primitives/sculpt-tree.d.ts +23 -0
  216. package/dist/primitives/sculpt.d.ts +27 -0
  217. package/dist/primitives/sdf-grid.d.ts +17 -0
  218. package/dist/primitives/sdf-ops.d.ts +37 -0
  219. package/dist/primitives/sdf.d.ts +21 -0
  220. package/dist/primitives/strip-uv.d.ts +11 -0
  221. package/dist/primitives/surface-nets.d.ts +7 -0
  222. package/dist/primitives/sweep.d.ts +19 -0
  223. package/dist/primitives/terrain-color.d.ts +30 -0
  224. package/dist/primitives/terrain-grid.d.ts +36 -0
  225. package/dist/primitives/text-layout.d.ts +22 -0
  226. package/dist/primitives/text.d.ts +27 -0
  227. package/dist/primitives/tiled.d.ts +22 -0
  228. package/dist/primitives/transport.d.ts +17 -0
  229. package/dist/primitives/triangulate.d.ts +2 -0
  230. package/dist/primitives/value-noise.d.ts +10 -0
  231. package/dist/qa/checks.d.ts +11 -0
  232. package/dist/qa/clash.d.ts +2 -0
  233. package/dist/qa/floating.d.ts +5 -0
  234. package/dist/qa/gates.d.ts +12 -0
  235. package/dist/qa/geometry.d.ts +21 -0
  236. package/dist/qa/index.d.ts +12 -0
  237. package/dist/qa/index.js +78 -0
  238. package/dist/qa/index.js.map +9 -0
  239. package/dist/qa/inside.d.ts +12 -0
  240. package/dist/qa/inspect.d.ts +18 -0
  241. package/dist/qa/lods.d.ts +12 -0
  242. package/dist/qa/measures.d.ts +4 -0
  243. package/dist/qa/occlusion.d.ts +3 -0
  244. package/dist/qa/sanity.d.ts +7 -0
  245. package/dist/qa/types.d.ts +12 -0
  246. package/dist/render/beauty.d.ts +40 -0
  247. package/dist/render/camera.d.ts +33 -0
  248. package/dist/render/depth-map.d.ts +38 -0
  249. package/dist/render/font.d.ts +1 -0
  250. package/dist/render/ground.d.ts +13 -0
  251. package/dist/render/index.d.ts +10 -0
  252. package/dist/render/index.js +47 -0
  253. package/dist/render/index.js.map +9 -0
  254. package/dist/render/lighting.d.ts +27 -0
  255. package/dist/render/lod-strip.d.ts +5 -0
  256. package/dist/render/metrics.d.ts +37 -0
  257. package/dist/render/options.d.ts +28 -0
  258. package/dist/render/photo-match.d.ts +19 -0
  259. package/dist/render/raster.d.ts +25 -0
  260. package/dist/render/resolve.d.ts +11 -0
  261. package/dist/render/review-prompt.d.ts +2 -0
  262. package/dist/render/review-warnings.d.ts +34 -0
  263. package/dist/render/review.d.ts +24 -0
  264. package/dist/render/scene.d.ts +18 -0
  265. package/dist/render/shade.d.ts +22 -0
  266. package/dist/render/sheet.d.ts +15 -0
  267. package/dist/render/texture.d.ts +15 -0
  268. package/dist/render/thumbnail.d.ts +8 -0
  269. package/dist/render/vis-buffer.d.ts +18 -0
  270. package/dist/testing/assert.d.ts +21 -0
  271. package/dist/testing/compiled.d.ts +15 -0
  272. package/dist/testing/hash.d.ts +1 -0
  273. package/dist/testing/index.d.ts +4 -0
  274. package/dist/testing/index.js +134 -0
  275. package/dist/testing/index.js.map +13 -0
  276. package/dist/testing/snapshot.d.ts +13 -0
  277. package/docs/README.md +56 -0
  278. package/docs/architecture/README.md +68 -0
  279. package/docs/architecture/adding-a-kind.md +44 -0
  280. package/docs/architecture/adding-a-primitive.md +49 -0
  281. package/docs/architecture/errors.md +47 -0
  282. package/docs/architecture/glb-production.md +214 -0
  283. package/docs/architecture/gltf-capabilities.md +117 -0
  284. package/docs/architecture/materials.md +151 -0
  285. package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
  286. package/docs/decisions/002-typescript-not-json.md +22 -0
  287. package/docs/decisions/003-shared-trim-sheet.md +21 -0
  288. package/docs/decisions/004-z-up-metres.md +21 -0
  289. package/docs/decisions/005-zero-runtime-deps.md +21 -0
  290. package/docs/decisions/006-single-package-subpath-exports.md +20 -0
  291. package/docs/decisions/007-cpu-contact-sheets.md +21 -0
  292. package/docs/decisions/008-surface-materials.md +21 -0
  293. package/docs/decisions/009-gltf-front-axis.md +21 -0
  294. package/docs/decisions/README.md +15 -0
  295. package/docs/idea/01-vision.md +117 -0
  296. package/docs/idea/02-dsl.md +343 -0
  297. package/docs/idea/03-ir.md +97 -0
  298. package/docs/idea/04-materials.md +114 -0
  299. package/docs/idea/05-agent-loop.md +141 -0
  300. package/docs/idea/06-qa.md +101 -0
  301. package/docs/idea/07-what-fits.md +45 -0
  302. package/docs/idea/08-neural-adapters.md +78 -0
  303. package/docs/idea/09-kinds.md +105 -0
  304. package/docs/idea/10-sculpting.md +166 -0
  305. package/docs/idea/11-materials-authoring.md +132 -0
  306. package/docs/idea/12-blender-bridge.md +136 -0
  307. package/docs/idea/13-asset-pipeline-research.md +89 -0
  308. package/docs/landscape.md +49 -0
  309. package/docs/plans/roadmap.md +139 -0
  310. package/examples/build-with-bun.ts +41 -0
  311. package/llms.txt +47 -0
  312. package/package.json +100 -4
  313. package/recipes/README.md +38 -0
  314. package/recipes/asteroid/README.md +46 -0
  315. package/recipes/asteroid/asteroid.ts +336 -0
  316. package/recipes/boulders/README.md +28 -0
  317. package/recipes/boulders/boulders.ts +117 -0
  318. package/recipes/boxcar/README.md +53 -0
  319. package/recipes/boxcar/boxcar.ts +1000 -0
  320. package/recipes/cargo_container_scifi/README.md +52 -0
  321. package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
  322. package/recipes/dropship/README.md +84 -0
  323. package/recipes/dropship/dropship.ts +1144 -0
  324. package/recipes/eiffel_tower/README.md +110 -0
  325. package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
  326. package/recipes/goblin_head/README.md +75 -0
  327. package/recipes/goblin_head/goblin_head.ts +38 -0
  328. package/recipes/human_head/README.md +68 -0
  329. package/recipes/human_head/human_head.ts +38 -0
  330. package/recipes/loco_440/README.md +32 -0
  331. package/recipes/loco_440/loco_440.ts +1404 -0
  332. package/recipes/material_swatches/README.md +21 -0
  333. package/recipes/material_swatches/material_swatches.ts +189 -0
  334. package/recipes/mountain_range/README.md +45 -0
  335. package/recipes/mountain_range/mountain_range.ts +128 -0
  336. package/recipes/oak/README.md +30 -0
  337. package/recipes/oak/oak.ts +19 -0
  338. package/recipes/orc_head/README.md +78 -0
  339. package/recipes/orc_head/orc_head.ts +41 -0
  340. package/recipes/palm/README.md +30 -0
  341. package/recipes/palm/palm.ts +19 -0
  342. package/recipes/pine/README.md +29 -0
  343. package/recipes/pine/pine.ts +19 -0
  344. package/recipes/signal_gantry/README.md +36 -0
  345. package/recipes/signal_gantry/signal_gantry.ts +522 -0
  346. package/recipes/station_halt/README.md +39 -0
  347. package/recipes/station_halt/station_halt.ts +722 -0
  348. package/recipes/t34/README.md +44 -0
  349. package/recipes/t34/t34.ts +1068 -0
  350. package/recipes/tiger_1/README.md +75 -0
  351. package/recipes/tiger_1/tiger_1.ts +1370 -0
  352. package/recipes/water_tower/README.md +40 -0
  353. package/recipes/water_tower/water_tower.ts +542 -0
  354. package/src/cli/args.ts +125 -0
  355. package/src/cli/bin.ts +7 -0
  356. package/src/cli/load.ts +17 -0
  357. package/src/cli/manifest.ts +95 -0
  358. package/src/cli/render-cmds.ts +66 -0
  359. package/src/cli/run.ts +192 -0
  360. package/src/cli/texture-cache.ts +24 -0
  361. package/src/core/asset.ts +129 -0
  362. package/src/core/definition.ts +156 -0
  363. package/src/core/errors.ts +50 -0
  364. package/src/core/family.ts +50 -0
  365. package/src/core/index.ts +66 -0
  366. package/src/core/mesh.ts +114 -0
  367. package/src/core/part.ts +130 -0
  368. package/src/core/png.ts +49 -0
  369. package/src/core/rng.ts +49 -0
  370. package/src/core/transform.ts +90 -0
  371. package/src/core/vec.ts +26 -0
  372. package/src/core/world.ts +80 -0
  373. package/src/export/bin-writer.ts +79 -0
  374. package/src/export/container.ts +35 -0
  375. package/src/export/credits.ts +25 -0
  376. package/src/export/glb-options.ts +53 -0
  377. package/src/export/glb.ts +187 -0
  378. package/src/export/hierarchy.ts +122 -0
  379. package/src/export/index.ts +34 -0
  380. package/src/export/lights.ts +49 -0
  381. package/src/export/lod-coverage.ts +71 -0
  382. package/src/export/materials.ts +126 -0
  383. package/src/export/merge-pivots.ts +146 -0
  384. package/src/export/metadata.ts +62 -0
  385. package/src/export/motion.ts +70 -0
  386. package/src/export/optimize-parts.ts +80 -0
  387. package/src/export/primitive.ts +35 -0
  388. package/src/export/tangents.ts +75 -0
  389. package/src/export/textures.ts +88 -0
  390. package/src/export/version.ts +6 -0
  391. package/src/grammar/index.ts +4 -0
  392. package/src/grammar/split.ts +87 -0
  393. package/src/grammar/whyte.ts +20 -0
  394. package/src/index.ts +14 -0
  395. package/src/kinds/building.ts +153 -0
  396. package/src/kinds/faces.ts +138 -0
  397. package/src/kinds/head-armour.ts +181 -0
  398. package/src/kinds/head-beard.ts +133 -0
  399. package/src/kinds/head-bust.ts +184 -0
  400. package/src/kinds/head-extras.ts +150 -0
  401. package/src/kinds/head-face.ts +242 -0
  402. package/src/kinds/head-garb.ts +181 -0
  403. package/src/kinds/head-geom.ts +31 -0
  404. package/src/kinds/head-hair.ts +193 -0
  405. package/src/kinds/head-layout.ts +63 -0
  406. package/src/kinds/head-mesh.ts +195 -0
  407. package/src/kinds/head-mouth.ts +111 -0
  408. package/src/kinds/head-params.ts +162 -0
  409. package/src/kinds/head-presets.ts +155 -0
  410. package/src/kinds/head-rim.ts +128 -0
  411. package/src/kinds/head-tone.ts +52 -0
  412. package/src/kinds/head-torso.ts +230 -0
  413. package/src/kinds/head.ts +176 -0
  414. package/src/kinds/hull-lines.ts +160 -0
  415. package/src/kinds/hull-skin.ts +83 -0
  416. package/src/kinds/hull.ts +209 -0
  417. package/src/kinds/index.ts +63 -0
  418. package/src/kinds/mass.ts +110 -0
  419. package/src/kinds/openings.ts +141 -0
  420. package/src/kinds/planned.ts +12 -0
  421. package/src/kinds/roof-chimney.ts +100 -0
  422. package/src/kinds/roof-shape.ts +230 -0
  423. package/src/kinds/roof.ts +250 -0
  424. package/src/kinds/vegetation-fronds.ts +126 -0
  425. package/src/kinds/vegetation-impostor.ts +118 -0
  426. package/src/kinds/vegetation-leaves.ts +168 -0
  427. package/src/kinds/vegetation-mesh.ts +174 -0
  428. package/src/kinds/vegetation-params.ts +147 -0
  429. package/src/kinds/vegetation-presets.ts +184 -0
  430. package/src/kinds/vegetation-skeleton.ts +168 -0
  431. package/src/kinds/vegetation.ts +199 -0
  432. package/src/kinds/vehicle.ts +144 -0
  433. package/src/material/cc0.ts +41 -0
  434. package/src/material/define.ts +130 -0
  435. package/src/material/generator.ts +63 -0
  436. package/src/material/index.ts +60 -0
  437. package/src/material/library.ts +196 -0
  438. package/src/material/material-def.ts +84 -0
  439. package/src/material/paint/bond.ts +60 -0
  440. package/src/material/paint/canvas.ts +101 -0
  441. package/src/material/paint/cell.ts +39 -0
  442. package/src/material/paint/cells-round.ts +119 -0
  443. package/src/material/paint/cells.ts +111 -0
  444. package/src/material/paint/compose.ts +53 -0
  445. package/src/material/paint/concrete.ts +62 -0
  446. package/src/material/paint/courses.ts +85 -0
  447. package/src/material/paint/decode-png.ts +106 -0
  448. package/src/material/paint/default-sheet.ts +73 -0
  449. package/src/material/paint/fabric.ts +63 -0
  450. package/src/material/paint/foliage-broad.ts +126 -0
  451. package/src/material/paint/foliage-common.ts +93 -0
  452. package/src/material/paint/foliage-compound.ts +135 -0
  453. package/src/material/paint/foliage-conifer.ts +143 -0
  454. package/src/material/paint/foliage-raster.ts +196 -0
  455. package/src/material/paint/foliage.ts +23 -0
  456. package/src/material/paint/image-strip.ts +88 -0
  457. package/src/material/paint/kit.ts +37 -0
  458. package/src/material/paint/masks.ts +46 -0
  459. package/src/material/paint/metal.ts +109 -0
  460. package/src/material/paint/noise.ts +52 -0
  461. package/src/material/paint/normal.ts +52 -0
  462. package/src/material/paint/orm.ts +108 -0
  463. package/src/material/paint/planks.ts +101 -0
  464. package/src/material/paint/plates.ts +72 -0
  465. package/src/material/paint/shapes.ts +28 -0
  466. package/src/material/paint/slate.ts +52 -0
  467. package/src/material/paint/stamp.ts +40 -0
  468. package/src/material/paint/strip.ts +48 -0
  469. package/src/material/paint/surface.ts +60 -0
  470. package/src/material/paint/types.ts +94 -0
  471. package/src/material/paint/validate.ts +74 -0
  472. package/src/material/palette.ts +46 -0
  473. package/src/material/shader-godot.ts +77 -0
  474. package/src/material/shader-three.ts +103 -0
  475. package/src/material/shader.ts +100 -0
  476. package/src/material/slots.ts +18 -0
  477. package/src/material/texture-source.ts +86 -0
  478. package/src/material/trim-sheet.ts +166 -0
  479. package/src/neural/generator.ts +37 -0
  480. package/src/neural/images.ts +27 -0
  481. package/src/neural/index.ts +10 -0
  482. package/src/passes/bake-ao.ts +183 -0
  483. package/src/passes/grime.ts +37 -0
  484. package/src/passes/index.ts +8 -0
  485. package/src/passes/livery.ts +66 -0
  486. package/src/passes/optimize-cache.ts +175 -0
  487. package/src/passes/optimize-weld.ts +147 -0
  488. package/src/passes/optimize.ts +85 -0
  489. package/src/passes/ray-grid.ts +237 -0
  490. package/src/passes/weighted-normals.ts +125 -0
  491. package/src/primitives/beam.ts +60 -0
  492. package/src/primitives/bevel-box.ts +140 -0
  493. package/src/primitives/box.ts +90 -0
  494. package/src/primitives/cell.ts +115 -0
  495. package/src/primitives/check.ts +49 -0
  496. package/src/primitives/closed.ts +74 -0
  497. package/src/primitives/erosion.ts +183 -0
  498. package/src/primitives/font-5x7.ts +117 -0
  499. package/src/primitives/heightfield.ts +101 -0
  500. package/src/primitives/index.ts +56 -0
  501. package/src/primitives/lathe-facets.ts +45 -0
  502. package/src/primitives/lathe.ts +174 -0
  503. package/src/primitives/lattice.ts +131 -0
  504. package/src/primitives/loft.ts +174 -0
  505. package/src/primitives/mc-table.ts +41 -0
  506. package/src/primitives/member.ts +130 -0
  507. package/src/primitives/noise.ts +157 -0
  508. package/src/primitives/planar.ts +55 -0
  509. package/src/primitives/prism.ts +85 -0
  510. package/src/primitives/sculpt-edit.ts +212 -0
  511. package/src/primitives/sculpt-tree.ts +226 -0
  512. package/src/primitives/sculpt.ts +80 -0
  513. package/src/primitives/sdf-grid.ts +131 -0
  514. package/src/primitives/sdf-ops.ts +145 -0
  515. package/src/primitives/sdf.ts +83 -0
  516. package/src/primitives/strip-uv.ts +26 -0
  517. package/src/primitives/surface-nets.ts +161 -0
  518. package/src/primitives/sweep.ts +162 -0
  519. package/src/primitives/terrain-color.ts +85 -0
  520. package/src/primitives/terrain-grid.ts +137 -0
  521. package/src/primitives/text-layout.ts +85 -0
  522. package/src/primitives/text.ts +197 -0
  523. package/src/primitives/tiled.ts +93 -0
  524. package/src/primitives/transport.ts +85 -0
  525. package/src/primitives/triangulate.ts +42 -0
  526. package/src/primitives/value-noise.ts +62 -0
  527. package/src/qa/checks.ts +110 -0
  528. package/src/qa/clash.ts +177 -0
  529. package/src/qa/floating.ts +63 -0
  530. package/src/qa/gates.ts +126 -0
  531. package/src/qa/geometry.ts +187 -0
  532. package/src/qa/index.ts +28 -0
  533. package/src/qa/inside.ts +114 -0
  534. package/src/qa/inspect.ts +107 -0
  535. package/src/qa/lods.ts +97 -0
  536. package/src/qa/measures.ts +67 -0
  537. package/src/qa/occlusion.ts +166 -0
  538. package/src/qa/sanity.ts +137 -0
  539. package/src/qa/types.ts +17 -0
  540. package/src/render/beauty.ts +115 -0
  541. package/src/render/camera.ts +142 -0
  542. package/src/render/depth-map.ts +191 -0
  543. package/src/render/font.ts +76 -0
  544. package/src/render/ground.ts +87 -0
  545. package/src/render/index.ts +34 -0
  546. package/src/render/lighting.ts +121 -0
  547. package/src/render/lod-strip.ts +34 -0
  548. package/src/render/metrics.ts +147 -0
  549. package/src/render/options.ts +81 -0
  550. package/src/render/photo-match.ts +28 -0
  551. package/src/render/raster.ts +143 -0
  552. package/src/render/resolve.ts +109 -0
  553. package/src/render/review-prompt.ts +59 -0
  554. package/src/render/review-warnings.ts +92 -0
  555. package/src/render/review.ts +103 -0
  556. package/src/render/scene.ts +119 -0
  557. package/src/render/shade.ts +204 -0
  558. package/src/render/sheet.ts +88 -0
  559. package/src/render/texture.ts +97 -0
  560. package/src/render/thumbnail.ts +75 -0
  561. package/src/render/vis-buffer.ts +119 -0
  562. package/src/testing/assert.ts +115 -0
  563. package/src/testing/compiled.ts +31 -0
  564. package/src/testing/hash.ts +14 -0
  565. package/src/testing/index.ts +14 -0
  566. package/src/testing/snapshot.ts +37 -0
  567. package/src/types/gltf-validator.d.ts +13 -0
@@ -0,0 +1,88 @@
1
+ // The trim sheet in glTF: three images (albedo sRGB, normal + ORM linear), three textures and one
2
+ // sampler (REPEAT along U so strips tile, CLAMP along V so the atlas never wraps rows, trilinear
3
+ // mips). Embedded: PNGs ride in the BIN chunk after the geometry (self-contained single files).
4
+ // External: images reference `<prefix>_{albedo,normal,orm}.png` by URI, so fifty assets share one
5
+ // texture set on disk and in GPU memory — the point of the trim sheet. Write the PNGs once with
6
+ // `sheetPngs`. Texture indices are fixed: 0 albedo, 1 normal, 2 ORM (materials.ts binds them).
7
+
8
+ import { encodePng } from '../core/index.ts';
9
+ import type { PaintedSheet } from '../material/index.ts';
10
+ import type { Json } from './bin-writer.ts';
11
+
12
+ export type SheetMap = 'albedo' | 'normal' | 'orm';
13
+ export const SHEET_MAPS: readonly SheetMap[] = ['albedo', 'normal', 'orm'];
14
+
15
+ /** Reference a shared sheet by URI instead of embedding it. */
16
+ export interface ExternalSheet {
17
+ readonly external: true;
18
+ /** URI prefix relative to the .glb, e.g. `../textures/trim_sheet`. Default `trim_sheet`. */
19
+ readonly prefix?: string;
20
+ }
21
+
22
+ export type SheetBinding = PaintedSheet | ExternalSheet;
23
+
24
+ export const sheetFileName = (map: SheetMap, prefix = 'trim_sheet'): string =>
25
+ `${prefix}_${map}.png`;
26
+
27
+ const LINEAR = 9729;
28
+ const LINEAR_MIPMAP_LINEAR = 9987;
29
+ const REPEAT = 10497;
30
+ const CLAMP_TO_EDGE = 33071;
31
+
32
+ const pngCache = new WeakMap<PaintedSheet, Readonly<Record<SheetMap, Uint8Array>>>();
33
+
34
+ /** PNG bytes of each map, encoded once per sheet (every LOD's GLB reuses them). */
35
+ export function sheetPngs(sheet: PaintedSheet): Readonly<Record<SheetMap, Uint8Array>> {
36
+ const hit = pngCache.get(sheet);
37
+ if (hit) return hit;
38
+ const pngs = {
39
+ albedo: encodePng(sheet.size, sheet.size, sheet.albedo),
40
+ normal: encodePng(sheet.size, sheet.size, sheet.normal),
41
+ orm: encodePng(sheet.size, sheet.size, sheet.orm),
42
+ };
43
+ pngCache.set(sheet, pngs);
44
+ return pngs;
45
+ }
46
+
47
+ const isExternal = (s: SheetBinding): s is ExternalSheet => 'external' in s;
48
+
49
+ /**
50
+ * glTF `images` / `textures` / `samplers` for the sheet. Embedded image bytes are returned as
51
+ * `tail`, to append to the BIN chunk at `offset` (4-aligned); their bufferViews go into `views`.
52
+ */
53
+ export function sheetGltf(
54
+ sheet: SheetBinding,
55
+ views: Json[],
56
+ offset: number,
57
+ ): { json: Json; tail: Uint8Array } {
58
+ const textures = SHEET_MAPS.map((_, i) => ({ sampler: 0, source: i }));
59
+ const samplers = [
60
+ { magFilter: LINEAR, minFilter: LINEAR_MIPMAP_LINEAR, wrapS: REPEAT, wrapT: CLAMP_TO_EDGE },
61
+ ];
62
+ if (isExternal(sheet)) {
63
+ const images = SHEET_MAPS.map((m) => ({
64
+ name: `trim_sheet_${m}`,
65
+ uri: sheetFileName(m, sheet.prefix),
66
+ }));
67
+ return { json: { images, textures, samplers }, tail: new Uint8Array() };
68
+ }
69
+ const pngs = sheetPngs(sheet);
70
+ const parts: Uint8Array[] = [];
71
+ const images: Json[] = [];
72
+ let at = offset;
73
+ for (const m of SHEET_MAPS) {
74
+ const png = pngs[m];
75
+ views.push({ buffer: 0, byteOffset: at, byteLength: png.byteLength });
76
+ images.push({ name: `trim_sheet_${m}`, bufferView: views.length - 1, mimeType: 'image/png' });
77
+ const pad = (4 - (png.byteLength % 4)) % 4;
78
+ parts.push(png, new Uint8Array(pad));
79
+ at += png.byteLength + pad;
80
+ }
81
+ const tail = new Uint8Array(at - offset);
82
+ let o = 0;
83
+ for (const p of parts) {
84
+ tail.set(p, o);
85
+ o += p.byteLength;
86
+ }
87
+ return { json: { images, textures, samplers }, tail };
88
+ }
@@ -0,0 +1,6 @@
1
+ // The package version written into every GLB (`asset.generator`, XMP CreatorTool). Kept as a
2
+ // constant because the library may not import package.json (rootDir is src); version.test.ts
3
+ // fails the gate when the two disagree, and `bun run release` stamps both.
4
+
5
+ export const VERSION = '0.2.0';
6
+ export const GENERATOR = `@developerz.ai/ts23d ${VERSION}`;
@@ -0,0 +1,4 @@
1
+ // Public API of @ts23d/grammar: string mini-languages that a recipe calls from TypeScript.
2
+
3
+ export { layoutSplit, parseSplit, type Slot, type SplitTerm, split } from './split.ts';
4
+ export { parseWhyte, type WheelGroup } from './whyte.ts';
@@ -0,0 +1,87 @@
1
+ // Split grammar (after CGA shape, Müller et al. 2006): one line lays out a facade strip.
2
+ // "pier:0.9 | {arch:2.5}* | door:3 | pier:~"
3
+ // x:n fixed width · {a:n | b:m}* repeat to fill · x:~ stretch (shares leftover equally)
4
+
5
+ import { Ts23dError } from '../core/index.ts';
6
+
7
+ export type SplitTerm =
8
+ | { readonly kind: 'fixed'; readonly symbol: string; readonly size: number }
9
+ | { readonly kind: 'stretch'; readonly symbol: string; readonly weight: number }
10
+ | { readonly kind: 'repeat'; readonly body: readonly SplitTerm[] };
11
+
12
+ export interface Slot {
13
+ readonly symbol: string;
14
+ readonly start: number;
15
+ readonly size: number;
16
+ }
17
+
18
+ const syntax = (src: string, why: string): Ts23dError =>
19
+ new Ts23dError('T3D_GRAMMAR_SYNTAX', {
20
+ cause: `cannot parse split "${src}": ${why}`,
21
+ fix: 'use `sym:<metres>`, `sym:~`, `sym:~<weight>` or `{a:1 | b:2}*`, separated by `|`',
22
+ });
23
+
24
+ function parseTerm(raw: string, src: string): SplitTerm {
25
+ const t = raw.trim();
26
+ const m = /^([a-z_][a-z0-9_]*):(~(\d*\.?\d+)?|\d*\.?\d+)$/.exec(t);
27
+ if (!m) throw syntax(src, `bad term '${t}'`);
28
+ const [, symbol = '', size = ''] = m;
29
+ if (size.startsWith('~')) return { kind: 'stretch', symbol, weight: m[3] ? Number(m[3]) : 1 };
30
+ return { kind: 'fixed', symbol, size: Number(size) };
31
+ }
32
+
33
+ export function parseSplit(src: string): SplitTerm[] {
34
+ const out: SplitTerm[] = [];
35
+ const re = /\{([^}]*)\}\*|([^|{}]+)/g;
36
+ for (const m of src.matchAll(re)) {
37
+ if (m[1] !== undefined) {
38
+ const body = m[1].split('|').map((s) => parseTerm(s, src));
39
+ if (body.some((b) => b.kind !== 'fixed'))
40
+ throw syntax(src, 'a repeat body takes fixed sizes');
41
+ out.push({ kind: 'repeat', body });
42
+ } else if (m[2] !== undefined && m[2].trim() !== '') {
43
+ out.push(parseTerm(m[2], src));
44
+ }
45
+ }
46
+ if (out.length === 0) throw syntax(src, 'empty');
47
+ if (out.filter((t) => t.kind === 'repeat').length > 1) throw syntax(src, 'at most one repeat');
48
+ return out;
49
+ }
50
+
51
+ const sizeOf = (terms: readonly SplitTerm[]): number =>
52
+ terms.reduce((s, t) => s + (t.kind === 'fixed' ? t.size : 0), 0);
53
+
54
+ /** Lay a parsed split across `length` metres. Repeats take whole copies; stretches share the rest. */
55
+ export function layoutSplit(terms: readonly SplitTerm[], length: number): Slot[] {
56
+ const fixed = sizeOf(terms);
57
+ const repeat = terms.find((t) => t.kind === 'repeat');
58
+ const unit = repeat?.kind === 'repeat' ? sizeOf(repeat.body) : 0;
59
+ if (fixed > length + 1e-9) {
60
+ throw new Ts23dError('T3D_GRAMMAR_OVERFLOW', {
61
+ cause: `fixed terms need ${fixed} m but the strip is ${length} m`,
62
+ fix: 'shrink a fixed term, or turn one into a stretch (`sym:~`)',
63
+ });
64
+ }
65
+ const copies = unit > 0 ? Math.floor((length - fixed) / unit) : 0;
66
+ const weights = terms.reduce((s, t) => s + (t.kind === 'stretch' ? t.weight : 0), 0);
67
+ const leftover = length - fixed - copies * unit;
68
+ // No stretch term: the repeat absorbs the leftover by widening each copy evenly.
69
+ const grow = weights === 0 && copies > 0 ? (copies * unit + leftover) / (copies * unit) : 1;
70
+ const slots: Slot[] = [];
71
+ let x = 0;
72
+ const push = (symbol: string, size: number): void => {
73
+ slots.push({ symbol, start: x, size });
74
+ x += size;
75
+ };
76
+ for (const t of terms) {
77
+ if (t.kind === 'fixed') push(t.symbol, t.size);
78
+ else if (t.kind === 'stretch')
79
+ push(t.symbol, weights > 0 ? (leftover * t.weight) / weights : 0);
80
+ else
81
+ for (let i = 0; i < copies; i++)
82
+ for (const b of t.body) if (b.kind === 'fixed') push(b.symbol, b.size * grow);
83
+ }
84
+ return slots;
85
+ }
86
+
87
+ export const split = (src: string, length: number): Slot[] => layoutSplit(parseSplit(src), length);
@@ -0,0 +1,20 @@
1
+ // Whyte notation ("4-4-0", "2-6-2") → wheel groups. Domain-native params: the loco kind reads it.
2
+
3
+ import { Ts23dError } from '../core/index.ts';
4
+
5
+ export interface WheelGroup {
6
+ readonly role: 'leading' | 'driving' | 'trailing';
7
+ readonly count: number;
8
+ }
9
+
10
+ export function parseWhyte(arrangement: string): WheelGroup[] {
11
+ const parts = arrangement.split('-').map(Number);
12
+ if (parts.length !== 3 || parts.some((n) => !Number.isInteger(n) || n < 0 || n % 2 !== 0)) {
13
+ throw new Ts23dError('T3D_GRAMMAR_SYNTAX', {
14
+ cause: `'${arrangement}' is not a rigid-frame Whyte arrangement`,
15
+ fix: "use three even numbers: leading-driving-trailing, e.g. '4-4-0' or '2-8-2'",
16
+ });
17
+ }
18
+ const roles = ['leading', 'driving', 'trailing'] as const;
19
+ return roles.map((role, i) => ({ role, count: parts[i] ?? 0 }));
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ // @developerz.ai/ts23d — the whole DSL from one import. Subpaths (`/core`, `/grammar`, …) exist for
2
+ // tree-shaking and for reading one layer at a time.
3
+
4
+ export { type AssetManifest, manifestOf } from './cli/manifest.ts';
5
+ export * from './core/index.ts';
6
+ export * from './export/index.ts';
7
+ export * from './grammar/index.ts';
8
+ export * from './kinds/index.ts';
9
+ export * from './material/index.ts';
10
+ export * from './neural/index.ts';
11
+ export * from './passes/index.ts';
12
+ export * from './primitives/index.ts';
13
+ export * from './qa/index.ts';
14
+ export * from './render/index.ts';
@@ -0,0 +1,153 @@
1
+ // Building helpers: masses, then details. A facade is a split-grammar line per floor and side;
2
+ // `win`/`door`/`arch`/`shop` slots become openings, everything else stays wall. Plain mode: inset
3
+ // glass/door panels. With `kit`: real openings (reveal, sill, lintel, sash, panelled door).
4
+
5
+ import type { BuildContext, Vec3 } from '../core/index.ts';
6
+ import { split } from '../grammar/index.ts';
7
+ import type { StripId } from '../material/index.ts';
8
+ import { box } from '../primitives/index.ts';
9
+ import { opening } from './openings.ts';
10
+
11
+ export type Side = 'front' | 'back' | 'left' | 'right';
12
+
13
+ export interface FacadeOptions {
14
+ readonly prefix: string;
15
+ /** Centre of the mass's base. */
16
+ readonly at: Vec3;
17
+ /** Footprint width (X) and depth (Y) of the mass this facade wraps. */
18
+ readonly footprint: readonly [number, number];
19
+ readonly floors: number;
20
+ readonly floorHeight: number;
21
+ /** Grammar per floor; the same line for every floor unless `ground` overrides floor 0. */
22
+ readonly grammar: string;
23
+ readonly ground?: string;
24
+ readonly sides: readonly Side[];
25
+ readonly glass: Vec3;
26
+ readonly frame?: Vec3;
27
+ /** Window height as a fraction of the floor. */
28
+ readonly windowRatio?: number;
29
+ /** Detailed openings (see `opening`): reveal in the wall colour, stone sill/lintel, sash bars. */
30
+ readonly kit?: {
31
+ readonly wall: Vec3;
32
+ readonly trim?: Vec3;
33
+ readonly depth?: number;
34
+ readonly panes?: readonly [number, number];
35
+ /** The wall's strip, so reveals continue its courses. Default 'plain'. */
36
+ readonly strip?: StripId;
37
+ };
38
+ }
39
+
40
+ const OPENINGS = new Set(['win', 'door', 'arch', 'shop']);
41
+
42
+ export function facade(ctx: BuildContext, o: FacadeOptions): void {
43
+ const [w, d] = o.footprint;
44
+ const ratio = o.windowRatio ?? 0.6;
45
+ const depth = 0.08;
46
+ for (const side of o.sides) {
47
+ const along = side === 'front' || side === 'back' ? d : w;
48
+ for (let f = 0; f < o.floors; f++) {
49
+ const line = f === 0 && o.ground ? o.ground : o.grammar;
50
+ const z = o.at[2] + f * o.floorHeight;
51
+ split(line, along).forEach((slot, i) => {
52
+ if (!OPENINGS.has(slot.symbol) || slot.size < 0.2) return;
53
+ const isDoor = slot.symbol === 'door' || slot.symbol === 'shop';
54
+ const h = isDoor ? o.floorHeight * 0.85 : o.floorHeight * ratio;
55
+ const sill = isDoor ? 0 : (o.floorHeight - h) / 2;
56
+ const c = slot.start + slot.size / 2 - along / 2;
57
+ const inset = slot.size * 0.85;
58
+ const pos: Vec3 =
59
+ side === 'front'
60
+ ? [o.at[0] + w / 2, o.at[1] + c, z + sill]
61
+ : side === 'back'
62
+ ? [o.at[0] - w / 2, o.at[1] - c, z + sill]
63
+ : side === 'left'
64
+ ? [o.at[0] - c, o.at[1] + d / 2, z + sill]
65
+ : [o.at[0] + c, o.at[1] - d / 2, z + sill];
66
+ const name = `${o.prefix}_${side}_${f}_${i}`;
67
+ if (o.kit) {
68
+ const k = o.kit;
69
+ ctx.part(
70
+ name,
71
+ opening(
72
+ {
73
+ kind: isDoor ? 'door' : 'window',
74
+ width: Math.max(0.3, inset - 0.2),
75
+ height: h - (isDoor ? 0.25 : 0.2),
76
+ at: pos,
77
+ facing: side,
78
+ glass: o.glass,
79
+ frame: o.frame ?? [0.92, 0.9, 0.85],
80
+ wall: k.wall,
81
+ ...(k.trim ? { trim: k.trim } : {}),
82
+ ...(k.depth !== undefined ? { depth: k.depth } : {}),
83
+ ...(k.panes ? { panes: k.panes } : {}),
84
+ ...(k.strip ? { wallStrip: k.strip } : {}),
85
+ },
86
+ ctx.lod,
87
+ ),
88
+ );
89
+ return;
90
+ }
91
+ const size: Vec3 =
92
+ side === 'front' || side === 'back' ? [depth, inset, h] : [inset, depth, h];
93
+ ctx.part(
94
+ name,
95
+ box({
96
+ size,
97
+ at: pos,
98
+ color: isDoor ? (o.frame ?? o.glass) : o.glass,
99
+ strip: 'glass',
100
+ omit: ['bottom'],
101
+ }),
102
+ );
103
+ });
104
+ }
105
+ }
106
+ }
107
+
108
+ export interface SetbackTier {
109
+ readonly footprint: readonly [number, number];
110
+ readonly floors: number;
111
+ }
112
+
113
+ /** Stack of shrinking masses (Empire State, Chrysler-style towers). Returns the top z. */
114
+ export function setbacks(
115
+ ctx: BuildContext,
116
+ o: {
117
+ prefix: string;
118
+ tiers: readonly SetbackTier[];
119
+ floorHeight: number;
120
+ color: Vec3;
121
+ grammar: string;
122
+ glass: Vec3;
123
+ detail: boolean;
124
+ },
125
+ ): number {
126
+ let z = 0;
127
+ o.tiers.forEach((t, i) => {
128
+ const h = t.floors * o.floorHeight;
129
+ ctx.part(
130
+ `${o.prefix}_${i}`,
131
+ box({
132
+ size: [t.footprint[0], t.footprint[1], h],
133
+ at: [0, 0, z],
134
+ color: o.color,
135
+ strip: 'stone',
136
+ omit: i === 0 ? ['bottom'] : [],
137
+ }),
138
+ );
139
+ if (o.detail)
140
+ facade(ctx, {
141
+ prefix: `${o.prefix}_${i}_win`,
142
+ at: [0, 0, z],
143
+ footprint: t.footprint,
144
+ floors: t.floors,
145
+ floorHeight: o.floorHeight,
146
+ grammar: o.grammar,
147
+ sides: ['front', 'back', 'left', 'right'],
148
+ glass: o.glass,
149
+ });
150
+ z += h;
151
+ });
152
+ return z;
153
+ }
@@ -0,0 +1,138 @@
1
+ // Convex-polygon toolkit for the building kinds: half-plane clipping in plan, and emitting a flat
2
+ // convex 3D polygon onto a trim strip AT TRUE SCALE. A strip tiles along U only, so the polygon is
3
+ // cut into bands `scaleMetres` tall along its V direction (courses start on `vOrigin`); each band
4
+ // maps V 0→1 across the strip. That is how slates run down a slope and bricks up a gable.
5
+
6
+ import {
7
+ cross,
8
+ dot,
9
+ type MeshBuilder,
10
+ normalize,
11
+ sub,
12
+ type Vec2,
13
+ type Vec3,
14
+ } from '../core/index.ts';
15
+ import { DEFAULT_SHEET, type StripId, stripRect } from '../material/index.ts';
16
+
17
+ const EPS = 1e-9;
18
+
19
+ /** Keep the part of a convex polygon where the linear function `f` is ≤ 0. */
20
+ export function clipPoly<P extends Vec2 | Vec3>(poly: readonly P[], f: (p: P) => number): P[] {
21
+ const out: P[] = [];
22
+ const n = poly.length;
23
+ for (let i = 0; i < n; i++) {
24
+ const a = poly[i];
25
+ const b = poly[(i + 1) % n];
26
+ if (a === undefined || b === undefined) continue;
27
+ const fa = f(a);
28
+ const fb = f(b);
29
+ if (fa <= EPS) out.push(a);
30
+ if (fa <= EPS !== fb <= EPS) {
31
+ const t = fa / (fa - fb);
32
+ out.push(a.map((v, k) => v + ((b[k] ?? v) - v) * t) as unknown as P);
33
+ }
34
+ }
35
+ return dedupe(out);
36
+ }
37
+
38
+ /** Drop consecutive duplicate points (clipping through a vertex emits it twice). */
39
+ export function dedupe<P extends Vec2 | Vec3>(poly: readonly P[]): P[] {
40
+ const out: P[] = [];
41
+ for (const p of poly) {
42
+ const q = out.at(-1);
43
+ if (!q || p.some((v, k) => Math.abs(v - (q[k] ?? 0)) > 1e-7)) out.push(p);
44
+ }
45
+ const first = out[0];
46
+ const last = out.at(-1);
47
+ if (out.length > 1 && first && last && first.every((v, k) => Math.abs(v - (last[k] ?? 0)) < 1e-7))
48
+ out.pop();
49
+ return out;
50
+ }
51
+
52
+ /** Signed area of a 2D polygon (CCW positive). */
53
+ export const area2 = (poly: readonly Vec2[]): number =>
54
+ poly.reduce((s, a, i) => {
55
+ const b = poly[(i + 1) % poly.length] ?? a;
56
+ return s + a[0] * b[1] - b[0] * a[1];
57
+ }, 0) / 2;
58
+
59
+ /** Vertical extent of one band (one full V of the strip) in metres, from the house sheet. */
60
+ export const stripScale = (strip: StripId): number => DEFAULT_SHEET.strips[strip]?.scaleMetres ?? 1;
61
+
62
+ export interface FaceOptions {
63
+ /** Convex and planar; either winding (it is turned to face `normal`). */
64
+ readonly points: readonly Vec3[];
65
+ readonly normal: Vec3;
66
+ /** Direction V grows along (up a slope, up a wall); projected into the face plane. */
67
+ readonly up: Vec3;
68
+ /** A point on the line where courses start (eave, wall foot). */
69
+ readonly vOrigin: Vec3;
70
+ readonly color: Vec3 | ((p: Vec3) => Vec3);
71
+ readonly strip: StripId;
72
+ /** false: one polygon, V squeezed over the whole face (far LODs). Default true. */
73
+ readonly banded?: boolean;
74
+ }
75
+
76
+ /** Emit a convex face into `b`, banded at the strip's true scale. Returns triangles added. */
77
+ export function emitFace(b: MeshBuilder, o: FaceOptions): number {
78
+ if (o.points.length < 3) return 0;
79
+ const n = normalize(o.normal);
80
+ const points = dot(newell(o.points), n) < 0 ? [...o.points].reverse() : o.points;
81
+ const vDir = normalize(sub(o.up, scaleV(n, dot(o.up, n))));
82
+ const uDir = cross(vDir, n);
83
+ const rect = stripRect(o.strip);
84
+ const scale = stripScale(o.strip);
85
+ const vOf = (p: Vec3): number => dot(sub(p, o.vOrigin), vDir);
86
+ const vs = points.map(vOf);
87
+ const lo = Math.min(...vs);
88
+ const hi = Math.max(...vs);
89
+ if (hi - lo < 1e-6) return 0;
90
+ const colour = (p: Vec3): Vec3 => (typeof o.color === 'function' ? o.color(p) : o.color);
91
+ const bands: [number, number][] = [];
92
+ if (o.banded === false) bands.push([lo, hi]);
93
+ else {
94
+ for (let k = Math.floor(lo / scale + 1e-9); k * scale < hi - 1e-9; k++) {
95
+ bands.push([k * scale, (k + 1) * scale]);
96
+ }
97
+ }
98
+ let tris = 0;
99
+ for (const [v0, v1] of bands) {
100
+ const span = v1 - v0;
101
+ const poly = clipPoly(
102
+ clipPoly(points, (p) => v0 - vOf(p)),
103
+ (p) => vOf(p) - v1,
104
+ );
105
+ if (poly.length < 3) continue;
106
+ const ids = poly.map((p) => {
107
+ const f = Math.min(1, Math.max(0, (vOf(p) - v0) / span));
108
+ return b.vertex({
109
+ p,
110
+ n,
111
+ uv: [
112
+ rect.u0 + (dot(p, uDir) / rect.metres) * (rect.u1 - rect.u0),
113
+ rect.v0 + f * (rect.v1 - rect.v0),
114
+ ],
115
+ color: colour(p),
116
+ });
117
+ });
118
+ const first = ids[0] ?? 0;
119
+ for (let i = 1; i + 1 < ids.length; i++) b.tri(first, ids[i] ?? 0, ids[i + 1] ?? 0);
120
+ tris += ids.length - 2;
121
+ }
122
+ return tris;
123
+ }
124
+
125
+ /** Newell's polygon normal (unnormalised): robust for any planar polygon. */
126
+ export function newell(poly: readonly Vec3[]): Vec3 {
127
+ const n: [number, number, number] = [0, 0, 0];
128
+ for (let i = 0; i < poly.length; i++) {
129
+ const a = poly[i] ?? [0, 0, 0];
130
+ const b = poly[(i + 1) % poly.length] ?? a;
131
+ n[0] += (a[1] - b[1]) * (a[2] + b[2]);
132
+ n[1] += (a[2] - b[2]) * (a[0] + b[0]);
133
+ n[2] += (a[0] - b[0]) * (a[1] + b[1]);
134
+ }
135
+ return n;
136
+ }
137
+
138
+ const scaleV = (v: Vec3, s: number): Vec3 => [v[0] * s, v[1] * s, v[2] * s];