@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.
- package/.claude/agents/bug-hunter.md +21 -0
- package/.claude/agents/docs-author.md +13 -0
- package/.claude/agents/dx-auditor.md +14 -0
- package/.claude/agents/primitive-author.md +22 -0
- package/.claude/agents/recipe-author.md +36 -0
- package/.claude/agents/visual-reviewer.md +23 -0
- package/.claude/commands/feature.md +28 -0
- package/.claude/commands/new-recipe.md +19 -0
- package/.claude/commands/release.md +14 -0
- package/.claude/commands/verify.md +9 -0
- package/.claude/skills/author-recipe/SKILL.md +93 -0
- package/CHANGELOG.md +90 -0
- package/CLAUDE.md +85 -0
- package/LICENSE +21 -0
- package/README.md +385 -2
- package/dist/chunk-6b05mnmx.js +19 -0
- package/dist/chunk-6b05mnmx.js.map +11 -0
- package/dist/chunk-7rtfjh9f.js +45 -0
- package/dist/chunk-7rtfjh9f.js.map +10 -0
- package/dist/chunk-8j4c28jt.js +4085 -0
- package/dist/chunk-8j4c28jt.js.map +46 -0
- package/dist/chunk-d1fv6hrm.js +2532 -0
- package/dist/chunk-d1fv6hrm.js.map +43 -0
- package/dist/chunk-dbq4qf52.js +902 -0
- package/dist/chunk-dbq4qf52.js.map +25 -0
- package/dist/chunk-drna2rny.js +836 -0
- package/dist/chunk-drna2rny.js.map +17 -0
- package/dist/chunk-dxvypr7y.js +89 -0
- package/dist/chunk-dxvypr7y.js.map +11 -0
- package/dist/chunk-f6yme003.js +1589 -0
- package/dist/chunk-f6yme003.js.map +30 -0
- package/dist/chunk-hg7pf34j.js +465 -0
- package/dist/chunk-hg7pf34j.js.map +19 -0
- package/dist/chunk-jq4e17tj.js +1031 -0
- package/dist/chunk-jq4e17tj.js.map +21 -0
- package/dist/chunk-y63c6zcv.js +2668 -0
- package/dist/chunk-y63c6zcv.js.map +53 -0
- package/dist/cli/args.d.ts +30 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +348 -0
- package/dist/cli/bin.js.map +15 -0
- package/dist/cli/load.d.ts +2 -0
- package/dist/cli/manifest.d.ts +53 -0
- package/dist/cli/render-cmds.d.ts +12 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/texture-cache.d.ts +4 -0
- package/dist/core/asset.d.ts +10 -0
- package/dist/core/definition.d.ts +155 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/family.d.ts +23 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +69 -0
- package/dist/core/index.js.map +9 -0
- package/dist/core/mesh.d.ts +35 -0
- package/dist/core/part.d.ts +114 -0
- package/dist/core/png.d.ts +1 -0
- package/dist/core/rng.d.ts +12 -0
- package/dist/core/transform.d.ts +20 -0
- package/dist/core/vec.d.ts +11 -0
- package/dist/core/world.d.ts +16 -0
- package/dist/export/bin-writer.d.ts +16 -0
- package/dist/export/container.d.ts +3 -0
- package/dist/export/credits.d.ts +4 -0
- package/dist/export/glb-options.d.ts +48 -0
- package/dist/export/glb.d.ts +8 -0
- package/dist/export/hierarchy.d.ts +23 -0
- package/dist/export/index.d.ts +5 -0
- package/dist/export/index.js +40 -0
- package/dist/export/index.js.map +9 -0
- package/dist/export/lights.d.ts +8 -0
- package/dist/export/lod-coverage.d.ts +12 -0
- package/dist/export/materials.d.ts +23 -0
- package/dist/export/merge-pivots.d.ts +6 -0
- package/dist/export/metadata.d.ts +21 -0
- package/dist/export/motion.d.ts +9 -0
- package/dist/export/optimize-parts.d.ts +26 -0
- package/dist/export/primitive.d.ts +3 -0
- package/dist/export/tangents.d.ts +2 -0
- package/dist/export/textures.d.ts +22 -0
- package/dist/export/version.d.ts +2 -0
- package/dist/grammar/index.d.ts +2 -0
- package/dist/grammar/index.js +16 -0
- package/dist/grammar/index.js.map +9 -0
- package/dist/grammar/split.d.ts +21 -0
- package/dist/grammar/whyte.d.ts +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +9 -0
- package/dist/kinds/building.d.ts +44 -0
- package/dist/kinds/faces.d.ts +27 -0
- package/dist/kinds/head-armour.d.ts +5 -0
- package/dist/kinds/head-beard.d.ts +6 -0
- package/dist/kinds/head-bust.d.ts +5 -0
- package/dist/kinds/head-extras.d.ts +15 -0
- package/dist/kinds/head-face.d.ts +8 -0
- package/dist/kinds/head-garb.d.ts +4 -0
- package/dist/kinds/head-geom.d.ts +7 -0
- package/dist/kinds/head-hair.d.ts +4 -0
- package/dist/kinds/head-layout.d.ts +29 -0
- package/dist/kinds/head-mesh.d.ts +31 -0
- package/dist/kinds/head-mouth.d.ts +4 -0
- package/dist/kinds/head-params.d.ts +116 -0
- package/dist/kinds/head-presets.d.ts +19 -0
- package/dist/kinds/head-rim.d.ts +10 -0
- package/dist/kinds/head-tone.d.ts +10 -0
- package/dist/kinds/head-torso.d.ts +35 -0
- package/dist/kinds/head.d.ts +39 -0
- package/dist/kinds/hull-lines.d.ts +65 -0
- package/dist/kinds/hull-skin.d.ts +6 -0
- package/dist/kinds/hull.d.ts +14 -0
- package/dist/kinds/index.d.ts +16 -0
- package/dist/kinds/index.js +78 -0
- package/dist/kinds/index.js.map +9 -0
- package/dist/kinds/mass.d.ts +15 -0
- package/dist/kinds/openings.d.ts +30 -0
- package/dist/kinds/planned.d.ts +6 -0
- package/dist/kinds/roof-chimney.d.ts +16 -0
- package/dist/kinds/roof-shape.d.ts +64 -0
- package/dist/kinds/roof.d.ts +31 -0
- package/dist/kinds/vegetation-fronds.d.ts +15 -0
- package/dist/kinds/vegetation-impostor.d.ts +9 -0
- package/dist/kinds/vegetation-leaves.d.ts +15 -0
- package/dist/kinds/vegetation-mesh.d.ts +21 -0
- package/dist/kinds/vegetation-params.d.ts +90 -0
- package/dist/kinds/vegetation-presets.d.ts +20 -0
- package/dist/kinds/vegetation-skeleton.d.ts +20 -0
- package/dist/kinds/vegetation.d.ts +42 -0
- package/dist/kinds/vehicle.d.ts +42 -0
- package/dist/material/cc0.d.ts +4 -0
- package/dist/material/define.d.ts +34 -0
- package/dist/material/generator.d.ts +4 -0
- package/dist/material/index.d.ts +9 -0
- package/dist/material/index.js +70 -0
- package/dist/material/index.js.map +9 -0
- package/dist/material/library.d.ts +7 -0
- package/dist/material/material-def.d.ts +30 -0
- package/dist/material/paint/bond.d.ts +3 -0
- package/dist/material/paint/canvas.d.ts +39 -0
- package/dist/material/paint/cell.d.ts +4 -0
- package/dist/material/paint/cells-round.d.ts +5 -0
- package/dist/material/paint/cells.d.ts +7 -0
- package/dist/material/paint/compose.d.ts +5 -0
- package/dist/material/paint/concrete.d.ts +3 -0
- package/dist/material/paint/courses.d.ts +31 -0
- package/dist/material/paint/decode-png.d.ts +6 -0
- package/dist/material/paint/default-sheet.d.ts +4 -0
- package/dist/material/paint/fabric.d.ts +4 -0
- package/dist/material/paint/foliage-broad.d.ts +5 -0
- package/dist/material/paint/foliage-common.d.ts +35 -0
- package/dist/material/paint/foliage-compound.d.ts +4 -0
- package/dist/material/paint/foliage-conifer.d.ts +5 -0
- package/dist/material/paint/foliage-raster.d.ts +26 -0
- package/dist/material/paint/foliage.d.ts +3 -0
- package/dist/material/paint/image-strip.d.ts +4 -0
- package/dist/material/paint/kit.d.ts +5 -0
- package/dist/material/paint/masks.d.ts +3 -0
- package/dist/material/paint/metal.d.ts +3 -0
- package/dist/material/paint/noise.d.ts +9 -0
- package/dist/material/paint/normal.d.ts +8 -0
- package/dist/material/paint/orm.d.ts +8 -0
- package/dist/material/paint/planks.d.ts +3 -0
- package/dist/material/paint/plates.d.ts +3 -0
- package/dist/material/paint/shapes.d.ts +8 -0
- package/dist/material/paint/slate.d.ts +3 -0
- package/dist/material/paint/stamp.d.ts +3 -0
- package/dist/material/paint/strip.d.ts +3 -0
- package/dist/material/paint/surface.d.ts +5 -0
- package/dist/material/paint/types.d.ts +65 -0
- package/dist/material/paint/validate.d.ts +3 -0
- package/dist/material/palette.d.ts +14 -0
- package/dist/material/shader-godot.d.ts +2 -0
- package/dist/material/shader-three.d.ts +2 -0
- package/dist/material/shader.d.ts +46 -0
- package/dist/material/slots.d.ts +5 -0
- package/dist/material/texture-source.d.ts +34 -0
- package/dist/material/trim-sheet.d.ts +38 -0
- package/dist/neural/generator.d.ts +25 -0
- package/dist/neural/images.d.ts +17 -0
- package/dist/neural/index.d.ts +2 -0
- package/dist/neural/index.js +14 -0
- package/dist/neural/index.js.map +9 -0
- package/dist/passes/bake-ao.d.ts +17 -0
- package/dist/passes/grime.d.ts +15 -0
- package/dist/passes/index.d.ts +6 -0
- package/dist/passes/index.js +22 -0
- package/dist/passes/index.js.map +9 -0
- package/dist/passes/livery.d.ts +19 -0
- package/dist/passes/optimize-cache.d.ts +15 -0
- package/dist/passes/optimize-weld.d.ts +15 -0
- package/dist/passes/optimize.d.ts +28 -0
- package/dist/passes/ray-grid.d.ts +23 -0
- package/dist/passes/weighted-normals.d.ts +8 -0
- package/dist/primitives/beam.d.ts +17 -0
- package/dist/primitives/bevel-box.d.ts +13 -0
- package/dist/primitives/box.d.ts +16 -0
- package/dist/primitives/cell.d.ts +20 -0
- package/dist/primitives/check.d.ts +12 -0
- package/dist/primitives/closed.d.ts +11 -0
- package/dist/primitives/erosion.d.ts +22 -0
- package/dist/primitives/font-5x7.d.ts +3 -0
- package/dist/primitives/heightfield.d.ts +16 -0
- package/dist/primitives/index.d.ts +23 -0
- package/dist/primitives/index.js +81 -0
- package/dist/primitives/index.js.map +9 -0
- package/dist/primitives/lathe-facets.d.ts +8 -0
- package/dist/primitives/lathe.d.ts +26 -0
- package/dist/primitives/lattice.d.ts +30 -0
- package/dist/primitives/loft.d.ts +15 -0
- package/dist/primitives/mc-table.d.ts +2 -0
- package/dist/primitives/member.d.ts +19 -0
- package/dist/primitives/noise.d.ts +34 -0
- package/dist/primitives/planar.d.ts +10 -0
- package/dist/primitives/prism.d.ts +13 -0
- package/dist/primitives/sculpt-edit.d.ts +56 -0
- package/dist/primitives/sculpt-tree.d.ts +23 -0
- package/dist/primitives/sculpt.d.ts +27 -0
- package/dist/primitives/sdf-grid.d.ts +17 -0
- package/dist/primitives/sdf-ops.d.ts +37 -0
- package/dist/primitives/sdf.d.ts +21 -0
- package/dist/primitives/strip-uv.d.ts +11 -0
- package/dist/primitives/surface-nets.d.ts +7 -0
- package/dist/primitives/sweep.d.ts +19 -0
- package/dist/primitives/terrain-color.d.ts +30 -0
- package/dist/primitives/terrain-grid.d.ts +36 -0
- package/dist/primitives/text-layout.d.ts +22 -0
- package/dist/primitives/text.d.ts +27 -0
- package/dist/primitives/tiled.d.ts +22 -0
- package/dist/primitives/transport.d.ts +17 -0
- package/dist/primitives/triangulate.d.ts +2 -0
- package/dist/primitives/value-noise.d.ts +10 -0
- package/dist/qa/checks.d.ts +11 -0
- package/dist/qa/clash.d.ts +2 -0
- package/dist/qa/floating.d.ts +5 -0
- package/dist/qa/gates.d.ts +12 -0
- package/dist/qa/geometry.d.ts +21 -0
- package/dist/qa/index.d.ts +12 -0
- package/dist/qa/index.js +78 -0
- package/dist/qa/index.js.map +9 -0
- package/dist/qa/inside.d.ts +12 -0
- package/dist/qa/inspect.d.ts +18 -0
- package/dist/qa/lods.d.ts +12 -0
- package/dist/qa/measures.d.ts +4 -0
- package/dist/qa/occlusion.d.ts +3 -0
- package/dist/qa/sanity.d.ts +7 -0
- package/dist/qa/types.d.ts +12 -0
- package/dist/render/beauty.d.ts +40 -0
- package/dist/render/camera.d.ts +33 -0
- package/dist/render/depth-map.d.ts +38 -0
- package/dist/render/font.d.ts +1 -0
- package/dist/render/ground.d.ts +13 -0
- package/dist/render/index.d.ts +10 -0
- package/dist/render/index.js +47 -0
- package/dist/render/index.js.map +9 -0
- package/dist/render/lighting.d.ts +27 -0
- package/dist/render/lod-strip.d.ts +5 -0
- package/dist/render/metrics.d.ts +37 -0
- package/dist/render/options.d.ts +28 -0
- package/dist/render/photo-match.d.ts +19 -0
- package/dist/render/raster.d.ts +25 -0
- package/dist/render/resolve.d.ts +11 -0
- package/dist/render/review-prompt.d.ts +2 -0
- package/dist/render/review-warnings.d.ts +34 -0
- package/dist/render/review.d.ts +24 -0
- package/dist/render/scene.d.ts +18 -0
- package/dist/render/shade.d.ts +22 -0
- package/dist/render/sheet.d.ts +15 -0
- package/dist/render/texture.d.ts +15 -0
- package/dist/render/thumbnail.d.ts +8 -0
- package/dist/render/vis-buffer.d.ts +18 -0
- package/dist/testing/assert.d.ts +21 -0
- package/dist/testing/compiled.d.ts +15 -0
- package/dist/testing/hash.d.ts +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +134 -0
- package/dist/testing/index.js.map +13 -0
- package/dist/testing/snapshot.d.ts +13 -0
- package/docs/README.md +56 -0
- package/docs/architecture/README.md +68 -0
- package/docs/architecture/adding-a-kind.md +44 -0
- package/docs/architecture/adding-a-primitive.md +49 -0
- package/docs/architecture/errors.md +47 -0
- package/docs/architecture/glb-production.md +214 -0
- package/docs/architecture/gltf-capabilities.md +117 -0
- package/docs/architecture/materials.md +151 -0
- package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
- package/docs/decisions/002-typescript-not-json.md +22 -0
- package/docs/decisions/003-shared-trim-sheet.md +21 -0
- package/docs/decisions/004-z-up-metres.md +21 -0
- package/docs/decisions/005-zero-runtime-deps.md +21 -0
- package/docs/decisions/006-single-package-subpath-exports.md +20 -0
- package/docs/decisions/007-cpu-contact-sheets.md +21 -0
- package/docs/decisions/008-surface-materials.md +21 -0
- package/docs/decisions/009-gltf-front-axis.md +21 -0
- package/docs/decisions/README.md +15 -0
- package/docs/idea/01-vision.md +117 -0
- package/docs/idea/02-dsl.md +343 -0
- package/docs/idea/03-ir.md +97 -0
- package/docs/idea/04-materials.md +114 -0
- package/docs/idea/05-agent-loop.md +141 -0
- package/docs/idea/06-qa.md +101 -0
- package/docs/idea/07-what-fits.md +45 -0
- package/docs/idea/08-neural-adapters.md +78 -0
- package/docs/idea/09-kinds.md +105 -0
- package/docs/idea/10-sculpting.md +166 -0
- package/docs/idea/11-materials-authoring.md +132 -0
- package/docs/idea/12-blender-bridge.md +136 -0
- package/docs/idea/13-asset-pipeline-research.md +89 -0
- package/docs/landscape.md +49 -0
- package/docs/plans/roadmap.md +139 -0
- package/examples/build-with-bun.ts +41 -0
- package/llms.txt +47 -0
- package/package.json +100 -4
- package/recipes/README.md +38 -0
- package/recipes/asteroid/README.md +46 -0
- package/recipes/asteroid/asteroid.ts +336 -0
- package/recipes/boulders/README.md +28 -0
- package/recipes/boulders/boulders.ts +117 -0
- package/recipes/boxcar/README.md +53 -0
- package/recipes/boxcar/boxcar.ts +1000 -0
- package/recipes/cargo_container_scifi/README.md +52 -0
- package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
- package/recipes/dropship/README.md +84 -0
- package/recipes/dropship/dropship.ts +1144 -0
- package/recipes/eiffel_tower/README.md +110 -0
- package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
- package/recipes/goblin_head/README.md +75 -0
- package/recipes/goblin_head/goblin_head.ts +38 -0
- package/recipes/human_head/README.md +68 -0
- package/recipes/human_head/human_head.ts +38 -0
- package/recipes/loco_440/README.md +32 -0
- package/recipes/loco_440/loco_440.ts +1404 -0
- package/recipes/material_swatches/README.md +21 -0
- package/recipes/material_swatches/material_swatches.ts +189 -0
- package/recipes/mountain_range/README.md +45 -0
- package/recipes/mountain_range/mountain_range.ts +128 -0
- package/recipes/oak/README.md +30 -0
- package/recipes/oak/oak.ts +19 -0
- package/recipes/orc_head/README.md +78 -0
- package/recipes/orc_head/orc_head.ts +41 -0
- package/recipes/palm/README.md +30 -0
- package/recipes/palm/palm.ts +19 -0
- package/recipes/pine/README.md +29 -0
- package/recipes/pine/pine.ts +19 -0
- package/recipes/signal_gantry/README.md +36 -0
- package/recipes/signal_gantry/signal_gantry.ts +522 -0
- package/recipes/station_halt/README.md +39 -0
- package/recipes/station_halt/station_halt.ts +722 -0
- package/recipes/t34/README.md +44 -0
- package/recipes/t34/t34.ts +1068 -0
- package/recipes/tiger_1/README.md +75 -0
- package/recipes/tiger_1/tiger_1.ts +1370 -0
- package/recipes/water_tower/README.md +40 -0
- package/recipes/water_tower/water_tower.ts +542 -0
- package/src/cli/args.ts +125 -0
- package/src/cli/bin.ts +7 -0
- package/src/cli/load.ts +17 -0
- package/src/cli/manifest.ts +95 -0
- package/src/cli/render-cmds.ts +66 -0
- package/src/cli/run.ts +192 -0
- package/src/cli/texture-cache.ts +24 -0
- package/src/core/asset.ts +129 -0
- package/src/core/definition.ts +156 -0
- package/src/core/errors.ts +50 -0
- package/src/core/family.ts +50 -0
- package/src/core/index.ts +66 -0
- package/src/core/mesh.ts +114 -0
- package/src/core/part.ts +130 -0
- package/src/core/png.ts +49 -0
- package/src/core/rng.ts +49 -0
- package/src/core/transform.ts +90 -0
- package/src/core/vec.ts +26 -0
- package/src/core/world.ts +80 -0
- package/src/export/bin-writer.ts +79 -0
- package/src/export/container.ts +35 -0
- package/src/export/credits.ts +25 -0
- package/src/export/glb-options.ts +53 -0
- package/src/export/glb.ts +187 -0
- package/src/export/hierarchy.ts +122 -0
- package/src/export/index.ts +34 -0
- package/src/export/lights.ts +49 -0
- package/src/export/lod-coverage.ts +71 -0
- package/src/export/materials.ts +126 -0
- package/src/export/merge-pivots.ts +146 -0
- package/src/export/metadata.ts +62 -0
- package/src/export/motion.ts +70 -0
- package/src/export/optimize-parts.ts +80 -0
- package/src/export/primitive.ts +35 -0
- package/src/export/tangents.ts +75 -0
- package/src/export/textures.ts +88 -0
- package/src/export/version.ts +6 -0
- package/src/grammar/index.ts +4 -0
- package/src/grammar/split.ts +87 -0
- package/src/grammar/whyte.ts +20 -0
- package/src/index.ts +14 -0
- package/src/kinds/building.ts +153 -0
- package/src/kinds/faces.ts +138 -0
- package/src/kinds/head-armour.ts +181 -0
- package/src/kinds/head-beard.ts +133 -0
- package/src/kinds/head-bust.ts +184 -0
- package/src/kinds/head-extras.ts +150 -0
- package/src/kinds/head-face.ts +242 -0
- package/src/kinds/head-garb.ts +181 -0
- package/src/kinds/head-geom.ts +31 -0
- package/src/kinds/head-hair.ts +193 -0
- package/src/kinds/head-layout.ts +63 -0
- package/src/kinds/head-mesh.ts +195 -0
- package/src/kinds/head-mouth.ts +111 -0
- package/src/kinds/head-params.ts +162 -0
- package/src/kinds/head-presets.ts +155 -0
- package/src/kinds/head-rim.ts +128 -0
- package/src/kinds/head-tone.ts +52 -0
- package/src/kinds/head-torso.ts +230 -0
- package/src/kinds/head.ts +176 -0
- package/src/kinds/hull-lines.ts +160 -0
- package/src/kinds/hull-skin.ts +83 -0
- package/src/kinds/hull.ts +209 -0
- package/src/kinds/index.ts +63 -0
- package/src/kinds/mass.ts +110 -0
- package/src/kinds/openings.ts +141 -0
- package/src/kinds/planned.ts +12 -0
- package/src/kinds/roof-chimney.ts +100 -0
- package/src/kinds/roof-shape.ts +230 -0
- package/src/kinds/roof.ts +250 -0
- package/src/kinds/vegetation-fronds.ts +126 -0
- package/src/kinds/vegetation-impostor.ts +118 -0
- package/src/kinds/vegetation-leaves.ts +168 -0
- package/src/kinds/vegetation-mesh.ts +174 -0
- package/src/kinds/vegetation-params.ts +147 -0
- package/src/kinds/vegetation-presets.ts +184 -0
- package/src/kinds/vegetation-skeleton.ts +168 -0
- package/src/kinds/vegetation.ts +199 -0
- package/src/kinds/vehicle.ts +144 -0
- package/src/material/cc0.ts +41 -0
- package/src/material/define.ts +130 -0
- package/src/material/generator.ts +63 -0
- package/src/material/index.ts +60 -0
- package/src/material/library.ts +196 -0
- package/src/material/material-def.ts +84 -0
- package/src/material/paint/bond.ts +60 -0
- package/src/material/paint/canvas.ts +101 -0
- package/src/material/paint/cell.ts +39 -0
- package/src/material/paint/cells-round.ts +119 -0
- package/src/material/paint/cells.ts +111 -0
- package/src/material/paint/compose.ts +53 -0
- package/src/material/paint/concrete.ts +62 -0
- package/src/material/paint/courses.ts +85 -0
- package/src/material/paint/decode-png.ts +106 -0
- package/src/material/paint/default-sheet.ts +73 -0
- package/src/material/paint/fabric.ts +63 -0
- package/src/material/paint/foliage-broad.ts +126 -0
- package/src/material/paint/foliage-common.ts +93 -0
- package/src/material/paint/foliage-compound.ts +135 -0
- package/src/material/paint/foliage-conifer.ts +143 -0
- package/src/material/paint/foliage-raster.ts +196 -0
- package/src/material/paint/foliage.ts +23 -0
- package/src/material/paint/image-strip.ts +88 -0
- package/src/material/paint/kit.ts +37 -0
- package/src/material/paint/masks.ts +46 -0
- package/src/material/paint/metal.ts +109 -0
- package/src/material/paint/noise.ts +52 -0
- package/src/material/paint/normal.ts +52 -0
- package/src/material/paint/orm.ts +108 -0
- package/src/material/paint/planks.ts +101 -0
- package/src/material/paint/plates.ts +72 -0
- package/src/material/paint/shapes.ts +28 -0
- package/src/material/paint/slate.ts +52 -0
- package/src/material/paint/stamp.ts +40 -0
- package/src/material/paint/strip.ts +48 -0
- package/src/material/paint/surface.ts +60 -0
- package/src/material/paint/types.ts +94 -0
- package/src/material/paint/validate.ts +74 -0
- package/src/material/palette.ts +46 -0
- package/src/material/shader-godot.ts +77 -0
- package/src/material/shader-three.ts +103 -0
- package/src/material/shader.ts +100 -0
- package/src/material/slots.ts +18 -0
- package/src/material/texture-source.ts +86 -0
- package/src/material/trim-sheet.ts +166 -0
- package/src/neural/generator.ts +37 -0
- package/src/neural/images.ts +27 -0
- package/src/neural/index.ts +10 -0
- package/src/passes/bake-ao.ts +183 -0
- package/src/passes/grime.ts +37 -0
- package/src/passes/index.ts +8 -0
- package/src/passes/livery.ts +66 -0
- package/src/passes/optimize-cache.ts +175 -0
- package/src/passes/optimize-weld.ts +147 -0
- package/src/passes/optimize.ts +85 -0
- package/src/passes/ray-grid.ts +237 -0
- package/src/passes/weighted-normals.ts +125 -0
- package/src/primitives/beam.ts +60 -0
- package/src/primitives/bevel-box.ts +140 -0
- package/src/primitives/box.ts +90 -0
- package/src/primitives/cell.ts +115 -0
- package/src/primitives/check.ts +49 -0
- package/src/primitives/closed.ts +74 -0
- package/src/primitives/erosion.ts +183 -0
- package/src/primitives/font-5x7.ts +117 -0
- package/src/primitives/heightfield.ts +101 -0
- package/src/primitives/index.ts +56 -0
- package/src/primitives/lathe-facets.ts +45 -0
- package/src/primitives/lathe.ts +174 -0
- package/src/primitives/lattice.ts +131 -0
- package/src/primitives/loft.ts +174 -0
- package/src/primitives/mc-table.ts +41 -0
- package/src/primitives/member.ts +130 -0
- package/src/primitives/noise.ts +157 -0
- package/src/primitives/planar.ts +55 -0
- package/src/primitives/prism.ts +85 -0
- package/src/primitives/sculpt-edit.ts +212 -0
- package/src/primitives/sculpt-tree.ts +226 -0
- package/src/primitives/sculpt.ts +80 -0
- package/src/primitives/sdf-grid.ts +131 -0
- package/src/primitives/sdf-ops.ts +145 -0
- package/src/primitives/sdf.ts +83 -0
- package/src/primitives/strip-uv.ts +26 -0
- package/src/primitives/surface-nets.ts +161 -0
- package/src/primitives/sweep.ts +162 -0
- package/src/primitives/terrain-color.ts +85 -0
- package/src/primitives/terrain-grid.ts +137 -0
- package/src/primitives/text-layout.ts +85 -0
- package/src/primitives/text.ts +197 -0
- package/src/primitives/tiled.ts +93 -0
- package/src/primitives/transport.ts +85 -0
- package/src/primitives/triangulate.ts +42 -0
- package/src/primitives/value-noise.ts +62 -0
- package/src/qa/checks.ts +110 -0
- package/src/qa/clash.ts +177 -0
- package/src/qa/floating.ts +63 -0
- package/src/qa/gates.ts +126 -0
- package/src/qa/geometry.ts +187 -0
- package/src/qa/index.ts +28 -0
- package/src/qa/inside.ts +114 -0
- package/src/qa/inspect.ts +107 -0
- package/src/qa/lods.ts +97 -0
- package/src/qa/measures.ts +67 -0
- package/src/qa/occlusion.ts +166 -0
- package/src/qa/sanity.ts +137 -0
- package/src/qa/types.ts +17 -0
- package/src/render/beauty.ts +115 -0
- package/src/render/camera.ts +142 -0
- package/src/render/depth-map.ts +191 -0
- package/src/render/font.ts +76 -0
- package/src/render/ground.ts +87 -0
- package/src/render/index.ts +34 -0
- package/src/render/lighting.ts +121 -0
- package/src/render/lod-strip.ts +34 -0
- package/src/render/metrics.ts +147 -0
- package/src/render/options.ts +81 -0
- package/src/render/photo-match.ts +28 -0
- package/src/render/raster.ts +143 -0
- package/src/render/resolve.ts +109 -0
- package/src/render/review-prompt.ts +59 -0
- package/src/render/review-warnings.ts +92 -0
- package/src/render/review.ts +103 -0
- package/src/render/scene.ts +119 -0
- package/src/render/shade.ts +204 -0
- package/src/render/sheet.ts +88 -0
- package/src/render/texture.ts +97 -0
- package/src/render/thumbnail.ts +75 -0
- package/src/render/vis-buffer.ts +119 -0
- package/src/testing/assert.ts +115 -0
- package/src/testing/compiled.ts +31 -0
- package/src/testing/hash.ts +14 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/snapshot.ts +37 -0
- package/src/types/gltf-validator.d.ts +13 -0
|
@@ -0,0 +1,2532 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Ts23dError2,
|
|
3
|
+
MeshBuilder2,
|
|
4
|
+
frame2,
|
|
5
|
+
applyAffine2,
|
|
6
|
+
sub2,
|
|
7
|
+
dot2,
|
|
8
|
+
cross2,
|
|
9
|
+
length2,
|
|
10
|
+
normalize2
|
|
11
|
+
} from "./chunk-hg7pf34j.js";
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_SHEET2,
|
|
14
|
+
TRIM_SHEET_SIZE2,
|
|
15
|
+
stripRect2,
|
|
16
|
+
cellRect2
|
|
17
|
+
} from "./chunk-y63c6zcv.js";
|
|
18
|
+
|
|
19
|
+
// src/primitives/bevel-box.ts
|
|
20
|
+
var FACES = [
|
|
21
|
+
{ name: "front", u: [0, 1, 0], v: [0, 0, 1] },
|
|
22
|
+
{ name: "back", u: [0, -1, 0], v: [0, 0, 1] },
|
|
23
|
+
{ name: "left", u: [-1, 0, 0], v: [0, 0, 1] },
|
|
24
|
+
{ name: "right", u: [1, 0, 0], v: [0, 0, 1] },
|
|
25
|
+
{ name: "top", u: [1, 0, 0], v: [0, 1, 0] },
|
|
26
|
+
{ name: "bottom", u: [1, 0, 0], v: [0, -1, 0] }
|
|
27
|
+
];
|
|
28
|
+
var SIGNS = [-1, 1];
|
|
29
|
+
function bevelBox(o) {
|
|
30
|
+
const { half, centre, bevel: bv } = o;
|
|
31
|
+
const rect = stripRect2(o.strip);
|
|
32
|
+
const b = new MeshBuilder2;
|
|
33
|
+
const dot3 = (a, c) => a[0] * c[0] + a[1] * c[1] + a[2] * c[2];
|
|
34
|
+
const ext = (a) => Math.abs(a[0]) * half[0] + Math.abs(a[1]) * half[1] + Math.abs(a[2]) * half[2];
|
|
35
|
+
const point = (s, k) => [
|
|
36
|
+
centre[0] + (s[0] ?? 1) * (half[0] - (k === 0 ? 0 : bv)),
|
|
37
|
+
centre[1] + (s[1] ?? 1) * (half[1] - (k === 1 ? 0 : bv)),
|
|
38
|
+
centre[2] + (s[2] ?? 1) * (half[2] - (k === 2 ? 0 : bv))
|
|
39
|
+
];
|
|
40
|
+
const put = (s, k, n) => {
|
|
41
|
+
const f = FACES[k * 2 + ((s[k] ?? 1) > 0 ? 0 : 1)] ?? { u: n, v: n };
|
|
42
|
+
const p = point(s, k);
|
|
43
|
+
const rel = [p[0] - centre[0], p[1] - centre[1], p[2] - centre[2]];
|
|
44
|
+
const eu = ext(f.u);
|
|
45
|
+
const ev = ext(f.v);
|
|
46
|
+
return b.vertex({
|
|
47
|
+
p,
|
|
48
|
+
n,
|
|
49
|
+
uv: [
|
|
50
|
+
rect.u0 + (dot3(rel, f.u) + eu) / rect.metres * (rect.u1 - rect.u0),
|
|
51
|
+
rect.v0 + (dot3(rel, f.v) + ev) / (2 * ev) * (rect.v1 - rect.v0)
|
|
52
|
+
],
|
|
53
|
+
color: o.color
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const poly = (corners, n) => {
|
|
57
|
+
const pts = corners.map(([s, k]) => point(s, k));
|
|
58
|
+
const [a = n, c = n, d = n] = pts;
|
|
59
|
+
const e1 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
|
|
60
|
+
const e2 = [d[0] - a[0], d[1] - a[1], d[2] - a[2]];
|
|
61
|
+
const g = [
|
|
62
|
+
e1[1] * e2[2] - e1[2] * e2[1],
|
|
63
|
+
e1[2] * e2[0] - e1[0] * e2[2],
|
|
64
|
+
e1[0] * e2[1] - e1[1] * e2[0]
|
|
65
|
+
];
|
|
66
|
+
const ids = corners.map(([s, k]) => put(s, k, n));
|
|
67
|
+
if (dot3(g, n) < 0)
|
|
68
|
+
ids.reverse();
|
|
69
|
+
const [i0 = 0, i1 = 0, i2 = 0, i3] = ids;
|
|
70
|
+
if (i3 === undefined)
|
|
71
|
+
b.tri(i0, i1, i2);
|
|
72
|
+
else
|
|
73
|
+
b.quad(i0, i1, i2, i3);
|
|
74
|
+
};
|
|
75
|
+
const unit = (k, s) => [k === 0 ? s : 0, k === 1 ? s : 0, k === 2 ? s : 0];
|
|
76
|
+
const corner = (k, sk, l, sl, m, sm) => {
|
|
77
|
+
const s = [1, 1, 1];
|
|
78
|
+
s[k] = sk;
|
|
79
|
+
s[l] = sl;
|
|
80
|
+
s[m] = sm;
|
|
81
|
+
return s;
|
|
82
|
+
};
|
|
83
|
+
for (let k = 0;k < 3; k++) {
|
|
84
|
+
const [l, m] = [(k + 1) % 3, (k + 2) % 3];
|
|
85
|
+
for (const sk of SIGNS) {
|
|
86
|
+
if (o.omit.includes((FACES[k * 2 + (sk > 0 ? 0 : 1)] ?? { name: "top" }).name))
|
|
87
|
+
continue;
|
|
88
|
+
const ring = [
|
|
89
|
+
[-1, -1],
|
|
90
|
+
[1, -1],
|
|
91
|
+
[1, 1],
|
|
92
|
+
[-1, 1]
|
|
93
|
+
].map(([a, c]) => [corner(k, sk, l, a, m, c), k]);
|
|
94
|
+
poly(ring, unit(k, sk));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (let m = 0;m < 3; m++) {
|
|
98
|
+
const [k, l] = [(m + 1) % 3, (m + 2) % 3];
|
|
99
|
+
for (const sk of SIGNS)
|
|
100
|
+
for (const sl of SIGNS) {
|
|
101
|
+
const lo = corner(k, sk, l, sl, m, -1);
|
|
102
|
+
const hi = corner(k, sk, l, sl, m, 1);
|
|
103
|
+
const n = normalize2([0, 1, 2].map((j) => j === k ? sk : j === l ? sl : 0));
|
|
104
|
+
poly([
|
|
105
|
+
[lo, k],
|
|
106
|
+
[hi, k],
|
|
107
|
+
[hi, l],
|
|
108
|
+
[lo, l]
|
|
109
|
+
], n);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const sx of SIGNS)
|
|
113
|
+
for (const sy of SIGNS)
|
|
114
|
+
for (const sz of SIGNS) {
|
|
115
|
+
const s = [sx, sy, sz];
|
|
116
|
+
poly([
|
|
117
|
+
[s, 0],
|
|
118
|
+
[s, 1],
|
|
119
|
+
[s, 2]
|
|
120
|
+
], normalize2([sx, sy, sz]));
|
|
121
|
+
}
|
|
122
|
+
return b.build();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/primitives/check.ts
|
|
126
|
+
function invalid(cause, fix) {
|
|
127
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", { cause, fix });
|
|
128
|
+
}
|
|
129
|
+
var finite = (v) => v.every(Number.isFinite);
|
|
130
|
+
function spanOf(what, origin, u, v) {
|
|
131
|
+
if (!finite(origin) || !finite(u) || !finite(v))
|
|
132
|
+
invalid(`${what}: origin/u/v has a NaN or Infinity`, "pass finite metre vectors");
|
|
133
|
+
const lu = length2(u);
|
|
134
|
+
const lv = length2(v);
|
|
135
|
+
if (lu < 0.000000001 || lv < 0.000000001)
|
|
136
|
+
invalid(`${what}: edge u (${lu} m) or v (${lv} m) has zero length`, "give u and v the face edges in metres, both non-zero (e.g. u: [4, 0, 0], v: [0, 0, 3])");
|
|
137
|
+
const c = cross2(u, v);
|
|
138
|
+
const lc = length2(c);
|
|
139
|
+
if (lc < 0.000001 * lu * lv)
|
|
140
|
+
invalid(`${what}: u and v are parallel, so the face has no area`, "make v perpendicular to u (u along the face, v up it)");
|
|
141
|
+
return { lu, lv, n: [c[0] / lc, c[1] / lc, c[2] / lc] };
|
|
142
|
+
}
|
|
143
|
+
function count(what, n, min, max) {
|
|
144
|
+
if (!Number.isInteger(n) || n < min || n > max)
|
|
145
|
+
invalid(`${what} is ${n}`, `use a whole number from ${min} to ${max}`);
|
|
146
|
+
return n;
|
|
147
|
+
}
|
|
148
|
+
function positive(what, n, example) {
|
|
149
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
150
|
+
invalid(`${what} is ${n}`, `use a length > 0 m (${example})`);
|
|
151
|
+
return n;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/primitives/box.ts
|
|
155
|
+
var FACES2 = [
|
|
156
|
+
{ name: "front", n: [1, 0, 0], u: [0, 1, 0], v: [0, 0, 1] },
|
|
157
|
+
{ name: "back", n: [-1, 0, 0], u: [0, -1, 0], v: [0, 0, 1] },
|
|
158
|
+
{ name: "right", n: [0, -1, 0], u: [1, 0, 0], v: [0, 0, 1] },
|
|
159
|
+
{ name: "left", n: [0, 1, 0], u: [-1, 0, 0], v: [0, 0, 1] },
|
|
160
|
+
{ name: "top", n: [0, 0, 1], u: [1, 0, 0], v: [0, 1, 0] },
|
|
161
|
+
{ name: "bottom", n: [0, 0, -1], u: [1, 0, 0], v: [0, -1, 0] }
|
|
162
|
+
];
|
|
163
|
+
function box2(o) {
|
|
164
|
+
const [w, d, h] = o.size;
|
|
165
|
+
const [cx, cy, cz] = o.at ?? [0, 0, 0];
|
|
166
|
+
const half = [w / 2, d / 2, h / 2];
|
|
167
|
+
const centre = [cx, cy, cz + h / 2];
|
|
168
|
+
const bevel = o.bevel ?? 0;
|
|
169
|
+
if (bevel !== 0) {
|
|
170
|
+
const limit = Math.min(w, d, h) / 2;
|
|
171
|
+
if (!(bevel > 0 && bevel < limit))
|
|
172
|
+
invalid(`box bevel ${bevel} m does not fit a ${w} × ${d} × ${h} m box`, `use 0 < bevel < ${limit} m (half the smallest size), e.g. ${Math.min(0.02, limit / 4)}`);
|
|
173
|
+
return bevelBox({
|
|
174
|
+
half,
|
|
175
|
+
centre,
|
|
176
|
+
bevel,
|
|
177
|
+
color: o.color,
|
|
178
|
+
strip: o.strip ?? "plain",
|
|
179
|
+
omit: o.omit ?? []
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
183
|
+
const b = new MeshBuilder2;
|
|
184
|
+
for (const f of FACES2) {
|
|
185
|
+
if (o.omit?.includes(f.name))
|
|
186
|
+
continue;
|
|
187
|
+
const extent = (axis) => Math.abs(axis[0]) * half[0] + Math.abs(axis[1]) * half[1] + Math.abs(axis[2]) * half[2];
|
|
188
|
+
const eu = extent(f.u);
|
|
189
|
+
const ev = extent(f.v);
|
|
190
|
+
const en = extent(f.n);
|
|
191
|
+
const ids = [
|
|
192
|
+
[-1, -1],
|
|
193
|
+
[1, -1],
|
|
194
|
+
[1, 1],
|
|
195
|
+
[-1, 1]
|
|
196
|
+
].map(([su = 0, sv = 0]) => {
|
|
197
|
+
const p = [0, 1, 2].map((k) => (centre[k] ?? 0) + (f.n[k] ?? 0) * en + (f.u[k] ?? 0) * eu * su + (f.v[k] ?? 0) * ev * sv);
|
|
198
|
+
const uMetres = (su + 1) / 2 * eu * 2;
|
|
199
|
+
const vFrac = (sv + 1) / 2;
|
|
200
|
+
return b.vertex({
|
|
201
|
+
p,
|
|
202
|
+
n: f.n,
|
|
203
|
+
uv: [
|
|
204
|
+
rect.u0 + uMetres / rect.metres * (rect.u1 - rect.u0),
|
|
205
|
+
rect.v0 + vFrac * (rect.v1 - rect.v0)
|
|
206
|
+
],
|
|
207
|
+
color: o.color
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
const [a = 0, c = 0, e = 0, g = 0] = ids;
|
|
211
|
+
b.quad(a, c, e, g);
|
|
212
|
+
}
|
|
213
|
+
return b.build();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/primitives/lathe-facets.ts
|
|
217
|
+
function facets(o, put, tri, sideUv) {
|
|
218
|
+
const half = Math.PI / o.segments;
|
|
219
|
+
const corner = (s) => {
|
|
220
|
+
const t = s / o.segments * Math.PI * 2 - Math.PI / 2;
|
|
221
|
+
return [Math.cos(t), Math.sin(t)];
|
|
222
|
+
};
|
|
223
|
+
for (let i = 0;i + 1 < o.profile.length; i++) {
|
|
224
|
+
const [r0 = 0, z0 = 0] = o.profile[i] ?? [];
|
|
225
|
+
const [r1 = 0, z1 = 0] = o.profile[i + 1] ?? [];
|
|
226
|
+
for (let s = 0;s < o.segments; s++) {
|
|
227
|
+
const tm = (s + 0.5) / o.segments * Math.PI * 2 - Math.PI / 2;
|
|
228
|
+
const nx = (z1 - z0) * Math.cos(tm);
|
|
229
|
+
const ny = (z1 - z0) * Math.sin(tm);
|
|
230
|
+
const nz = -(r1 - r0) * Math.cos(half);
|
|
231
|
+
const l = Math.hypot(nx, ny, nz) || 1;
|
|
232
|
+
const n = [nx / l, ny / l, nz / l];
|
|
233
|
+
const [c0, s0] = corner(s);
|
|
234
|
+
const [c1, s1] = corner(s + 1);
|
|
235
|
+
const a = put([r0 * c0, r0 * s0, z0], n, sideUv(s, i, r0));
|
|
236
|
+
const bb = put([r0 * c1, r0 * s1, z0], n, sideUv(s + 1, i, r0));
|
|
237
|
+
const c = put([r1 * c1, r1 * s1, z1], n, sideUv(s + 1, i + 1, r1));
|
|
238
|
+
const d = put([r1 * c0, r1 * s0, z1], n, sideUv(s, i + 1, r1));
|
|
239
|
+
tri(a, bb, c);
|
|
240
|
+
tri(a, c, d);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/primitives/strip-uv.ts
|
|
246
|
+
var HALF_TEXEL = 0.5 / TRIM_SHEET_SIZE2;
|
|
247
|
+
var stripVMetres = (id) => DEFAULT_SHEET2.strips[id]?.scaleMetres ?? 1;
|
|
248
|
+
var stripU = (rect, metres) => rect.u0 + metres / rect.metres * (rect.u1 - rect.u0);
|
|
249
|
+
var stripV = (rect, frac) => rect.v0 + HALF_TEXEL + frac * (rect.v1 - rect.v0 - 2 * HALF_TEXEL);
|
|
250
|
+
function mirroredV(rect, metres, vMetres) {
|
|
251
|
+
const t = metres / vMetres;
|
|
252
|
+
const m = t - 2 * Math.floor(t / 2);
|
|
253
|
+
return stripV(rect, m <= 1 ? m : 2 - m);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/primitives/lathe.ts
|
|
257
|
+
var orient = (p, axis) => axis === "z" ? p : axis === "y" ? [p[0], p[2], p[1]] : [p[2], p[1], p[0]];
|
|
258
|
+
function lathe2(o) {
|
|
259
|
+
const axis = o.axis ?? "z";
|
|
260
|
+
const [ox, oy, oz] = o.at ?? [0, 0, 0];
|
|
261
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
262
|
+
const b = new MeshBuilder2;
|
|
263
|
+
const mirrored = axis !== "z";
|
|
264
|
+
const tri = (x, y, z) => {
|
|
265
|
+
if (mirrored)
|
|
266
|
+
b.tri(x, z, y);
|
|
267
|
+
else
|
|
268
|
+
b.tri(x, y, z);
|
|
269
|
+
};
|
|
270
|
+
const ring = [];
|
|
271
|
+
const put = (p, n, uv) => {
|
|
272
|
+
const q = orient(p, axis);
|
|
273
|
+
return b.vertex({
|
|
274
|
+
p: [q[0] + ox, q[1] + oy, q[2] + oz],
|
|
275
|
+
n: orient(n, axis),
|
|
276
|
+
uv,
|
|
277
|
+
color: o.color
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
const metres = o.uv === "metres";
|
|
281
|
+
const vm = stripVMetres(o.strip ?? "plain");
|
|
282
|
+
const run = [0];
|
|
283
|
+
for (let i = 1;i < o.profile.length; i++) {
|
|
284
|
+
const [r0 = 0, z0 = 0] = o.profile[i - 1] ?? [];
|
|
285
|
+
const [r1 = 0, z1 = 0] = o.profile[i] ?? [];
|
|
286
|
+
run.push((run[i - 1] ?? 0) + Math.hypot(r1 - r0, z1 - z0));
|
|
287
|
+
}
|
|
288
|
+
const sideUv = (s, i, r) => metres ? [stripU(rect, Math.PI * 2 * r * s / o.segments), mirroredV(rect, run[i] ?? 0, vm)] : [
|
|
289
|
+
rect.u0 + s / o.segments * (rect.u1 - rect.u0),
|
|
290
|
+
rect.v0 + i / Math.max(1, o.profile.length - 1) * (rect.v1 - rect.v0)
|
|
291
|
+
];
|
|
292
|
+
const capUv = (c, sn, r) => metres ? [stripU(rect, c * r), mirroredV(rect, (sn + 1) * r, vm)] : [
|
|
293
|
+
rect.u0 + (0.5 + c / 2) * (rect.u1 - rect.u0),
|
|
294
|
+
rect.v0 + (0.5 + sn / 2) * (rect.v1 - rect.v0)
|
|
295
|
+
];
|
|
296
|
+
const creaseCos = Math.cos((o.creaseDeg ?? 50) * Math.PI / 180);
|
|
297
|
+
const emitRing = (i, dr, dz) => {
|
|
298
|
+
const [r, z] = o.profile[i] ?? [0, 0];
|
|
299
|
+
const len = Math.hypot(dr, dz) || 1;
|
|
300
|
+
const row = [];
|
|
301
|
+
for (let s = 0;s <= o.segments; s++) {
|
|
302
|
+
const t = s / o.segments * Math.PI * 2 - Math.PI / 2;
|
|
303
|
+
const c = Math.cos(t);
|
|
304
|
+
const sn = Math.sin(t);
|
|
305
|
+
row.push(put([r * c, r * sn, z], [dz / len * c, dz / len * sn, -dr / len], sideUv(s, i, r)));
|
|
306
|
+
}
|
|
307
|
+
return row;
|
|
308
|
+
};
|
|
309
|
+
const top = [];
|
|
310
|
+
if (o.faceted)
|
|
311
|
+
facets(o, put, tri, sideUv);
|
|
312
|
+
else
|
|
313
|
+
o.profile.forEach(([r, z], i) => {
|
|
314
|
+
const prev = o.profile[i - 1] ?? o.profile[i] ?? [r, z];
|
|
315
|
+
const next = o.profile[i + 1] ?? o.profile[i] ?? [r, z];
|
|
316
|
+
const ir = r - prev[0];
|
|
317
|
+
const iz = z - prev[1];
|
|
318
|
+
const or = next[0] - r;
|
|
319
|
+
const oz = next[1] - z;
|
|
320
|
+
const li = Math.hypot(ir, iz);
|
|
321
|
+
const lo = Math.hypot(or, oz);
|
|
322
|
+
if (li > 0.000000000001 && lo > 0.000000000001 && (ir * or + iz * oz) / (li * lo) < creaseCos) {
|
|
323
|
+
top.push(emitRing(i, ir, iz));
|
|
324
|
+
ring.push(emitRing(i, or, oz));
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const row = emitRing(i, next[0] - prev[0], next[1] - prev[1]);
|
|
328
|
+
top.push(row);
|
|
329
|
+
ring.push(row);
|
|
330
|
+
});
|
|
331
|
+
for (let i = 0;i + 1 < ring.length; i++) {
|
|
332
|
+
for (let s = 0;s < o.segments; s++) {
|
|
333
|
+
const a = ring[i]?.[s] ?? 0;
|
|
334
|
+
const bb = ring[i]?.[s + 1] ?? 0;
|
|
335
|
+
const c = top[i + 1]?.[s + 1] ?? 0;
|
|
336
|
+
const d = top[i + 1]?.[s] ?? 0;
|
|
337
|
+
tri(a, bb, c);
|
|
338
|
+
tri(a, c, d);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (o.caps) {
|
|
342
|
+
const ends = [
|
|
343
|
+
{ pt: o.profile[0], n: -1 },
|
|
344
|
+
{ pt: o.profile[o.profile.length - 1], n: 1 }
|
|
345
|
+
];
|
|
346
|
+
for (const { pt, n } of ends) {
|
|
347
|
+
if (!pt)
|
|
348
|
+
continue;
|
|
349
|
+
const centre = put([0, 0, pt[1]], [0, 0, n], capUv(0, 0, pt[0]));
|
|
350
|
+
const rim = [];
|
|
351
|
+
for (let s = 0;s <= o.segments; s++) {
|
|
352
|
+
const t = s / o.segments * Math.PI * 2 - Math.PI / 2;
|
|
353
|
+
rim.push(put([pt[0] * Math.cos(t), pt[0] * Math.sin(t), pt[1]], [0, 0, n], capUv(Math.cos(t), Math.sin(t), pt[0])));
|
|
354
|
+
}
|
|
355
|
+
for (let s = 0;s < o.segments; s++) {
|
|
356
|
+
const r0 = rim[s] ?? 0;
|
|
357
|
+
const r1 = rim[s + 1] ?? 0;
|
|
358
|
+
if (n > 0)
|
|
359
|
+
tri(centre, r0, r1);
|
|
360
|
+
else
|
|
361
|
+
tri(centre, r1, r0);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return b.build();
|
|
366
|
+
}
|
|
367
|
+
var cylinder2 = (o) => lathe2({
|
|
368
|
+
...o,
|
|
369
|
+
profile: [
|
|
370
|
+
[o.radius, -o.length / 2],
|
|
371
|
+
[o.radius, o.length / 2]
|
|
372
|
+
],
|
|
373
|
+
caps: true
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// src/primitives/beam.ts
|
|
377
|
+
function beam2(o) {
|
|
378
|
+
const dir = sub2(o.to, o.from);
|
|
379
|
+
const len = length2(dir);
|
|
380
|
+
const w = normalize2(dir);
|
|
381
|
+
const ref = o.up ?? (Math.abs(w[2]) > 0.95 ? [1, 0, 0] : [0, 0, 1]);
|
|
382
|
+
const u = normalize2(cross2(ref, w));
|
|
383
|
+
const v = cross2(w, u);
|
|
384
|
+
const local = o.segments && o.segments >= 3 ? lathe2({
|
|
385
|
+
profile: [
|
|
386
|
+
[o.width / 2, 0],
|
|
387
|
+
[o.width / 2, len]
|
|
388
|
+
],
|
|
389
|
+
segments: o.segments,
|
|
390
|
+
color: o.color,
|
|
391
|
+
caps: o.caps ?? true,
|
|
392
|
+
...o.strip ? { strip: o.strip } : {}
|
|
393
|
+
}) : box2({
|
|
394
|
+
size: [o.width, o.height ?? o.width, len],
|
|
395
|
+
...o.caps === false ? { omit: ["top", "bottom"] } : {},
|
|
396
|
+
color: o.color,
|
|
397
|
+
...o.strip ? { strip: o.strip } : {}
|
|
398
|
+
});
|
|
399
|
+
return applyAffine2(local, frame2(u, v, w, o.from));
|
|
400
|
+
}
|
|
401
|
+
// src/primitives/cell.ts
|
|
402
|
+
var add3 = (a, b, s) => [
|
|
403
|
+
a[0] + b[0] * s,
|
|
404
|
+
a[1] + b[1] * s,
|
|
405
|
+
a[2] + b[2] * s
|
|
406
|
+
];
|
|
407
|
+
function cell2(o) {
|
|
408
|
+
const { lu, lv, n } = spanOf("cell", o.origin, o.u, o.v);
|
|
409
|
+
const d = o.inset ?? 0;
|
|
410
|
+
if (!Number.isFinite(d))
|
|
411
|
+
invalid(`cell inset is ${d}`, "use a finite depth in metres (0.1 = a 10 cm recess)");
|
|
412
|
+
if (d !== 0 && o.doubleSided)
|
|
413
|
+
invalid("cell: inset with doubleSided", "drop doubleSided for a recessed/raised panel, or set inset: 0");
|
|
414
|
+
const r = cellRect2(o.cell);
|
|
415
|
+
const e = HALF_TEXEL;
|
|
416
|
+
const rim = [
|
|
417
|
+
o.origin,
|
|
418
|
+
add3(o.origin, o.u, 1),
|
|
419
|
+
add3(add3(o.origin, o.u, 1), o.v, 1),
|
|
420
|
+
add3(o.origin, o.v, 1)
|
|
421
|
+
];
|
|
422
|
+
const face = rim.map((p) => add3(p, n, -d));
|
|
423
|
+
const uvs = [
|
|
424
|
+
[r.u0 + e, r.v0 + e],
|
|
425
|
+
[r.u1 - e, r.v0 + e],
|
|
426
|
+
[r.u1 - e, r.v1 - e],
|
|
427
|
+
[r.u0 + e, r.v1 - e]
|
|
428
|
+
];
|
|
429
|
+
const b = new MeshBuilder2;
|
|
430
|
+
for (const side of o.doubleSided ? [1, -1] : [1]) {
|
|
431
|
+
const nn = [n[0] * side, n[1] * side, n[2] * side];
|
|
432
|
+
const [i0 = 0, i1 = 0, i2 = 0, i3 = 0] = face.map((p, k) => b.vertex({ p, n: nn, uv: uvs[k] ?? [0, 0], color: o.color }));
|
|
433
|
+
if (side > 0)
|
|
434
|
+
b.quad(i0, i1, i2, i3);
|
|
435
|
+
else
|
|
436
|
+
b.quad(i0, i3, i2, i1);
|
|
437
|
+
}
|
|
438
|
+
if (d !== 0)
|
|
439
|
+
reveals(b, o, rim, face, n, d, [lu, lv, lu, lv]);
|
|
440
|
+
return b.build();
|
|
441
|
+
}
|
|
442
|
+
function reveals(b, o, rim, face, n, d, lens) {
|
|
443
|
+
const strip = o.reveal ?? "plain";
|
|
444
|
+
const rect = stripRect2(strip);
|
|
445
|
+
const fv = Math.min(1, Math.abs(d) / stripVMetres(strip));
|
|
446
|
+
const color = o.revealColor ?? o.color;
|
|
447
|
+
for (let i = 0;i < 4; i++) {
|
|
448
|
+
const a = rim[i] ?? n;
|
|
449
|
+
const c = rim[(i + 1) % 4] ?? n;
|
|
450
|
+
const ed = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
|
|
451
|
+
const s = Math.sign(d);
|
|
452
|
+
const nn = normalize2([
|
|
453
|
+
(n[1] * ed[2] - n[2] * ed[1]) * s,
|
|
454
|
+
(n[2] * ed[0] - n[0] * ed[2]) * s,
|
|
455
|
+
(n[0] * ed[1] - n[1] * ed[0]) * s
|
|
456
|
+
]);
|
|
457
|
+
const len = lens[i] ?? 0;
|
|
458
|
+
const q = [a, c, face[(i + 1) % 4] ?? c, face[i] ?? a];
|
|
459
|
+
const uv = [
|
|
460
|
+
[stripU(rect, 0), stripV(rect, 0)],
|
|
461
|
+
[stripU(rect, len), stripV(rect, 0)],
|
|
462
|
+
[stripU(rect, len), stripV(rect, fv)],
|
|
463
|
+
[stripU(rect, 0), stripV(rect, fv)]
|
|
464
|
+
];
|
|
465
|
+
const [j0 = 0, j1 = 0, j2 = 0, j3 = 0] = q.map((p, k) => b.vertex({ p, n: nn, uv: uv[k] ?? [0, 0], color }));
|
|
466
|
+
b.quad(j0, j1, j2, j3);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// src/primitives/erosion.ts
|
|
470
|
+
var put = (h, dep, k, a) => {
|
|
471
|
+
h[k] = (h[k] ?? 0) + a;
|
|
472
|
+
dep[k] = (dep[k] ?? 0) + a;
|
|
473
|
+
};
|
|
474
|
+
function makeBrush(radius) {
|
|
475
|
+
const dx = [];
|
|
476
|
+
const dy = [];
|
|
477
|
+
const w = [];
|
|
478
|
+
for (let y = -radius;y <= radius; y++)
|
|
479
|
+
for (let x = -radius;x <= radius; x++) {
|
|
480
|
+
const d = Math.sqrt(x * x + y * y);
|
|
481
|
+
if (d < radius) {
|
|
482
|
+
dx.push(x);
|
|
483
|
+
dy.push(y);
|
|
484
|
+
w.push(radius - d);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const sum = w.reduce((s, v) => s + v, 0);
|
|
488
|
+
return {
|
|
489
|
+
dx: Int32Array.from(dx),
|
|
490
|
+
dy: Int32Array.from(dy),
|
|
491
|
+
w: Float64Array.from(w, (v) => v / sum)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function droplet(s, b, x0, y0) {
|
|
495
|
+
const { h, nx, ny } = s;
|
|
496
|
+
let x = x0;
|
|
497
|
+
let y = y0;
|
|
498
|
+
let dx = 0;
|
|
499
|
+
let dy = 0;
|
|
500
|
+
let speed = 1;
|
|
501
|
+
let water = 1;
|
|
502
|
+
let sediment = 0;
|
|
503
|
+
for (let step = 0;step < s.life; step++) {
|
|
504
|
+
const i = Math.floor(x);
|
|
505
|
+
const j = Math.floor(y);
|
|
506
|
+
const u = x - i;
|
|
507
|
+
const v = y - j;
|
|
508
|
+
const k = j * nx + i;
|
|
509
|
+
const a = h[k] ?? 0;
|
|
510
|
+
const bb = h[k + 1] ?? 0;
|
|
511
|
+
const c = h[k + nx] ?? 0;
|
|
512
|
+
const d = h[k + nx + 1] ?? 0;
|
|
513
|
+
const height = a * (1 - u) * (1 - v) + bb * u * (1 - v) + c * (1 - u) * v + d * u * v;
|
|
514
|
+
const gx = (bb - a) * (1 - v) + (d - c) * v;
|
|
515
|
+
const gy = (c - a) * (1 - u) + (d - bb) * u;
|
|
516
|
+
dx = dx * s.inertia - gx * (1 - s.inertia);
|
|
517
|
+
dy = dy * s.inertia - gy * (1 - s.inertia);
|
|
518
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
519
|
+
if (len === 0)
|
|
520
|
+
return;
|
|
521
|
+
dx /= len;
|
|
522
|
+
dy /= len;
|
|
523
|
+
x += dx;
|
|
524
|
+
y += dy;
|
|
525
|
+
s.flow[k] = (s.flow[k] ?? 0) + water;
|
|
526
|
+
if (x < 0 || y < 0 || x >= nx - 1 || y >= ny - 1)
|
|
527
|
+
return;
|
|
528
|
+
const dh = bilinear(h, nx, x, y) - height;
|
|
529
|
+
const cap = Math.max(-dh * speed * water * s.capK, s.minCap);
|
|
530
|
+
if (sediment > cap || dh > 0) {
|
|
531
|
+
const amount = dh > 0 ? Math.min(dh, sediment) : (sediment - cap) * s.depositK;
|
|
532
|
+
sediment -= amount;
|
|
533
|
+
put(h, s.dep, k, amount * (1 - u) * (1 - v));
|
|
534
|
+
put(h, s.dep, k + 1, amount * u * (1 - v));
|
|
535
|
+
put(h, s.dep, k + nx, amount * (1 - u) * v);
|
|
536
|
+
put(h, s.dep, k + nx + 1, amount * u * v);
|
|
537
|
+
} else {
|
|
538
|
+
const amount = Math.min((cap - sediment) * s.erodeK, -dh);
|
|
539
|
+
for (let n = 0;n < b.w.length; n++) {
|
|
540
|
+
const ci = i + (b.dx[n] ?? 0);
|
|
541
|
+
const cj = j + (b.dy[n] ?? 0);
|
|
542
|
+
if (ci < 0 || cj < 0 || ci >= nx || cj >= ny)
|
|
543
|
+
continue;
|
|
544
|
+
const cell = cj * nx + ci;
|
|
545
|
+
const take = Math.min(h[cell] ?? 0, amount * (b.w[n] ?? 0));
|
|
546
|
+
h[cell] = (h[cell] ?? 0) - take;
|
|
547
|
+
sediment += take;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
speed = Math.sqrt(Math.max(0, speed * speed - dh * s.gravity));
|
|
551
|
+
water *= 1 - s.evap;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function bilinear(h, nx, x, y) {
|
|
555
|
+
const i = Math.floor(x);
|
|
556
|
+
const j = Math.floor(y);
|
|
557
|
+
const u = x - i;
|
|
558
|
+
const v = y - j;
|
|
559
|
+
const k = j * nx + i;
|
|
560
|
+
const a = h[k] ?? 0;
|
|
561
|
+
const b = h[k + 1] ?? 0;
|
|
562
|
+
const c = h[k + nx] ?? 0;
|
|
563
|
+
const d = h[k + nx + 1] ?? 0;
|
|
564
|
+
return a * (1 - u) * (1 - v) + b * u * (1 - v) + c * (1 - u) * v + d * u * v;
|
|
565
|
+
}
|
|
566
|
+
function hydraulicErosion2(g, rng, o = {}) {
|
|
567
|
+
const drops = count("erosion droplets", o.droplets ?? 70000, 0, 1e7);
|
|
568
|
+
const life = count("erosion lifetime", o.lifetime ?? 30, 1, 1000);
|
|
569
|
+
const radius = count("erosion radius", o.radius ?? 3, 1, 32);
|
|
570
|
+
const { nx, ny } = g;
|
|
571
|
+
let lo = Number.POSITIVE_INFINITY;
|
|
572
|
+
let hi = Number.NEGATIVE_INFINITY;
|
|
573
|
+
for (const v of g.z) {
|
|
574
|
+
lo = Math.min(lo, v);
|
|
575
|
+
hi = Math.max(hi, v);
|
|
576
|
+
}
|
|
577
|
+
const relief = hi - lo || 1;
|
|
578
|
+
const h = Float64Array.from(g.z, (v) => (v - lo) / relief);
|
|
579
|
+
const flow = new Float64Array(h.length);
|
|
580
|
+
const dep = new Float64Array(h.length);
|
|
581
|
+
const sim = {
|
|
582
|
+
...{ h, flow, dep, nx, ny, life, inertia: o.inertia ?? 0.05, capK: o.capacity ?? 4 },
|
|
583
|
+
...{ minCap: o.minCapacity ?? 0.01, erodeK: o.erode ?? 0.3, depositK: o.deposit ?? 0.3 },
|
|
584
|
+
...{ evap: o.evaporate ?? 0.01, gravity: o.gravity ?? 4 }
|
|
585
|
+
};
|
|
586
|
+
const brush = makeBrush(radius);
|
|
587
|
+
for (let n = 0;n < drops; n++)
|
|
588
|
+
droplet(sim, brush, rng.next() * (nx - 2), rng.next() * (ny - 2));
|
|
589
|
+
const norm = (m) => {
|
|
590
|
+
let top = 0;
|
|
591
|
+
for (const v of m)
|
|
592
|
+
top = Math.max(top, v);
|
|
593
|
+
return top > 0 ? m.map((v) => Math.sqrt(v / top)) : m;
|
|
594
|
+
};
|
|
595
|
+
return { ...g, z: h.map((v) => lo + v * relief), flow: norm(flow), deposit: norm(dep) };
|
|
596
|
+
}
|
|
597
|
+
// src/primitives/heightfield.ts
|
|
598
|
+
function heightfield2(o) {
|
|
599
|
+
const [x0, y0, x1, y1] = o.rect;
|
|
600
|
+
const [nx, ny] = o.cells;
|
|
601
|
+
const dx = (x1 - x0) / nx;
|
|
602
|
+
const dy = (y1 - y0) / ny;
|
|
603
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
604
|
+
const vm = stripVMetres(o.strip ?? "plain");
|
|
605
|
+
const b = new MeshBuilder2;
|
|
606
|
+
const ids = [];
|
|
607
|
+
for (let j = 0;j <= ny; j++) {
|
|
608
|
+
for (let i = 0;i <= nx; i++) {
|
|
609
|
+
const x = x0 + i * dx;
|
|
610
|
+
const y = y0 + j * dy;
|
|
611
|
+
const z = o.height(x, y);
|
|
612
|
+
const n = normalize2([
|
|
613
|
+
o.height(x - dx, y) - o.height(x + dx, y),
|
|
614
|
+
o.height(x, y - dy) - o.height(x, y + dy),
|
|
615
|
+
2 * Math.min(dx, dy)
|
|
616
|
+
]);
|
|
617
|
+
const color = typeof o.color === "function" ? o.color(x, y, z) : o.color;
|
|
618
|
+
const u = (x - x0) / rect.metres;
|
|
619
|
+
ids.push(b.vertex({
|
|
620
|
+
p: [x, y, z],
|
|
621
|
+
n,
|
|
622
|
+
uv: [rect.u0 + u * (rect.u1 - rect.u0), mirroredV(rect, y - y0, vm)],
|
|
623
|
+
color
|
|
624
|
+
}));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
const at = (i, j) => ids[j * (nx + 1) + i] ?? 0;
|
|
628
|
+
for (let j = 0;j < ny; j++)
|
|
629
|
+
for (let i = 0;i < nx; i++)
|
|
630
|
+
b.quad(at(i, j), at(i + 1, j), at(i + 1, j + 1), at(i, j + 1));
|
|
631
|
+
if (o.skirt !== undefined)
|
|
632
|
+
skirt(b, o, rect);
|
|
633
|
+
return b.build();
|
|
634
|
+
}
|
|
635
|
+
function skirt(b, o, rect) {
|
|
636
|
+
const [x0, y0, x1, y1] = o.rect;
|
|
637
|
+
const [nx, ny] = o.cells;
|
|
638
|
+
const base = o.skirt ?? 0;
|
|
639
|
+
const edges = [
|
|
640
|
+
[x0, y0, (x1 - x0) / nx, 0, nx, [0, -1, 0]],
|
|
641
|
+
[x1, y0, 0, (y1 - y0) / ny, ny, [1, 0, 0]],
|
|
642
|
+
[x1, y1, -(x1 - x0) / nx, 0, nx, [0, 1, 0]],
|
|
643
|
+
[x0, y1, 0, -(y1 - y0) / ny, ny, [-1, 0, 0]]
|
|
644
|
+
];
|
|
645
|
+
const col = (x, y, z) => o.skirtColor ?? (typeof o.color === "function" ? o.color(x, y, z) : o.color);
|
|
646
|
+
const v = (x, y, z, n, u, color) => b.vertex({
|
|
647
|
+
p: [x, y, z],
|
|
648
|
+
n,
|
|
649
|
+
uv: [rect.u0 + u / rect.metres * (rect.u1 - rect.u0), z > base ? rect.v1 : rect.v0],
|
|
650
|
+
color
|
|
651
|
+
});
|
|
652
|
+
let run = 0;
|
|
653
|
+
for (const [sx, sy, dx, dy, n, nrm] of edges) {
|
|
654
|
+
const step = Math.hypot(dx, dy);
|
|
655
|
+
for (let k = 0;k < n; k++) {
|
|
656
|
+
const [ax, ay, bx, by] = [sx + dx * k, sy + dy * k, sx + dx * (k + 1), sy + dy * (k + 1)];
|
|
657
|
+
const az = o.height(ax, ay);
|
|
658
|
+
const bz = o.height(bx, by);
|
|
659
|
+
const ca = col(ax, ay, az);
|
|
660
|
+
const cb = col(bx, by, bz);
|
|
661
|
+
const ta = v(ax, ay, az, nrm, run, ca);
|
|
662
|
+
const tb = v(bx, by, bz, nrm, run + step, cb);
|
|
663
|
+
b.quad(v(ax, ay, base, nrm, run, ca), v(bx, by, base, nrm, run + step, cb), tb, ta);
|
|
664
|
+
run += step;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// src/primitives/triangulate.ts
|
|
669
|
+
var area = (p) => p.reduce((s, a, i) => {
|
|
670
|
+
const b = p[(i + 1) % p.length] ?? a;
|
|
671
|
+
return s + a[0] * b[1] - b[0] * a[1];
|
|
672
|
+
}, 0) / 2;
|
|
673
|
+
var cross = (a, b, c) => (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
|
|
674
|
+
function inside(p, a, b, c) {
|
|
675
|
+
return cross(a, b, p) >= 0 && cross(b, c, p) >= 0 && cross(c, a, p) >= 0;
|
|
676
|
+
}
|
|
677
|
+
function triangulate2(poly) {
|
|
678
|
+
const idx = poly.map((_, i) => i);
|
|
679
|
+
if (area(poly) < 0)
|
|
680
|
+
idx.reverse();
|
|
681
|
+
const out = [];
|
|
682
|
+
let guard = idx.length * idx.length;
|
|
683
|
+
while (idx.length > 3 && guard-- > 0) {
|
|
684
|
+
for (let i = 0;i < idx.length; i++) {
|
|
685
|
+
const ia = idx[(i + idx.length - 1) % idx.length] ?? 0;
|
|
686
|
+
const ib = idx[i] ?? 0;
|
|
687
|
+
const ic = idx[(i + 1) % idx.length] ?? 0;
|
|
688
|
+
const a = poly[ia] ?? [0, 0];
|
|
689
|
+
const b = poly[ib] ?? [0, 0];
|
|
690
|
+
const c = poly[ic] ?? [0, 0];
|
|
691
|
+
if (cross(a, b, c) <= 0)
|
|
692
|
+
continue;
|
|
693
|
+
if (idx.some((j) => j !== ia && j !== ib && j !== ic && inside(poly[j] ?? [0, 0], a, b, c)))
|
|
694
|
+
continue;
|
|
695
|
+
out.push([ia, ib, ic]);
|
|
696
|
+
idx.splice(i, 1);
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (idx.length === 3)
|
|
701
|
+
out.push([idx[0] ?? 0, idx[1] ?? 0, idx[2] ?? 0]);
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/primitives/member.ts
|
|
706
|
+
function section(kind, w) {
|
|
707
|
+
const h = w / 2;
|
|
708
|
+
const t = w * 0.15;
|
|
709
|
+
const ring = kind === "square" ? [
|
|
710
|
+
[-h, -h],
|
|
711
|
+
[h, -h],
|
|
712
|
+
[h, h],
|
|
713
|
+
[-h, h]
|
|
714
|
+
] : [
|
|
715
|
+
[-h, -h],
|
|
716
|
+
[h, -h],
|
|
717
|
+
[h, -h + t],
|
|
718
|
+
[-h + t, -h + t],
|
|
719
|
+
[-h + t, h],
|
|
720
|
+
[-h, h]
|
|
721
|
+
];
|
|
722
|
+
const lens = ring.map((a, i) => {
|
|
723
|
+
const b = ring[(i + 1) % ring.length] ?? a;
|
|
724
|
+
return Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
725
|
+
});
|
|
726
|
+
const total = lens.reduce((s, l) => s + l, 0);
|
|
727
|
+
let acc = 0;
|
|
728
|
+
const run = lens.map((l) => {
|
|
729
|
+
const r = acc / total;
|
|
730
|
+
acc += l;
|
|
731
|
+
return r;
|
|
732
|
+
});
|
|
733
|
+
return { ring, caps: triangulate2(ring), run, sideTris: ring.length * 2 };
|
|
734
|
+
}
|
|
735
|
+
function emitMember(mb, s, a, b, y, caps, color, rect) {
|
|
736
|
+
const d = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
|
737
|
+
const len = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
|
|
738
|
+
const w = [d[0] / len, d[1] / len, d[2] / len];
|
|
739
|
+
const k = y[0] * w[0] + y[1] * w[1] + y[2] * w[2];
|
|
740
|
+
const yr = [y[0] - w[0] * k, y[1] - w[1] * k, y[2] - w[2] * k];
|
|
741
|
+
const yl = Math.sqrt(yr[0] * yr[0] + yr[1] * yr[1] + yr[2] * yr[2]);
|
|
742
|
+
const yy = [yr[0] / yl, yr[1] / yl, yr[2] / yl];
|
|
743
|
+
const x = [
|
|
744
|
+
yy[1] * w[2] - yy[2] * w[1],
|
|
745
|
+
yy[2] * w[0] - yy[0] * w[2],
|
|
746
|
+
yy[0] * w[1] - yy[1] * w[0]
|
|
747
|
+
];
|
|
748
|
+
const at = (p, end) => [
|
|
749
|
+
end[0] + x[0] * p[0] + yy[0] * p[1],
|
|
750
|
+
end[1] + x[1] * p[0] + yy[1] * p[1],
|
|
751
|
+
end[2] + x[2] * p[0] + yy[2] * p[1]
|
|
752
|
+
];
|
|
753
|
+
const n = s.ring.length;
|
|
754
|
+
const u1 = stripU(rect, len);
|
|
755
|
+
const u0 = stripU(rect, 0);
|
|
756
|
+
for (let i = 0;i < n; i++) {
|
|
757
|
+
const p = s.ring[i] ?? [0, 0];
|
|
758
|
+
const q = s.ring[(i + 1) % n] ?? p;
|
|
759
|
+
const ex = q[0] - p[0];
|
|
760
|
+
const ey = q[1] - p[1];
|
|
761
|
+
const el = Math.hypot(ex, ey);
|
|
762
|
+
const nx = ey / el;
|
|
763
|
+
const ny = -ex / el;
|
|
764
|
+
const nrm = [x[0] * nx + yy[0] * ny, x[1] * nx + yy[1] * ny, x[2] * nx + yy[2] * ny];
|
|
765
|
+
const v0 = stripV(rect, s.run[i] ?? 0);
|
|
766
|
+
const v1 = stripV(rect, i + 1 < n ? s.run[i + 1] ?? 1 : 1);
|
|
767
|
+
const i0 = mb.vertex({ p: at(p, a), n: nrm, uv: [u0, v0], color });
|
|
768
|
+
const i1 = mb.vertex({ p: at(q, a), n: nrm, uv: [u0, v1], color });
|
|
769
|
+
const i2 = mb.vertex({ p: at(q, b), n: nrm, uv: [u1, v1], color });
|
|
770
|
+
const i3 = mb.vertex({ p: at(p, b), n: nrm, uv: [u1, v0], color });
|
|
771
|
+
mb.quad(i0, i1, i2, i3);
|
|
772
|
+
}
|
|
773
|
+
if (!caps)
|
|
774
|
+
return;
|
|
775
|
+
for (const [end, sign] of [
|
|
776
|
+
[a, -1],
|
|
777
|
+
[b, 1]
|
|
778
|
+
]) {
|
|
779
|
+
const nrm = [w[0] * sign, w[1] * sign, w[2] * sign];
|
|
780
|
+
const first = mb.vertex({
|
|
781
|
+
p: at(s.ring[0] ?? [0, 0], end),
|
|
782
|
+
n: nrm,
|
|
783
|
+
uv: [u0, stripV(rect, 0)],
|
|
784
|
+
color
|
|
785
|
+
});
|
|
786
|
+
for (let i = 1;i < n; i++) {
|
|
787
|
+
const p = s.ring[i] ?? [0, 0];
|
|
788
|
+
mb.vertex({ p: at(p, end), n: nrm, uv: [u0, stripV(rect, s.run[i] ?? 0)], color });
|
|
789
|
+
}
|
|
790
|
+
for (const [i, j, l] of s.caps) {
|
|
791
|
+
if (sign > 0)
|
|
792
|
+
mb.tri(first + i, first + j, first + l);
|
|
793
|
+
else
|
|
794
|
+
mb.tri(first + i, first + l, first + j);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/primitives/lattice.ts
|
|
800
|
+
function lattice2(o) {
|
|
801
|
+
const d = [o.to[0] - o.from[0], o.to[1] - o.from[1], o.to[2] - o.from[2]];
|
|
802
|
+
const len = Math.hypot(d[0], d[1], d[2]);
|
|
803
|
+
if (!(len > 0.000001))
|
|
804
|
+
invalid("lattice: from and to coincide", "give the truss two distinct end points in metres");
|
|
805
|
+
const depth = positive("lattice depth", o.depth, "e.g. 2 for a 2 m deep girder");
|
|
806
|
+
const chord = positive("lattice chord", o.chord, "e.g. 0.2");
|
|
807
|
+
if (chord >= depth)
|
|
808
|
+
invalid(`lattice chord ${chord} m ≥ depth ${depth} m`, "make the chord member thinner than the depth");
|
|
809
|
+
const web = positive("lattice web", o.web ?? chord * 0.6, "e.g. 0.6 · chord");
|
|
810
|
+
const k = count("lattice bays", o.bays ?? Math.max(1, Math.round(len / depth)), 1, 2000);
|
|
811
|
+
const w = normalize2(d);
|
|
812
|
+
const upHint = o.up ?? (Math.abs(w[2]) > 0.95 ? [1, 0, 0] : [0, 0, 1]);
|
|
813
|
+
const dotU = upHint[0] * w[0] + upHint[1] * w[1] + upHint[2] * w[2];
|
|
814
|
+
const upR = [upHint[0] - w[0] * dotU, upHint[1] - w[1] * dotU, upHint[2] - w[2] * dotU];
|
|
815
|
+
if (Math.hypot(upR[0], upR[1], upR[2]) < 0.000001)
|
|
816
|
+
invalid("lattice: up is parallel to from → to", "pass an `up` perpendicular to the truss axis");
|
|
817
|
+
const up = normalize2(upR);
|
|
818
|
+
const side = [
|
|
819
|
+
w[1] * up[2] - w[2] * up[1],
|
|
820
|
+
w[2] * up[0] - w[0] * up[2],
|
|
821
|
+
w[0] * up[1] - w[1] * up[0]
|
|
822
|
+
];
|
|
823
|
+
const dEnd = positive("lattice depthEnd", o.depthEnd ?? depth, "e.g. 1");
|
|
824
|
+
const wid = o.width === undefined ? 0 : positive("lattice width", o.width, "e.g. 2");
|
|
825
|
+
const wEnd = o.width === undefined ? 0 : positive("lattice widthEnd", o.widthEnd ?? wid, "e.g. 1");
|
|
826
|
+
const c0 = o.centred ? -0.5 : 0;
|
|
827
|
+
const line = (a, c) => (t) => {
|
|
828
|
+
const along = len * t;
|
|
829
|
+
const lat = a * (wid + (wEnd - wid) * t) / 2;
|
|
830
|
+
const h = (c0 + c) * (depth + (dEnd - depth) * t);
|
|
831
|
+
return [
|
|
832
|
+
o.from[0] + w[0] * along + side[0] * lat + up[0] * h,
|
|
833
|
+
o.from[1] + w[1] * along + side[1] * lat + up[1] * h,
|
|
834
|
+
o.from[2] + w[2] * along + side[2] * lat + up[2] * h
|
|
835
|
+
];
|
|
836
|
+
};
|
|
837
|
+
const neg = (v) => [-v[0], -v[1], -v[2]];
|
|
838
|
+
const faces = o.width === undefined ? [[line(0, 0), line(0, 1), side]] : [
|
|
839
|
+
[line(-1, 0), line(-1, 1), neg(side)],
|
|
840
|
+
[line(1, 0), line(1, 1), side],
|
|
841
|
+
[line(-1, 1), line(1, 1), up],
|
|
842
|
+
[line(-1, 0), line(1, 0), neg(up)]
|
|
843
|
+
];
|
|
844
|
+
const chords = o.width === undefined ? [line(0, 0), line(0, 1)] : [line(-1, 0), line(1, 0), line(-1, 1), line(1, 1)];
|
|
845
|
+
const rect = stripRect2(o.strip ?? "iron");
|
|
846
|
+
const sc = section(o.section ?? "square", chord);
|
|
847
|
+
const sw = section(o.section ?? "square", web);
|
|
848
|
+
const mb = new MeshBuilder2;
|
|
849
|
+
for (const c of chords)
|
|
850
|
+
emitMember(mb, sc, c(0), c(1), up, true, o.color, rect);
|
|
851
|
+
for (const [lo, hi, n] of faces)
|
|
852
|
+
for (const [ta, la, tb, lb] of webMembers(o.pattern ?? "warren", k))
|
|
853
|
+
emitMember(mb, sw, (la ? hi : lo)(ta), (lb ? hi : lo)(tb), n, false, o.color, rect);
|
|
854
|
+
return mb.build();
|
|
855
|
+
}
|
|
856
|
+
function webMembers(pattern, k) {
|
|
857
|
+
const out = [];
|
|
858
|
+
const t = (i) => i / k;
|
|
859
|
+
if (pattern !== "warren")
|
|
860
|
+
for (let i = 0;i <= k; i++)
|
|
861
|
+
out.push([t(i), false, t(i), true]);
|
|
862
|
+
for (let i = 0;i < k; i++) {
|
|
863
|
+
const down = [t(i), true, t(i + 1), false];
|
|
864
|
+
const rise = [t(i), false, t(i + 1), true];
|
|
865
|
+
const middle = k % 2 === 1 && i === (k - 1) / 2;
|
|
866
|
+
const left = i < k / 2;
|
|
867
|
+
if (pattern === "warren")
|
|
868
|
+
out.push(i % 2 === 0 ? rise : down);
|
|
869
|
+
else if (pattern === "x" || middle)
|
|
870
|
+
out.push(rise, down);
|
|
871
|
+
else if (pattern === "pratt")
|
|
872
|
+
out.push(left ? down : rise);
|
|
873
|
+
else
|
|
874
|
+
out.push(left ? rise : down);
|
|
875
|
+
}
|
|
876
|
+
return out;
|
|
877
|
+
}
|
|
878
|
+
// src/primitives/loft.ts
|
|
879
|
+
function ringXY2(centre, rx, ry, segments, wobble, phase = 0) {
|
|
880
|
+
const out = [];
|
|
881
|
+
for (let k = 0;k < segments; k++) {
|
|
882
|
+
const a = phase + k / segments * Math.PI * 2;
|
|
883
|
+
const w = wobble ? wobble(a) : 1;
|
|
884
|
+
out.push([centre[0] + rx * Math.cos(a) * w, centre[1] + ry * Math.sin(a) * w, centre[2]]);
|
|
885
|
+
}
|
|
886
|
+
return out;
|
|
887
|
+
}
|
|
888
|
+
var invalid2 = (cause, fix) => new Ts23dError2("T3D_RECIPE_INVALID", { cause, fix });
|
|
889
|
+
var centroid = (r) => {
|
|
890
|
+
let x = 0;
|
|
891
|
+
let y = 0;
|
|
892
|
+
let z = 0;
|
|
893
|
+
for (const p of r) {
|
|
894
|
+
x += p[0];
|
|
895
|
+
y += p[1];
|
|
896
|
+
z += p[2];
|
|
897
|
+
}
|
|
898
|
+
return [x / r.length, y / r.length, z / r.length];
|
|
899
|
+
};
|
|
900
|
+
function loft2(o) {
|
|
901
|
+
const R = o.rings.length;
|
|
902
|
+
if (R < 2)
|
|
903
|
+
throw invalid2(`loft has ${R} ring(s)`, "pass at least 2 rings (bottom → top)");
|
|
904
|
+
const N = o.rings[0]?.length ?? 0;
|
|
905
|
+
if (N < 3)
|
|
906
|
+
throw invalid2(`loft rings have ${N} vertices`, "give every ring at least 3 vertices");
|
|
907
|
+
for (const [i, r] of o.rings.entries()) {
|
|
908
|
+
if (r.length !== N) {
|
|
909
|
+
throw invalid2(`loft ring ${i} has ${r.length} vertices, ring 0 has ${N}`, "build every ring with the same vertex count (same `segments` in ringXY)");
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const at = (i, k) => o.rings[i]?.[(k % N + N) % N] ?? [0, 0, 0];
|
|
913
|
+
const cents = o.rings.map(centroid);
|
|
914
|
+
const along = [0];
|
|
915
|
+
for (let i = 1;i < R; i++) {
|
|
916
|
+
const a = cents[i - 1] ?? [0, 0, 0];
|
|
917
|
+
const b = cents[i] ?? a;
|
|
918
|
+
along.push((along[i - 1] ?? 0) + Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]));
|
|
919
|
+
}
|
|
920
|
+
const total = along[R - 1] || 1;
|
|
921
|
+
const raw = [];
|
|
922
|
+
let facing = 0;
|
|
923
|
+
for (let i = 0;i < R; i++) {
|
|
924
|
+
const c = cents[i] ?? [0, 0, 0];
|
|
925
|
+
const ax = cents[Math.min(R - 1, i + 1)] ?? c;
|
|
926
|
+
const bx = cents[Math.max(0, i - 1)] ?? c;
|
|
927
|
+
const axis = [ax[0] - bx[0], ax[1] - bx[1], ax[2] - bx[2]];
|
|
928
|
+
for (let k = 0;k < N; k++) {
|
|
929
|
+
const p = at(i, k);
|
|
930
|
+
const u0 = at(i, k - 1);
|
|
931
|
+
const u1 = at(i, k + 1);
|
|
932
|
+
const v0 = at(Math.max(0, i - 1), k);
|
|
933
|
+
const v1 = at(Math.min(R - 1, i + 1), k);
|
|
934
|
+
const tu = [u1[0] - u0[0], u1[1] - u0[1], u1[2] - u0[2]];
|
|
935
|
+
const tv = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
|
|
936
|
+
let n = [
|
|
937
|
+
tu[1] * tv[2] - tu[2] * tv[1],
|
|
938
|
+
tu[2] * tv[0] - tu[0] * tv[2],
|
|
939
|
+
tu[0] * tv[1] - tu[1] * tv[0]
|
|
940
|
+
];
|
|
941
|
+
if (Math.hypot(n[0], n[1], n[2]) < 0.000000000001)
|
|
942
|
+
n = i === 0 ? [-axis[0], -axis[1], -axis[2]] : axis;
|
|
943
|
+
facing += n[0] * (p[0] - c[0]) + n[1] * (p[1] - c[1]) + n[2] * (p[2] - c[2]);
|
|
944
|
+
raw.push(n);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const flip = facing < 0 ? -1 : 1;
|
|
948
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
949
|
+
const du = rect.u1 - rect.u0;
|
|
950
|
+
const dv = rect.v1 - rect.v0;
|
|
951
|
+
const mode = o.uv ?? "length";
|
|
952
|
+
const color = (i, p) => typeof o.color === "function" ? o.color(o.rings[i] ?? [], i, p) : o.color;
|
|
953
|
+
const b = new MeshBuilder2;
|
|
954
|
+
for (let i = 0;i < R; i++) {
|
|
955
|
+
let around = 0;
|
|
956
|
+
for (let k = 0;k <= N; k++) {
|
|
957
|
+
const p = at(i, k);
|
|
958
|
+
if (k > 0) {
|
|
959
|
+
const q = at(i, k - 1);
|
|
960
|
+
around += Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
|
|
961
|
+
}
|
|
962
|
+
const n = raw[i * N + k % N] ?? [0, 0, 1];
|
|
963
|
+
const l = (Math.hypot(n[0], n[1], n[2]) || 1) * flip;
|
|
964
|
+
const s = (along[i] ?? 0) / rect.metres;
|
|
965
|
+
const uv = mode === "row" ? [rect.u0 + around / rect.metres * du, rect.v0 + (along[i] ?? 0) / total * dv] : mode === "swap" ? [rect.u0 + k / N * du, rect.v0 + (along[i] ?? 0) / total * dv] : [rect.u0 + s * du, rect.v0 + k / N * dv];
|
|
966
|
+
b.vertex({ p, n: [n[0] / l, n[1] / l, n[2] / l], uv, color: color(i, p) });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
for (let i = 0;i + 1 < R; i++) {
|
|
970
|
+
const r0 = i * (N + 1);
|
|
971
|
+
const r1 = r0 + N + 1;
|
|
972
|
+
for (let k = 0;k < N; k++) {
|
|
973
|
+
if (flip > 0)
|
|
974
|
+
b.quad(r0 + k, r0 + k + 1, r1 + k + 1, r1 + k);
|
|
975
|
+
else
|
|
976
|
+
b.quad(r0 + k, r1 + k, r1 + k + 1, r0 + k + 1);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const ends = [
|
|
980
|
+
[0, o.capStart === true],
|
|
981
|
+
[R - 1, o.capEnd === true]
|
|
982
|
+
];
|
|
983
|
+
for (const [i, on] of ends) {
|
|
984
|
+
if (!on)
|
|
985
|
+
continue;
|
|
986
|
+
const c = cents[i] ?? [0, 0, 0];
|
|
987
|
+
const other = cents[i === 0 ? 1 : R - 2] ?? c;
|
|
988
|
+
const d = [c[0] - other[0], c[1] - other[1], c[2] - other[2]];
|
|
989
|
+
const l = Math.hypot(d[0], d[1], d[2]) || 1;
|
|
990
|
+
const nn = [d[0] / l, d[1] / l, d[2] / l];
|
|
991
|
+
const mid = b.vertex({ p: c, n: nn, uv: [rect.u0, rect.v0], color: color(i, c) });
|
|
992
|
+
for (let k = 0;k < N; k++) {
|
|
993
|
+
const p = at(i, k);
|
|
994
|
+
b.vertex({ p, n: nn, uv: [rect.u0, rect.v1], color: color(i, p) });
|
|
995
|
+
}
|
|
996
|
+
const e1 = at(i, 0);
|
|
997
|
+
const e2 = at(i, 1);
|
|
998
|
+
const cr = [
|
|
999
|
+
(e1[1] - c[1]) * (e2[2] - c[2]) - (e1[2] - c[2]) * (e2[1] - c[1]),
|
|
1000
|
+
(e1[2] - c[2]) * (e2[0] - c[0]) - (e1[0] - c[0]) * (e2[2] - c[2]),
|
|
1001
|
+
(e1[0] - c[0]) * (e2[1] - c[1]) - (e1[1] - c[1]) * (e2[0] - c[0])
|
|
1002
|
+
];
|
|
1003
|
+
const ccw = cr[0] * nn[0] + cr[1] * nn[1] + cr[2] * nn[2] >= 0;
|
|
1004
|
+
for (let k = 0;k < N; k++) {
|
|
1005
|
+
const a = mid + 1 + k;
|
|
1006
|
+
const z = mid + 1 + (k + 1) % N;
|
|
1007
|
+
if (ccw)
|
|
1008
|
+
b.tri(mid, a, z);
|
|
1009
|
+
else
|
|
1010
|
+
b.tri(mid, z, a);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return b.build();
|
|
1014
|
+
}
|
|
1015
|
+
// src/primitives/noise.ts
|
|
1016
|
+
var R = Math.SQRT1_2;
|
|
1017
|
+
var GX = [1, -1, 0, 0, R, -R, R, -R];
|
|
1018
|
+
var GY = [0, 0, 1, -1, R, R, -R, -R];
|
|
1019
|
+
function permutation(rng) {
|
|
1020
|
+
const p = new Uint8Array(512);
|
|
1021
|
+
for (let i = 0;i < 256; i++)
|
|
1022
|
+
p[i] = i;
|
|
1023
|
+
for (let i = 255;i > 0; i--) {
|
|
1024
|
+
const j = rng.int(0, i);
|
|
1025
|
+
const t = p[i] ?? 0;
|
|
1026
|
+
p[i] = p[j] ?? 0;
|
|
1027
|
+
p[j] = t;
|
|
1028
|
+
}
|
|
1029
|
+
for (let i = 0;i < 256; i++)
|
|
1030
|
+
p[i + 256] = p[i] ?? 0;
|
|
1031
|
+
return p;
|
|
1032
|
+
}
|
|
1033
|
+
var fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
|
|
1034
|
+
function gradientNoise22(rng) {
|
|
1035
|
+
const p = permutation(rng);
|
|
1036
|
+
const grad = (ix, iy, dx, dy) => {
|
|
1037
|
+
const g = (p[(p[ix & 255] ?? 0) + (iy & 255)] ?? 0) & 7;
|
|
1038
|
+
return (GX[g] ?? 0) * dx + (GY[g] ?? 0) * dy;
|
|
1039
|
+
};
|
|
1040
|
+
return (x, y) => {
|
|
1041
|
+
const ix = Math.floor(x);
|
|
1042
|
+
const iy = Math.floor(y);
|
|
1043
|
+
const fx = x - ix;
|
|
1044
|
+
const fy = y - iy;
|
|
1045
|
+
const u = fade(fx);
|
|
1046
|
+
const v = fade(fy);
|
|
1047
|
+
const a = grad(ix, iy, fx, fy);
|
|
1048
|
+
const b = grad(ix + 1, iy, fx - 1, fy);
|
|
1049
|
+
const c = grad(ix, iy + 1, fx, fy - 1);
|
|
1050
|
+
const d = grad(ix + 1, iy + 1, fx - 1, fy - 1);
|
|
1051
|
+
const top = a + (b - a) * u;
|
|
1052
|
+
return (top + (c + (d - c) * u - top) * v) * Math.SQRT2;
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
function valueNoise22(rng) {
|
|
1056
|
+
const p = permutation(rng);
|
|
1057
|
+
const vals = new Float64Array(256);
|
|
1058
|
+
for (let i = 0;i < 256; i++)
|
|
1059
|
+
vals[i] = rng.next() * 2 - 1;
|
|
1060
|
+
const at = (ix, iy) => vals[p[(p[ix & 255] ?? 0) + (iy & 255)] ?? 0] ?? 0;
|
|
1061
|
+
return (x, y) => {
|
|
1062
|
+
const ix = Math.floor(x);
|
|
1063
|
+
const iy = Math.floor(y);
|
|
1064
|
+
const u = fade(x - ix);
|
|
1065
|
+
const v = fade(y - iy);
|
|
1066
|
+
const top = at(ix, iy) + (at(ix + 1, iy) - at(ix, iy)) * u;
|
|
1067
|
+
const bot = at(ix, iy + 1) + (at(ix + 1, iy + 1) - at(ix, iy + 1)) * u;
|
|
1068
|
+
return top + (bot - top) * v;
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function fractal(n, o, shape) {
|
|
1072
|
+
const s = 1 / (o.scale ?? 1);
|
|
1073
|
+
const octaves = o.octaves ?? 5;
|
|
1074
|
+
const lac = o.lacunarity ?? 2;
|
|
1075
|
+
const gain = o.gain ?? 0.5;
|
|
1076
|
+
return (x, y) => {
|
|
1077
|
+
let sum = 0;
|
|
1078
|
+
let amp = 1;
|
|
1079
|
+
let norm = 0;
|
|
1080
|
+
let f = s;
|
|
1081
|
+
for (let i = 0;i < octaves; i++) {
|
|
1082
|
+
sum += amp * shape(n(x * f + i * 17.31, y * f - i * 9.73));
|
|
1083
|
+
norm += amp;
|
|
1084
|
+
amp *= gain;
|
|
1085
|
+
f *= lac;
|
|
1086
|
+
}
|
|
1087
|
+
return sum / norm;
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
var fbm2 = (n, o = {}) => fractal(n, o, (v) => v);
|
|
1091
|
+
var billow2 = (n, o = {}) => fractal(n, o, (v) => 2 * Math.abs(v) - 1);
|
|
1092
|
+
function ridged2(n, o = {}) {
|
|
1093
|
+
const s = 1 / (o.scale ?? 1);
|
|
1094
|
+
const octaves = o.octaves ?? 6;
|
|
1095
|
+
const lac = o.lacunarity ?? 2;
|
|
1096
|
+
const gain = o.gain ?? 0.5;
|
|
1097
|
+
const offset = o.offset ?? 1;
|
|
1098
|
+
return (x, y) => {
|
|
1099
|
+
let sum = 0;
|
|
1100
|
+
let norm = 0;
|
|
1101
|
+
let amp = 1;
|
|
1102
|
+
let weight = 1;
|
|
1103
|
+
let f = s;
|
|
1104
|
+
for (let i = 0;i < octaves; i++) {
|
|
1105
|
+
const crest = offset - Math.abs(n(x * f + i * 17.31, y * f - i * 9.73));
|
|
1106
|
+
const signal = crest * crest * weight;
|
|
1107
|
+
weight = Math.min(1, Math.max(0, signal * 2));
|
|
1108
|
+
sum += signal * amp;
|
|
1109
|
+
norm += amp * offset * offset;
|
|
1110
|
+
amp *= gain;
|
|
1111
|
+
f *= lac;
|
|
1112
|
+
}
|
|
1113
|
+
return sum / norm;
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
var warp2 = (n, wx, wy, amount) => (x, y) => n(x + amount * wx(x, y), y + amount * wy(x, y));
|
|
1117
|
+
function terrace2(h, step, sharpness = 0.5) {
|
|
1118
|
+
const k = Math.floor(h / step);
|
|
1119
|
+
const t = h / step - k;
|
|
1120
|
+
const s = Math.min(1, Math.max(0, sharpness));
|
|
1121
|
+
const u = s >= 1 ? 0 : Math.min(1, Math.max(0, (t - s) / (1 - s)));
|
|
1122
|
+
const riser = u * u * (3 - 2 * u);
|
|
1123
|
+
return (k + t + (riser - t) * s) * step;
|
|
1124
|
+
}
|
|
1125
|
+
// src/primitives/planar.ts
|
|
1126
|
+
function planar2(o) {
|
|
1127
|
+
const [a = [0, 0, 0], b = [1, 0, 0], c = [0, 1, 0]] = o.points;
|
|
1128
|
+
const n = normalize2(cross2(sub2(b, a), sub2(c, a)));
|
|
1129
|
+
const u = normalize2(sub2(b, a));
|
|
1130
|
+
const v = cross2(n, u);
|
|
1131
|
+
const flat = o.points.map((p) => [dot2(sub2(p, a), u), dot2(sub2(p, a), v)]);
|
|
1132
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
1133
|
+
const tris = triangulate2(flat);
|
|
1134
|
+
const mb = new MeshBuilder2;
|
|
1135
|
+
for (const side of o.doubleSided ? [1, -1] : [1]) {
|
|
1136
|
+
const nn = [n[0] * side, n[1] * side, n[2] * side];
|
|
1137
|
+
const ids = o.points.map((p, i) => {
|
|
1138
|
+
const f = flat[i] ?? [0, 0];
|
|
1139
|
+
return mb.vertex({
|
|
1140
|
+
p,
|
|
1141
|
+
n: nn,
|
|
1142
|
+
uv: [
|
|
1143
|
+
rect.u0 + f[0] / rect.metres * (rect.u1 - rect.u0),
|
|
1144
|
+
rect.v0 + (f[1] / rect.metres % 1 + 1) % 1 * (rect.v1 - rect.v0)
|
|
1145
|
+
],
|
|
1146
|
+
color: o.color
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
for (const [x, y, z] of tris) {
|
|
1150
|
+
const [i0 = 0, i1 = 0, i2 = 0] = [ids[x], ids[y], ids[z]];
|
|
1151
|
+
if (side > 0)
|
|
1152
|
+
mb.tri(i0, i1, i2);
|
|
1153
|
+
else
|
|
1154
|
+
mb.tri(i0, i2, i1);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return mb.build();
|
|
1158
|
+
}
|
|
1159
|
+
// src/primitives/prism.ts
|
|
1160
|
+
var lift = (plane, p, d) => plane === "xz" ? [p[0], d, p[1]] : plane === "xy" ? [p[0], p[1], d] : [d, p[0], p[1]];
|
|
1161
|
+
function prism2(o) {
|
|
1162
|
+
const plane = o.plane ?? "xy";
|
|
1163
|
+
const d0 = o.centred === false ? 0 : -o.depth / 2;
|
|
1164
|
+
const d1 = d0 + o.depth;
|
|
1165
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
1166
|
+
const uv = (u, v) => [
|
|
1167
|
+
rect.u0 + u / rect.metres * (rect.u1 - rect.u0),
|
|
1168
|
+
rect.v0 + (v % 1 + 1) % 1 * (rect.v1 - rect.v0)
|
|
1169
|
+
];
|
|
1170
|
+
const b = new MeshBuilder2;
|
|
1171
|
+
const n = o.profile.length;
|
|
1172
|
+
const sign = o.profile.reduce((s, a, i) => {
|
|
1173
|
+
const c = o.profile[(i + 1) % n] ?? a;
|
|
1174
|
+
return s + a[0] * c[1] - c[0] * a[1];
|
|
1175
|
+
}, 0) > 0 ? 1 : -1;
|
|
1176
|
+
const hand = plane === "xz" ? -1 : 1;
|
|
1177
|
+
let run = 0;
|
|
1178
|
+
for (let i = 0;i < n; i++) {
|
|
1179
|
+
const a = o.profile[i] ?? [0, 0];
|
|
1180
|
+
const c = o.profile[(i + 1) % n] ?? a;
|
|
1181
|
+
const e = [c[0] - a[0], c[1] - a[1]];
|
|
1182
|
+
const len = Math.hypot(e[0], e[1]);
|
|
1183
|
+
if (len < 0.000000001)
|
|
1184
|
+
continue;
|
|
1185
|
+
const n2 = [e[1] / len * sign, -e[0] / len * sign];
|
|
1186
|
+
const nrm = normalize2(lift(plane, n2, 0));
|
|
1187
|
+
const p = [lift(plane, a, d0), lift(plane, c, d0), lift(plane, c, d1), lift(plane, a, d1)];
|
|
1188
|
+
const ids = p.map((q, k) => b.vertex({
|
|
1189
|
+
p: q,
|
|
1190
|
+
n: nrm,
|
|
1191
|
+
uv: uv(run + (k === 1 || k === 2 ? len : 0), k >= 2 ? o.depth : 0),
|
|
1192
|
+
color: o.color
|
|
1193
|
+
}));
|
|
1194
|
+
const [i0 = 0, i1 = 0, i2 = 0, i3 = 0] = ids;
|
|
1195
|
+
if (sign * hand > 0)
|
|
1196
|
+
b.quad(i0, i1, i2, i3);
|
|
1197
|
+
else
|
|
1198
|
+
b.quad(i0, i3, i2, i1);
|
|
1199
|
+
run += len;
|
|
1200
|
+
}
|
|
1201
|
+
if (o.caps !== false) {
|
|
1202
|
+
const tris = triangulate2(o.profile);
|
|
1203
|
+
for (const [d, s] of [
|
|
1204
|
+
[d0, -1],
|
|
1205
|
+
[d1, 1]
|
|
1206
|
+
]) {
|
|
1207
|
+
const nrm = normalize2(lift(plane, [0, 0], s));
|
|
1208
|
+
const ids = o.profile.map((q) => b.vertex({ p: lift(plane, q, d), n: nrm, uv: uv(q[0], q[1]), color: o.color }));
|
|
1209
|
+
for (const [x, y, z] of tris) {
|
|
1210
|
+
const [a = 0, c = 0, e = 0] = [ids[x], ids[y], ids[z]];
|
|
1211
|
+
if (s * hand > 0)
|
|
1212
|
+
b.tri(a, c, e);
|
|
1213
|
+
else
|
|
1214
|
+
b.tri(a, e, c);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
return b.build();
|
|
1219
|
+
}
|
|
1220
|
+
// src/primitives/sdf-ops.ts
|
|
1221
|
+
var clamp01 = (x) => x < 0 ? 0 : Math.min(x, 1);
|
|
1222
|
+
var shapes = {
|
|
1223
|
+
sphere: (x, y, z, r) => Math.sqrt(x * x + y * y + z * z) - r,
|
|
1224
|
+
ellipsoid(x, y, z, rx, ry, rz) {
|
|
1225
|
+
const k0 = Math.sqrt((x / rx) ** 2 + (y / ry) ** 2 + (z / rz) ** 2);
|
|
1226
|
+
const k1 = Math.sqrt((x / (rx * rx)) ** 2 + (y / (ry * ry)) ** 2 + (z / (rz * rz)) ** 2);
|
|
1227
|
+
return k1 === 0 ? -Math.min(rx, ry, rz) : k0 * (k0 - 1) / k1;
|
|
1228
|
+
},
|
|
1229
|
+
roundBox(x, y, z, hx, hy, hz, r) {
|
|
1230
|
+
const qx = Math.abs(x) - hx + r;
|
|
1231
|
+
const qy = Math.abs(y) - hy + r;
|
|
1232
|
+
const qz = Math.abs(z) - hz + r;
|
|
1233
|
+
const ox = Math.max(qx, 0);
|
|
1234
|
+
const oy = Math.max(qy, 0);
|
|
1235
|
+
const oz = Math.max(qz, 0);
|
|
1236
|
+
return Math.sqrt(ox * ox + oy * oy + oz * oz) + Math.min(Math.max(qx, qy, qz), 0) - r;
|
|
1237
|
+
},
|
|
1238
|
+
capsule(x, y, z, len, r) {
|
|
1239
|
+
const t = len > 0 ? Math.min(Math.max(z, 0), len) : 0;
|
|
1240
|
+
return Math.sqrt(x * x + y * y + (z - t) * (z - t)) - r;
|
|
1241
|
+
},
|
|
1242
|
+
cone(x, y, z, r1, r2, h) {
|
|
1243
|
+
const hh = h / 2;
|
|
1244
|
+
const qx = Math.sqrt(x * x + y * y);
|
|
1245
|
+
const qy = z - hh;
|
|
1246
|
+
const cax = qx - Math.min(qx, qy < 0 ? r1 : r2);
|
|
1247
|
+
const cay = Math.abs(qy) - hh;
|
|
1248
|
+
const k2x = r2 - r1;
|
|
1249
|
+
const k2y = 2 * hh;
|
|
1250
|
+
const t = clamp01(((r2 - qx) * k2x + (hh - qy) * k2y) / (k2x * k2x + k2y * k2y));
|
|
1251
|
+
const cbx = qx - r2 + k2x * t;
|
|
1252
|
+
const cby = qy - hh + k2y * t;
|
|
1253
|
+
const s = cbx < 0 && cay < 0 ? -1 : 1;
|
|
1254
|
+
return s * Math.sqrt(Math.min(cax * cax + cay * cay, cbx * cbx + cby * cby));
|
|
1255
|
+
},
|
|
1256
|
+
torus(x, y, z, R, r) {
|
|
1257
|
+
const q = Math.sqrt(x * x + y * y) - R;
|
|
1258
|
+
return Math.sqrt(q * q + z * z) - r;
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
var smoothWeight = (a, b, k) => k > 0 ? clamp01(0.5 + 0.5 * (b - a) / k) : a < b ? 1 : 0;
|
|
1262
|
+
var sd2 = {
|
|
1263
|
+
sphere: (at, r) => (p) => shapes.sphere(p[0] - at[0], p[1] - at[1], p[2] - at[2], r),
|
|
1264
|
+
ellipsoid: (at, radii) => (p) => shapes.ellipsoid(p[0] - at[0], p[1] - at[1], p[2] - at[2], radii[0], radii[1], radii[2]),
|
|
1265
|
+
box: (at, size, round = 0) => (p) => shapes.roundBox(p[0] - at[0], p[1] - at[1], p[2] - at[2], size[0] / 2, size[1] / 2, size[2] / 2, round),
|
|
1266
|
+
capsule(a, b, r) {
|
|
1267
|
+
const bx = b[0] - a[0];
|
|
1268
|
+
const by = b[1] - a[1];
|
|
1269
|
+
const bz = b[2] - a[2];
|
|
1270
|
+
const bb = bx * bx + by * by + bz * bz;
|
|
1271
|
+
return (p) => {
|
|
1272
|
+
const px = p[0] - a[0];
|
|
1273
|
+
const py = p[1] - a[1];
|
|
1274
|
+
const pz = p[2] - a[2];
|
|
1275
|
+
const h = bb > 0 ? clamp01((px * bx + py * by + pz * bz) / bb) : 0;
|
|
1276
|
+
return Math.sqrt((px - bx * h) ** 2 + (py - by * h) ** 2 + (pz - bz * h) ** 2) - r;
|
|
1277
|
+
};
|
|
1278
|
+
},
|
|
1279
|
+
cone: (at, r1, r2, h) => (p) => shapes.cone(p[0] - at[0], p[1] - at[1], p[2] - at[2], r1, r2, h),
|
|
1280
|
+
torus: (at, R, r) => (p) => shapes.torus(p[0] - at[0], p[1] - at[1], p[2] - at[2], R, r),
|
|
1281
|
+
union: (...fs) => (p) => {
|
|
1282
|
+
let d = Number.POSITIVE_INFINITY;
|
|
1283
|
+
for (const f of fs)
|
|
1284
|
+
d = Math.min(d, f(p));
|
|
1285
|
+
return d;
|
|
1286
|
+
},
|
|
1287
|
+
intersect: (a, b) => (p) => Math.max(a(p), b(p)),
|
|
1288
|
+
subtract: (a, b) => (p) => Math.max(a(p), -b(p)),
|
|
1289
|
+
smoothUnion: (k, a, b) => (p) => {
|
|
1290
|
+
const da = a(p);
|
|
1291
|
+
const db = b(p);
|
|
1292
|
+
const h = smoothWeight(da, db, k);
|
|
1293
|
+
return db + (da - db) * h - k * h * (1 - h);
|
|
1294
|
+
},
|
|
1295
|
+
smoothSubtract: (k, a, b) => (p) => {
|
|
1296
|
+
const da = a(p);
|
|
1297
|
+
const db = -b(p);
|
|
1298
|
+
const h = smoothWeight(-da, -db, k);
|
|
1299
|
+
return da + (db - da) * (1 - h) + k * h * (1 - h);
|
|
1300
|
+
},
|
|
1301
|
+
displace: (f, noise, amount) => (p) => f(p) + amount * noise(p)
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
// src/primitives/sculpt-edit.ts
|
|
1305
|
+
var dims = (e) => {
|
|
1306
|
+
if (typeof e.size === "number")
|
|
1307
|
+
return [e.size, e.size, e.size];
|
|
1308
|
+
const [a, b, c] = e.size;
|
|
1309
|
+
return [a, b, c ?? b];
|
|
1310
|
+
};
|
|
1311
|
+
function sculptShape(e, i, mirrorAll) {
|
|
1312
|
+
const [a, b, c] = dims(e);
|
|
1313
|
+
const where = `sculpt edit ${i} (${e.shape})`;
|
|
1314
|
+
if (![a, b, c].every((x) => Number.isFinite(x) && x > 0))
|
|
1315
|
+
invalid(`${where}: size has a value ≤ 0`, "give every size component in metres, > 0");
|
|
1316
|
+
if ((e.blend ?? 0) < 0 || !Number.isFinite(e.blend ?? 0))
|
|
1317
|
+
invalid(`${where}: blend is ${e.blend}`, "use a blend radius ≥ 0 m (0.01 = 1 cm fillet)");
|
|
1318
|
+
if (e.shape === "capsule" && !e.to)
|
|
1319
|
+
invalid(`${where}: capsule needs \`to\``, "add to: [x, y, z], the far end");
|
|
1320
|
+
const [rx, ry, rz] = (e.rotate ?? [0, 0, 0]).map((d) => d * Math.PI / 180);
|
|
1321
|
+
const [cx, sx, cy, sy, cz, sz] = [
|
|
1322
|
+
Math.cos(rx ?? 0),
|
|
1323
|
+
Math.sin(rx ?? 0),
|
|
1324
|
+
Math.cos(ry ?? 0),
|
|
1325
|
+
Math.sin(ry ?? 0),
|
|
1326
|
+
Math.cos(rz ?? 0),
|
|
1327
|
+
Math.sin(rz ?? 0)
|
|
1328
|
+
];
|
|
1329
|
+
const m = [
|
|
1330
|
+
cz * cy,
|
|
1331
|
+
cz * sy * sx - sz * cx,
|
|
1332
|
+
cz * sy * cx + sz * sx,
|
|
1333
|
+
sz * cy,
|
|
1334
|
+
sz * sy * sx + cz * cx,
|
|
1335
|
+
sz * sy * cx - cz * sx,
|
|
1336
|
+
-sy,
|
|
1337
|
+
cy * sx,
|
|
1338
|
+
cy * cx
|
|
1339
|
+
];
|
|
1340
|
+
const [m0 = 1, m1 = 0, m2 = 0, m3 = 0, m4 = 1, m5 = 0, m6 = 0, m7 = 0, m8 = 1] = m;
|
|
1341
|
+
const [ox, oy, oz] = e.at;
|
|
1342
|
+
const to = e.to ?? e.at;
|
|
1343
|
+
const [dx, dy, dz] = [to[0] - ox, to[1] - oy, to[2] - oz];
|
|
1344
|
+
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1345
|
+
const round = Math.min(e.round ?? 0, a / 2, b / 2, c / 2);
|
|
1346
|
+
const kernel = e.shape === "sphere" ? (x, y, z) => shapes.sphere(x, y, z, a) : e.shape === "ellipsoid" ? (x, y, z) => shapes.ellipsoid(x, y, z, a, b, c) : e.shape === "roundBox" ? (x, y, z) => shapes.roundBox(x, y, z, a / 2, b / 2, c / 2, round) : e.shape === "cone" ? (x, y, z) => shapes.cone(x, y, z, a, b, c) : (x, y, z) => shapes.torus(x, y, z, a, b);
|
|
1347
|
+
const rotated = e.rotate?.some((d) => d !== 0) ?? false;
|
|
1348
|
+
const one = e.shape === "capsule" ? (px, py, pz) => {
|
|
1349
|
+
const wx = px - ox;
|
|
1350
|
+
const wy = py - oy;
|
|
1351
|
+
const wz = pz - oz;
|
|
1352
|
+
const t = len > 0 ? Math.min(1, Math.max(0, (wx * dx + wy * dy + wz * dz) / (len * len))) : 0;
|
|
1353
|
+
return Math.sqrt((wx - dx * t) ** 2 + (wy - dy * t) ** 2 + (wz - dz * t) ** 2) - a;
|
|
1354
|
+
} : rotated ? (px, py, pz) => {
|
|
1355
|
+
const wx = px - ox;
|
|
1356
|
+
const wy = py - oy;
|
|
1357
|
+
const wz = pz - oz;
|
|
1358
|
+
return kernel(m0 * wx + m3 * wy + m6 * wz, m1 * wx + m4 * wy + m7 * wz, m2 * wx + m5 * wy + m8 * wz);
|
|
1359
|
+
} : (px, py, pz) => kernel(px - ox, py - oy, pz - oz);
|
|
1360
|
+
if (!(e.mirror ?? mirrorAll))
|
|
1361
|
+
return (p) => one(p[0], p[1], p[2]);
|
|
1362
|
+
const { c: sc, r: sr, scale, inside } = shapeSphere(e);
|
|
1363
|
+
const bound = (dist) => {
|
|
1364
|
+
const out = dist - sr;
|
|
1365
|
+
return out > 0 ? scale * out : inside ? out : Number.NEGATIVE_INFINITY;
|
|
1366
|
+
};
|
|
1367
|
+
return (p) => {
|
|
1368
|
+
const dx0 = p[0] - sc[0];
|
|
1369
|
+
const dz0 = p[2] - sc[2];
|
|
1370
|
+
const near = Math.sqrt(dx0 * dx0 + (p[1] - sc[1]) ** 2 + dz0 * dz0);
|
|
1371
|
+
const far = Math.sqrt(dx0 * dx0 + (p[1] + sc[1]) ** 2 + dz0 * dz0);
|
|
1372
|
+
const [first, second, lb] = near <= far ? [p[1], -p[1], bound(far)] : [-p[1], p[1], bound(near)];
|
|
1373
|
+
const v = one(p[0], first, p[2]);
|
|
1374
|
+
return lb >= v ? v : Math.min(v, one(p[0], second, p[2]));
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
function shapeSphere(e) {
|
|
1378
|
+
const [a, b, c] = dims(e);
|
|
1379
|
+
const to = e.to ?? e.at;
|
|
1380
|
+
const centre = e.shape === "capsule" ? [(e.at[0] + to[0]) / 2, (e.at[1] + to[1]) / 2, (e.at[2] + to[2]) / 2] : e.at;
|
|
1381
|
+
const r = e.shape === "sphere" ? a : e.shape === "ellipsoid" ? Math.max(a, b, c) : e.shape === "roundBox" ? Math.hypot(a, b, c) / 2 : e.shape === "cone" ? c + Math.max(a, b) : e.shape === "torus" ? a + b : a + Math.hypot(to[0] - e.at[0], to[1] - e.at[1], to[2] - e.at[2]) / 2;
|
|
1382
|
+
const ell = e.shape === "ellipsoid";
|
|
1383
|
+
const lo = Math.min(a, b, c);
|
|
1384
|
+
return { c: centre, r, scale: ell ? (lo / r) ** 2 : 1, inside: !ell };
|
|
1385
|
+
}
|
|
1386
|
+
function editBounds(e, mirrorAll) {
|
|
1387
|
+
if ("group" in e)
|
|
1388
|
+
return groupBounds(e, mirrorAll);
|
|
1389
|
+
const { c: centre, r } = shapeSphere(e);
|
|
1390
|
+
const g = r + (e.blend ?? 0);
|
|
1391
|
+
const lo = [centre[0] - g, centre[1] - g, centre[2] - g];
|
|
1392
|
+
const hi = [centre[0] + g, centre[1] + g, centre[2] + g];
|
|
1393
|
+
if (e.mirror ?? mirrorAll) {
|
|
1394
|
+
const [l, h] = [lo[1], hi[1]];
|
|
1395
|
+
lo[1] = Math.min(l, -h);
|
|
1396
|
+
hi[1] = Math.max(h, -l);
|
|
1397
|
+
}
|
|
1398
|
+
return [lo, hi];
|
|
1399
|
+
}
|
|
1400
|
+
function groupBounds(g, mirrorAll) {
|
|
1401
|
+
const lo = [Infinity, Infinity, Infinity];
|
|
1402
|
+
const hi = [-Infinity, -Infinity, -Infinity];
|
|
1403
|
+
const k = g.blend ?? 0;
|
|
1404
|
+
for (const e of g.group) {
|
|
1405
|
+
if ((e.op ?? "add") !== "add")
|
|
1406
|
+
continue;
|
|
1407
|
+
const [a, b] = editBounds(e, g.mirror ?? mirrorAll);
|
|
1408
|
+
for (let i = 0;i < 3; i++) {
|
|
1409
|
+
lo[i] = Math.min(lo[i] ?? 0, (a[i] ?? 0) - k);
|
|
1410
|
+
hi[i] = Math.max(hi[i] ?? 0, (b[i] ?? 0) + k);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return [lo, hi];
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// src/primitives/sculpt-tree.ts
|
|
1417
|
+
var ADD = 0;
|
|
1418
|
+
var SUB = 1;
|
|
1419
|
+
var INTER = 2;
|
|
1420
|
+
var PAINT = 3;
|
|
1421
|
+
var OPS = { add: ADD, subtract: SUB, intersect: INTER, paint: PAINT };
|
|
1422
|
+
function compile(edits, base, mirror, path = "sculpt") {
|
|
1423
|
+
if (!edits.some((e) => (e.op ?? "add") === "add"))
|
|
1424
|
+
invalid(`${path} has no add edits, so nothing to carve from`, "make the first edit op: 'add'");
|
|
1425
|
+
return edits.map((e, i) => {
|
|
1426
|
+
const op = OPS[e.op ?? "add"];
|
|
1427
|
+
const k = e.blend ?? 0;
|
|
1428
|
+
const m = e.mirror ?? mirror;
|
|
1429
|
+
if ("group" in e) {
|
|
1430
|
+
const kids = compile(e.group, e.color ?? base, m, `${path} group ${i}`);
|
|
1431
|
+
const b = enclose(kids);
|
|
1432
|
+
return node(op, k, e.color ?? null, null, kids, e.color ?? base, 3, b.c, b.r, b.scale, b.inside, false, b.under);
|
|
1433
|
+
}
|
|
1434
|
+
const s = shapeSphere(e);
|
|
1435
|
+
return node(op, k, e.color ?? null, sculptShape(e, i, m), null, base, 0, s.c, s.r, s.scale, s.inside, m, 0);
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
function node(op, k, color, shape, kids, base, rgb, c, r, scale, inside, mirror, under) {
|
|
1439
|
+
return {
|
|
1440
|
+
op,
|
|
1441
|
+
k,
|
|
1442
|
+
color,
|
|
1443
|
+
shape,
|
|
1444
|
+
kids,
|
|
1445
|
+
base,
|
|
1446
|
+
rgb: new Float64Array(rgb),
|
|
1447
|
+
c,
|
|
1448
|
+
r,
|
|
1449
|
+
scale,
|
|
1450
|
+
inside,
|
|
1451
|
+
mirror,
|
|
1452
|
+
under
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function enclose(kids) {
|
|
1456
|
+
const adds = kids.filter((n) => n.op === ADD);
|
|
1457
|
+
const spheres = adds.flatMap((n) => n.mirror ? [n, { ...n, c: [n.c[0], -n.c[1], n.c[2]] }] : [n]);
|
|
1458
|
+
const lo = [0, 1, 2].map((a) => Math.min(...spheres.map((n) => (n.c[a] ?? 0) - n.r)));
|
|
1459
|
+
const hi = [0, 1, 2].map((a) => Math.max(...spheres.map((n) => (n.c[a] ?? 0) + n.r)));
|
|
1460
|
+
const c = [
|
|
1461
|
+
((lo[0] ?? 0) + (hi[0] ?? 0)) / 2,
|
|
1462
|
+
((lo[1] ?? 0) + (hi[1] ?? 0)) / 2,
|
|
1463
|
+
((lo[2] ?? 0) + (hi[2] ?? 0)) / 2
|
|
1464
|
+
];
|
|
1465
|
+
const r = Math.max(...spheres.map((n) => Math.hypot(n.c[0] - c[0], n.c[1] - c[1], n.c[2] - c[2]) + n.r));
|
|
1466
|
+
return {
|
|
1467
|
+
c,
|
|
1468
|
+
r,
|
|
1469
|
+
scale: Math.min(...adds.map((n) => n.scale)),
|
|
1470
|
+
inside: adds.every((n) => n.inside),
|
|
1471
|
+
mirror: false,
|
|
1472
|
+
under: adds.reduce((u, n) => u + n.k / 4 + n.under, 0)
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function lowerBound(n, p) {
|
|
1476
|
+
const dx = p[0] - n.c[0];
|
|
1477
|
+
const dz = p[2] - n.c[2];
|
|
1478
|
+
let dy = p[1] - n.c[1];
|
|
1479
|
+
let q = dx * dx + dy * dy + dz * dz;
|
|
1480
|
+
if (n.mirror) {
|
|
1481
|
+
dy = p[1] + n.c[1];
|
|
1482
|
+
q = Math.min(q, dx * dx + dy * dy + dz * dz);
|
|
1483
|
+
}
|
|
1484
|
+
const out = Math.sqrt(q) - n.r;
|
|
1485
|
+
if (out > 0)
|
|
1486
|
+
return n.scale * out - n.under;
|
|
1487
|
+
return n.inside ? out - n.under : Number.NEGATIVE_INFINITY;
|
|
1488
|
+
}
|
|
1489
|
+
var clamp012 = (x) => x < 0 ? 0 : Math.min(x, 1);
|
|
1490
|
+
var weight = (a, b, k) => k > 0 ? clamp012(0.5 + 0.5 * (b - a) / k) : a < b ? 1 : 0;
|
|
1491
|
+
var slack = (d) => 0.000000001 * (1 + Math.abs(d));
|
|
1492
|
+
function fold(nodes, p, rgb, base, sparse = true) {
|
|
1493
|
+
let d = Number.POSITIVE_INFINITY;
|
|
1494
|
+
if (rgb)
|
|
1495
|
+
rgb.set(base);
|
|
1496
|
+
for (const n of nodes) {
|
|
1497
|
+
const k = n.k;
|
|
1498
|
+
if (n.op === PAINT) {
|
|
1499
|
+
if (!rgb || !n.color || !n.shape)
|
|
1500
|
+
continue;
|
|
1501
|
+
const kk = Math.max(k, 0.000000001);
|
|
1502
|
+
if (sparse && lowerBound(n, p) > kk / 2 + slack(kk))
|
|
1503
|
+
continue;
|
|
1504
|
+
const w = 1 - clamp012(0.5 - n.shape(p) / kk);
|
|
1505
|
+
if (w < 1)
|
|
1506
|
+
mix(rgb, n.color, w);
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
const empty = d === Number.POSITIVE_INFINITY;
|
|
1510
|
+
if (empty && n.op !== ADD)
|
|
1511
|
+
continue;
|
|
1512
|
+
if (sparse && !empty) {
|
|
1513
|
+
if (n.op === ADD && lowerBound(n, p) > d + k + slack(d))
|
|
1514
|
+
continue;
|
|
1515
|
+
if (n.op === SUB && lowerBound(n, p) > k - d + slack(d))
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
const v = n.kids ? fold(n.kids, p, rgb ? n.rgb : null, n.base, sparse) : n.shape ? n.shape(p) : 0;
|
|
1519
|
+
let w = 0;
|
|
1520
|
+
if (empty)
|
|
1521
|
+
d = v;
|
|
1522
|
+
else if (n.op === ADD) {
|
|
1523
|
+
w = weight(d, v, k);
|
|
1524
|
+
if (w !== 1)
|
|
1525
|
+
d = v + (d - v) * w - k * w * (1 - w);
|
|
1526
|
+
} else {
|
|
1527
|
+
const b = n.op === SUB ? -v : v;
|
|
1528
|
+
w = weight(-d, -b, k);
|
|
1529
|
+
if (w !== 1)
|
|
1530
|
+
d = -(-b + (-d + b) * w - k * w * (1 - w));
|
|
1531
|
+
}
|
|
1532
|
+
if (!rgb || w >= 1)
|
|
1533
|
+
continue;
|
|
1534
|
+
if (n.kids) {
|
|
1535
|
+
if (n.color || n.op === ADD)
|
|
1536
|
+
mixArr(rgb, n.rgb, w);
|
|
1537
|
+
} else if (n.color || n.op === ADD)
|
|
1538
|
+
mix(rgb, n.color ?? base, w);
|
|
1539
|
+
}
|
|
1540
|
+
return d;
|
|
1541
|
+
}
|
|
1542
|
+
var mix = (rgb, c, w) => {
|
|
1543
|
+
for (let j = 0;j < 3; j++)
|
|
1544
|
+
rgb[j] = (c[j] ?? 0) + ((rgb[j] ?? 0) - (c[j] ?? 0)) * w;
|
|
1545
|
+
};
|
|
1546
|
+
var mixArr = (rgb, c, w) => {
|
|
1547
|
+
for (let j = 0;j < 3; j++)
|
|
1548
|
+
rgb[j] = (c[j] ?? 0) + ((rgb[j] ?? 0) - (c[j] ?? 0)) * w;
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// src/primitives/sdf-grid.ts
|
|
1552
|
+
var MAX_POINTS = 1 << 24;
|
|
1553
|
+
var BLOCK = 4;
|
|
1554
|
+
function sampleGrid(raw, bounds, h, lipschitz, symmetricY = false) {
|
|
1555
|
+
const [lo0, hi0] = bounds;
|
|
1556
|
+
if (!Number.isFinite(h) || h <= 0)
|
|
1557
|
+
invalid(`sdf resolution is ${h}`, "pass the cell edge in metres, e.g. ctx.pick([0.004, 0.012, 0.03])");
|
|
1558
|
+
if (![0, 1, 2].every((k) => Number.isFinite(lo0[k] ?? Number.NaN) && (hi0[k] ?? 0) > (lo0[k] ?? 0)))
|
|
1559
|
+
invalid(`sdf bounds [${lo0.join(", ")}] → [${hi0.join(", ")}] are empty or inverted`, "give bounds as [min, max] corners with max > min on every axis, in metres");
|
|
1560
|
+
const m = Math.ceil(Math.max(Math.abs(lo0[1]), Math.abs(hi0[1])) / h);
|
|
1561
|
+
const lo = symmetricY ? [lo0[0], -m * h, lo0[2]] : lo0;
|
|
1562
|
+
const hi = symmetricY ? [hi0[0], m * h, hi0[2]] : hi0;
|
|
1563
|
+
const size = (k) => Math.ceil(((hi[k] ?? 0) - (lo[k] ?? 0)) / h) + 3;
|
|
1564
|
+
const [nx, ny, nz] = [size(0), symmetricY ? 2 * m + 3 : size(1), size(2)];
|
|
1565
|
+
const yAt = (j) => symmetricY ? (j - m - 1) * h : lo[1] - h + j * h;
|
|
1566
|
+
if (nx * ny * nz > MAX_POINTS) {
|
|
1567
|
+
const vol = [0, 1, 2].reduce((v, k) => v * ((hi[k] ?? 0) - (lo[k] ?? 0)), 1);
|
|
1568
|
+
invalid(`sdf grid ${nx}×${ny}×${nz} exceeds ${MAX_POINTS} points at ${h} m cells`, `use cells ≥ ${Math.cbrt(vol / MAX_POINTS).toPrecision(2)} m, or tighten the bounds`);
|
|
1569
|
+
}
|
|
1570
|
+
const cx = (lo[0] + hi[0]) / 2;
|
|
1571
|
+
const cy = (lo[1] + hi[1]) / 2;
|
|
1572
|
+
const cz = (lo[2] + hi[2]) / 2;
|
|
1573
|
+
const [bx, by, bz] = [(hi[0] - lo[0]) / 2, (hi[1] - lo[1]) / 2, (hi[2] - lo[2]) / 2];
|
|
1574
|
+
const field = (p) => {
|
|
1575
|
+
const qx = Math.abs(p[0] - cx) - bx;
|
|
1576
|
+
const qy = Math.abs(p[1] - cy) - by;
|
|
1577
|
+
const qz = Math.abs(p[2] - cz) - bz;
|
|
1578
|
+
const box = Math.hypot(Math.max(qx, 0), Math.max(qy, 0), Math.max(qz, 0)) + Math.min(Math.max(qx, qy, qz), 0);
|
|
1579
|
+
const f = raw(p);
|
|
1580
|
+
if (Number.isNaN(f))
|
|
1581
|
+
invalid(`sdf field returned NaN at [${p.join(", ")}]`, "make the field return a finite distance everywhere in bounds");
|
|
1582
|
+
return Math.max(f, box);
|
|
1583
|
+
};
|
|
1584
|
+
const origin = [lo[0] - h, symmetricY ? -(m + 1) * h : lo[1] - h, lo[2] - h];
|
|
1585
|
+
const values = new Float32Array(nx * ny * nz);
|
|
1586
|
+
const exact = new Uint8Array(nx * ny * nz);
|
|
1587
|
+
const [kx, ky, kz] = [
|
|
1588
|
+
Math.ceil((nx - 1) / BLOCK),
|
|
1589
|
+
Math.ceil((ny - 1) / BLOCK),
|
|
1590
|
+
Math.ceil((nz - 1) / BLOCK)
|
|
1591
|
+
];
|
|
1592
|
+
const coarse = new Float32Array(kx * ky * kz);
|
|
1593
|
+
const p = [0, 0, 0];
|
|
1594
|
+
const floor = h * 0.01;
|
|
1595
|
+
const reach = lipschitz * BLOCK * h * Math.sqrt(3) * 0.5 + h;
|
|
1596
|
+
for (let k = 0;k < kz; k++)
|
|
1597
|
+
for (let j = 0;j < ky; j++)
|
|
1598
|
+
for (let i = 0;i < kx; i++) {
|
|
1599
|
+
p[0] = origin[0] + (i + 0.5) * BLOCK * h;
|
|
1600
|
+
p[1] = origin[1] + (j + 0.5) * BLOCK * h;
|
|
1601
|
+
p[2] = origin[2] + (k + 0.5) * BLOCK * h;
|
|
1602
|
+
const v = field(p);
|
|
1603
|
+
coarse[i + kx * (j + ky * k)] = v;
|
|
1604
|
+
if (Math.abs(v) > reach)
|
|
1605
|
+
continue;
|
|
1606
|
+
for (let z = k * BLOCK;z <= Math.min(nz - 1, (k + 1) * BLOCK); z++)
|
|
1607
|
+
for (let y = j * BLOCK;y <= Math.min(ny - 1, (j + 1) * BLOCK); y++)
|
|
1608
|
+
for (let x = i * BLOCK;x <= Math.min(nx - 1, (i + 1) * BLOCK); x++)
|
|
1609
|
+
exact[x + nx * (y + ny * z)] = 1;
|
|
1610
|
+
}
|
|
1611
|
+
for (let z = 0;z < nz; z++)
|
|
1612
|
+
for (let y = 0;y < ny; y++)
|
|
1613
|
+
for (let x = 0;x < nx; x++) {
|
|
1614
|
+
const id = x + nx * (y + ny * z);
|
|
1615
|
+
if (exact[id]) {
|
|
1616
|
+
p[0] = origin[0] + x * h;
|
|
1617
|
+
p[1] = yAt(y);
|
|
1618
|
+
p[2] = origin[2] + z * h;
|
|
1619
|
+
const f = field(p);
|
|
1620
|
+
values[id] = Math.abs(f) >= floor ? f : f < 0 ? -floor : floor;
|
|
1621
|
+
} else {
|
|
1622
|
+
const b = Math.min(kx - 1, Math.floor(x / BLOCK)) + kx * (Math.min(ky - 1, Math.floor(y / BLOCK)) + ky * Math.min(kz - 1, Math.floor(z / BLOCK)));
|
|
1623
|
+
values[id] = coarse[b] ?? 1;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return { nx, ny, nz, origin, h, values, field };
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// src/primitives/mc-table.ts
|
|
1630
|
+
var PACKED = ".083.019.183981.12a.08312a.92a029.2832a8a98.3b2.0b28b0.19023b.1b219b98b.3a1ba3.0a108a8ba.3" + "903b9ba9.98aa8b.478.430734.019847.419471731.12a847.34730412a.92a902847.2a9297273794.8473b2" + ".b47b24204.90184723b.47b94b9b2921.3a13ba784.1ba14b1047b4.47890b9bab03.47b4b99ba.954.954083" + ".054150.854835315.12a954.30812a495.52a542402.2a5325354348.95423b.0b208b495.05401523b.21525" + "828b485.a3ba13954.4950818a18ba.54050b5bab03.54858aa8b.978579.930953573.078017157.153357.97" + "8957a12.a12950530573.802825857a52.2a5253357.7957893b2.95797292027b.23b018178157.b21b17715." + "958857a13a3b.5705097b010aba0.ba0b03a50807570.ba57b5.a65.0835a6.9015a6.1831985a6.165261.165" + "126308.965906026.598582526328.23ba65.b08b20a65.01923b5a6.5a61929b298b.63b653513.08b0b50515" + "b6.3b6036065059.65969bb98.5a6478.43047365a.1905a6847.a65197173794.612651478.125526304347.8" + "47905065026.739794329596269.3b2784a65.5a647242027b.01947823b5a6.9219b294b7b45a6.8473b53515" + "b6.51b5b610b7b404b.059065036b63847.65969b4797b9.a4964a.4a649a083.a01a60640.83181686461a.14" + "9124264.308129249264.024426.832824426.a49a64b23.08228b49a4a6.3b201606461a.64161a48121b8b1." + "964936913b63.8b1810b61914641.3b6360064.648b68.7a678a89a.0730a709a67a.a671a7178180.a67a7117" + "3.126168189867.269291679093739.780706602.732672.23ba68a89867.20727b09767a9a7.1801781a767a2" + "3b.b21b17a61671.896867916b63136.091b67.7807063b0b60.7b6.76b.308b76.019b76.819831b76.a126b7" + ".12a3086b7.2902a96b7.6b72a3a83a98.723627.708760620.276237019.162186198876.a76a17137.a7617a" + "187108.03707a0a96a7.76a7a88a9.684b86.36b306046.86b846901.946963931b36.6846b82a1.12a30b06b0" + "46.4b846b0292a9.a93a32943b36463.823842462.042462.190234246438.194142246.8138618466a1.a10a0" + "6604.4634386a3039a93.a946a4.49576b.083495b76.50154076b.b76834354315.954a1276b.6b712a083495" + ".76b54a42a402.348354325a52b76.723762549.954086062687.362376150540.628687218485158.954a1617" + "6137.16a176107870954.40a4a503a6a737a.76a7a854a48a.6956b9b89.36b063056095.0b805b01556b.6b36" + "35531.12a95b9b8b56.0b306b09656912a.b85b56805a52025.6b36352a3a53.589528562382.956960062.158" + "180568382628.156216.13616a386569896.a10a06950560.03856a.a56.b5a75b.b5ab75830.5b75ab190.a75" + "ab7981831.b12b71751.08312717572b.9759279022b7.75272b592328982.25a235375.820852875a25.9015a" + "35373a2.982921872a25752.135375.087071175.903935537.987597.5845a8ab8.5045b05abb30.01984a8ab" + "a45.ab4a45b34941314.2512852b8458.04b0b345b2b151b.0250592b5458b85.9452b3.25a352345384.5a252" + "4420.3a235a385458019.5a2524192942.845853351.045105.845853905035.945.4b749b9ab.0834979b79ab" + ".1ab1b414074b.3143481a474bab4.4b79b492b912.9749b791b2b1083.b74b42240.b74b42834324.29a27923" + "7749.9a7974a27870207.37a3a274a1a040a.1a2874.491417713.491417081871.403743.487.9a8ab8.30939" + "bb9a.01a0a88ab.31ab3a.12b1b99b8.30939b1292b9.02b80b.32b.23828aa89.9a2092.23828a0181a8.1a2." + "138918.091.038.";
|
|
1631
|
+
var TRI = PACKED.split(".").map((c) => [...c].map((h) => Number.parseInt(h, 16)));
|
|
1632
|
+
|
|
1633
|
+
// src/primitives/surface-nets.ts
|
|
1634
|
+
var CORNER = [0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1];
|
|
1635
|
+
var EDGE = [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7];
|
|
1636
|
+
function surfaceNets(g) {
|
|
1637
|
+
const { nx, ny, nz, values: v, h, origin: o } = g;
|
|
1638
|
+
const cells = new Int32Array((nx - 1) * (ny - 1) * (nz - 1)).fill(-1);
|
|
1639
|
+
const pos = [];
|
|
1640
|
+
const c = new Float64Array(8);
|
|
1641
|
+
for (let z = 0;z < nz - 1; z++)
|
|
1642
|
+
for (let y = 0;y < ny - 1; y++)
|
|
1643
|
+
for (let x = 0;x < nx - 1; x++) {
|
|
1644
|
+
let mask = 0;
|
|
1645
|
+
for (let k = 0;k < 8; k++) {
|
|
1646
|
+
const val = v[x + (CORNER[k * 3] ?? 0) + nx * (y + (CORNER[k * 3 + 1] ?? 0) + ny * (z + (CORNER[k * 3 + 2] ?? 0)))] ?? 1;
|
|
1647
|
+
c[k] = val;
|
|
1648
|
+
if (val < 0)
|
|
1649
|
+
mask |= 1 << k;
|
|
1650
|
+
}
|
|
1651
|
+
if (mask === 0 || mask === 255)
|
|
1652
|
+
continue;
|
|
1653
|
+
let sx = 0;
|
|
1654
|
+
let sy = 0;
|
|
1655
|
+
let sz = 0;
|
|
1656
|
+
let n = 0;
|
|
1657
|
+
for (let e = 0;e < 12; e++) {
|
|
1658
|
+
const a = EDGE[e * 2] ?? 0;
|
|
1659
|
+
const b = EDGE[e * 2 + 1] ?? 0;
|
|
1660
|
+
const va = c[a] ?? 0;
|
|
1661
|
+
const vb = c[b] ?? 0;
|
|
1662
|
+
if (va < 0 === vb < 0)
|
|
1663
|
+
continue;
|
|
1664
|
+
const t = va / (va - vb);
|
|
1665
|
+
sx += (CORNER[a * 3] ?? 0) + t * ((CORNER[b * 3] ?? 0) - (CORNER[a * 3] ?? 0));
|
|
1666
|
+
sy += (CORNER[a * 3 + 1] ?? 0) + t * ((CORNER[b * 3 + 1] ?? 0) - (CORNER[a * 3 + 1] ?? 0));
|
|
1667
|
+
sz += (CORNER[a * 3 + 2] ?? 0) + t * ((CORNER[b * 3 + 2] ?? 0) - (CORNER[a * 3 + 2] ?? 0));
|
|
1668
|
+
n++;
|
|
1669
|
+
}
|
|
1670
|
+
cells[x + (nx - 1) * (y + (ny - 1) * z)] = pos.length / 3;
|
|
1671
|
+
pos.push(o[0] + (x + sx / n) * h, o[1] + (y + sy / n) * h, o[2] + (z + sz / n) * h);
|
|
1672
|
+
}
|
|
1673
|
+
const tris = [];
|
|
1674
|
+
const cell = (x, y, z) => cells[x + (nx - 1) * (y + (ny - 1) * z)] ?? 0;
|
|
1675
|
+
const P = (i, k) => pos[i * 3 + k] ?? 0;
|
|
1676
|
+
const n1 = new Float64Array(3);
|
|
1677
|
+
const n2 = new Float64Array(3);
|
|
1678
|
+
const normal = (a, b, c, n) => {
|
|
1679
|
+
const ex = P(b, 0) - P(a, 0);
|
|
1680
|
+
const ey = P(b, 1) - P(a, 1);
|
|
1681
|
+
const ez = P(b, 2) - P(a, 2);
|
|
1682
|
+
const fx = P(c, 0) - P(a, 0);
|
|
1683
|
+
const fy = P(c, 1) - P(a, 1);
|
|
1684
|
+
const fz = P(c, 2) - P(a, 2);
|
|
1685
|
+
const x = ey * fz - ez * fy;
|
|
1686
|
+
const y = ez * fx - ex * fz;
|
|
1687
|
+
const z = ex * fy - ey * fx;
|
|
1688
|
+
const l = Math.sqrt(x * x + y * y + z * z) || 1;
|
|
1689
|
+
n[0] = x / l;
|
|
1690
|
+
n[1] = y / l;
|
|
1691
|
+
n[2] = z / l;
|
|
1692
|
+
};
|
|
1693
|
+
const flatness = () => (n1[0] ?? 0) * (n2[0] ?? 0) + (n1[1] ?? 0) * (n2[1] ?? 0) + (n1[2] ?? 0) * (n2[2] ?? 0);
|
|
1694
|
+
const quad = (a, b, cc, d, out) => {
|
|
1695
|
+
const [q0, q1, q2, q3] = out ? [a, b, cc, d] : [a, d, cc, b];
|
|
1696
|
+
normal(q0, q1, q2, n1);
|
|
1697
|
+
normal(q0, q2, q3, n2);
|
|
1698
|
+
const along02 = flatness();
|
|
1699
|
+
normal(q0, q1, q3, n1);
|
|
1700
|
+
normal(q1, q2, q3, n2);
|
|
1701
|
+
if (along02 >= flatness())
|
|
1702
|
+
tris.push(q0, q1, q2, q0, q2, q3);
|
|
1703
|
+
else
|
|
1704
|
+
tris.push(q0, q1, q3, q1, q2, q3);
|
|
1705
|
+
};
|
|
1706
|
+
for (let z = 1;z < nz - 1; z++)
|
|
1707
|
+
for (let y = 1;y < ny - 1; y++)
|
|
1708
|
+
for (let x = 1;x < nx - 1; x++) {
|
|
1709
|
+
const inside = (v[x + nx * (y + ny * z)] ?? 1) < 0;
|
|
1710
|
+
if (x < nx - 1 && inside !== (v[x + 1 + nx * (y + ny * z)] ?? 1) < 0)
|
|
1711
|
+
quad(cell(x, y - 1, z - 1), cell(x, y, z - 1), cell(x, y, z), cell(x, y - 1, z), inside);
|
|
1712
|
+
if (y < ny - 1 && inside !== (v[x + nx * (y + 1 + ny * z)] ?? 1) < 0)
|
|
1713
|
+
quad(cell(x - 1, y, z - 1), cell(x - 1, y, z), cell(x, y, z), cell(x, y, z - 1), inside);
|
|
1714
|
+
if (z < nz - 1 && inside !== (v[x + nx * (y + ny * (z + 1))] ?? 1) < 0)
|
|
1715
|
+
quad(cell(x - 1, y - 1, z), cell(x, y - 1, z), cell(x, y, z), cell(x - 1, y, z), inside);
|
|
1716
|
+
}
|
|
1717
|
+
return { pos, tris };
|
|
1718
|
+
}
|
|
1719
|
+
function marchingCubes(g) {
|
|
1720
|
+
const { nx, ny, nz, values: v, h, origin: o } = g;
|
|
1721
|
+
const shared = new Map;
|
|
1722
|
+
const pos = [];
|
|
1723
|
+
const tris = [];
|
|
1724
|
+
for (let z = 0;z < nz - 1; z++)
|
|
1725
|
+
for (let y = 0;y < ny - 1; y++)
|
|
1726
|
+
for (let x = 0;x < nx - 1; x++) {
|
|
1727
|
+
let mask = 0;
|
|
1728
|
+
for (let k = 0;k < 8; k++)
|
|
1729
|
+
if ((v[x + (CORNER[k * 3] ?? 0) + nx * (y + (CORNER[k * 3 + 1] ?? 0) + ny * (z + (CORNER[k * 3 + 2] ?? 0)))] ?? 1) < 0)
|
|
1730
|
+
mask |= 1 << k;
|
|
1731
|
+
const list = TRI[mask] ?? [];
|
|
1732
|
+
for (let t = 0;t < list.length; t += 3)
|
|
1733
|
+
for (const j of [0, 2, 1]) {
|
|
1734
|
+
const e = list[t + j] ?? 0;
|
|
1735
|
+
const a = EDGE[e * 2] ?? 0;
|
|
1736
|
+
const b = EDGE[e * 2 + 1] ?? 0;
|
|
1737
|
+
const ax = x + (CORNER[a * 3] ?? 0);
|
|
1738
|
+
const ay = y + (CORNER[a * 3 + 1] ?? 0);
|
|
1739
|
+
const az = z + (CORNER[a * 3 + 2] ?? 0);
|
|
1740
|
+
const bx = x + (CORNER[b * 3] ?? 0);
|
|
1741
|
+
const by = y + (CORNER[b * 3 + 1] ?? 0);
|
|
1742
|
+
const bz = z + (CORNER[b * 3 + 2] ?? 0);
|
|
1743
|
+
const ia = ax + nx * (ay + ny * az);
|
|
1744
|
+
const ib = bx + nx * (by + ny * bz);
|
|
1745
|
+
const lowId = Math.min(ia, ib);
|
|
1746
|
+
const key = lowId * 3 + (ax !== bx ? 0 : ay !== by ? 1 : 2);
|
|
1747
|
+
let id = shared.get(key);
|
|
1748
|
+
if (id === undefined) {
|
|
1749
|
+
const va = v[ia] ?? 0;
|
|
1750
|
+
const s = va / (va - (v[ib] ?? 0));
|
|
1751
|
+
id = pos.length / 3;
|
|
1752
|
+
pos.push(o[0] + (ax + s * (bx - ax)) * h, o[1] + (ay + s * (by - ay)) * h, o[2] + (az + s * (bz - az)) * h);
|
|
1753
|
+
shared.set(key, id);
|
|
1754
|
+
}
|
|
1755
|
+
tris.push(id);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return { pos, tris };
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/primitives/sdf.ts
|
|
1762
|
+
function sdf2(o) {
|
|
1763
|
+
const lip = o.lipschitz ?? 1.25;
|
|
1764
|
+
if (!(lip >= 1))
|
|
1765
|
+
invalid(`sdf lipschitz is ${lip}`, "use ≥ 1 (1 = exact distance field)");
|
|
1766
|
+
const g = sampleGrid(o.field, o.bounds, o.resolution, lip, o.symmetry === "y");
|
|
1767
|
+
const raw = o.mesher === "cubes" ? marchingCubes(g) : surfaceNets(g);
|
|
1768
|
+
if (raw.tris.length === 0)
|
|
1769
|
+
invalid("sdf field has no surface inside its bounds (never crosses zero)", "return negative distances inside the shape, positive outside, and make bounds contain it");
|
|
1770
|
+
const strip = o.strip ?? "plain";
|
|
1771
|
+
const rect = stripRect2(strip);
|
|
1772
|
+
const vm = stripVMetres(strip);
|
|
1773
|
+
const e = g.h * 0.5;
|
|
1774
|
+
const q = [0, 0, 0];
|
|
1775
|
+
const at = (x, y, z) => {
|
|
1776
|
+
q[0] = x;
|
|
1777
|
+
q[1] = y;
|
|
1778
|
+
q[2] = z;
|
|
1779
|
+
return g.field(q);
|
|
1780
|
+
};
|
|
1781
|
+
const b = new MeshBuilder2;
|
|
1782
|
+
for (let i = 0;i < raw.pos.length; i += 3) {
|
|
1783
|
+
const x = raw.pos[i] ?? 0;
|
|
1784
|
+
const y = raw.pos[i + 1] ?? 0;
|
|
1785
|
+
const z = raw.pos[i + 2] ?? 0;
|
|
1786
|
+
const gx = at(x + e, y, z) - at(x - e, y, z);
|
|
1787
|
+
const gy = at(x, y + e, z) - at(x, y - e, z);
|
|
1788
|
+
const gz = at(x, y, z + e) - at(x, y, z - e);
|
|
1789
|
+
const gl = Math.sqrt(gx * gx + gy * gy + gz * gz);
|
|
1790
|
+
const n = gl > 0 ? [gx / gl, gy / gl, gz / gl] : [0, 0, 1];
|
|
1791
|
+
const ax = Math.abs(n[0]);
|
|
1792
|
+
const ay = Math.abs(n[1]);
|
|
1793
|
+
const az = Math.abs(n[2]);
|
|
1794
|
+
const [s, t] = az >= ax && az >= ay ? [x, y] : ax >= ay ? [y, z] : [x, z];
|
|
1795
|
+
const p = [x, y, z];
|
|
1796
|
+
b.vertex({
|
|
1797
|
+
p,
|
|
1798
|
+
n,
|
|
1799
|
+
uv: [stripU(rect, s), mirroredV(rect, t, vm)],
|
|
1800
|
+
color: typeof o.color === "function" ? o.color(p, n) : o.color
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
for (let i = 0;i < raw.tris.length; i += 3)
|
|
1804
|
+
b.tri(raw.tris[i] ?? 0, raw.tris[i + 1] ?? 0, raw.tris[i + 2] ?? 0);
|
|
1805
|
+
return b.build();
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/primitives/sculpt.ts
|
|
1809
|
+
function sculptField2(edits, o) {
|
|
1810
|
+
if (edits.length === 0)
|
|
1811
|
+
invalid("sculpt has no edits", "start with an { shape: 'sphere', op: 'add', ... } edit");
|
|
1812
|
+
const nodes = compile(edits, o.color, o.mirror === "y");
|
|
1813
|
+
const rgb = new Float64Array(3);
|
|
1814
|
+
const adds = edits.filter((e) => (e.op ?? "add") === "add");
|
|
1815
|
+
const lo = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
|
|
1816
|
+
const hi = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
|
1817
|
+
for (const e of adds) {
|
|
1818
|
+
const [a, b] = editBounds(e, o.mirror === "y");
|
|
1819
|
+
for (let k = 0;k < 3; k++) {
|
|
1820
|
+
lo[k] = Math.min(lo[k] ?? 0, a[k] ?? 0);
|
|
1821
|
+
hi[k] = Math.max(hi[k] ?? 0, b[k] ?? 0);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
field: (p) => fold(nodes, p, null, o.color),
|
|
1826
|
+
colorAt: (p) => {
|
|
1827
|
+
fold(nodes, p, rgb, o.color);
|
|
1828
|
+
return [rgb[0] ?? 0, rgb[1] ?? 0, rgb[2] ?? 0];
|
|
1829
|
+
},
|
|
1830
|
+
bounds: [
|
|
1831
|
+
[lo[0] ?? 0, lo[1] ?? 0, lo[2] ?? 0],
|
|
1832
|
+
[hi[0] ?? 0, hi[1] ?? 0, hi[2] ?? 0]
|
|
1833
|
+
]
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
function sculpt2(o) {
|
|
1837
|
+
const s = sculptField2(o.edits, { color: o.color, ...o.mirror ? { mirror: o.mirror } : {} });
|
|
1838
|
+
return sdf2({
|
|
1839
|
+
field: s.field,
|
|
1840
|
+
bounds: s.bounds,
|
|
1841
|
+
resolution: o.resolution,
|
|
1842
|
+
color: (p) => s.colorAt(p),
|
|
1843
|
+
...o.mesher ? { mesher: o.mesher } : {},
|
|
1844
|
+
...o.mirror === "y" ? { symmetry: "y" } : {},
|
|
1845
|
+
...o.strip ? { strip: o.strip } : {}
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
// src/primitives/transport.ts
|
|
1849
|
+
var sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
1850
|
+
var dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
1851
|
+
var unit = (a) => {
|
|
1852
|
+
const l = Math.hypot(a[0], a[1], a[2]) || 1;
|
|
1853
|
+
return [a[0] / l, a[1] / l, a[2] / l];
|
|
1854
|
+
};
|
|
1855
|
+
var cross3 = (a, b) => [
|
|
1856
|
+
a[1] * b[2] - a[2] * b[1],
|
|
1857
|
+
a[2] * b[0] - a[0] * b[2],
|
|
1858
|
+
a[0] * b[1] - a[1] * b[0]
|
|
1859
|
+
];
|
|
1860
|
+
var reflect = (v, axis, c) => {
|
|
1861
|
+
const k = 2 / c * dot(axis, v);
|
|
1862
|
+
return [v[0] - k * axis[0], v[1] - k * axis[1], v[2] - k * axis[2]];
|
|
1863
|
+
};
|
|
1864
|
+
function arcLengths2(path) {
|
|
1865
|
+
if (path.length < 2) {
|
|
1866
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", {
|
|
1867
|
+
cause: `sweep path has ${path.length} point(s)`,
|
|
1868
|
+
fix: "pass at least 2 path points (from → to); a single point has no direction to sweep along"
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
const s = [0];
|
|
1872
|
+
for (let i = 1;i < path.length; i++) {
|
|
1873
|
+
const d = Math.hypot(...sub(path[i] ?? [0, 0, 0], path[i - 1] ?? [0, 0, 0]));
|
|
1874
|
+
if (d < 0.000000001) {
|
|
1875
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", {
|
|
1876
|
+
cause: `sweep path points ${i - 1} and ${i} coincide (zero-length segment)`,
|
|
1877
|
+
fix: "make consecutive path points distinct — drop duplicates before sweeping"
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
s.push((s[i - 1] ?? 0) + d);
|
|
1881
|
+
}
|
|
1882
|
+
return s;
|
|
1883
|
+
}
|
|
1884
|
+
function transportFrames2(path) {
|
|
1885
|
+
arcLengths2(path);
|
|
1886
|
+
const last = path.length - 1;
|
|
1887
|
+
const tangents = path.map((_, i) => unit(sub(path[Math.min(last, i + 1)] ?? [0, 0, 0], path[Math.max(0, i - 1)] ?? [0, 0, 0])));
|
|
1888
|
+
const t0 = tangents[0] ?? [0, 0, 1];
|
|
1889
|
+
const ref = Math.abs(t0[2]) > 0.9 ? [1, 0, 0] : [0, 0, 1];
|
|
1890
|
+
const k = dot(ref, t0);
|
|
1891
|
+
let n = unit([ref[0] - k * t0[0], ref[1] - k * t0[1], ref[2] - k * t0[2]]);
|
|
1892
|
+
const out = [{ t: t0, n, b: cross3(t0, n) }];
|
|
1893
|
+
for (let i = 1;i <= last; i++) {
|
|
1894
|
+
const p0 = path[i - 1] ?? [0, 0, 0];
|
|
1895
|
+
const p1 = path[i] ?? [0, 0, 0];
|
|
1896
|
+
const ti = tangents[i - 1] ?? t0;
|
|
1897
|
+
const tj = tangents[i] ?? t0;
|
|
1898
|
+
const v1 = sub(p1, p0);
|
|
1899
|
+
const c1 = dot(v1, v1);
|
|
1900
|
+
const nL = reflect(n, v1, c1);
|
|
1901
|
+
const tL = reflect(ti, v1, c1);
|
|
1902
|
+
const v2 = sub(tj, tL);
|
|
1903
|
+
const c2 = dot(v2, v2);
|
|
1904
|
+
n = unit(c2 < 0.000000000001 ? nL : reflect(nL, v2, c2));
|
|
1905
|
+
out.push({ t: tj, n, b: cross3(tj, n) });
|
|
1906
|
+
}
|
|
1907
|
+
return out;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// src/primitives/sweep.ts
|
|
1911
|
+
function section2(s) {
|
|
1912
|
+
if (!Array.isArray(s)) {
|
|
1913
|
+
const c = s;
|
|
1914
|
+
if (c.segments < 3 || !(c.radius > 0)) {
|
|
1915
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", {
|
|
1916
|
+
cause: `sweep circle section has radius ${c.radius} and ${c.segments} segments`,
|
|
1917
|
+
fix: "use radius > 0 and segments >= 3 (pick segments from ctx.lod: e.g. 8 / 5 / 3)"
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
const nrm = [];
|
|
1921
|
+
for (let k = 0;k < c.segments; k++) {
|
|
1922
|
+
const a = k / c.segments * Math.PI * 2;
|
|
1923
|
+
nrm.push([Math.cos(a), Math.sin(a)]);
|
|
1924
|
+
}
|
|
1925
|
+
return { pts: nrm.map(([x, y]) => [x * c.radius, y * c.radius]), nrm };
|
|
1926
|
+
}
|
|
1927
|
+
const poly = s;
|
|
1928
|
+
if (poly.length < 3) {
|
|
1929
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", {
|
|
1930
|
+
cause: `sweep section has ${poly.length} point(s)`,
|
|
1931
|
+
fix: "pass a closed polygon of at least 3 points, or { radius, segments }"
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
const area = poly.reduce((a, p, i) => {
|
|
1935
|
+
const q = poly[(i + 1) % poly.length] ?? p;
|
|
1936
|
+
return a + p[0] * q[1] - q[0] * p[1];
|
|
1937
|
+
}, 0);
|
|
1938
|
+
const pts = area < 0 ? [...poly].reverse() : poly;
|
|
1939
|
+
const nrm = pts.map((p, i) => {
|
|
1940
|
+
const a = pts[(i + pts.length - 1) % pts.length] ?? p;
|
|
1941
|
+
const b = pts[(i + 1) % pts.length] ?? p;
|
|
1942
|
+
const e1 = Math.hypot(p[0] - a[0], p[1] - a[1]) || 1;
|
|
1943
|
+
const e2 = Math.hypot(b[0] - p[0], b[1] - p[1]) || 1;
|
|
1944
|
+
const x = (p[1] - a[1]) / e1 + (b[1] - p[1]) / e2;
|
|
1945
|
+
const y = -(p[0] - a[0]) / e1 - (b[0] - p[0]) / e2;
|
|
1946
|
+
const l = Math.hypot(x, y) || 1;
|
|
1947
|
+
return [x / l, y / l];
|
|
1948
|
+
});
|
|
1949
|
+
return { pts, nrm };
|
|
1950
|
+
}
|
|
1951
|
+
function sweep2(o) {
|
|
1952
|
+
const arc = arcLengths2(o.path);
|
|
1953
|
+
const frames = transportFrames2(o.path);
|
|
1954
|
+
const P = o.path.length;
|
|
1955
|
+
if (o.scale && o.scale.length !== P) {
|
|
1956
|
+
throw new Ts23dError2("T3D_RECIPE_INVALID", {
|
|
1957
|
+
cause: `sweep has ${P} path points but ${o.scale.length} scales`,
|
|
1958
|
+
fix: "pass exactly one scale per path point (or omit scale for a constant section)"
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
const sec = section2(o.section);
|
|
1962
|
+
const S = sec.pts.length;
|
|
1963
|
+
const total = arc[P - 1] ?? 1;
|
|
1964
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
1965
|
+
const du = (rect.u1 - rect.u0) / rect.metres;
|
|
1966
|
+
const twist = (o.twist ?? 0) * Math.PI / 180;
|
|
1967
|
+
const b = new MeshBuilder2;
|
|
1968
|
+
const rim = new Float64Array(P * S * 3);
|
|
1969
|
+
for (let i = 0;i < P; i++) {
|
|
1970
|
+
const c = o.path[i] ?? [0, 0, 0];
|
|
1971
|
+
const { t, n, b: bn } = frames[i] ?? { t: [0, 0, 1], n: [1, 0, 0], b: [0, 1, 0] };
|
|
1972
|
+
const k = o.scale?.[i] ?? 1;
|
|
1973
|
+
const i0 = Math.max(0, i - 1);
|
|
1974
|
+
const i1 = Math.min(P - 1, i + 1);
|
|
1975
|
+
const slope = ((o.scale?.[i1] ?? 1) - (o.scale?.[i0] ?? 1)) / ((arc[i1] ?? 0) - (arc[i0] ?? 0) || 1);
|
|
1976
|
+
const rot = twist * (arc[i] ?? 0) / total;
|
|
1977
|
+
const cr = Math.cos(rot);
|
|
1978
|
+
const sr = Math.sin(rot);
|
|
1979
|
+
const u = rect.u0 + (arc[i] ?? 0) * du;
|
|
1980
|
+
for (let j = 0;j <= S; j++) {
|
|
1981
|
+
const [px, py] = sec.pts[j % S] ?? [0, 0];
|
|
1982
|
+
const [nx, ny] = sec.nrm[j % S] ?? [1, 0];
|
|
1983
|
+
const x = (px * cr - py * sr) * k;
|
|
1984
|
+
const y = (px * sr + py * cr) * k;
|
|
1985
|
+
const mx = nx * cr - ny * sr;
|
|
1986
|
+
const my = nx * sr + ny * cr;
|
|
1987
|
+
const back = -slope * (px * nx + py * ny);
|
|
1988
|
+
const qx = mx * n[0] + my * bn[0] + back * t[0];
|
|
1989
|
+
const qy = mx * n[1] + my * bn[1] + back * t[1];
|
|
1990
|
+
const qz = mx * n[2] + my * bn[2] + back * t[2];
|
|
1991
|
+
const ql = Math.hypot(qx, qy, qz) || 1;
|
|
1992
|
+
const p = [
|
|
1993
|
+
c[0] + x * n[0] + y * bn[0],
|
|
1994
|
+
c[1] + x * n[1] + y * bn[1],
|
|
1995
|
+
c[2] + x * n[2] + y * bn[2]
|
|
1996
|
+
];
|
|
1997
|
+
if (j < S)
|
|
1998
|
+
rim.set(p, (i * S + j) * 3);
|
|
1999
|
+
b.vertex({
|
|
2000
|
+
p,
|
|
2001
|
+
n: [qx / ql, qy / ql, qz / ql],
|
|
2002
|
+
uv: [u, rect.v0 + j / S * (rect.v1 - rect.v0)],
|
|
2003
|
+
color: o.color
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
for (let i = 0;i + 1 < P; i++) {
|
|
2008
|
+
const r0 = i * (S + 1);
|
|
2009
|
+
const r1 = r0 + S + 1;
|
|
2010
|
+
for (let j = 0;j < S; j++)
|
|
2011
|
+
b.quad(r0 + j, r0 + j + 1, r1 + j + 1, r1 + j);
|
|
2012
|
+
}
|
|
2013
|
+
const caps = o.caps === true ? ["start", "end"] : o.caps ? [o.caps] : [];
|
|
2014
|
+
for (const end of caps) {
|
|
2015
|
+
const i = end === "start" ? 0 : P - 1;
|
|
2016
|
+
const f = frames[i]?.t ?? [0, 0, 1];
|
|
2017
|
+
const sign = end === "start" ? -1 : 1;
|
|
2018
|
+
const nn = [f[0] * sign, f[1] * sign, f[2] * sign];
|
|
2019
|
+
const centre = b.vertex({
|
|
2020
|
+
p: o.path[i] ?? [0, 0, 0],
|
|
2021
|
+
n: nn,
|
|
2022
|
+
uv: [rect.u0, rect.v0],
|
|
2023
|
+
color: o.color
|
|
2024
|
+
});
|
|
2025
|
+
const first = centre + 1;
|
|
2026
|
+
for (let j = 0;j < S; j++) {
|
|
2027
|
+
const q = (i * S + j) * 3;
|
|
2028
|
+
const p = [rim[q] ?? 0, rim[q + 1] ?? 0, rim[q + 2] ?? 0];
|
|
2029
|
+
b.vertex({ p, n: nn, uv: [rect.u0, rect.v1], color: o.color });
|
|
2030
|
+
}
|
|
2031
|
+
for (let j = 0;j < S; j++) {
|
|
2032
|
+
const a = first + j;
|
|
2033
|
+
const c = first + (j + 1) % S;
|
|
2034
|
+
if (sign > 0)
|
|
2035
|
+
b.tri(centre, a, c);
|
|
2036
|
+
else
|
|
2037
|
+
b.tri(centre, c, a);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return b.build();
|
|
2041
|
+
}
|
|
2042
|
+
// src/primitives/terrain-grid.ts
|
|
2043
|
+
function terrainGrid2(o) {
|
|
2044
|
+
const [x0, y0, x1, y1] = o.rect;
|
|
2045
|
+
if (!(x1 > x0 && y1 > y0))
|
|
2046
|
+
invalid(`terrain rect [${o.rect.join(", ")}] is empty`, "pass [minX, minY, maxX, maxY] with max > min");
|
|
2047
|
+
const n = count("terrain cells", o.cells, 2, 4096);
|
|
2048
|
+
const z = new Float64Array((n + 1) * (n + 1));
|
|
2049
|
+
for (let j = 0;j <= n; j++)
|
|
2050
|
+
for (let i = 0;i <= n; i++) {
|
|
2051
|
+
const h = o.height(x0 + (x1 - x0) * i / n, y0 + (y1 - y0) * j / n);
|
|
2052
|
+
if (!Number.isFinite(h))
|
|
2053
|
+
invalid(`terrain height is ${h} at point (${i}, ${j})`, "return a finite height in metres");
|
|
2054
|
+
z[j * (n + 1) + i] = h;
|
|
2055
|
+
}
|
|
2056
|
+
return { nx: n + 1, ny: n + 1, rect: o.rect, z };
|
|
2057
|
+
}
|
|
2058
|
+
function sampler2(g, map = g.z) {
|
|
2059
|
+
const [x0, y0, x1, y1] = g.rect;
|
|
2060
|
+
const sx = (g.nx - 1) / (x1 - x0);
|
|
2061
|
+
const sy = (g.ny - 1) / (y1 - y0);
|
|
2062
|
+
return (x, y) => {
|
|
2063
|
+
const fx = Math.min(g.nx - 1, Math.max(0, (x - x0) * sx));
|
|
2064
|
+
const fy = Math.min(g.ny - 1, Math.max(0, (y - y0) * sy));
|
|
2065
|
+
const i = Math.min(g.nx - 2, Math.floor(fx));
|
|
2066
|
+
const j = Math.min(g.ny - 2, Math.floor(fy));
|
|
2067
|
+
const u = fx - i;
|
|
2068
|
+
const v = fy - j;
|
|
2069
|
+
const k = j * g.nx + i;
|
|
2070
|
+
const a = map[k] ?? 0;
|
|
2071
|
+
const b = map[k + 1] ?? 0;
|
|
2072
|
+
const c = map[k + g.nx] ?? 0;
|
|
2073
|
+
const d = map[k + g.nx + 1] ?? 0;
|
|
2074
|
+
return a + (b - a) * u + (c + (d - c) * u - (a + (b - a) * u)) * v;
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function slopeMap2(g) {
|
|
2078
|
+
const [x0, y0, x1, y1] = g.rect;
|
|
2079
|
+
const dx = (x1 - x0) / (g.nx - 1);
|
|
2080
|
+
const dy = (y1 - y0) / (g.ny - 1);
|
|
2081
|
+
const out = new Float64Array(g.z.length);
|
|
2082
|
+
for (let j = 0;j < g.ny; j++)
|
|
2083
|
+
for (let i = 0;i < g.nx; i++) {
|
|
2084
|
+
const l = g.z[j * g.nx + Math.max(0, i - 1)] ?? 0;
|
|
2085
|
+
const r = g.z[j * g.nx + Math.min(g.nx - 1, i + 1)] ?? 0;
|
|
2086
|
+
const b = g.z[Math.max(0, j - 1) * g.nx + i] ?? 0;
|
|
2087
|
+
const t = g.z[Math.min(g.ny - 1, j + 1) * g.nx + i] ?? 0;
|
|
2088
|
+
const sx = (r - l) / (dx * (Math.min(g.nx - 1, i + 1) - Math.max(0, i - 1)));
|
|
2089
|
+
const sy = (t - b) / (dy * (Math.min(g.ny - 1, j + 1) - Math.max(0, j - 1)));
|
|
2090
|
+
out[j * g.nx + i] = Math.sqrt(sx * sx + sy * sy);
|
|
2091
|
+
}
|
|
2092
|
+
return out;
|
|
2093
|
+
}
|
|
2094
|
+
function thermalErosion2(g, o = {}) {
|
|
2095
|
+
const talus = o.talus ?? 0.7;
|
|
2096
|
+
const rate = Math.min(0.5, Math.max(0, o.rate ?? 0.4));
|
|
2097
|
+
const iterations = count("thermal iterations", o.iterations ?? 30, 0, 1e4);
|
|
2098
|
+
const [x0, , x1] = g.rect;
|
|
2099
|
+
const cell = (x1 - x0) / (g.nx - 1);
|
|
2100
|
+
const D = [-1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1];
|
|
2101
|
+
const limit = D.map((_, k) => k < 8 ? talus * cell : talus * cell * Math.SQRT2);
|
|
2102
|
+
let z = Float64Array.from(g.z);
|
|
2103
|
+
for (let it = 0;it < iterations; it++) {
|
|
2104
|
+
const next = Float64Array.from(z);
|
|
2105
|
+
for (let j = 1;j < g.ny - 1; j++)
|
|
2106
|
+
for (let i = 1;i < g.nx - 1; i++) {
|
|
2107
|
+
const k = j * g.nx + i;
|
|
2108
|
+
const h = z[k] ?? 0;
|
|
2109
|
+
let total = 0;
|
|
2110
|
+
let most = 0;
|
|
2111
|
+
for (let d = 0;d < 16; d += 2) {
|
|
2112
|
+
const ex = h - (z[k + (D[d] ?? 0) + (D[d + 1] ?? 0) * g.nx] ?? 0) - (limit[d] ?? 0);
|
|
2113
|
+
if (ex > 0) {
|
|
2114
|
+
total += ex;
|
|
2115
|
+
if (ex > most)
|
|
2116
|
+
most = ex;
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (total <= 0)
|
|
2120
|
+
continue;
|
|
2121
|
+
const move = rate * most;
|
|
2122
|
+
next[k] = (next[k] ?? 0) - move;
|
|
2123
|
+
for (let d = 0;d < 16; d += 2) {
|
|
2124
|
+
const n = k + (D[d] ?? 0) + (D[d + 1] ?? 0) * g.nx;
|
|
2125
|
+
const ex = h - (z[n] ?? 0) - (limit[d] ?? 0);
|
|
2126
|
+
if (ex > 0)
|
|
2127
|
+
next[n] = (next[n] ?? 0) + move * ex / total;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
z = next;
|
|
2131
|
+
}
|
|
2132
|
+
return { ...g, z };
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// src/primitives/terrain-color.ts
|
|
2136
|
+
var ramp = (a, b, x) => {
|
|
2137
|
+
const t = Math.min(1, Math.max(0, (x - a) / (b - a)));
|
|
2138
|
+
return t * t * (3 - 2 * t);
|
|
2139
|
+
};
|
|
2140
|
+
function terrainColor2(g, o) {
|
|
2141
|
+
const slope = sampler2(g, slopeMap2(g));
|
|
2142
|
+
const flow = g.flow ? sampler2(g, g.flow) : () => 0;
|
|
2143
|
+
const dep = g.deposit ? sampler2(g, g.deposit) : () => 0;
|
|
2144
|
+
const P = o.palette;
|
|
2145
|
+
const datum = o.datum ?? 0;
|
|
2146
|
+
const rockS = o.rockSlope ?? 0.8;
|
|
2147
|
+
const snowS = o.snowSlope ?? 1;
|
|
2148
|
+
const jit = o.jitter ?? 60;
|
|
2149
|
+
const c = [0, 0, 0];
|
|
2150
|
+
const mix = (to, w) => {
|
|
2151
|
+
for (let k = 0;k < 3; k++)
|
|
2152
|
+
c[k] = (c[k] ?? 0) + ((to[k] ?? 0) - (c[k] ?? 0)) * w;
|
|
2153
|
+
};
|
|
2154
|
+
return (x, y, z) => {
|
|
2155
|
+
const n = o.noise ? o.noise(x, y) : 0;
|
|
2156
|
+
const alt = z + datum + n * jit;
|
|
2157
|
+
const s = slope(x, y) * (1 + 0.15 * n);
|
|
2158
|
+
c[0] = P.forest[0];
|
|
2159
|
+
c[1] = P.forest[1];
|
|
2160
|
+
c[2] = P.forest[2];
|
|
2161
|
+
mix(P.grass, ramp(o.treeline - 80, o.treeline + 80, alt));
|
|
2162
|
+
mix(P.scree, 0.85 * ramp(o.treeline + 350, o.treeline + 800, alt));
|
|
2163
|
+
mix(P.grass, ramp(0.55, 0.8, s) * (1 - ramp(o.treeline - 80, o.treeline + 80, alt)) * 0.6);
|
|
2164
|
+
mix(P.scree, ramp(0.4, 0.8, dep(x, y)) * ramp(0.2, 0.45, s) * (1 - ramp(rockS, rockS + 0.3, s)));
|
|
2165
|
+
mix(P.wet, 0.7 * ramp(0.35, 0.8, flow(x, y)) * (1 - ramp(o.snowline - 100, o.snowline, alt)));
|
|
2166
|
+
mix(P.rock, ramp(rockS - 0.15, rockS + 0.15, s));
|
|
2167
|
+
mix(P.snow, ramp(o.snowline - 60, o.snowline + 120, alt) * (1 - ramp(snowS - 0.3, snowS + 0.2, s)));
|
|
2168
|
+
return [c[0] ?? 0, c[1] ?? 0, c[2] ?? 0];
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
// src/primitives/font-5x7.ts
|
|
2172
|
+
var A = [14, 17, 17, 17, 31, 17, 17];
|
|
2173
|
+
var B = [30, 17, 17, 30, 17, 17, 30];
|
|
2174
|
+
var C = [14, 17, 16, 16, 16, 17, 14];
|
|
2175
|
+
var E = [31, 16, 16, 30, 16, 16, 31];
|
|
2176
|
+
var H = [17, 17, 17, 31, 17, 17, 17];
|
|
2177
|
+
var K = [17, 18, 20, 24, 20, 18, 17];
|
|
2178
|
+
var M = [17, 27, 21, 21, 17, 17, 17];
|
|
2179
|
+
var O = [14, 17, 17, 17, 17, 17, 14];
|
|
2180
|
+
var P = [30, 17, 17, 30, 16, 16, 16];
|
|
2181
|
+
var T = [31, 4, 4, 4, 4, 4, 4];
|
|
2182
|
+
var X = [17, 17, 10, 4, 10, 17, 17];
|
|
2183
|
+
var GLYPHS = {
|
|
2184
|
+
" ": [0, 0, 0, 0, 0, 0, 0],
|
|
2185
|
+
A,
|
|
2186
|
+
B,
|
|
2187
|
+
C,
|
|
2188
|
+
D: [28, 18, 17, 17, 17, 18, 28],
|
|
2189
|
+
E,
|
|
2190
|
+
F: [31, 16, 16, 30, 16, 16, 16],
|
|
2191
|
+
G: [14, 17, 16, 23, 17, 17, 15],
|
|
2192
|
+
H,
|
|
2193
|
+
I: [14, 4, 4, 4, 4, 4, 14],
|
|
2194
|
+
J: [7, 2, 2, 2, 2, 18, 12],
|
|
2195
|
+
K,
|
|
2196
|
+
L: [16, 16, 16, 16, 16, 16, 31],
|
|
2197
|
+
M,
|
|
2198
|
+
N: [17, 17, 25, 21, 19, 17, 17],
|
|
2199
|
+
O,
|
|
2200
|
+
P,
|
|
2201
|
+
Q: [14, 17, 17, 17, 21, 18, 13],
|
|
2202
|
+
R: [30, 17, 17, 30, 20, 18, 17],
|
|
2203
|
+
S: [15, 16, 16, 14, 1, 1, 30],
|
|
2204
|
+
T,
|
|
2205
|
+
U: [17, 17, 17, 17, 17, 17, 14],
|
|
2206
|
+
V: [17, 17, 17, 17, 17, 10, 4],
|
|
2207
|
+
W: [17, 17, 17, 21, 21, 21, 10],
|
|
2208
|
+
X,
|
|
2209
|
+
Y: [17, 17, 10, 4, 4, 4, 4],
|
|
2210
|
+
Z: [31, 1, 2, 4, 8, 16, 31],
|
|
2211
|
+
"0": [14, 17, 19, 21, 25, 17, 14],
|
|
2212
|
+
"1": [4, 12, 4, 4, 4, 4, 14],
|
|
2213
|
+
"2": [14, 17, 1, 2, 4, 8, 31],
|
|
2214
|
+
"3": [31, 2, 4, 2, 1, 17, 14],
|
|
2215
|
+
"4": [2, 6, 10, 18, 31, 2, 2],
|
|
2216
|
+
"5": [31, 16, 30, 1, 1, 17, 14],
|
|
2217
|
+
"6": [6, 8, 16, 30, 17, 17, 14],
|
|
2218
|
+
"7": [31, 1, 2, 4, 8, 8, 8],
|
|
2219
|
+
"8": [14, 17, 17, 14, 17, 17, 14],
|
|
2220
|
+
"9": [14, 17, 17, 15, 1, 2, 12],
|
|
2221
|
+
".": [0, 0, 0, 0, 0, 12, 12],
|
|
2222
|
+
",": [0, 0, 0, 0, 12, 4, 8],
|
|
2223
|
+
"-": [0, 0, 0, 31, 0, 0, 0],
|
|
2224
|
+
"/": [0, 1, 2, 4, 8, 16, 0],
|
|
2225
|
+
":": [0, 12, 12, 0, 12, 12, 0],
|
|
2226
|
+
"'": [12, 4, 8, 0, 0, 0, 0],
|
|
2227
|
+
'"': [10, 10, 10, 0, 0, 0, 0],
|
|
2228
|
+
"&": [12, 18, 20, 8, 21, 18, 13],
|
|
2229
|
+
"!": [4, 4, 4, 4, 4, 0, 4],
|
|
2230
|
+
"?": [14, 17, 1, 2, 4, 0, 4],
|
|
2231
|
+
"#": [10, 10, 31, 10, 31, 10, 10],
|
|
2232
|
+
"+": [0, 4, 4, 31, 4, 4, 0],
|
|
2233
|
+
"=": [0, 0, 31, 0, 31, 0, 0],
|
|
2234
|
+
"*": [0, 4, 21, 14, 21, 4, 0],
|
|
2235
|
+
"(": [2, 4, 8, 8, 8, 4, 2],
|
|
2236
|
+
")": [8, 4, 2, 2, 2, 4, 8],
|
|
2237
|
+
"%": [24, 25, 2, 4, 8, 19, 3],
|
|
2238
|
+
"°": [12, 18, 18, 12, 0, 0, 0],
|
|
2239
|
+
"№": [18, 26, 26, 22, 22, 18, 21],
|
|
2240
|
+
А: A,
|
|
2241
|
+
Б: [31, 16, 16, 30, 17, 17, 30],
|
|
2242
|
+
В: B,
|
|
2243
|
+
Г: [31, 16, 16, 16, 16, 16, 16],
|
|
2244
|
+
Д: [6, 10, 10, 10, 10, 31, 17],
|
|
2245
|
+
Е: E,
|
|
2246
|
+
Ё: [10, 0, 31, 16, 30, 16, 31],
|
|
2247
|
+
Ж: [21, 21, 21, 14, 21, 21, 21],
|
|
2248
|
+
З: [14, 17, 1, 6, 1, 17, 14],
|
|
2249
|
+
И: [17, 17, 19, 21, 25, 17, 17],
|
|
2250
|
+
Й: [10, 4, 17, 19, 21, 25, 17],
|
|
2251
|
+
К: K,
|
|
2252
|
+
Л: [7, 9, 9, 9, 9, 9, 17],
|
|
2253
|
+
М: M,
|
|
2254
|
+
Н: H,
|
|
2255
|
+
О: O,
|
|
2256
|
+
П: [31, 17, 17, 17, 17, 17, 17],
|
|
2257
|
+
Р: P,
|
|
2258
|
+
С: C,
|
|
2259
|
+
Т: T,
|
|
2260
|
+
У: [17, 17, 17, 15, 1, 17, 14],
|
|
2261
|
+
Ф: [4, 14, 21, 21, 21, 14, 4],
|
|
2262
|
+
Х: X,
|
|
2263
|
+
Ц: [18, 18, 18, 18, 18, 31, 1],
|
|
2264
|
+
Ч: [17, 17, 17, 15, 1, 1, 1],
|
|
2265
|
+
Ш: [21, 21, 21, 21, 21, 21, 31],
|
|
2266
|
+
Щ: [21, 21, 21, 21, 21, 31, 1],
|
|
2267
|
+
Ъ: [24, 8, 8, 14, 9, 9, 14],
|
|
2268
|
+
Ы: [17, 17, 17, 29, 19, 19, 29],
|
|
2269
|
+
Ь: [16, 16, 16, 30, 17, 17, 30],
|
|
2270
|
+
Э: [14, 17, 1, 7, 1, 17, 14],
|
|
2271
|
+
Ю: [18, 21, 21, 29, 21, 21, 18],
|
|
2272
|
+
Я: [15, 17, 17, 15, 5, 9, 17]
|
|
2273
|
+
};
|
|
2274
|
+
var group = (c) => /[A-Z]/.test(c) ? 0 : /[0-9]/.test(c) ? 1 : /[А-ЯЁ]/.test(c) ? 3 : 2;
|
|
2275
|
+
var SUPPORTED_CHARS = Object.keys(GLYPHS).filter((c) => c !== " ").sort((a, b) => group(a) - group(b) || (a < b ? -1 : a > b ? 1 : 0)).join("").concat(" and space");
|
|
2276
|
+
|
|
2277
|
+
// src/primitives/text-layout.ts
|
|
2278
|
+
var SPACE = 3;
|
|
2279
|
+
function rows(ch, font) {
|
|
2280
|
+
const g = GLYPHS[ch] ?? GLYPHS[ch.toUpperCase()];
|
|
2281
|
+
if (!g)
|
|
2282
|
+
invalid(`text: no glyph for ${JSON.stringify(ch)} (U+${(ch.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")})`, `use only these characters (lower case is set as upper case): ${SUPPORTED_CHARS}`);
|
|
2283
|
+
if (ch === " ")
|
|
2284
|
+
return { rows: g, width: SPACE };
|
|
2285
|
+
return font === "block" ? { rows: g.map((r) => r << 1 | r), width: 6 } : { rows: g, width: 5 };
|
|
2286
|
+
}
|
|
2287
|
+
function widthPx(str, font, tracking) {
|
|
2288
|
+
const chars = [...str];
|
|
2289
|
+
return chars.reduce((w, ch) => w + rows(ch, font).width, 0) + Math.max(0, chars.length - 1) * tracking;
|
|
2290
|
+
}
|
|
2291
|
+
function measureText2(str, o) {
|
|
2292
|
+
return widthPx(str, o.font ?? "5x7", o.tracking ?? 1) * o.height / 7;
|
|
2293
|
+
}
|
|
2294
|
+
function layout(str, font, tracking) {
|
|
2295
|
+
const rects = [];
|
|
2296
|
+
let pen = 0;
|
|
2297
|
+
for (const ch of str) {
|
|
2298
|
+
const g = rows(ch, font);
|
|
2299
|
+
let open = new Map;
|
|
2300
|
+
for (let r = 0;r <= 7; r++) {
|
|
2301
|
+
const bits = g.rows[r] ?? 0;
|
|
2302
|
+
const next = new Map;
|
|
2303
|
+
let c = 0;
|
|
2304
|
+
while (c < g.width) {
|
|
2305
|
+
if (!(bits >> g.width - 1 - c & 1)) {
|
|
2306
|
+
c++;
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
const start = c;
|
|
2310
|
+
while (c < g.width && bits >> g.width - 1 - c & 1)
|
|
2311
|
+
c++;
|
|
2312
|
+
const key = `${start},${c}`;
|
|
2313
|
+
next.set(key, open.get(key) ?? { c0: start, c1: c, r0: r });
|
|
2314
|
+
}
|
|
2315
|
+
for (const [key, run] of open)
|
|
2316
|
+
if (!next.has(key))
|
|
2317
|
+
rects.push({ c0: pen + run.c0, c1: pen + run.c1, r0: run.r0, r1: r });
|
|
2318
|
+
open = next;
|
|
2319
|
+
}
|
|
2320
|
+
pen += g.width + tracking;
|
|
2321
|
+
}
|
|
2322
|
+
return { rects, width: Math.max(0, pen - tracking) };
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// src/primitives/text.ts
|
|
2326
|
+
var LIFT = 0.002;
|
|
2327
|
+
function text2(str, o) {
|
|
2328
|
+
if (!(Number.isFinite(o.height) && o.height > 0 && o.height < 1000))
|
|
2329
|
+
invalid(`text height is ${o.height}`, "give the cap height in metres, > 0 (e.g. 0.3 for wagon lettering)");
|
|
2330
|
+
const depth = o.depth ?? 0;
|
|
2331
|
+
if (!(Number.isFinite(depth) && depth >= 0))
|
|
2332
|
+
invalid(`text depth is ${depth}`, "use 0 (flush) or a raised thickness > 0 m");
|
|
2333
|
+
const font = o.font ?? "5x7";
|
|
2334
|
+
const tracking = o.tracking ?? 1;
|
|
2335
|
+
let height = o.height;
|
|
2336
|
+
if (o.maxWidth !== undefined) {
|
|
2337
|
+
if (!(o.maxWidth > 0))
|
|
2338
|
+
invalid(`text maxWidth is ${o.maxWidth}`, "give the panel width in metres, > 0");
|
|
2339
|
+
const w = measureText2(str, { height, font, tracking });
|
|
2340
|
+
if (w > o.maxWidth)
|
|
2341
|
+
height *= o.maxWidth / w;
|
|
2342
|
+
}
|
|
2343
|
+
const along = normalize2(o.along ?? [0, 1, 0]);
|
|
2344
|
+
const up0 = o.up ?? [0, 0, 1];
|
|
2345
|
+
const k = up0[0] * along[0] + up0[1] * along[1] + up0[2] * along[2];
|
|
2346
|
+
const upR = [up0[0] - along[0] * k, up0[1] - along[1] * k, up0[2] - along[2] * k];
|
|
2347
|
+
if (Math.hypot(upR[0], upR[1], upR[2]) < 0.000000001)
|
|
2348
|
+
invalid("text: up is parallel to along", "pass an `up` perpendicular to the reading direction");
|
|
2349
|
+
const up = normalize2(upR);
|
|
2350
|
+
const n = [
|
|
2351
|
+
along[1] * up[2] - along[2] * up[1],
|
|
2352
|
+
along[2] * up[0] - along[0] * up[2],
|
|
2353
|
+
along[0] * up[1] - along[1] * up[0]
|
|
2354
|
+
];
|
|
2355
|
+
const px = height / 7;
|
|
2356
|
+
const { rects, width } = layout(str, font, tracking);
|
|
2357
|
+
const start = o.align === "centre" ? -width * px / 2 : o.align === "right" ? -width * px : 0;
|
|
2358
|
+
const rect = stripRect2(o.strip ?? "plain");
|
|
2359
|
+
const b = new MeshBuilder2;
|
|
2360
|
+
const at = (u, v, w) => [
|
|
2361
|
+
o.origin[0] + along[0] * u + up[0] * v + n[0] * w,
|
|
2362
|
+
o.origin[1] + along[1] * u + up[1] * v + n[1] * w,
|
|
2363
|
+
o.origin[2] + along[2] * u + up[2] * v + n[2] * w
|
|
2364
|
+
];
|
|
2365
|
+
const quad = (c, nrm, us, vs) => {
|
|
2366
|
+
const ids = c.map((p, i) => b.vertex({
|
|
2367
|
+
p,
|
|
2368
|
+
n: nrm,
|
|
2369
|
+
uv: [
|
|
2370
|
+
stripU(rect, us[i] ?? 0),
|
|
2371
|
+
stripV(rect, Math.min(1, Math.max(0, (vs[i] ?? 0) / height)))
|
|
2372
|
+
],
|
|
2373
|
+
color: o.color
|
|
2374
|
+
}));
|
|
2375
|
+
const [a = n, e = n, f = n] = c;
|
|
2376
|
+
const g = [0, 1, 2].map((j) => {
|
|
2377
|
+
const [x, y] = [(j + 1) % 3, (j + 2) % 3];
|
|
2378
|
+
return ((e[x] ?? 0) - (a[x] ?? 0)) * ((f[y] ?? 0) - (a[y] ?? 0)) - ((e[y] ?? 0) - (a[y] ?? 0)) * ((f[x] ?? 0) - (a[x] ?? 0));
|
|
2379
|
+
});
|
|
2380
|
+
const [i0 = 0, i1 = 0, i2 = 0, i3 = 0] = ids;
|
|
2381
|
+
if ((g[0] ?? 0) * nrm[0] + (g[1] ?? 0) * nrm[1] + (g[2] ?? 0) * nrm[2] >= 0)
|
|
2382
|
+
b.quad(i0, i1, i2, i3);
|
|
2383
|
+
else
|
|
2384
|
+
b.quad(i0, i3, i2, i1);
|
|
2385
|
+
};
|
|
2386
|
+
const neg = (v) => [-v[0], -v[1], -v[2]];
|
|
2387
|
+
for (const piece of pieces(rects, px, start, o.surface)) {
|
|
2388
|
+
const { u0, u1, v0, v1, s } = piece;
|
|
2389
|
+
const lo = depth > 0 ? s : s + LIFT;
|
|
2390
|
+
const hi = depth > 0 ? s + depth : lo;
|
|
2391
|
+
quad([at(u0, v0, hi), at(u1, v0, hi), at(u1, v1, hi), at(u0, v1, hi)], n, [u0, u1, u1, u0], [v0, v0, v1, v1]);
|
|
2392
|
+
if (depth <= 0)
|
|
2393
|
+
continue;
|
|
2394
|
+
quad([at(u0, v0, lo), at(u1, v0, lo), at(u1, v0, hi), at(u0, v0, hi)], neg(up), [u0, u1, u1, u0], [0, 0, depth, depth]);
|
|
2395
|
+
quad([at(u1, v0, lo), at(u1, v1, lo), at(u1, v1, hi), at(u1, v0, hi)], along, [v0, v1, v1, v0], [0, 0, depth, depth]);
|
|
2396
|
+
quad([at(u1, v1, lo), at(u0, v1, lo), at(u0, v1, hi), at(u1, v1, hi)], up, [u1, u0, u0, u1], [0, 0, depth, depth]);
|
|
2397
|
+
quad([at(u0, v1, lo), at(u0, v0, lo), at(u0, v0, hi), at(u0, v1, hi)], neg(along), [v1, v0, v0, v1], [0, 0, depth, depth]);
|
|
2398
|
+
}
|
|
2399
|
+
return b.build();
|
|
2400
|
+
}
|
|
2401
|
+
function pieces(rects, px, start, surface) {
|
|
2402
|
+
const out = [];
|
|
2403
|
+
for (const r of rects) {
|
|
2404
|
+
const v0 = (7 - r.r1) * px;
|
|
2405
|
+
const v1 = (7 - r.r0) * px;
|
|
2406
|
+
if (!surface) {
|
|
2407
|
+
out.push({ u0: start + r.c0 * px, u1: start + r.c1 * px, v0, v1, s: 0 });
|
|
2408
|
+
continue;
|
|
2409
|
+
}
|
|
2410
|
+
const lift = (c) => {
|
|
2411
|
+
let top = Number.NEGATIVE_INFINITY;
|
|
2412
|
+
for (const fu of [0.02, 0.5, 0.98])
|
|
2413
|
+
for (const v of [v0 + 0.02 * px, (v0 + v1) / 2, v1 - 0.02 * px])
|
|
2414
|
+
top = Math.max(top, surface(start + (c + fu) * px, v));
|
|
2415
|
+
return top;
|
|
2416
|
+
};
|
|
2417
|
+
let c = r.c0;
|
|
2418
|
+
while (c < r.c1) {
|
|
2419
|
+
const s = lift(c);
|
|
2420
|
+
let e = c + 1;
|
|
2421
|
+
while (e < r.c1 && Math.abs(lift(e) - s) < 0.000001)
|
|
2422
|
+
e++;
|
|
2423
|
+
out.push({ u0: start + c * px, u1: start + e * px, v0, v1, s });
|
|
2424
|
+
c = e;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
return out;
|
|
2428
|
+
}
|
|
2429
|
+
// src/primitives/tiled.ts
|
|
2430
|
+
function tiled2(o) {
|
|
2431
|
+
const { lu, lv, n } = spanOf("tiled", o.origin, o.u, o.v);
|
|
2432
|
+
const strip = o.strip ?? "plain";
|
|
2433
|
+
const segs = count("tiled segments", o.segments ?? 1, 1, 4096);
|
|
2434
|
+
const vm = positive("tiled vMetres", o.vMetres ?? stripVMetres(strip), "e.g. 0.45 for brick");
|
|
2435
|
+
const rect = stripRect2(strip);
|
|
2436
|
+
const uOff = o.uOffset ?? 0;
|
|
2437
|
+
const b = new MeshBuilder2;
|
|
2438
|
+
const sides = o.doubleSided ? [1, -1] : [1];
|
|
2439
|
+
let t = 0;
|
|
2440
|
+
while (t < lv - 0.000000001) {
|
|
2441
|
+
let local = (o.vOffset ?? 0) + t;
|
|
2442
|
+
local -= Math.floor(local / vm) * vm;
|
|
2443
|
+
if (vm - local < 0.000000001)
|
|
2444
|
+
local = 0;
|
|
2445
|
+
const step = Math.min(lv - t, vm - local);
|
|
2446
|
+
const fv0 = local / vm;
|
|
2447
|
+
const fv1 = (local + step) / vm;
|
|
2448
|
+
for (const side of sides) {
|
|
2449
|
+
const nn = [n[0] * side, n[1] * side, n[2] * side];
|
|
2450
|
+
const first = row(b, o, nn, rect, uOff, lu, segs, t / lv, stripV(rect, fv0));
|
|
2451
|
+
row(b, o, nn, rect, uOff, lu, segs, (t + step) / lv, stripV(rect, fv1));
|
|
2452
|
+
for (let j = 0;j < segs; j++) {
|
|
2453
|
+
const a = first + j;
|
|
2454
|
+
const c = first + segs + 1 + j;
|
|
2455
|
+
if (side > 0)
|
|
2456
|
+
b.quad(a, a + 1, c + 1, c);
|
|
2457
|
+
else
|
|
2458
|
+
b.quad(a, c, c + 1, a + 1);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
t += step;
|
|
2462
|
+
}
|
|
2463
|
+
return b.build();
|
|
2464
|
+
}
|
|
2465
|
+
function row(b, o, n, rect, uOff, lu, segs, fv, v) {
|
|
2466
|
+
let first = -1;
|
|
2467
|
+
for (let j = 0;j <= segs; j++) {
|
|
2468
|
+
const fu = j / segs;
|
|
2469
|
+
const id = b.vertex({
|
|
2470
|
+
p: [
|
|
2471
|
+
o.origin[0] + o.u[0] * fu + o.v[0] * fv,
|
|
2472
|
+
o.origin[1] + o.u[1] * fu + o.v[1] * fv,
|
|
2473
|
+
o.origin[2] + o.u[2] * fu + o.v[2] * fv
|
|
2474
|
+
],
|
|
2475
|
+
n,
|
|
2476
|
+
uv: [stripU(rect, uOff + lu * fu), v],
|
|
2477
|
+
color: o.color
|
|
2478
|
+
});
|
|
2479
|
+
if (first < 0)
|
|
2480
|
+
first = id;
|
|
2481
|
+
}
|
|
2482
|
+
return first;
|
|
2483
|
+
}
|
|
2484
|
+
// src/primitives/value-noise.ts
|
|
2485
|
+
function valueNoise3(rng, o = {}) {
|
|
2486
|
+
const perm = new Uint8Array(512);
|
|
2487
|
+
const vals = new Float64Array(256);
|
|
2488
|
+
for (let i = 0;i < 256; i++) {
|
|
2489
|
+
perm[i] = i;
|
|
2490
|
+
vals[i] = rng.next() * 2 - 1;
|
|
2491
|
+
}
|
|
2492
|
+
for (let i = 255;i > 0; i--) {
|
|
2493
|
+
const j = rng.int(0, i);
|
|
2494
|
+
const t = perm[i] ?? 0;
|
|
2495
|
+
perm[i] = perm[j] ?? 0;
|
|
2496
|
+
perm[j] = t;
|
|
2497
|
+
}
|
|
2498
|
+
for (let i = 0;i < 256; i++)
|
|
2499
|
+
perm[i + 256] = perm[i] ?? 0;
|
|
2500
|
+
const at = (x, y, z) => vals[perm[(perm[(perm[x & 255] ?? 0) + (y & 255)] ?? 0) + (z & 255)] ?? 0] ?? 0;
|
|
2501
|
+
const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
|
|
2502
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
2503
|
+
const one = (x, y, z) => {
|
|
2504
|
+
const xi = Math.floor(x);
|
|
2505
|
+
const yi = Math.floor(y);
|
|
2506
|
+
const zi = Math.floor(z);
|
|
2507
|
+
const u = fade(x - xi);
|
|
2508
|
+
const v = fade(y - yi);
|
|
2509
|
+
const w = fade(z - zi);
|
|
2510
|
+
const plane = (k) => lerp(lerp(at(xi, yi, k), at(xi + 1, yi, k), u), lerp(at(xi, yi + 1, k), at(xi + 1, yi + 1, k), u), v);
|
|
2511
|
+
return lerp(plane(zi), plane(zi + 1), w);
|
|
2512
|
+
};
|
|
2513
|
+
const s = 1 / (o.scale ?? 1);
|
|
2514
|
+
const octaves = o.octaves ?? 3;
|
|
2515
|
+
return (p) => {
|
|
2516
|
+
let sum = 0;
|
|
2517
|
+
let amp = 1;
|
|
2518
|
+
let norm = 0;
|
|
2519
|
+
let f = s;
|
|
2520
|
+
for (let i = 0;i < octaves; i++) {
|
|
2521
|
+
sum += amp * one(p[0] * f, p[1] * f, p[2] * f);
|
|
2522
|
+
norm += amp;
|
|
2523
|
+
amp *= 0.5;
|
|
2524
|
+
f *= 2;
|
|
2525
|
+
}
|
|
2526
|
+
return sum / norm;
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
export { box2, lathe2, cylinder2, beam2, cell2, hydraulicErosion2, heightfield2, triangulate2, lattice2, ringXY2, loft2, gradientNoise22, valueNoise22, fbm2, billow2, ridged2, warp2, terrace2, planar2, prism2, sd2, sdf2, sculptField2, sculpt2, arcLengths2, transportFrames2, sweep2, terrainGrid2, sampler2, slopeMap2, thermalErosion2, terrainColor2, measureText2, text2, tiled2, valueNoise3 };
|
|
2530
|
+
|
|
2531
|
+
//# debugId=1A8BAFD57A1802CD64756E2164756E21
|
|
2532
|
+
//# sourceMappingURL=chunk-d1fv6hrm.js.map
|