@johnmorrisdotca/uchusen-rockets 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/CHANGELOG.md +23 -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 +5 -0
- package/dist/data/falcon.js +123 -0
- package/dist/data/grasshopper.d.ts +2 -0
- package/dist/data/grasshopper.js +21 -0
- package/dist/data/sources.d.ts +181 -0
- package/dist/data/sources.js +26 -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 +118 -0
- package/dist/element/scene.js +861 -0
- package/dist/element.d.ts +80 -0
- package/dist/element.js +526 -0
- package/dist/fit.d.ts +43 -0
- package/dist/fit.js +93 -0
- package/dist/framing.d.ts +42 -0
- package/dist/framing.js +56 -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 +49 -0
- package/dist/model/falcon.js +208 -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 +45 -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 +66 -4
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Starship's variants as the sources give them. No figure here is measured from a picture or worked out to fill a gap: a blank stays blank. Column layout matters in the Wikipedia table these come from:
|
|
2
|
+
// its "81 m" is Block 4's booster, not Block 3's, which is how one secondary site came to print it for Block 3.
|
|
3
|
+
import { blank } from "./types.js";
|
|
4
|
+
const WP = "wikipedia-starship";
|
|
5
|
+
const SH = "wikipedia-super-heavy";
|
|
6
|
+
const LAUNCHES = "wikipedia-launches";
|
|
7
|
+
const m = (value, source, extra = {}) => ({ value, source, ...extra });
|
|
8
|
+
const said = (value, source, extra = {}) => ({ value, source, ...extra });
|
|
9
|
+
const num = (value, source, extra = {}) => ({ value, source, ...extra });
|
|
10
|
+
const GUNTER_NOTE = "Gunter's Space Page gives one set of figures for every version, 118 m with stages of 68 m and 50 m. It does not change them by version, so the block table is used.";
|
|
11
|
+
export const STARSHIP_VARIANTS = [
|
|
12
|
+
{
|
|
13
|
+
id: "v1-flight1",
|
|
14
|
+
rocket: "starship",
|
|
15
|
+
name: "Block 1, first flight (Ship 24, Booster 7)",
|
|
16
|
+
status: "flown",
|
|
17
|
+
firstFlight: said("2023-04-20", LAUNCHES, { brief: "Starship's first flight", note: "Flight test 1, Booster 7 and Ship 24. Gunter's Space Page calls this version v1.1 and the ringed one v1.2." }),
|
|
18
|
+
slots: ["booster", "ship"],
|
|
19
|
+
note: "The one vehicle of flight test 1, with no vented ring: that came with the next flight. Its stated height and its parts do not add up, by 1.7 m: the parts are drawn to the parts' figures, so it is drawn 119.3 m tall.",
|
|
20
|
+
facts: {
|
|
21
|
+
height: m(121, "wikipedia-flight-1", { date: "2023-04-20", brief: "as stated; its parts add to 119.3 m", note: "The flight's article says 121 m. Its parts' own figures add to 119.3 m, and the drawing follows the parts." }),
|
|
22
|
+
diameter: m(9, WP),
|
|
23
|
+
parts: {
|
|
24
|
+
booster: m(69, SH, { note: "Wikipedia's infobox: 69 m without the vented interstage. It gives no reference for that figure." }),
|
|
25
|
+
ship: m(50.3, WP),
|
|
26
|
+
},
|
|
27
|
+
engines: {
|
|
28
|
+
booster: said("33 Raptor 2", SH),
|
|
29
|
+
ship: said("3 sea-level and 3 vacuum Raptor 2", WP),
|
|
30
|
+
},
|
|
31
|
+
more: {
|
|
32
|
+
gridFins: num(4, "satnews-v3"),
|
|
33
|
+
forwardFlaps: said("Both at 180 degrees", "wikipedia-ship"),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "v1",
|
|
39
|
+
rocket: "starship",
|
|
40
|
+
name: "Block 1 with the vented ring (V1)",
|
|
41
|
+
status: "flown",
|
|
42
|
+
firstFlight: said("2023-11-18", LAUNCHES, { brief: "first with the ring", note: "Flight test 2, Booster 9 and Ship 25. Block 1 itself first flew on 2023-04-20, without the ring: that is the variant before this one." }),
|
|
43
|
+
slots: ["booster", "ship"],
|
|
44
|
+
facts: {
|
|
45
|
+
height: m(121.3, WP, { date: "2023", others: [{ value: 118, source: "gunter", note: GUNTER_NOTE }] }),
|
|
46
|
+
diameter: m(9, WP),
|
|
47
|
+
parts: {
|
|
48
|
+
booster: m(71, SH, { note: "With the vented interstage. 69 m without it.", others: [{ value: 68, source: "gunter", note: GUNTER_NOTE }] }),
|
|
49
|
+
ship: m(50.3, WP, { others: [{ value: 50, source: "gunter", note: GUNTER_NOTE }] }),
|
|
50
|
+
},
|
|
51
|
+
engines: {
|
|
52
|
+
booster: said("33 Raptor 2", SH),
|
|
53
|
+
ship: said("3 sea-level and 3 vacuum Raptor 2", WP),
|
|
54
|
+
},
|
|
55
|
+
more: {
|
|
56
|
+
gridFins: num(4, "satnews-v3", { note: "V3 went from four to three." }),
|
|
57
|
+
forwardFlaps: said("Both at 180 degrees", "wikipedia-ship"),
|
|
58
|
+
boosterPropellantT: num(3250, WP, { others: [{ value: 3400, source: SH, note: "The Super Heavy article's infobox gives 3,400 t for Blocks 1 and 2; the block table is used." }] }),
|
|
59
|
+
shipPropellantT: num(1200, WP),
|
|
60
|
+
boosterThrustTf: num(7500, WP),
|
|
61
|
+
shipThrustTf: num(1250, WP),
|
|
62
|
+
payloadT: num(15, WP),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "v2",
|
|
68
|
+
rocket: "starship",
|
|
69
|
+
name: "Block 2 (V2)",
|
|
70
|
+
status: "flown",
|
|
71
|
+
firstFlight: said("2025-01-16", LAUNCHES, { note: "Flight test 7, Booster 14 and Ship 33." }),
|
|
72
|
+
slots: ["booster", "ship"],
|
|
73
|
+
facts: {
|
|
74
|
+
height: m(123.1, WP, { date: "2025", others: [{ value: 123.3, source: WP, note: "The same article's table says 123.3 m; its infobox says 123.1 m, which is also 71 m plus 52.1 m." }] }),
|
|
75
|
+
diameter: m(9, WP),
|
|
76
|
+
parts: {
|
|
77
|
+
booster: m(71, SH),
|
|
78
|
+
ship: m(52.1, WP),
|
|
79
|
+
},
|
|
80
|
+
engines: {
|
|
81
|
+
booster: said("33 Raptor 2", SH),
|
|
82
|
+
ship: said("3 sea-level and 3 vacuum Raptor 2", WP),
|
|
83
|
+
},
|
|
84
|
+
more: {
|
|
85
|
+
gridFins: num(4, "satnews-v3"),
|
|
86
|
+
forwardFlaps: said("Thinner, moved leeward, angled to about 140 degrees", "wikipedia-ship"),
|
|
87
|
+
boosterPropellantT: num(3250, WP),
|
|
88
|
+
shipPropellantT: num(1500, WP),
|
|
89
|
+
boosterThrustTf: num(7500, WP),
|
|
90
|
+
shipThrustTf: num(1400, WP),
|
|
91
|
+
payloadT: num(35, WP),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "v3",
|
|
97
|
+
rocket: "starship",
|
|
98
|
+
name: "Block 3 (V3)",
|
|
99
|
+
status: "flown",
|
|
100
|
+
firstFlight: said("2026-05-22", LAUNCHES, { note: "Flight test 12, Booster 19 and Ship 39." }),
|
|
101
|
+
slots: ["booster", "ship"],
|
|
102
|
+
facts: {
|
|
103
|
+
height: m(124.4, WP, {
|
|
104
|
+
date: "2026",
|
|
105
|
+
others: [
|
|
106
|
+
{ value: 124, source: "spacex-starship", note: "SpaceX's vehicle page, as a search engine quoted it: 124 m (407 ft), rounded. The page itself would not load." },
|
|
107
|
+
{ value: 124, source: "satnews-v3", note: "About 124 m, roughly 1.5 m taller than V2, from SpaceX's briefing." },
|
|
108
|
+
],
|
|
109
|
+
}),
|
|
110
|
+
diameter: m(9, SH),
|
|
111
|
+
parts: {
|
|
112
|
+
booster: m(72.3, SH, {
|
|
113
|
+
date: "2025-05-30",
|
|
114
|
+
note: "Wikipedia cites NASASpaceflight, 2025-05-30, which could not be read directly.",
|
|
115
|
+
others: [
|
|
116
|
+
{ value: 72, source: "spacex-starship", note: "SpaceX's page as quoted by a search engine: 72 m (236 ft), rounded." },
|
|
117
|
+
{ value: 81, source: WP, note: "81 m is Block 4's cell in Wikipedia's table, where colspans shift the columns. A site that printed it for Block 3 misread it: 81 m plus 52.1 m does not make 124.4 m." },
|
|
118
|
+
],
|
|
119
|
+
}),
|
|
120
|
+
ship: m(52.1, WP, { others: [{ value: 52, source: "spacex-starship", note: "SpaceX's page as quoted by a search engine: 52 m (171 ft), rounded." }] }),
|
|
121
|
+
},
|
|
122
|
+
engines: {
|
|
123
|
+
booster: said("33 Raptor 3", SH),
|
|
124
|
+
ship: said("3 sea-level and 3 vacuum Raptor 3", WP),
|
|
125
|
+
},
|
|
126
|
+
more: {
|
|
127
|
+
gridFins: num(3, SH, { note: "In a 90/90/180 degree arrangement. About 50% larger each, by SatNews." }),
|
|
128
|
+
hotStage: said("Vented interstage and forward dome integrated into the methane tank", SH),
|
|
129
|
+
docking: said("Docking ports for refuelling", WP),
|
|
130
|
+
boosterPropellantT: num(3650, WP),
|
|
131
|
+
shipPropellantT: num(1600, WP),
|
|
132
|
+
boosterThrustTf: num(8240, WP),
|
|
133
|
+
shipThrustTf: num(1600, WP),
|
|
134
|
+
payloadT: num(100, WP),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "v4",
|
|
140
|
+
rocket: "starship",
|
|
141
|
+
name: "Block 4 (V4), announced",
|
|
142
|
+
status: "announced",
|
|
143
|
+
firstFlight: blank("Announced, not built, so it has not flown.", "Not flown"),
|
|
144
|
+
slots: ["booster", "ship"],
|
|
145
|
+
note: "Announced, not built: every figure here is a plan, not a measurement. Its height is marked an estimate at the source, and may change. Its drawing is Block 3's details on Block 4's sizes, which is thin evidence: no source shows its parts.",
|
|
146
|
+
facts: {
|
|
147
|
+
height: m(142, WP, { brief: "announced, an estimate", note: "Marked an estimate at the source." }),
|
|
148
|
+
diameter: m(9, WP, { brief: "announced" }),
|
|
149
|
+
parts: {
|
|
150
|
+
booster: m(81, WP, { brief: "announced", note: "From the block table, which adds it up to the 142 m it marks an estimate elsewhere. SpaceX's planned high bay is also quoted as clearing vehicles up to 81 m." }),
|
|
151
|
+
ship: m(61, WP, { brief: "announced", note: "From the block table, which adds it up to the 142 m it marks an estimate elsewhere." }),
|
|
152
|
+
},
|
|
153
|
+
engines: {
|
|
154
|
+
booster: said("33 Raptor 3", "gunter", { brief: "announced" }),
|
|
155
|
+
ship: said("3 sea-level and 6 vacuum", WP, { brief: "announced", note: "Three more vacuum engines, from a public statement by Elon Musk." }),
|
|
156
|
+
},
|
|
157
|
+
more: {
|
|
158
|
+
gridFins: blank("No source gives it."),
|
|
159
|
+
boosterPropellantT: num(4050, WP),
|
|
160
|
+
shipPropellantT: num(2300, WP),
|
|
161
|
+
boosterThrustTf: num(10000, WP),
|
|
162
|
+
shipThrustTf: num(2700, WP),
|
|
163
|
+
payloadT: num(200, WP),
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "2019",
|
|
169
|
+
rocket: "starship",
|
|
170
|
+
name: "2019 design (artist's model)",
|
|
171
|
+
status: "artist-model",
|
|
172
|
+
firstFlight: blank("It never flew: this is a design, drawn when it was shown.", "Never flew"),
|
|
173
|
+
slots: ["ship"],
|
|
174
|
+
note: "An artist's model of the upper stage shown in September 2019, published on 2019-09-30, not a flown block. It has no booster. The height is the Mk1 prototype's, which was built to that design.",
|
|
175
|
+
facts: {
|
|
176
|
+
height: m(50, "spacecom-mk1", { date: "2019-09", note: "Musk's figure for the Mk1 prototype, as a search engine quoted the article." }),
|
|
177
|
+
diameter: blank("No source read here gives the 2019 design's diameter."),
|
|
178
|
+
parts: { ship: m(50, "spacecom-mk1", { date: "2019-09" }) },
|
|
179
|
+
engines: { ship: said("3 sea-level and 3 vacuum", WP, { note: "The design had settled on six Raptors by 2019." }) },
|
|
180
|
+
more: {},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SourceId } from "./sources.ts";
|
|
2
|
+
export interface Disagreement<T> {
|
|
3
|
+
value: T;
|
|
4
|
+
source: SourceId;
|
|
5
|
+
note: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Figure<T = number> {
|
|
8
|
+
value: T | null;
|
|
9
|
+
source: SourceId | null;
|
|
10
|
+
/** The date the figure is from, where it has one. */
|
|
11
|
+
date?: string;
|
|
12
|
+
note?: string;
|
|
13
|
+
/** A few words that must stay beside the value wherever it is shown, so it cannot be misread: "announced", "in this form". For a blank, what to say in place of a value. */
|
|
14
|
+
brief?: string;
|
|
15
|
+
others?: readonly Disagreement<T>[];
|
|
16
|
+
}
|
|
17
|
+
export declare const blank: <T = number>(note?: string, brief?: string) => Figure<T>;
|
|
18
|
+
/** A figure as words for a table: its value and whatever must stay beside it; a blank says why, or that no source gives it. */
|
|
19
|
+
export declare function figureWords(figure: Figure<number | string> | undefined, unit?: string): string;
|
|
20
|
+
export type VariantStatus = "flown" | "announced" | "artist-model" | "test-vehicle";
|
|
21
|
+
export interface VariantFacts {
|
|
22
|
+
/** Overall height with every part in place, metres. */
|
|
23
|
+
height: Figure;
|
|
24
|
+
diameter: Figure;
|
|
25
|
+
/** Each part's height by its slot, metres. */
|
|
26
|
+
parts: Readonly<Record<string, Figure>>;
|
|
27
|
+
/** Engines by slot. */
|
|
28
|
+
engines: Readonly<Record<string, Figure<string>>>;
|
|
29
|
+
/** Other facts, each with its own source. */
|
|
30
|
+
more: Readonly<Record<string, Figure<number | string>>>;
|
|
31
|
+
}
|
|
32
|
+
export interface Variant {
|
|
33
|
+
/** "v1", "v2" and so on; a label for what is not a numbered version. */
|
|
34
|
+
id: string;
|
|
35
|
+
rocket: string;
|
|
36
|
+
/** What it is called, in a line: "Block 3 (V3)". */
|
|
37
|
+
name: string;
|
|
38
|
+
status: VariantStatus;
|
|
39
|
+
/** When this variant, in this form, first flew. Its `brief` says so when the form is not the block's first. */
|
|
40
|
+
firstFlight: Figure<string>;
|
|
41
|
+
/** The parts this variant has, in stacking order from the bottom. */
|
|
42
|
+
slots: readonly string[];
|
|
43
|
+
facts: VariantFacts;
|
|
44
|
+
/** A slot's part when it is not this variant's own: a part shared with another rocket. */
|
|
45
|
+
partIds?: Readonly<Record<string, string>>;
|
|
46
|
+
/** What a reader should be told about it. */
|
|
47
|
+
note?: string;
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const blank = (note, brief) => ({ value: null, source: null, ...(note ? { note } : {}), ...(brief ? { brief } : {}) });
|
|
2
|
+
/** A figure as words for a table: its value and whatever must stay beside it; a blank says why, or that no source gives it. */
|
|
3
|
+
export function figureWords(figure, unit = "") {
|
|
4
|
+
if (!figure || figure.value === null)
|
|
5
|
+
return figure?.brief ?? "No source";
|
|
6
|
+
const value = typeof figure.value === "number" ? `${figure.value}${unit}` : figure.value;
|
|
7
|
+
return figure.brief ? `${value}, ${figure.brief}` : value;
|
|
8
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Angle, type Vec3 } from "../framing.ts";
|
|
2
|
+
import { type LookName } from "../looks.ts";
|
|
3
|
+
import type { Bounds, Piece } from "../model/pieces.ts";
|
|
4
|
+
type Point = [number, number];
|
|
5
|
+
/** A camera over a picture: perspective by default, or orthographic at a fixed scale for drawings side by side. */
|
|
6
|
+
export interface Camera {
|
|
7
|
+
project: (p: Vec3) => Point;
|
|
8
|
+
/** Depth along the view, far is larger. */
|
|
9
|
+
depth: (p: Vec3) => number;
|
|
10
|
+
/** The unit vector from a point towards the camera. */
|
|
11
|
+
toward: (p: Vec3) => Vec3;
|
|
12
|
+
above: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function perspective(bounds: Bounds, angle: Angle, width?: number, height?: number): Camera;
|
|
15
|
+
/** Orthographic, `scale` pixels a metre, the origin at (x0, y0) on the picture. */
|
|
16
|
+
export declare function orthographic(angle: Angle, scale: number, x0: number, y0: number): Camera;
|
|
17
|
+
export interface DrawOptions {
|
|
18
|
+
look: LookName;
|
|
19
|
+
/** Weights scale with the drawing's size: 1 at the standard picture. */
|
|
20
|
+
weight?: number;
|
|
21
|
+
}
|
|
22
|
+
/** One part's picture at `at`, as SVG text in painter's order. */
|
|
23
|
+
export declare function drawPart(pieces: readonly Piece[], at: Vec3, cam: Camera, look: LookName, weight?: number): string;
|
|
24
|
+
/** The picture's gradients and, on the tiled side, a mosaic of hexagons, for a look. */
|
|
25
|
+
export declare function defsOf(look: LookName): string;
|
|
26
|
+
export {};
|
package/dist/draw/svg.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// The drawings: dark gradient faces, a light outline, a soft halo. Each is made from the same pieces as the mesh, seen from the very camera the 3D view uses, so the drawing turns into the 3D view without a
|
|
2
|
+
// jump. Painter's order: the body first, what hangs under it and what stands behind it by its own depth. Pure: no three.js and no DOM.
|
|
3
|
+
import { axesOf, directionOf, frameFor, PICTURE_H, PICTURE_W, FOV_DEG, deg } from "../framing.js";
|
|
4
|
+
import { LOOKS } from "../looks.js";
|
|
5
|
+
import { MATERIALS } from "../model/materials.js";
|
|
6
|
+
import { pointOf, radiusAt } from "../model/pieces.js";
|
|
7
|
+
const n = (v) => (Math.round(v * 10) / 10).toString();
|
|
8
|
+
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
9
|
+
const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
10
|
+
function hull(points) {
|
|
11
|
+
const p = [...points].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
12
|
+
if (p.length < 3)
|
|
13
|
+
return p;
|
|
14
|
+
const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
15
|
+
const half = (list) => {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const q of list) {
|
|
18
|
+
while (out.length >= 2 && cross(out[out.length - 2], out[out.length - 1], q) <= 0)
|
|
19
|
+
out.pop();
|
|
20
|
+
out.push(q);
|
|
21
|
+
}
|
|
22
|
+
out.pop();
|
|
23
|
+
return out;
|
|
24
|
+
};
|
|
25
|
+
return [...half(p), ...half([...p].reverse())];
|
|
26
|
+
}
|
|
27
|
+
const pathOf = (pts, close) => `M${pts.map(([x, y]) => `${n(x)} ${n(y)}`).join("L")}${close ? "Z" : ""}`;
|
|
28
|
+
export function perspective(bounds, angle, width = PICTURE_W, height = PICTURE_H) {
|
|
29
|
+
const { target, position } = frameFor(bounds.min, bounds.max, angle, width / height);
|
|
30
|
+
const { right, up, forward } = axesOf(directionOf(angle));
|
|
31
|
+
const f = 1 / Math.tan(deg(FOV_DEG) / 2);
|
|
32
|
+
return {
|
|
33
|
+
project: (p) => {
|
|
34
|
+
const q = sub(p, position);
|
|
35
|
+
const z = dot(q, forward);
|
|
36
|
+
return [width / 2 + (dot(q, right) * f * (height / 2)) / z, height / 2 - (dot(q, up) * f * (height / 2)) / z];
|
|
37
|
+
},
|
|
38
|
+
depth: (p) => dot(sub(p, position), forward),
|
|
39
|
+
toward: (p) => {
|
|
40
|
+
const q = sub(position, p);
|
|
41
|
+
const l = Math.hypot(...q) || 1;
|
|
42
|
+
return [q[0] / l, q[1] / l, q[2] / l];
|
|
43
|
+
},
|
|
44
|
+
above: position[1] > target[1] - 1e9 && angle.el >= 0,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Orthographic, `scale` pixels a metre, the origin at (x0, y0) on the picture. */
|
|
48
|
+
export function orthographic(angle, scale, x0, y0) {
|
|
49
|
+
const direction = directionOf(angle);
|
|
50
|
+
const { right, up, forward } = axesOf(direction);
|
|
51
|
+
return {
|
|
52
|
+
project: (p) => [x0 + dot(p, right) * scale, y0 - dot(p, up) * scale],
|
|
53
|
+
depth: (p) => dot(p, forward),
|
|
54
|
+
toward: () => direction,
|
|
55
|
+
above: angle.el >= 0,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const fillId = (material) => MATERIALS[material].fill;
|
|
59
|
+
function latheItem(piece, cam, at, style, list) {
|
|
60
|
+
const [cx, cz] = piece.at ?? [0, 0];
|
|
61
|
+
const profile = piece.profile;
|
|
62
|
+
const steps = piece.segs > 24 ? 36 : 14;
|
|
63
|
+
const here = (r, y, a) => [at[0] + cx + r * Math.sin(a), at[1] + y, at[2] + cz + r * Math.cos(a)];
|
|
64
|
+
const middleY = (profile[0][1] + profile[profile.length - 1][1]) / 2;
|
|
65
|
+
const middle = [at[0] + cx, at[1] + middleY, at[2] + cz];
|
|
66
|
+
const axisHere = [at[0], at[1] + middleY, at[2]];
|
|
67
|
+
const parts = [];
|
|
68
|
+
const fill = `url(#${fillId(piece.material)})`;
|
|
69
|
+
// which way a station's surface faces, for what shows
|
|
70
|
+
const tilt = profile.map(([r, y], i) => {
|
|
71
|
+
const [r1, y1] = profile[Math.min(i + 1, profile.length - 1)];
|
|
72
|
+
const [r0, y0] = profile[Math.max(i - 1, 0)];
|
|
73
|
+
const dr = r1 - r0;
|
|
74
|
+
const dy = y1 - y0;
|
|
75
|
+
const l = Math.hypot(dr, dy) || 1;
|
|
76
|
+
return { r, y, nr: dy / l, ny: -dr / l };
|
|
77
|
+
});
|
|
78
|
+
const faces = (i, a) => {
|
|
79
|
+
const p = here(i.r, i.y, a);
|
|
80
|
+
const t = cam.toward(p);
|
|
81
|
+
return i.nr * Math.sin(a) * t[0] + i.ny * t[1] + i.nr * Math.cos(a) * t[2] > 0;
|
|
82
|
+
};
|
|
83
|
+
if (piece.arc) {
|
|
84
|
+
// an arc of a hull: from station to station, only the part that faces the camera
|
|
85
|
+
const left = [];
|
|
86
|
+
const right = [];
|
|
87
|
+
const leftEdge = [];
|
|
88
|
+
const rightEdge = [];
|
|
89
|
+
for (const station of tilt) {
|
|
90
|
+
const [from, to] = piece.arc(station.y);
|
|
91
|
+
let lo = null;
|
|
92
|
+
let hi = null;
|
|
93
|
+
const count = 48;
|
|
94
|
+
for (let k = 0; k <= count; k++) {
|
|
95
|
+
const a = from + ((to - from) * k) / count;
|
|
96
|
+
if (faces(station, a)) {
|
|
97
|
+
if (lo === null)
|
|
98
|
+
lo = a;
|
|
99
|
+
hi = a;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (lo === null || hi === null) {
|
|
103
|
+
leftEdge.push(null);
|
|
104
|
+
rightEdge.push(null);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
left.push(cam.project(here(station.r, station.y, lo)));
|
|
108
|
+
right.push(cam.project(here(station.r, station.y, hi)));
|
|
109
|
+
// an edge shows only where it is the arc's own edge, not where the hull turns away
|
|
110
|
+
leftEdge.push(lo > from + 1e-6 ? null : cam.project(here(station.r, station.y, lo)));
|
|
111
|
+
rightEdge.push(hi < to - 1e-6 ? null : cam.project(here(station.r, station.y, hi)));
|
|
112
|
+
}
|
|
113
|
+
if (left.length > 1) {
|
|
114
|
+
const shape = pathOf([...left, ...right.reverse()], true);
|
|
115
|
+
parts.push(`<path d="${shape}" fill="${fill}"/>`);
|
|
116
|
+
if (piece.material === "tile")
|
|
117
|
+
parts.push(`<path d="${shape}" fill="url(#hex)"/>`);
|
|
118
|
+
for (const edge of [leftEdge, rightEdge]) {
|
|
119
|
+
let run = [];
|
|
120
|
+
const flush = () => {
|
|
121
|
+
if (run.length > 1)
|
|
122
|
+
parts.push(`<path d="${pathOf(run, false)}" fill="none" stroke="${style.line}" stroke-width="${n(style.feature)}"/>`);
|
|
123
|
+
run = [];
|
|
124
|
+
};
|
|
125
|
+
for (const p of edge) {
|
|
126
|
+
if (p)
|
|
127
|
+
run.push(p);
|
|
128
|
+
else
|
|
129
|
+
flush();
|
|
130
|
+
}
|
|
131
|
+
flush();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else if (piece.outline !== false) {
|
|
136
|
+
const pts = [];
|
|
137
|
+
for (const [r, y] of profile)
|
|
138
|
+
for (let k = 0; k < steps; k++)
|
|
139
|
+
pts.push(cam.project(here(r, y, (k / steps) * 2 * Math.PI)));
|
|
140
|
+
const shape = hull(pts);
|
|
141
|
+
parts.push(`<path d="${pathOf(shape, true)}" fill="${fill}"${piece.outline ? ` stroke="${style.line}" stroke-width="${n(style.outline * (piece.under ? 0.55 : 1))}"` : ""}/>`);
|
|
142
|
+
}
|
|
143
|
+
// rings: the front of each
|
|
144
|
+
const ringsOf = (ys, width, opacity) => {
|
|
145
|
+
if (!ys?.length)
|
|
146
|
+
return;
|
|
147
|
+
let d = "";
|
|
148
|
+
for (const y of ys) {
|
|
149
|
+
const i = profile.findIndex(([, yy], k) => k + 1 < profile.length && y >= Math.min(yy, profile[k + 1][1]) && y <= Math.max(yy, profile[k + 1][1]));
|
|
150
|
+
if (i < 0)
|
|
151
|
+
continue;
|
|
152
|
+
const r = radiusAt(profile, y);
|
|
153
|
+
const [r1, y1] = profile[i + 1];
|
|
154
|
+
const [r0, y0] = profile[i];
|
|
155
|
+
const l = Math.hypot(r1 - r0, y1 - y0) || 1;
|
|
156
|
+
const station = { r, y, nr: (y1 - y0) / l, ny: -(r1 - r0) / l };
|
|
157
|
+
const [from, to] = piece.arc ? piece.arc(y) : [0, 2 * Math.PI];
|
|
158
|
+
const count = 20;
|
|
159
|
+
let run = [];
|
|
160
|
+
const flush = () => {
|
|
161
|
+
if (run.length > 1)
|
|
162
|
+
d += pathOf(run, false);
|
|
163
|
+
run = [];
|
|
164
|
+
};
|
|
165
|
+
for (let k = 0; k <= count; k++) {
|
|
166
|
+
const a = from + ((to - from) * k) / count;
|
|
167
|
+
if (faces(station, a))
|
|
168
|
+
run.push(cam.project(here(r, y, a)));
|
|
169
|
+
else
|
|
170
|
+
flush();
|
|
171
|
+
}
|
|
172
|
+
flush();
|
|
173
|
+
}
|
|
174
|
+
if (d)
|
|
175
|
+
parts.push(`<path d="${d}" fill="none" stroke="${style.line}" stroke-width="${n(width)}"${opacity ?? ""}/>`);
|
|
176
|
+
};
|
|
177
|
+
ringsOf(piece.welds, style.weld, ' opacity=".6"');
|
|
178
|
+
ringsOf(piece.rings, style.feature);
|
|
179
|
+
if (!parts.length)
|
|
180
|
+
return;
|
|
181
|
+
// the body's own pieces: far end first
|
|
182
|
+
const key = piece.body ? (cam.depth(middle) - cam.depth([at[0], at[1], at[2]])) * 1e-3 : cam.depth(middle) - cam.depth(axisHere);
|
|
183
|
+
list.push({ key: piece.under ? (cam.above ? 1e6 + key : -1e6 + key - middleY * 0.01) : key, svg: parts.join("") });
|
|
184
|
+
}
|
|
185
|
+
function plateItem(piece, cam, at, style, list) {
|
|
186
|
+
const place = (a, b, c) => {
|
|
187
|
+
const p = pointOf(piece.frame, a, b, c);
|
|
188
|
+
return [p[0] + at[0], p[1] + at[1], p[2] + at[2]];
|
|
189
|
+
};
|
|
190
|
+
const half = piece.thickness / 2;
|
|
191
|
+
const pts = [];
|
|
192
|
+
for (const [a, b] of piece.outline)
|
|
193
|
+
pts.push(cam.project(place(a, b, half)), cam.project(place(a, b, -half)));
|
|
194
|
+
const shape = hull(pts);
|
|
195
|
+
const centre = piece.outline.reduce((s, p) => [s[0] + p[0] / piece.outline.length, s[1] + p[1] / piece.outline.length], [0, 0]);
|
|
196
|
+
const c = place(centre[0], centre[1], 0);
|
|
197
|
+
const seen = cam.toward(c);
|
|
198
|
+
const facing = dot(piece.frame.w, seen);
|
|
199
|
+
const faceMat = facing > 0 && piece.top ? piece.top : piece.material;
|
|
200
|
+
const parts = [`<path d="${pathOf(shape, true)}" fill="url(#${fillId(faceMat)})" stroke="${style.line}" stroke-width="${n(piece.plain ? style.weld : style.feature * 1.1)}"/>`];
|
|
201
|
+
if (piece.ribs?.length && Math.abs(facing) > 0.35) {
|
|
202
|
+
const side = facing > 0 ? half : -half;
|
|
203
|
+
let d = "";
|
|
204
|
+
for (const [p, q] of piece.ribs)
|
|
205
|
+
d += pathOf([cam.project(place(p[0], p[1], side)), cam.project(place(q[0], q[1], side))], false);
|
|
206
|
+
parts.push(`<path d="${d}" fill="none" stroke="${style.line}" stroke-width="${n(style.weld)}"/>`);
|
|
207
|
+
}
|
|
208
|
+
const axisHere = [at[0], c[1], at[2]];
|
|
209
|
+
list.push({ key: cam.depth(c) - cam.depth(axisHere), svg: parts.join("") });
|
|
210
|
+
}
|
|
211
|
+
function linesItem(piece, cam, at, style, list) {
|
|
212
|
+
let d = "";
|
|
213
|
+
for (const path of piece.paths) {
|
|
214
|
+
const pts = path.map(([x, y, z]) => [x + at[0], y + at[1], z + at[2]]);
|
|
215
|
+
// a line round a hull: only the runs that face the camera
|
|
216
|
+
let run = [];
|
|
217
|
+
const flush = () => {
|
|
218
|
+
if (run.length > 1)
|
|
219
|
+
d += pathOf(run, false);
|
|
220
|
+
run = [];
|
|
221
|
+
};
|
|
222
|
+
for (const p of pts) {
|
|
223
|
+
const out = [p[0] - at[0], 0, p[2] - at[2]];
|
|
224
|
+
const t = cam.toward(p);
|
|
225
|
+
if (dot(out, t) > 0)
|
|
226
|
+
run.push(cam.project(p));
|
|
227
|
+
else
|
|
228
|
+
flush();
|
|
229
|
+
}
|
|
230
|
+
flush();
|
|
231
|
+
}
|
|
232
|
+
if (!d)
|
|
233
|
+
return;
|
|
234
|
+
list.push({ key: -1e3, svg: `<path d="${d}" fill="none" stroke="${style.line}" stroke-width="${n(piece.weight === "feature" ? style.feature : style.weld)}"/>` });
|
|
235
|
+
}
|
|
236
|
+
/** One part's picture at `at`, as SVG text in painter's order. */
|
|
237
|
+
export function drawPart(pieces, at, cam, look, weight = 1) {
|
|
238
|
+
const base = LOOKS[look];
|
|
239
|
+
const style = { line: base.line, outline: base.outline * weight, feature: base.feature * weight, weld: base.weld * weight };
|
|
240
|
+
const items = [];
|
|
241
|
+
for (const piece of pieces) {
|
|
242
|
+
if (piece.only === "mesh")
|
|
243
|
+
continue;
|
|
244
|
+
if (piece.kind === "lathe")
|
|
245
|
+
latheItem(piece, cam, at, style, items);
|
|
246
|
+
else if (piece.kind === "plate")
|
|
247
|
+
plateItem(piece, cam, at, style, items);
|
|
248
|
+
else
|
|
249
|
+
linesItem(piece, cam, at, style, items);
|
|
250
|
+
}
|
|
251
|
+
// far first
|
|
252
|
+
items.sort((a, b) => b.key - a.key);
|
|
253
|
+
return items.map((item) => item.svg).join("");
|
|
254
|
+
}
|
|
255
|
+
const HEX_W = 9;
|
|
256
|
+
const HEX_H = 15.6;
|
|
257
|
+
/** The picture's gradients and, on the tiled side, a mosaic of hexagons, for a look. */
|
|
258
|
+
export function defsOf(look) {
|
|
259
|
+
const { fills, halo } = LOOKS[look];
|
|
260
|
+
const stops = ([a, b, c]) => `<stop offset="0" stop-color="${a}"/><stop offset=".55" stop-color="${b}"/><stop offset="1" stop-color="${c}"/>`;
|
|
261
|
+
return `<defs>${Object.keys(fills).map((name) => `<linearGradient id="${name}" x1="0" y1="0" x2="1" y2="0">${stops(fills[name])}</linearGradient>`).join("")}<pattern id="hex" width="${HEX_W}" height="${HEX_H}" patternUnits="userSpaceOnUse"><path d="M4.5 0L9 2.6V7.8L4.5 10.4L0 7.8V2.6ZM4.5 10.4V15.6M0 2.6V0M9 2.6V0" fill="none" stroke="${look === "painted" ? "#3a3d42" : LOOKS[look].line}" stroke-width=".35" opacity=".5"/></pattern>${halo ? '<radialGradient id="halo" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#6cd0ff" stop-opacity=".14"/><stop offset="1" stop-color="#6cd0ff" stop-opacity="0"/></radialGradient>' : ""}</defs>`;
|
|
262
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function canvas(w: number, h: number): HTMLCanvasElement;
|
|
2
|
+
/** One repeat of a stainless hull: panels of slightly different tone, with weld lines between them. Its colour multiplies the steel's. `across` panels wide and `down` rings tall. */
|
|
3
|
+
export declare function paintSteel(across: number, down: number): HTMLCanvasElement;
|
|
4
|
+
/** A mosaic of hexagonal tiles in black with hair-thin gaps. `across` flats wide in one repeat. */
|
|
5
|
+
export declare function paintTiles(across: number): HTMLCanvasElement;
|
|
6
|
+
/** A soft round glow: the pool of light under a rocket. */
|
|
7
|
+
export declare function paintGlow(inner: string, outer: string): HTMLCanvasElement;
|
|
8
|
+
/** A day's sky for reflections, wrapped round a sphere: bright to the horizon, a pale ground, and tall dark and bright streaks, which is what gives a steel hull its long vertical sheen. */
|
|
9
|
+
export declare function paintSky(): HTMLCanvasElement;
|