@johnmorrisdotca/uchusen-rockets 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/LICENSES/CC-BY-4.0.txt +396 -0
- package/LICENSES/MIT.txt +21 -0
- package/NOTICE.md +66 -0
- package/README.md +171 -2
- package/dist/assets/dragon-dragon-1-apart.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-apart.svg +1 -0
- package/dist/assets/dragon-dragon-1-apart.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.glb +0 -0
- package/dist/assets/dragon-dragon-1-capsule.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.glb +0 -0
- package/dist/assets/dragon-dragon-1-trunk.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1.glb +0 -0
- package/dist/assets/dragon-dragon-1.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1.svg +1 -0
- package/dist/assets/dragon-dragon-1.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.glb +0 -0
- package/dist/assets/dragon-dragon-2-capsule.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.glb +0 -0
- package/dist/assets/dragon-dragon-2-trunk.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2.glb +0 -0
- package/dist/assets/dragon-dragon-2.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2.svg +1 -0
- package/dist/assets/dragon-dragon-2.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.glb +0 -0
- package/dist/assets/falcon-9-block-5-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5.glb +0 -0
- package/dist/assets/falcon-9-block-5.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5.svg +1 -0
- package/dist/assets/falcon-9-block-5.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.glb +0 -0
- package/dist/assets/falcon-9-ft-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.glb +0 -0
- package/dist/assets/falcon-9-ft-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.glb +0 -0
- package/dist/assets/falcon-9-ft-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-ft.glb +0 -0
- package/dist/assets/falcon-9-ft.painted.svg +1 -0
- package/dist/assets/falcon-9-ft.svg +1 -0
- package/dist/assets/falcon-9-ft.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.glb +0 -0
- package/dist/assets/falcon-9-v1.0-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0.glb +0 -0
- package/dist/assets/falcon-9-v1.0.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0.svg +1 -0
- package/dist/assets/falcon-9-v1.0.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.glb +0 -0
- package/dist/assets/falcon-9-v1.1-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.1-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.1-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1.glb +0 -0
- package/dist/assets/falcon-9-v1.1.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1.svg +1 -0
- package/dist/assets/falcon-9-v1.1.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.thick.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.glb +0 -0
- package/dist/assets/grasshopper-grasshopper.painted.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.thick.svg +1 -0
- package/dist/assets/models/dragon-dragon-1-capsule.glb +0 -0
- package/dist/assets/models/dragon-dragon-1-trunk.glb +0 -0
- package/dist/assets/models/dragon-dragon-2-capsule.glb +0 -0
- package/dist/assets/models/dragon-dragon-2-trunk.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-fairing.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-first-stage.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-second-stage.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-centre.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-side-left.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-side-right.glb +0 -0
- package/dist/assets/models/grasshopper-grasshopper-vehicle.glb +0 -0
- package/dist/assets/models/starship-2019-ship.glb +0 -0
- package/dist/assets/models/starship-v1-flight1-booster.glb +0 -0
- package/dist/assets/models/starship-v1-flight1-ship.glb +0 -0
- package/dist/assets/models/starship-v3-booster.glb +0 -0
- package/dist/assets/models/starship-v3-ship.glb +0 -0
- package/dist/assets/parts/flap-aft.glb +0 -0
- package/dist/assets/parts/flap-aft.painted.svg +1 -0
- package/dist/assets/parts/flap-aft.svg +1 -0
- package/dist/assets/parts/flap-aft.thick.svg +1 -0
- package/dist/assets/parts/flap-forward.glb +0 -0
- package/dist/assets/parts/flap-forward.painted.svg +1 -0
- package/dist/assets/parts/flap-forward.svg +1 -0
- package/dist/assets/parts/flap-forward.thick.svg +1 -0
- package/dist/assets/parts/grid-fin-four.glb +0 -0
- package/dist/assets/parts/grid-fin-four.painted.svg +1 -0
- package/dist/assets/parts/grid-fin-four.svg +1 -0
- package/dist/assets/parts/grid-fin-four.thick.svg +1 -0
- package/dist/assets/parts/grid-fin-three.glb +0 -0
- package/dist/assets/parts/grid-fin-three.painted.svg +1 -0
- package/dist/assets/parts/grid-fin-three.svg +1 -0
- package/dist/assets/parts/grid-fin-three.thick.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.glb +0 -0
- package/dist/assets/parts/merlin-sea-level.painted.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.thick.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.glb +0 -0
- package/dist/assets/parts/raptor-sea-level.painted.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.thick.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.glb +0 -0
- package/dist/assets/parts/raptor-vacuum.painted.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.thick.svg +1 -0
- package/dist/assets/starship-2019.glb +0 -0
- package/dist/assets/starship-2019.painted.svg +1 -0
- package/dist/assets/starship-2019.svg +1 -0
- package/dist/assets/starship-2019.thick.svg +1 -0
- package/dist/assets/starship-v1-apart.painted.svg +1 -0
- package/dist/assets/starship-v1-apart.svg +1 -0
- package/dist/assets/starship-v1-apart.thick.svg +1 -0
- package/dist/assets/starship-v1-booster.glb +0 -0
- package/dist/assets/starship-v1-booster.painted.svg +1 -0
- package/dist/assets/starship-v1-booster.svg +1 -0
- package/dist/assets/starship-v1-booster.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.glb +0 -0
- package/dist/assets/starship-v1-flight1-booster.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.glb +0 -0
- package/dist/assets/starship-v1-flight1-ship.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1.glb +0 -0
- package/dist/assets/starship-v1-flight1.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1.svg +1 -0
- package/dist/assets/starship-v1-flight1.thick.svg +1 -0
- package/dist/assets/starship-v1-ship.glb +0 -0
- package/dist/assets/starship-v1-ship.painted.svg +1 -0
- package/dist/assets/starship-v1-ship.svg +1 -0
- package/dist/assets/starship-v1-ship.thick.svg +1 -0
- package/dist/assets/starship-v1.glb +0 -0
- package/dist/assets/starship-v1.painted.svg +1 -0
- package/dist/assets/starship-v1.svg +1 -0
- package/dist/assets/starship-v1.thick.svg +1 -0
- package/dist/assets/starship-v2-apart.painted.svg +1 -0
- package/dist/assets/starship-v2-apart.svg +1 -0
- package/dist/assets/starship-v2-apart.thick.svg +1 -0
- package/dist/assets/starship-v2-booster.glb +0 -0
- package/dist/assets/starship-v2-booster.painted.svg +1 -0
- package/dist/assets/starship-v2-booster.svg +1 -0
- package/dist/assets/starship-v2-booster.thick.svg +1 -0
- package/dist/assets/starship-v2-ship.glb +0 -0
- package/dist/assets/starship-v2-ship.painted.svg +1 -0
- package/dist/assets/starship-v2-ship.svg +1 -0
- package/dist/assets/starship-v2-ship.thick.svg +1 -0
- package/dist/assets/starship-v2.glb +0 -0
- package/dist/assets/starship-v2.painted.svg +1 -0
- package/dist/assets/starship-v2.svg +1 -0
- package/dist/assets/starship-v2.thick.svg +1 -0
- package/dist/assets/starship-v3-apart.painted.svg +1 -0
- package/dist/assets/starship-v3-apart.svg +1 -0
- package/dist/assets/starship-v3-apart.thick.svg +1 -0
- package/dist/assets/starship-v3-booster.glb +0 -0
- package/dist/assets/starship-v3-booster.painted.svg +1 -0
- package/dist/assets/starship-v3-booster.svg +1 -0
- package/dist/assets/starship-v3-booster.thick.svg +1 -0
- package/dist/assets/starship-v3-ship.glb +0 -0
- package/dist/assets/starship-v3-ship.painted.svg +1 -0
- package/dist/assets/starship-v3-ship.svg +1 -0
- package/dist/assets/starship-v3-ship.thick.svg +1 -0
- package/dist/assets/starship-v3.glb +0 -0
- package/dist/assets/starship-v3.painted.svg +1 -0
- package/dist/assets/starship-v3.svg +1 -0
- package/dist/assets/starship-v3.thick.svg +1 -0
- package/dist/assets/starship-v4-apart.painted.svg +1 -0
- package/dist/assets/starship-v4-apart.svg +1 -0
- package/dist/assets/starship-v4-apart.thick.svg +1 -0
- package/dist/assets/starship-v4-booster.glb +0 -0
- package/dist/assets/starship-v4-booster.painted.svg +1 -0
- package/dist/assets/starship-v4-booster.svg +1 -0
- package/dist/assets/starship-v4-booster.thick.svg +1 -0
- package/dist/assets/starship-v4-ship.glb +0 -0
- package/dist/assets/starship-v4-ship.painted.svg +1 -0
- package/dist/assets/starship-v4-ship.svg +1 -0
- package/dist/assets/starship-v4-ship.thick.svg +1 -0
- package/dist/assets/starship-v4.glb +0 -0
- package/dist/assets/starship-v4.painted.svg +1 -0
- package/dist/assets/starship-v4.svg +1 -0
- package/dist/assets/starship-v4.thick.svg +1 -0
- package/dist/build/glb.d.ts +12 -0
- package/dist/build/glb.js +157 -0
- package/dist/build/index.d.ts +59 -0
- package/dist/build/index.js +118 -0
- package/dist/build/lineup.d.ts +40 -0
- package/dist/build/lineup.js +97 -0
- package/dist/crossfade.d.ts +6 -0
- package/dist/crossfade.js +11 -0
- package/dist/data/credits.d.ts +87 -0
- package/dist/data/credits.js +82 -0
- package/dist/data/dragon.d.ts +2 -0
- package/dist/data/dragon.js +45 -0
- package/dist/data/falcon.d.ts +3 -0
- package/dist/data/falcon.js +121 -0
- package/dist/data/grasshopper.d.ts +2 -0
- package/dist/data/grasshopper.js +21 -0
- package/dist/data/sources.d.ts +173 -0
- package/dist/data/sources.js +25 -0
- package/dist/data/starship.d.ts +2 -0
- package/dist/data/starship.js +183 -0
- package/dist/data/types.d.ts +48 -0
- package/dist/data/types.js +8 -0
- package/dist/draw/svg.d.ts +26 -0
- package/dist/draw/svg.js +262 -0
- package/dist/element/paint.d.ts +9 -0
- package/dist/element/paint.js +138 -0
- package/dist/element/scene.d.ts +104 -0
- package/dist/element/scene.js +758 -0
- package/dist/element.d.ts +74 -0
- package/dist/element.js +488 -0
- package/dist/fit.d.ts +43 -0
- package/dist/fit.js +93 -0
- package/dist/framing.d.ts +32 -0
- package/dist/framing.js +52 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +13 -0
- package/dist/layout.d.ts +17 -0
- package/dist/layout.js +33 -0
- package/dist/looks.d.ts +25 -0
- package/dist/looks.js +36 -0
- package/dist/model/falcon.d.ts +48 -0
- package/dist/model/falcon.js +206 -0
- package/dist/model/materials.d.ts +113 -0
- package/dist/model/materials.js +19 -0
- package/dist/model/pieces.d.ts +91 -0
- package/dist/model/pieces.js +223 -0
- package/dist/model/registry.d.ts +8 -0
- package/dist/model/registry.js +104 -0
- package/dist/model/starship.d.ts +67 -0
- package/dist/model/starship.js +503 -0
- package/dist/parts.d.ts +44 -0
- package/dist/parts.js +1 -0
- package/dist/react.d.ts +314 -0
- package/dist/react.js +8 -0
- package/dist/rockets.d.ts +27 -0
- package/dist/rockets.js +36 -0
- package/dist/tilt.d.ts +21 -0
- package/dist/tilt.js +23 -0
- package/package.json +65 -4
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// The writer: a rocket's parts into one small .glb, in Node or a browser, with no dependency. One node per part, one primitive per material, plus the lines of the line looks as line primitives; positions
|
|
2
|
+
// and normals packed into 16 bits (KHR_mesh_quantization). The same parts always give the same bytes: no clock, no random, a fixed order.
|
|
3
|
+
import { MATERIALS } from "../model/materials.js";
|
|
4
|
+
const SHORT = 5122;
|
|
5
|
+
const USHORT = 5123;
|
|
6
|
+
const UINT = 5125;
|
|
7
|
+
const ARRAY_BUFFER = 34962;
|
|
8
|
+
const ELEMENT_BUFFER = 34963;
|
|
9
|
+
const LINES = 1;
|
|
10
|
+
const round6 = (v) => Math.round(v * 1e6) / 1e6;
|
|
11
|
+
const toLinear = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
|
12
|
+
const linearOf = (hex) => [1, 3, 5].map((i) => round6(toLinear(Number.parseInt(hex.slice(i, i + 2), 16) / 255)));
|
|
13
|
+
export function toGlb(parts) {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
let offset = 0;
|
|
16
|
+
const bufferViews = [];
|
|
17
|
+
const accessors = [];
|
|
18
|
+
const put = (data, target, byteStride) => {
|
|
19
|
+
const pad = (4 - (offset % 4)) % 4;
|
|
20
|
+
if (pad) {
|
|
21
|
+
chunks.push(new Uint8Array(pad));
|
|
22
|
+
offset += pad;
|
|
23
|
+
}
|
|
24
|
+
chunks.push(data);
|
|
25
|
+
bufferViews.push({ buffer: 0, byteOffset: offset, byteLength: data.byteLength, target, ...(byteStride ? { byteStride } : {}) });
|
|
26
|
+
offset += data.byteLength;
|
|
27
|
+
return bufferViews.length - 1;
|
|
28
|
+
};
|
|
29
|
+
const accessor = (a) => accessors.push(a) - 1;
|
|
30
|
+
const materials = [];
|
|
31
|
+
const materialIndex = new Map();
|
|
32
|
+
const useMaterial = (name) => {
|
|
33
|
+
const known = materialIndex.get(name);
|
|
34
|
+
if (known !== undefined)
|
|
35
|
+
return known;
|
|
36
|
+
const spec = name.startsWith("line-") ? { color: "#8fa1b8", metal: 0, rough: 1 } : MATERIALS[name];
|
|
37
|
+
materials.push({ name, pbrMetallicRoughness: { baseColorFactor: [...linearOf(spec.color), 1], metallicFactor: spec.metal, roughnessFactor: spec.rough } });
|
|
38
|
+
materialIndex.set(name, materials.length - 1);
|
|
39
|
+
return materials.length - 1;
|
|
40
|
+
};
|
|
41
|
+
const indexAccessor = (list, count) => {
|
|
42
|
+
const wide = count >= 65536;
|
|
43
|
+
const data = wide ? Uint32Array.from(list) : Uint16Array.from(list);
|
|
44
|
+
return accessor({ bufferView: put(new Uint8Array(data.buffer), ELEMENT_BUFFER), componentType: wide ? UINT : USHORT, count: list.length, type: "SCALAR" });
|
|
45
|
+
};
|
|
46
|
+
let triangles = 0;
|
|
47
|
+
const meshes = [];
|
|
48
|
+
const nodes = [];
|
|
49
|
+
const roots = [];
|
|
50
|
+
for (const part of parts) {
|
|
51
|
+
const { mesh } = part;
|
|
52
|
+
const faces = [...mesh.faces];
|
|
53
|
+
// the packing's middle and half-size, over all of the part's points
|
|
54
|
+
const lo = [Infinity, Infinity, Infinity];
|
|
55
|
+
const hi = [-Infinity, -Infinity, -Infinity];
|
|
56
|
+
const sweep = (positions) => {
|
|
57
|
+
for (let i = 0; i < positions.length; i += 3)
|
|
58
|
+
for (let c = 0; c < 3; c++) {
|
|
59
|
+
if (positions[i + c] < lo[c])
|
|
60
|
+
lo[c] = positions[i + c];
|
|
61
|
+
if (positions[i + c] > hi[c])
|
|
62
|
+
hi[c] = positions[i + c];
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
for (const [, b] of faces)
|
|
66
|
+
sweep(b.positions);
|
|
67
|
+
sweep(mesh.feature.positions);
|
|
68
|
+
sweep(mesh.weld.positions);
|
|
69
|
+
const q = [0, 1, 2].map((c) => (lo[c] + hi[c]) / 2);
|
|
70
|
+
const half = Math.max(1e-3, ...[0, 1, 2].map((c) => (hi[c] - lo[c]) / 2));
|
|
71
|
+
const packPositions = (positions) => {
|
|
72
|
+
const count = positions.length / 3;
|
|
73
|
+
const out = new Int16Array(count * 4);
|
|
74
|
+
const min = [32767, 32767, 32767];
|
|
75
|
+
const max = [-32767, -32767, -32767];
|
|
76
|
+
for (let i = 0; i < count; i++)
|
|
77
|
+
for (let c = 0; c < 3; c++) {
|
|
78
|
+
const v = Math.round(((positions[i * 3 + c] - q[c]) / half) * 32767);
|
|
79
|
+
out[i * 4 + c] = v;
|
|
80
|
+
if (v < min[c])
|
|
81
|
+
min[c] = v;
|
|
82
|
+
if (v > max[c])
|
|
83
|
+
max[c] = v;
|
|
84
|
+
}
|
|
85
|
+
return accessor({ bufferView: put(new Uint8Array(out.buffer), ARRAY_BUFFER, 8), componentType: SHORT, count, type: "VEC3", normalized: true, min, max });
|
|
86
|
+
};
|
|
87
|
+
const primitives = [];
|
|
88
|
+
for (const [name, b] of faces) {
|
|
89
|
+
const count = b.positions.length / 3;
|
|
90
|
+
const normal = new Int16Array(count * 4);
|
|
91
|
+
for (let i = 0; i < count; i++)
|
|
92
|
+
for (let c = 0; c < 3; c++)
|
|
93
|
+
normal[i * 4 + c] = Math.round(b.normals[i * 3 + c] * 32767);
|
|
94
|
+
const attributes = {
|
|
95
|
+
POSITION: packPositions(b.positions),
|
|
96
|
+
NORMAL: accessor({ bufferView: put(new Uint8Array(normal.buffer), ARRAY_BUFFER, 8), componentType: SHORT, count, type: "VEC3", normalized: true }),
|
|
97
|
+
};
|
|
98
|
+
if (b.hasUv) {
|
|
99
|
+
const uv = new Uint16Array(count * 2);
|
|
100
|
+
for (let i = 0; i < count * 2; i++)
|
|
101
|
+
uv[i] = Math.round(Math.min(1, Math.max(0, b.uvs[i])) * 65535);
|
|
102
|
+
attributes.TEXCOORD_0 = accessor({ bufferView: put(new Uint8Array(uv.buffer), ARRAY_BUFFER, 4), componentType: USHORT, count, type: "VEC2", normalized: true });
|
|
103
|
+
}
|
|
104
|
+
triangles += b.indices.length / 3;
|
|
105
|
+
primitives.push({ attributes, indices: indexAccessor(b.indices, count), material: useMaterial(name) });
|
|
106
|
+
}
|
|
107
|
+
for (const [name, lines] of [
|
|
108
|
+
["line-feature", mesh.feature],
|
|
109
|
+
["line-weld", mesh.weld],
|
|
110
|
+
]) {
|
|
111
|
+
if (lines.indices.length === 0)
|
|
112
|
+
continue;
|
|
113
|
+
primitives.push({ attributes: { POSITION: packPositions(lines.positions) }, indices: indexAccessor(lines.indices, lines.positions.length / 3), material: useMaterial(name), mode: LINES });
|
|
114
|
+
}
|
|
115
|
+
meshes.push({ name: part.name, primitives });
|
|
116
|
+
// a holder that the viewer moves, and the packed mesh under it
|
|
117
|
+
nodes.push({ name: `${part.name}-mesh`, mesh: meshes.length - 1, translation: q.map(round6), scale: [round6(half), round6(half), round6(half)] });
|
|
118
|
+
const child = nodes.length - 1;
|
|
119
|
+
nodes.push({ name: part.name, children: [child], translation: part.at.map(round6) });
|
|
120
|
+
roots.push(nodes.length - 1);
|
|
121
|
+
}
|
|
122
|
+
const json = {
|
|
123
|
+
asset: { version: "2.0", generator: "uchusen-rockets" },
|
|
124
|
+
extensionsUsed: ["KHR_mesh_quantization"],
|
|
125
|
+
extensionsRequired: ["KHR_mesh_quantization"],
|
|
126
|
+
scene: 0,
|
|
127
|
+
scenes: [{ name: "rocket", nodes: roots }],
|
|
128
|
+
nodes,
|
|
129
|
+
meshes,
|
|
130
|
+
materials,
|
|
131
|
+
accessors,
|
|
132
|
+
bufferViews,
|
|
133
|
+
buffers: [{ byteLength: offset }],
|
|
134
|
+
};
|
|
135
|
+
const text = new TextEncoder().encode(JSON.stringify(json));
|
|
136
|
+
const jsonPad = (4 - (text.byteLength % 4)) % 4;
|
|
137
|
+
const binPad = (4 - (offset % 4)) % 4;
|
|
138
|
+
const total = 12 + 8 + text.byteLength + jsonPad + 8 + offset + binPad;
|
|
139
|
+
const bytes = new Uint8Array(total);
|
|
140
|
+
const view = new DataView(bytes.buffer);
|
|
141
|
+
view.setUint32(0, 0x46546c67, true);
|
|
142
|
+
view.setUint32(4, 2, true);
|
|
143
|
+
view.setUint32(8, total, true);
|
|
144
|
+
view.setUint32(12, text.byteLength + jsonPad, true);
|
|
145
|
+
view.setUint32(16, 0x4e4f534a, true);
|
|
146
|
+
bytes.set(text, 20);
|
|
147
|
+
bytes.fill(0x20, 20 + text.byteLength, 20 + text.byteLength + jsonPad);
|
|
148
|
+
let at = 20 + text.byteLength + jsonPad;
|
|
149
|
+
view.setUint32(at, offset + binPad, true);
|
|
150
|
+
view.setUint32(at + 4, 0x004e4942, true);
|
|
151
|
+
at += 8;
|
|
152
|
+
for (const chunk of chunks) {
|
|
153
|
+
bytes.set(chunk, at);
|
|
154
|
+
at += chunk.byteLength;
|
|
155
|
+
}
|
|
156
|
+
return { bytes, triangles };
|
|
157
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Description, type FitReport } from "../fit.ts";
|
|
2
|
+
import { type Angle, type Vec3, type ViewName } from "../framing.ts";
|
|
3
|
+
import { type LookName } from "../looks.ts";
|
|
4
|
+
import { type PartMesh, type Bounds, type Piece } from "../model/pieces.ts";
|
|
5
|
+
import type { PartRecord } from "../parts.ts";
|
|
6
|
+
import { type Glb, toGlb } from "./glb.ts";
|
|
7
|
+
/** Raised when a description does not fit: its report says why. */
|
|
8
|
+
export declare class FitError extends Error {
|
|
9
|
+
readonly report: FitReport;
|
|
10
|
+
constructor(report: FitReport);
|
|
11
|
+
}
|
|
12
|
+
export interface BuiltPart {
|
|
13
|
+
slot: string;
|
|
14
|
+
record: PartRecord;
|
|
15
|
+
pieces: readonly Piece[];
|
|
16
|
+
mesh: PartMesh;
|
|
17
|
+
bounds: Bounds;
|
|
18
|
+
}
|
|
19
|
+
export interface SvgOptions {
|
|
20
|
+
look?: LookName;
|
|
21
|
+
view?: ViewName | Angle;
|
|
22
|
+
show?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface Built {
|
|
25
|
+
report: FitReport;
|
|
26
|
+
parts: readonly BuiltPart[];
|
|
27
|
+
/** What can be shown: together, apart and each part. */
|
|
28
|
+
shows: readonly string[];
|
|
29
|
+
/** The bounds of a choice of what to show, in metres. */
|
|
30
|
+
boundsOf(show?: string): Bounds;
|
|
31
|
+
/** Where each part stands for a choice of what to show. */
|
|
32
|
+
placesOf(show?: string): {
|
|
33
|
+
slot: string;
|
|
34
|
+
at: Vec3;
|
|
35
|
+
shown: boolean;
|
|
36
|
+
}[];
|
|
37
|
+
/** The .glb file's bytes: every part stacked, each its own node to move. */
|
|
38
|
+
glb(): Glb;
|
|
39
|
+
/** The drawing, in a look, from an angle. */
|
|
40
|
+
svg(options?: SvgOptions): string;
|
|
41
|
+
}
|
|
42
|
+
/** What a drawing draws of a part: in a stack, what nests inside the part below is out of sight. */
|
|
43
|
+
export declare function drawnPieces(part: {
|
|
44
|
+
record: PartRecord;
|
|
45
|
+
pieces: readonly Piece[];
|
|
46
|
+
}, together: boolean): readonly Piece[];
|
|
47
|
+
/** A rocket from a description. Throws FitError when it does not go together. */
|
|
48
|
+
export declare function build(description: Description): Built;
|
|
49
|
+
/** One part on its own, by its id. */
|
|
50
|
+
export declare function buildPart(id: string): {
|
|
51
|
+
record: PartRecord;
|
|
52
|
+
bounds: Bounds;
|
|
53
|
+
glb: () => Glb;
|
|
54
|
+
svg: (options?: SvgOptions) => string;
|
|
55
|
+
};
|
|
56
|
+
export { toGlb };
|
|
57
|
+
export type { Glb };
|
|
58
|
+
export { COMPARES, lineupSvg } from "./lineup.ts";
|
|
59
|
+
export type { CompareId, LineupItem, LineupOptions } from "./lineup.ts";
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// The builder: one description into one mesh and one drawing. Works in Node and in a browser, with no dependency: the geometry here is this package's own.
|
|
2
|
+
import { check } from "../fit.js";
|
|
3
|
+
import { cleanView, DEFAULT_VIEW, PICTURE_H, PICTURE_W, VIEWS } from "../framing.js";
|
|
4
|
+
import { cleanShow, placesFor, showsOf } from "../layout.js";
|
|
5
|
+
import { DEFAULT_LOOK } from "../looks.js";
|
|
6
|
+
import { boundsOf, meshOf } from "../model/pieces.js";
|
|
7
|
+
import { partEntry } from "../model/registry.js";
|
|
8
|
+
import { defsOf, drawPart, perspective } from "../draw/svg.js";
|
|
9
|
+
import { toGlb } from "./glb.js";
|
|
10
|
+
/** Raised when a description does not fit: its report says why. */
|
|
11
|
+
export class FitError extends Error {
|
|
12
|
+
report;
|
|
13
|
+
constructor(report) {
|
|
14
|
+
super(report.issues.filter((issue) => issue.level === "error").map((issue) => issue.message).join(" "));
|
|
15
|
+
this.name = "FitError";
|
|
16
|
+
this.report = report;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const memo = new Map();
|
|
20
|
+
function made(id) {
|
|
21
|
+
const known = memo.get(id);
|
|
22
|
+
if (known)
|
|
23
|
+
return known;
|
|
24
|
+
const entry = partEntry(id);
|
|
25
|
+
if (!entry)
|
|
26
|
+
throw new Error(`There is no part "${id}".`);
|
|
27
|
+
const pieces = entry.pieces();
|
|
28
|
+
const mesh = meshOf(pieces);
|
|
29
|
+
const result = { pieces, mesh, bounds: boundsOf(mesh) };
|
|
30
|
+
memo.set(id, result);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
const xmlText = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
34
|
+
function wrap(inner, look, width = PICTURE_W, height = PICTURE_H, label) {
|
|
35
|
+
const halo = look !== "painted" ? `<ellipse cx="${width / 2}" cy="${height / 2}" rx="${width / 2}" ry="${height / 2}" fill="url(#halo)"/>` : "";
|
|
36
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img"${label ? ` aria-label="${xmlText(label)}"` : ""}>${defsOf(look)}${halo}${inner}</svg>`;
|
|
37
|
+
}
|
|
38
|
+
/** The widest a part reaches from its own axis. */
|
|
39
|
+
const widthOf = (id) => {
|
|
40
|
+
const { min, max } = made(id).bounds;
|
|
41
|
+
return Math.max(Math.abs(min[0]), Math.abs(max[0]), Math.abs(min[2]), Math.abs(max[2]));
|
|
42
|
+
};
|
|
43
|
+
function assembled(report) {
|
|
44
|
+
const parts = report.parts.map(({ slot, part }) => {
|
|
45
|
+
const { pieces, mesh, bounds } = made(part.id);
|
|
46
|
+
return { slot, record: part, pieces, mesh, bounds };
|
|
47
|
+
});
|
|
48
|
+
const slots = parts.map((part) => part.slot);
|
|
49
|
+
const placesOf = (show) => placesFor(report.parts, cleanShow(show, slots), (part) => widthOf(part.id));
|
|
50
|
+
const boundsOfShow = (show) => {
|
|
51
|
+
const min = [Infinity, Infinity, Infinity];
|
|
52
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
53
|
+
for (const place of placesOf(show)) {
|
|
54
|
+
if (!place.shown)
|
|
55
|
+
continue;
|
|
56
|
+
const { bounds } = parts.find((part) => part.slot === place.slot);
|
|
57
|
+
for (let c = 0; c < 3; c++) {
|
|
58
|
+
min[c] = Math.min(min[c], bounds.min[c] + place.at[c]);
|
|
59
|
+
max[c] = Math.max(max[c], bounds.max[c] + place.at[c]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { min, max };
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
report,
|
|
66
|
+
parts,
|
|
67
|
+
shows: showsOf(slots),
|
|
68
|
+
boundsOf: boundsOfShow,
|
|
69
|
+
placesOf,
|
|
70
|
+
glb: () => toGlb(parts.map((part) => ({ name: part.slot, mesh: part.mesh, at: placesOf("together").find((place) => place.slot === part.slot)?.at ?? [0, 0, 0] }))),
|
|
71
|
+
svg: ({ look = DEFAULT_LOOK, view = DEFAULT_VIEW, show } = {}) => {
|
|
72
|
+
const angle = typeof view === "string" ? VIEWS[cleanView(view)] : view;
|
|
73
|
+
const places = placesOf(show).filter((place) => place.shown);
|
|
74
|
+
const cam = perspective(boundsOfShow(show), angle);
|
|
75
|
+
const ordered = places
|
|
76
|
+
.map((place) => {
|
|
77
|
+
const part = parts.find((p) => p.slot === place.slot);
|
|
78
|
+
const middle = [place.at[0], place.at[1] + part.record.height / 2, place.at[2]];
|
|
79
|
+
return { place, part, depth: cam.depth(middle) };
|
|
80
|
+
})
|
|
81
|
+
.sort((a, b) => b.depth - a.depth);
|
|
82
|
+
const together = !show || show === "together";
|
|
83
|
+
return wrap(ordered.map(({ place, part }) => drawPart(drawnPieces(part, together), place.at, cam, look)).join(""), look, PICTURE_W, PICTURE_H, report.label);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** What a drawing draws of a part: in a stack, what nests inside the part below is out of sight. */
|
|
88
|
+
export function drawnPieces(part, together) {
|
|
89
|
+
const joint = part.record.joints.bottom?.y ?? 0;
|
|
90
|
+
if (!together || joint <= 0)
|
|
91
|
+
return part.pieces;
|
|
92
|
+
return part.pieces.filter((piece) => !(piece.kind === "lathe" && piece.under && Math.max(...piece.profile.map(([, y]) => y)) <= joint + 0.01));
|
|
93
|
+
}
|
|
94
|
+
/** A rocket from a description. Throws FitError when it does not go together. */
|
|
95
|
+
export function build(description) {
|
|
96
|
+
const report = check(description);
|
|
97
|
+
if (!report.ok)
|
|
98
|
+
throw new FitError(report);
|
|
99
|
+
return assembled(report);
|
|
100
|
+
}
|
|
101
|
+
/** One part on its own, by its id. */
|
|
102
|
+
export function buildPart(id) {
|
|
103
|
+
const entry = partEntry(id);
|
|
104
|
+
if (!entry)
|
|
105
|
+
throw new Error(`There is no part "${id}".`);
|
|
106
|
+
const { pieces, mesh, bounds } = made(id);
|
|
107
|
+
return {
|
|
108
|
+
record: entry.record,
|
|
109
|
+
bounds,
|
|
110
|
+
glb: () => toGlb([{ name: entry.record.id, mesh, at: [0, 0, 0] }]),
|
|
111
|
+
svg: ({ look = DEFAULT_LOOK, view = DEFAULT_VIEW } = {}) => {
|
|
112
|
+
const angle = typeof view === "string" ? VIEWS[cleanView(view)] : view;
|
|
113
|
+
return wrap(drawPart(pieces, [0, 0, 0], perspective(bounds, angle), look, 1), look, PICTURE_W, PICTURE_H, entry.record.name);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export { toGlb };
|
|
118
|
+
export { COMPARES, lineupSvg } from "./lineup.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ViewName } from "../framing.ts";
|
|
2
|
+
import { type LookName } from "../looks.ts";
|
|
3
|
+
/** Comparison shapes: plain round sizes, for scale, not facts about anything. */
|
|
4
|
+
export declare const COMPARES: {
|
|
5
|
+
readonly person: {
|
|
6
|
+
readonly name: "A person, 1.8 m";
|
|
7
|
+
readonly height: 1.8;
|
|
8
|
+
readonly width: 0.5;
|
|
9
|
+
};
|
|
10
|
+
readonly building: {
|
|
11
|
+
readonly name: "A ten-storey building, 30 m";
|
|
12
|
+
readonly height: 30;
|
|
13
|
+
readonly width: 18;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type CompareId = keyof typeof COMPARES;
|
|
17
|
+
export interface LineupItem {
|
|
18
|
+
/** "starship:v3" or a comparison's name. */
|
|
19
|
+
key: string;
|
|
20
|
+
label: string;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
export interface LineupOptions {
|
|
24
|
+
/** "starship:v1,starship:v3,person,building". */
|
|
25
|
+
items: readonly string[];
|
|
26
|
+
/** Pixels a metre. */
|
|
27
|
+
unit: number;
|
|
28
|
+
look?: LookName;
|
|
29
|
+
view?: ViewName;
|
|
30
|
+
show?: string;
|
|
31
|
+
ruler?: boolean;
|
|
32
|
+
labels?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** The whole picture as SVG text. Unknown items are left out. */
|
|
35
|
+
export declare function lineupSvg(options: LineupOptions): {
|
|
36
|
+
svg: string;
|
|
37
|
+
items: LineupItem[];
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Rockets and what to compare them with, side by side on one baseline at one scale, as one drawing with a ruler. Orthographic, so a metre is the same on every one. Pure.
|
|
2
|
+
import { build, drawnPieces } from "./index.js";
|
|
3
|
+
import { check } from "../fit.js";
|
|
4
|
+
import { axesOf, cleanView, directionOf, VIEWS } from "../framing.js";
|
|
5
|
+
import { DEFAULT_LOOK, LOOKS } from "../looks.js";
|
|
6
|
+
import { defsOf, drawPart, orthographic } from "../draw/svg.js";
|
|
7
|
+
import { variantOf } from "../rockets.js";
|
|
8
|
+
/** Comparison shapes: plain round sizes, for scale, not facts about anything. */
|
|
9
|
+
export const COMPARES = {
|
|
10
|
+
person: { name: "A person, 1.8 m", height: 1.8, width: 0.5 },
|
|
11
|
+
building: { name: "A ten-storey building, 30 m", height: 30, width: 18 },
|
|
12
|
+
};
|
|
13
|
+
const xmlText = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
14
|
+
const n = (v) => (Math.round(v * 10) / 10).toString();
|
|
15
|
+
function comparison(id, x, base, unit, line, fill) {
|
|
16
|
+
const { height, width } = COMPARES[id];
|
|
17
|
+
const h = height * unit;
|
|
18
|
+
const w = width * unit;
|
|
19
|
+
if (id === "person") {
|
|
20
|
+
const head = Math.max(0.6, h * 0.11);
|
|
21
|
+
return `<circle cx="${n(x + w / 2)}" cy="${n(base - h + head)}" r="${n(head)}" fill="${fill}" stroke="${line}" stroke-width=".6"/><path d="M${n(x + w * 0.12)} ${n(base)}L${n(x + w * 0.2)} ${n(base - h * 0.76)}H${n(x + w * 0.8)}L${n(x + w * 0.88)} ${n(base)}Z" fill="${fill}" stroke="${line}" stroke-width=".6"/>`;
|
|
22
|
+
}
|
|
23
|
+
let floors = "";
|
|
24
|
+
for (let i = 1; i < 10; i++)
|
|
25
|
+
floors += `M${n(x)} ${n(base - (h * i) / 10)}h${n(w)}`;
|
|
26
|
+
return `<rect x="${n(x)}" y="${n(base - h)}" width="${n(w)}" height="${n(h)}" fill="${fill}" stroke="${line}" stroke-width=".9"/><path d="${floors}" stroke="${line}" stroke-width=".35" opacity=".6"/>`;
|
|
27
|
+
}
|
|
28
|
+
/** The whole picture as SVG text. Unknown items are left out. */
|
|
29
|
+
export function lineupSvg(options) {
|
|
30
|
+
const { items, unit, look = DEFAULT_LOOK, view = "front", ruler = true, labels = true } = options;
|
|
31
|
+
const angle = VIEWS[cleanView(view)];
|
|
32
|
+
const known = [];
|
|
33
|
+
for (const raw of items) {
|
|
34
|
+
const key = raw.trim();
|
|
35
|
+
if (Object.hasOwn(COMPARES, key)) {
|
|
36
|
+
const c = COMPARES[key];
|
|
37
|
+
known.push({ key, compare: key, height: c.height, width: c.width, label: c.name });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const [rocket, variant] = key.split(":");
|
|
41
|
+
const named = variantOf(rocket, variant);
|
|
42
|
+
if (!named)
|
|
43
|
+
continue;
|
|
44
|
+
const report = check({ rocket, variant: named.id });
|
|
45
|
+
if (!report.ok)
|
|
46
|
+
continue;
|
|
47
|
+
const built = build({ rocket, variant: named.id });
|
|
48
|
+
const show = options.show && built.shows.includes(options.show) ? options.show : built.shows[0];
|
|
49
|
+
const b = built.boundsOf(show);
|
|
50
|
+
known.push({ key, built, height: b.max[1], width: Math.max(Math.abs(b.min[0]), Math.abs(b.max[0]), Math.abs(b.min[2]), Math.abs(b.max[2])) * 2, label: named.name });
|
|
51
|
+
}
|
|
52
|
+
const gap = 28;
|
|
53
|
+
const top = 26;
|
|
54
|
+
const labelRoom = labels ? 44 : 0;
|
|
55
|
+
const rulerRoom = ruler ? 46 : 8;
|
|
56
|
+
const tallest = Math.max(10, ...known.map((item) => item.height));
|
|
57
|
+
const height = top + tallest * unit + labelRoom + 10;
|
|
58
|
+
const base = top + tallest * unit;
|
|
59
|
+
const minCell = 96;
|
|
60
|
+
let x = rulerRoom;
|
|
61
|
+
const body = [];
|
|
62
|
+
const { line } = LOOKS[look];
|
|
63
|
+
for (const item of known) {
|
|
64
|
+
const cell = Math.max(minCell, item.width * unit + gap);
|
|
65
|
+
const cx = x + cell / 2;
|
|
66
|
+
if (item.compare)
|
|
67
|
+
body.push(comparison(item.compare, cx - (COMPARES[item.compare].width * unit) / 2, base, unit, line, "url(#metal)"));
|
|
68
|
+
else if (item.built) {
|
|
69
|
+
const show = options.show && item.built.shows.includes(options.show) ? options.show : item.built.shows[0];
|
|
70
|
+
const cam = orthographic(angle, unit, cx, base);
|
|
71
|
+
const places = item.built.placesOf(show).filter((place) => place.shown);
|
|
72
|
+
const { right, forward } = axesOf(directionOf(angle));
|
|
73
|
+
void right;
|
|
74
|
+
for (const place of places.sort((a, b) => b.at[0] * forward[0] + b.at[2] * forward[2] - a.at[0] * forward[0] - a.at[2] * forward[2])) {
|
|
75
|
+
const part = item.built.parts.find((p) => p.slot === place.slot);
|
|
76
|
+
if (part)
|
|
77
|
+
body.push(drawPart(drawnPieces(part, show === "together"), place.at, cam, look, 0.7));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (labels) {
|
|
81
|
+
const words = item.label.length > 19 ? [item.label.slice(0, item.label.lastIndexOf(" ", 19)), item.label.slice(item.label.lastIndexOf(" ", 19) + 1)] : [item.label];
|
|
82
|
+
body.push(`<text x="${n(cx)}" y="${n(base + 16)}" text-anchor="middle" font-size="11" fill="currentColor">${words.map((word, i) => `<tspan x="${n(cx)}" dy="${i ? 13 : 0}">${xmlText(word)}</tspan>`).join("")}</text>`);
|
|
83
|
+
}
|
|
84
|
+
x += cell;
|
|
85
|
+
}
|
|
86
|
+
const width = x + 8;
|
|
87
|
+
let rule = "";
|
|
88
|
+
if (ruler) {
|
|
89
|
+
const step = tallest * unit < 240 ? 10 : 20;
|
|
90
|
+
rule += `<path d="M${rulerRoom - 8} ${n(base)}V${n(base - tallest * unit)}" stroke="currentColor" stroke-width="1" opacity=".7"/>`;
|
|
91
|
+
for (let m = 0; m <= tallest + 0.001; m += step)
|
|
92
|
+
rule += `<path d="M${rulerRoom - 8} ${n(base - m * unit)}h-5" stroke="currentColor" stroke-width="1" opacity=".7"/><text x="${rulerRoom - 16}" y="${n(base - m * unit + 3.5)}" text-anchor="end" font-size="10" fill="currentColor" opacity=".85">${m}</text>`;
|
|
93
|
+
rule += `<text x="${rulerRoom - 16}" y="${n(base - tallest * unit - 10)}" text-anchor="end" font-size="10" fill="currentColor" opacity=".85">m</text><path d="M${rulerRoom - 8} ${n(base)}H${n(width)}" stroke="currentColor" stroke-width=".6" opacity=".4"/>`;
|
|
94
|
+
}
|
|
95
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n(width)} ${n(height)}" width="${n(width)}" height="${n(height)}" role="img" aria-label="${xmlText(`To scale: ${known.map((item) => item.label).join(", ")}`)}">${defsOf(look)}${rule}${body.join("")}</svg>`;
|
|
96
|
+
return { svg, width, height, items: known.map(({ key, label, height: h }) => ({ key, label, height: h })) };
|
|
97
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The drawing stays at least this long after it first comes on screen. */
|
|
2
|
+
export declare const DRAWING_HOLD_MS = 1200;
|
|
3
|
+
export declare const CROSSFADE_MS = 1500;
|
|
4
|
+
export declare const REDUCED_FADE_MS = 400;
|
|
5
|
+
/** How long to wait, once the 3D view is ready at `readyAt`, before it takes over from a drawing first seen at `shownAt` (0 under reduced motion). */
|
|
6
|
+
export declare function revealDelay(shownAt: number, readyAt: number, reducedMotion: boolean): number;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The change from a rocket's drawing to its 3D view: the drawing stays a moment even when the 3D view is ready sooner, then a slow crossfade; under reduced motion, a plain short fade. Pure.
|
|
2
|
+
/** The drawing stays at least this long after it first comes on screen. */
|
|
3
|
+
export const DRAWING_HOLD_MS = 1200;
|
|
4
|
+
export const CROSSFADE_MS = 1500;
|
|
5
|
+
export const REDUCED_FADE_MS = 400;
|
|
6
|
+
/** How long to wait, once the 3D view is ready at `readyAt`, before it takes over from a drawing first seen at `shownAt` (0 under reduced motion). */
|
|
7
|
+
export function revealDelay(shownAt, readyAt, reducedMotion) {
|
|
8
|
+
if (reducedMotion)
|
|
9
|
+
return 0;
|
|
10
|
+
return Math.max(0, Math.min(DRAWING_HOLD_MS, shownAt + DRAWING_HOLD_MS - readyAt));
|
|
11
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface ModelCredit {
|
|
2
|
+
title: string;
|
|
3
|
+
creator: string;
|
|
4
|
+
creatorUrl: string;
|
|
5
|
+
sourceUrl: string;
|
|
6
|
+
licence: "CC BY 4.0";
|
|
7
|
+
licenceUrl: string;
|
|
8
|
+
/** What this package changed. */
|
|
9
|
+
changes: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const MODEL_CREDITS: {
|
|
12
|
+
readonly "clarence365-block3": {
|
|
13
|
+
readonly title: "SpaceX Starship Block 3";
|
|
14
|
+
readonly creator: "Clarence365";
|
|
15
|
+
readonly creatorUrl: "https://sketchfab.com/clarence365";
|
|
16
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-starship-block-3-6f6c6f88a3eb4b4d822fdca66733fbb2";
|
|
17
|
+
readonly licence: "CC BY 4.0";
|
|
18
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
19
|
+
readonly changes: "Reduced from 913,217 triangles; logos, wordmark and serial number removed; rescaled to sourced sizes; separated into booster and ship; unseen payload bay interior removed; materials merged and renamed.";
|
|
20
|
+
};
|
|
21
|
+
readonly "clarence365-s24b7": {
|
|
22
|
+
readonly title: "Spacex Starship Ship 24 & Booster 7 V4";
|
|
23
|
+
readonly creator: "clarence365";
|
|
24
|
+
readonly creatorUrl: "https://sketchfab.com/clarence365";
|
|
25
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-starship-ship-24-booster-7-v4-97875d14b63e4b9ca9ed425ef4253306";
|
|
26
|
+
readonly licence: "CC BY 4.0";
|
|
27
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
28
|
+
readonly changes: "Reduced from 870,993 triangles; logo, serial number and engine decals removed; rescaled to sourced sizes; separated into booster and ship; unseen payload bay interior removed; materials merged and renamed.";
|
|
29
|
+
};
|
|
30
|
+
readonly "martiandays-2019": {
|
|
31
|
+
readonly title: "SpaceX Starship";
|
|
32
|
+
readonly creator: "MartianDays";
|
|
33
|
+
readonly creatorUrl: "https://sketchfab.com/MartianDays";
|
|
34
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-starship-a8a0b69f776841a1a465cd9fb3762fd2";
|
|
35
|
+
readonly licence: "CC BY 4.0";
|
|
36
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
37
|
+
readonly changes: "Rescaled to the sourced height; turned to this package's axes; material renamed. Its triangles and images are otherwise unchanged.";
|
|
38
|
+
};
|
|
39
|
+
readonly "stanley-falcon-9": {
|
|
40
|
+
readonly title: "Falcon 9 - SpaceX";
|
|
41
|
+
readonly creator: "Stanley Creative";
|
|
42
|
+
readonly creatorUrl: "https://sketchfab.com/Stanley_Creative";
|
|
43
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/falcon-9-spacex-394f7cf52d124bbd9db69f24d1ff2f08";
|
|
44
|
+
readonly licence: "CC BY 4.0";
|
|
45
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
46
|
+
readonly changes: "Reduced from 157,889 triangles; the wordmark, logos, flags and mission marks removed with the two textures that carried them; rescaled to sourced sizes; separated into first stage, second stage and fairing; materials merged and renamed.";
|
|
47
|
+
};
|
|
48
|
+
readonly "kubaha-dragon-2": {
|
|
49
|
+
readonly title: "SpaceX - Dragon 2";
|
|
50
|
+
readonly creator: "KUBAHA";
|
|
51
|
+
readonly creatorUrl: "https://sketchfab.com/KUBAHA";
|
|
52
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-dragon-2-1b125e30a2cb442288580290536fbe5d";
|
|
53
|
+
readonly licence: "CC BY 4.0";
|
|
54
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
55
|
+
readonly changes: "Reduced from 42,974 triangles; rescaled to sourced sizes; separated into capsule and trunk; materials merged and renamed.";
|
|
56
|
+
};
|
|
57
|
+
readonly "progressth-dragon-1": {
|
|
58
|
+
readonly title: "SpaceX Cargo Dragon";
|
|
59
|
+
readonly creator: "ProgressTH";
|
|
60
|
+
readonly creatorUrl: "https://sketchfab.com/ProgressTH";
|
|
61
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-cargo-dragon-0ca8951e47b343dc8a41e23b3e9c55e5";
|
|
62
|
+
readonly licence: "CC BY 4.0";
|
|
63
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
64
|
+
readonly changes: "Merged from 414 meshes; the logo decal and the hidden pressure vessel removed; rescaled to sourced sizes; stood upright; separated into capsule and trunk; materials merged and renamed.";
|
|
65
|
+
};
|
|
66
|
+
readonly "antonio-falcon-heavy": {
|
|
67
|
+
readonly title: "SpaceX Falcon Heavy(Low Poly)";
|
|
68
|
+
readonly creator: "Antonio_Adams";
|
|
69
|
+
readonly creatorUrl: "https://sketchfab.com/AAdams3112";
|
|
70
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-falcon-heavylow-poly-0dc068a462e34258b3872085a7f592c8";
|
|
71
|
+
readonly licence: "CC BY 4.0";
|
|
72
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
73
|
+
readonly changes: "The wordmark and logos painted out of its colour, roughness and normal maps, which were also resized; rescaled to sourced sizes; separated into its centre core and two side boosters by its own connected pieces.";
|
|
74
|
+
};
|
|
75
|
+
readonly "echo3-grasshopper": {
|
|
76
|
+
readonly title: "SpaceX Grasshopper";
|
|
77
|
+
readonly creator: "Echo_3";
|
|
78
|
+
readonly creatorUrl: "https://sketchfab.com/Echo_3";
|
|
79
|
+
readonly sourceUrl: "https://sketchfab.com/3d-models/spacex-grasshopper-35c07a68cdb246c59bb425610b934bf8";
|
|
80
|
+
readonly licence: "CC BY 4.0";
|
|
81
|
+
readonly licenceUrl: "https://creativecommons.org/licenses/by/4.0/";
|
|
82
|
+
readonly changes: "The wordmark and logo removed with the texture that carried them; rescaled to the sourced height; reduced from 55,455 triangles; materials merged and renamed.";
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
export type CreditId = keyof typeof MODEL_CREDITS;
|
|
86
|
+
/** One credit as a line of text. */
|
|
87
|
+
export declare function creditLine(id: CreditId): string;
|