@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
package/dist/fit.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Does this rocket go together? One description, checked against the parts' records: only the joints and sizes matter, wherever a part came from. Pure, so a form can ask it on every change; the builder
|
|
2
|
+
// asks it first and refuses what does not fit. A rocket labelled as a real version uses only parts true for it; anything else is a custom build and says so.
|
|
3
|
+
import { MODEL_CREDITS } from "./data/credits.js";
|
|
4
|
+
import { partEntry } from "./model/registry.js";
|
|
5
|
+
import { partIdOf, ROCKETS, variantOf } from "./rockets.js";
|
|
6
|
+
const TOLERANCE = 0.1;
|
|
7
|
+
/** The whole height, joint to joint up the stack, and the widest part. */
|
|
8
|
+
function sizeOf(parts) {
|
|
9
|
+
let y = 0;
|
|
10
|
+
let top = 0;
|
|
11
|
+
for (const { part } of parts) {
|
|
12
|
+
if (part.strapOn) {
|
|
13
|
+
top = Math.max(top, part.height);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const base = y - (part.joints.bottom?.y ?? 0);
|
|
17
|
+
top = Math.max(top, base + part.height);
|
|
18
|
+
y = base + (part.joints.top ? part.joints.top.y : part.height);
|
|
19
|
+
}
|
|
20
|
+
return { height: Math.round(top * 1000) / 1000, diameter: Math.max(...parts.map(({ part }) => part.diameter)) };
|
|
21
|
+
}
|
|
22
|
+
export function check(description) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
const fail = (code, message) => issues.push({ code, level: "error", message });
|
|
25
|
+
const done = (variant, parts, custom) => {
|
|
26
|
+
const ok = !issues.some((issue) => issue.level === "error");
|
|
27
|
+
const used = new Map();
|
|
28
|
+
for (const { part } of parts)
|
|
29
|
+
if (part.painted) {
|
|
30
|
+
if (!used.has(part.painted.credit))
|
|
31
|
+
used.set(part.painted.credit, []);
|
|
32
|
+
used.get(part.painted.credit).push(part.name);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
ok,
|
|
36
|
+
issues,
|
|
37
|
+
variant,
|
|
38
|
+
parts,
|
|
39
|
+
custom,
|
|
40
|
+
label: custom ? `Custom build, not a vehicle that flew: ${parts.map(({ part }) => part.name).join(" with ")}` : (variant?.name ?? ""),
|
|
41
|
+
size: ok ? sizeOf(parts) : null,
|
|
42
|
+
credits: [...used].map(([id, usedFor]) => ({ ...MODEL_CREDITS[id], id, usedFor })),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
if (!Object.hasOwn(ROCKETS, description.rocket)) {
|
|
46
|
+
fail("unknown-rocket", `There is no rocket "${description.rocket}" to build.`);
|
|
47
|
+
return done(null, [], false);
|
|
48
|
+
}
|
|
49
|
+
const variant = variantOf(description.rocket, description.variant);
|
|
50
|
+
if (!variant) {
|
|
51
|
+
fail("unknown-variant", `${ROCKETS[description.rocket].name} has no variant "${description.variant}".`);
|
|
52
|
+
return done(null, [], false);
|
|
53
|
+
}
|
|
54
|
+
const named = description.parts ?? {};
|
|
55
|
+
for (const slot of Object.keys(named))
|
|
56
|
+
if (!variant.slots.includes(slot))
|
|
57
|
+
fail("unknown-slot", `${variant.name} has no slot "${slot}"; it has ${variant.slots.join(" and ")}.`);
|
|
58
|
+
let custom = false;
|
|
59
|
+
const parts = [];
|
|
60
|
+
for (const slot of variant.slots) {
|
|
61
|
+
const id = Object.hasOwn(named, slot) ? named[slot] : partIdOf(variant.rocket, variant.id, slot);
|
|
62
|
+
const entry = partEntry(id);
|
|
63
|
+
if (!entry) {
|
|
64
|
+
fail("unknown-part", `There is no part "${id}".`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const home = partEntry(partIdOf(variant.rocket, variant.id, slot));
|
|
68
|
+
if (home && entry.record.kind !== home.record.kind)
|
|
69
|
+
fail("wrong-kind", `${entry.record.name} is a ${entry.record.kind}; this slot takes a ${home.record.kind}.`);
|
|
70
|
+
if (!entry.record.trueTo.includes(variant.id))
|
|
71
|
+
custom = true;
|
|
72
|
+
parts.push({ slot, part: entry.record });
|
|
73
|
+
}
|
|
74
|
+
// a side booster stands beside the stack, so the joints that must meet are those of the rest
|
|
75
|
+
const stacked = parts.filter(({ part }) => !part.strapOn);
|
|
76
|
+
for (let i = 0; i + 1 < stacked.length; i++) {
|
|
77
|
+
const below = stacked[i].part;
|
|
78
|
+
const above = stacked[i + 1].part;
|
|
79
|
+
const top = below.joints.top;
|
|
80
|
+
const bottom = above.joints.bottom;
|
|
81
|
+
if (!top || !bottom) {
|
|
82
|
+
fail("no-joint", `${below.name} and ${above.name} have no joint to meet at.`);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (top.kind !== bottom.kind)
|
|
86
|
+
fail("joint-kind", `${below.name} ends in a ${top.kind} joint and ${above.name} starts with a ${bottom.kind} joint.`);
|
|
87
|
+
if (Math.abs(top.diameter - bottom.diameter) > TOLERANCE)
|
|
88
|
+
fail("joint-diameter", `${below.name} is ${top.diameter} m across at the joint and ${above.name} is ${bottom.diameter} m.`);
|
|
89
|
+
}
|
|
90
|
+
if (custom)
|
|
91
|
+
issues.push({ code: "custom", level: "note", message: "A part here is not true for this variant, so this is a custom build, not a vehicle that flew." });
|
|
92
|
+
return done(variant, parts, custom);
|
|
93
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type Vec3 = [number, number, number];
|
|
2
|
+
/** The camera's field of view, in degrees, top to bottom. Narrow, because a rocket is long. */
|
|
3
|
+
export declare const FOV_DEG = 24;
|
|
4
|
+
/** The picture's shape. */
|
|
5
|
+
export declare const PICTURE_W = 480;
|
|
6
|
+
export declare const PICTURE_H = 640;
|
|
7
|
+
export declare const VIEW_NAMES: readonly ["three-quarter", "front", "back", "side", "below"];
|
|
8
|
+
export type ViewName = (typeof VIEW_NAMES)[number];
|
|
9
|
+
export declare const DEFAULT_VIEW: ViewName;
|
|
10
|
+
/** An angle to look from: `az` degrees round from the windward side towards +X, `el` degrees above the horizon. Front is the windward, tiled side; back is the steel side. */
|
|
11
|
+
export interface Angle {
|
|
12
|
+
az: number;
|
|
13
|
+
el: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const VIEWS: Readonly<Record<ViewName, Angle>>;
|
|
16
|
+
export declare const cleanView: (value: unknown) => ViewName;
|
|
17
|
+
export declare const deg: (d: number) => number;
|
|
18
|
+
/** The unit vector from what is looked at towards the camera. */
|
|
19
|
+
export declare function directionOf({ az, el }: Angle): Vec3;
|
|
20
|
+
export interface Framing {
|
|
21
|
+
target: Vec3;
|
|
22
|
+
position: Vec3;
|
|
23
|
+
distance: number;
|
|
24
|
+
}
|
|
25
|
+
/** The camera's own axes for looking along `direction`, with up as near to +Y as it can be. */
|
|
26
|
+
export declare function axesOf(direction: Vec3): {
|
|
27
|
+
right: Vec3;
|
|
28
|
+
up: Vec3;
|
|
29
|
+
forward: Vec3;
|
|
30
|
+
};
|
|
31
|
+
/** The nearest camera that holds the whole of `min` to `max` in a picture `aspect` wide for 1 tall, from `angle`. */
|
|
32
|
+
export declare function frameFor(min: Vec3, max: Vec3, angle: Angle, aspect: number, margin?: number): Framing;
|
package/dist/framing.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Where the camera stands. One rule for the drawing and the 3D view, so the drawing turns into the 3D view without a jump. Pure, in metres.
|
|
2
|
+
/** The camera's field of view, in degrees, top to bottom. Narrow, because a rocket is long. */
|
|
3
|
+
export const FOV_DEG = 24;
|
|
4
|
+
/** The picture's shape. */
|
|
5
|
+
export const PICTURE_W = 480;
|
|
6
|
+
export const PICTURE_H = 640;
|
|
7
|
+
export const VIEW_NAMES = ["three-quarter", "front", "back", "side", "below"];
|
|
8
|
+
export const DEFAULT_VIEW = "three-quarter";
|
|
9
|
+
export const VIEWS = {
|
|
10
|
+
"three-quarter": { az: 32, el: 9 },
|
|
11
|
+
front: { az: 0, el: 3 },
|
|
12
|
+
back: { az: 180, el: 3 },
|
|
13
|
+
side: { az: 90, el: 3 },
|
|
14
|
+
below: { az: 24, el: -70 },
|
|
15
|
+
};
|
|
16
|
+
export const cleanView = (value) => (VIEW_NAMES.includes(value) ? value : DEFAULT_VIEW);
|
|
17
|
+
export const deg = (d) => (d * Math.PI) / 180;
|
|
18
|
+
/** The unit vector from what is looked at towards the camera. */
|
|
19
|
+
export function directionOf({ az, el }) {
|
|
20
|
+
const c = Math.cos(deg(el));
|
|
21
|
+
return [c * Math.sin(deg(az)), Math.sin(deg(el)), c * Math.cos(deg(az))];
|
|
22
|
+
}
|
|
23
|
+
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
24
|
+
const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
25
|
+
const unit = (a) => {
|
|
26
|
+
const l = Math.hypot(...a) || 1;
|
|
27
|
+
return [a[0] / l, a[1] / l, a[2] / l];
|
|
28
|
+
};
|
|
29
|
+
/** The camera's own axes for looking along `direction`, with up as near to +Y as it can be. */
|
|
30
|
+
export function axesOf(direction) {
|
|
31
|
+
const forward = [-direction[0], -direction[1], -direction[2]];
|
|
32
|
+
const near = Math.abs(forward[1]) > 0.999;
|
|
33
|
+
const right = unit(cross(forward, near ? [0, 0, -1] : [0, 1, 0]));
|
|
34
|
+
return { right, up: cross(right, forward), forward };
|
|
35
|
+
}
|
|
36
|
+
/** The nearest camera that holds the whole of `min` to `max` in a picture `aspect` wide for 1 tall, from `angle`. */
|
|
37
|
+
export function frameFor(min, max, angle, aspect, margin = 1.08) {
|
|
38
|
+
const target = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
|
|
39
|
+
const direction = directionOf(angle);
|
|
40
|
+
const { right, up, forward } = axesOf(direction);
|
|
41
|
+
const tanV = Math.tan(deg(FOV_DEG) / 2);
|
|
42
|
+
const tanH = tanV * aspect;
|
|
43
|
+
let distance = 0;
|
|
44
|
+
for (const x of [min[0], max[0]])
|
|
45
|
+
for (const y of [min[1], max[1]])
|
|
46
|
+
for (const z of [min[2], max[2]]) {
|
|
47
|
+
const q = [x - target[0], y - target[1], z - target[2]];
|
|
48
|
+
const depth = dot(q, forward);
|
|
49
|
+
distance = Math.max(distance, (Math.abs(dot(q, right)) * margin) / tanH - depth, (Math.abs(dot(q, up)) * margin) / tanV - depth);
|
|
50
|
+
}
|
|
51
|
+
return { target, distance, position: [target[0] + direction[0] * distance, target[1] + direction[1] * distance, target[2] + direction[2] * distance] };
|
|
52
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { PLANNED_ROCKETS, ROCKETS, partIdOf, SLOT_NAMES, variantOf } from "./rockets.ts";
|
|
2
|
+
export type { PlannedRocket, RocketEntry, RocketStatus } from "./rockets.ts";
|
|
3
|
+
export { blank, figureWords } from "./data/types.ts";
|
|
4
|
+
export type { Disagreement, Figure, Variant, VariantFacts, VariantStatus } from "./data/types.ts";
|
|
5
|
+
export { SOURCES } from "./data/sources.ts";
|
|
6
|
+
export type { Source, SourceId } from "./data/sources.ts";
|
|
7
|
+
export { creditLine, MODEL_CREDITS } from "./data/credits.ts";
|
|
8
|
+
export type { CreditId, ModelCredit } from "./data/credits.ts";
|
|
9
|
+
export type { Joint, PartKind, PartRecord } from "./parts.ts";
|
|
10
|
+
export { PART_ENTRIES, partEntry } from "./model/registry.ts";
|
|
11
|
+
export { check } from "./fit.ts";
|
|
12
|
+
export type { Description, FitCode, FitIssue, FitReport, SlotPart } from "./fit.ts";
|
|
13
|
+
export { axesOf, cleanView, DEFAULT_VIEW, directionOf, FOV_DEG, frameFor, PICTURE_H, PICTURE_W, VIEW_NAMES, VIEWS } from "./framing.ts";
|
|
14
|
+
export type { Angle, Framing, Vec3, ViewName } from "./framing.ts";
|
|
15
|
+
export { cleanLook, DEFAULT_LOOK, LOOK_NAMES, LOOKS } from "./looks.ts";
|
|
16
|
+
export type { Look, LookName } from "./looks.ts";
|
|
17
|
+
export { cleanShow, placesFor, showsOf } from "./layout.ts";
|
|
18
|
+
export type { Place, ShowName } from "./layout.ts";
|
|
19
|
+
export { CROSSFADE_MS, DRAWING_HOLD_MS, REDUCED_FADE_MS, revealDelay } from "./crossfade.ts";
|
|
20
|
+
export { tiltMode, tiltTurn } from "./tilt.ts";
|
|
21
|
+
export type { Reading, TiltMode } from "./tilt.ts";
|
|
22
|
+
export { MATERIALS } from "./model/materials.ts";
|
|
23
|
+
export type { MaterialName, MaterialSpec } from "./model/materials.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The pure core: the rockets and their variants with sourced sizes, the parts and how they fit, the camera rule, the looks and the layout. No three.js, no DOM.
|
|
2
|
+
export { PLANNED_ROCKETS, ROCKETS, partIdOf, SLOT_NAMES, variantOf } from "./rockets.js";
|
|
3
|
+
export { blank, figureWords } from "./data/types.js";
|
|
4
|
+
export { SOURCES } from "./data/sources.js";
|
|
5
|
+
export { creditLine, MODEL_CREDITS } from "./data/credits.js";
|
|
6
|
+
export { PART_ENTRIES, partEntry } from "./model/registry.js";
|
|
7
|
+
export { check } from "./fit.js";
|
|
8
|
+
export { axesOf, cleanView, DEFAULT_VIEW, directionOf, FOV_DEG, frameFor, PICTURE_H, PICTURE_W, VIEW_NAMES, VIEWS } from "./framing.js";
|
|
9
|
+
export { cleanLook, DEFAULT_LOOK, LOOK_NAMES, LOOKS } from "./looks.js";
|
|
10
|
+
export { cleanShow, placesFor, showsOf } from "./layout.js";
|
|
11
|
+
export { CROSSFADE_MS, DRAWING_HOLD_MS, REDUCED_FADE_MS, revealDelay } from "./crossfade.js";
|
|
12
|
+
export { tiltMode, tiltTurn } from "./tilt.js";
|
|
13
|
+
export { MATERIALS } from "./model/materials.js";
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Vec3 } from "./framing.ts";
|
|
2
|
+
import type { PartRecord } from "./parts.ts";
|
|
3
|
+
export type ShowName = "together" | "apart" | (string & {});
|
|
4
|
+
/** A part's own width from the axis, for keeping apart parts clear of each other. */
|
|
5
|
+
export type WidthOf = (part: PartRecord) => number;
|
|
6
|
+
export interface Place {
|
|
7
|
+
slot: string;
|
|
8
|
+
at: Vec3;
|
|
9
|
+
shown: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** The first choices of `show`, then one per slot. */
|
|
12
|
+
export declare const showsOf: (slots: readonly string[]) => string[];
|
|
13
|
+
export declare function cleanShow(value: unknown, slots: readonly string[]): string;
|
|
14
|
+
export declare function placesFor(parts: readonly {
|
|
15
|
+
slot: string;
|
|
16
|
+
part: PartRecord;
|
|
17
|
+
}[], show: string, widthOf: WidthOf): Place[];
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Seen square-on to the diagonal the parts take the whole distance; from the front or the side, seven tenths of it. */
|
|
2
|
+
const APART_SPREAD = 1.45;
|
|
3
|
+
/** The first choices of `show`, then one per slot. */
|
|
4
|
+
export const showsOf = (slots) => (slots.length > 1 ? ["together", "apart", ...slots] : [...slots]);
|
|
5
|
+
export function cleanShow(value, slots) {
|
|
6
|
+
const shows = showsOf(slots);
|
|
7
|
+
return typeof value === "string" && shows.includes(value) ? value : shows[0];
|
|
8
|
+
}
|
|
9
|
+
export function placesFor(parts, show, widthOf) {
|
|
10
|
+
if (show === "apart") {
|
|
11
|
+
const widths = parts.map(({ part }) => widthOf(part));
|
|
12
|
+
const total = widths.reduce((sum, width) => sum + width * 2, 0) + (parts.length - 1) * 3;
|
|
13
|
+
let x = -total / 2;
|
|
14
|
+
// along a diagonal of the ground, so they stand clear of each other from the front, the back and the side alike
|
|
15
|
+
return parts.map(({ slot }, i) => {
|
|
16
|
+
const along = (x + widths[i]) * APART_SPREAD;
|
|
17
|
+
const at = [along * Math.SQRT1_2, 0, -along * Math.SQRT1_2];
|
|
18
|
+
x += widths[i] * 2 + 3;
|
|
19
|
+
return { slot, at, shown: true };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// up the stack, joint to joint; a side booster stands on the ground beside it
|
|
23
|
+
let y = 0;
|
|
24
|
+
return parts.map(({ slot, part }) => {
|
|
25
|
+
const together = show === "together";
|
|
26
|
+
if (part.strapOn)
|
|
27
|
+
return { slot, at: [together ? part.strapOn.x : 0, 0, together ? part.strapOn.z : 0], shown: together || show === slot };
|
|
28
|
+
const base = y - (part.joints.bottom?.y ?? 0);
|
|
29
|
+
const at = [0, together ? base : 0, 0];
|
|
30
|
+
y = base + (part.joints.top ? part.joints.top.y : part.height);
|
|
31
|
+
return { slot, at, shown: together || show === slot };
|
|
32
|
+
});
|
|
33
|
+
}
|
package/dist/looks.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const LOOK_NAMES: readonly ["line", "thick", "painted"];
|
|
2
|
+
export type LookName = (typeof LOOK_NAMES)[number];
|
|
3
|
+
export declare const DEFAULT_LOOK: LookName;
|
|
4
|
+
export interface Look {
|
|
5
|
+
/** The colour of its lines. */
|
|
6
|
+
line: string;
|
|
7
|
+
/** Pixels wide in a 3D view: the outline and the lines between. */
|
|
8
|
+
outlinePx: number;
|
|
9
|
+
featurePx: number;
|
|
10
|
+
weldPx: number;
|
|
11
|
+
/** How much of the weld lines shows. */
|
|
12
|
+
weldOpacity: number;
|
|
13
|
+
/** The drawing's strokes, in its units. */
|
|
14
|
+
outline: number;
|
|
15
|
+
feature: number;
|
|
16
|
+
weld: number;
|
|
17
|
+
/** The drawing's fills, by material group. */
|
|
18
|
+
fills: Readonly<Record<FillName, readonly [string, string, string]>>;
|
|
19
|
+
/** The soft light behind the rocket in a drawing. */
|
|
20
|
+
halo: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type FillName = "hull" | "white" | "tile" | "metal" | "dark" | "bell";
|
|
23
|
+
export declare const LOOKS: Readonly<Record<LookName, Look>>;
|
|
24
|
+
/** A value from outside as a look's name: the default for anything else. */
|
|
25
|
+
export declare const cleanLook: (value: unknown) => LookName;
|
package/dist/looks.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The three looks, each one table entry: the line drawing, the thick lines, and the painted rocket. A look changes lines, colours and fills, never a shape. Pure.
|
|
2
|
+
export const LOOK_NAMES = ["line", "thick", "painted"];
|
|
3
|
+
export const DEFAULT_LOOK = "line";
|
|
4
|
+
const NIGHT = {
|
|
5
|
+
hull: ["#0c1422", "#2a3850", "#0c1422"],
|
|
6
|
+
white: ["#0e1626", "#33435f", "#0e1626"],
|
|
7
|
+
tile: ["#05080e", "#101827", "#05080e"],
|
|
8
|
+
metal: ["#16202f", "#3a475c", "#16202f"],
|
|
9
|
+
dark: ["#070b12", "#121a28", "#070b12"],
|
|
10
|
+
bell: ["#0a0f18", "#242f42", "#0a0f18"],
|
|
11
|
+
};
|
|
12
|
+
export const LOOKS = {
|
|
13
|
+
line: { line: "#8fa1b8", outlinePx: 1.4, featurePx: 1, weldPx: 0.6, weldOpacity: 0.45, outline: 1.2, feature: 0.8, weld: 0.45, fills: NIGHT, halo: true },
|
|
14
|
+
thick: { line: "#b4c2d4", outlinePx: 3.4, featurePx: 2, weldPx: 0.9, weldOpacity: 0.5, outline: 3.4, feature: 1.7, weld: 0.6, fills: NIGHT, halo: true },
|
|
15
|
+
painted: {
|
|
16
|
+
line: "#30343a",
|
|
17
|
+
outlinePx: 0,
|
|
18
|
+
featurePx: 1,
|
|
19
|
+
weldPx: 1,
|
|
20
|
+
weldOpacity: 0.28,
|
|
21
|
+
outline: 0.6,
|
|
22
|
+
feature: 0.5,
|
|
23
|
+
weld: 0.3,
|
|
24
|
+
fills: {
|
|
25
|
+
hull: ["#7f858c", "#e6e9ec", "#8d9399"],
|
|
26
|
+
white: ["#b9bab5", "#f4f4ef", "#c2c3be"],
|
|
27
|
+
tile: ["#0b0c0e", "#26282c", "#0b0c0e"],
|
|
28
|
+
metal: ["#5a5f66", "#a9afb6", "#5a5f66"],
|
|
29
|
+
dark: ["#111316", "#2a2d31", "#111316"],
|
|
30
|
+
bell: ["#141518", "#3c3f44", "#141518"],
|
|
31
|
+
},
|
|
32
|
+
halo: false,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/** A value from outside as a look's name: the default for anything else. */
|
|
36
|
+
export const cleanLook = (value) => (LOOK_NAMES.includes(value) ? value : DEFAULT_LOOK);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Piece } from "./pieces.ts";
|
|
2
|
+
/** A rocket engine from its exit up: bell, dark inside and a body. */
|
|
3
|
+
export declare function merlin(at: readonly [number, number], y: number, exit: number, length: number): Piece[];
|
|
4
|
+
export interface FirstStageDraw {
|
|
5
|
+
/** With its interstage. */
|
|
6
|
+
height: number;
|
|
7
|
+
interstage: number;
|
|
8
|
+
radius: number;
|
|
9
|
+
/** "octaweb": eight round one. "grid": three by three, as v1.0. */
|
|
10
|
+
engines: "octaweb" | "grid";
|
|
11
|
+
legs: boolean;
|
|
12
|
+
fins: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface FalconDraw {
|
|
15
|
+
first: FirstStageDraw;
|
|
16
|
+
second: {
|
|
17
|
+
height: number;
|
|
18
|
+
joint: number;
|
|
19
|
+
radius: number;
|
|
20
|
+
nozzle: number;
|
|
21
|
+
};
|
|
22
|
+
fairing: {
|
|
23
|
+
height: number;
|
|
24
|
+
radius: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare const FALCON_DRAWS: Readonly<Record<string, FalconDraw>>;
|
|
28
|
+
export declare const falconFirstStage: (id: string) => Piece[];
|
|
29
|
+
export declare const falconSecondStage: (id: string) => Piece[];
|
|
30
|
+
export declare const falconFairing: (id: string) => Piece[];
|
|
31
|
+
export interface DragonDraw {
|
|
32
|
+
capsule: number;
|
|
33
|
+
/** How far its heat shield nests into the trunk. */
|
|
34
|
+
nest: number;
|
|
35
|
+
trunk: number;
|
|
36
|
+
baseRadius: number;
|
|
37
|
+
trunkRadius: number;
|
|
38
|
+
windows: boolean;
|
|
39
|
+
wings: boolean;
|
|
40
|
+
fins: boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare const DRAGON_DRAWS: Readonly<Record<string, DragonDraw>>;
|
|
43
|
+
export declare function capsule(d: DragonDraw): Piece[];
|
|
44
|
+
export declare function trunk(d: DragonDraw): Piece[];
|
|
45
|
+
/** A side booster, with its nose cone in place of an upper stage. */
|
|
46
|
+
export declare function sideBooster(height: number, r: number, side: 1 | -1): Piece[];
|
|
47
|
+
export declare function centreCore(height: number, r: number): Piece[];
|
|
48
|
+
export declare function grasshopper(height: number): Piece[];
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Falcon 9, Falcon Heavy, Dragon and Grasshopper from tables of numbers. Heights and diameters are the sourced figures where there are any. How a part is drawn is not a published figure: Block 5, Dragon 2, Dragon 1, Falcon Heavy
|
|
2
|
+
// and Grasshopper take their proportions from their licensed models at the sourced sizes (see NOTICE.md), and the older Falcons from the sourced totals and the documented differences.
|
|
3
|
+
import { deg } from "../framing.js";
|
|
4
|
+
import { pointOf, radial, strut } from "./pieces.js";
|
|
5
|
+
import { gridFin } from "./starship.js";
|
|
6
|
+
/** A rocket engine from its exit up: bell, dark inside and a body. */
|
|
7
|
+
export function merlin(at, y, exit, length) {
|
|
8
|
+
const neck = y + length * 0.62;
|
|
9
|
+
const throat = exit * 0.34;
|
|
10
|
+
const bell = { kind: "lathe", material: "bell", at, segs: 12, under: true, outline: true, rings: [y], profile: [[exit, y], [exit * 0.82, y + (neck - y) * 0.4], [exit * 0.5, y + (neck - y) * 0.8], [throat, neck]] };
|
|
11
|
+
const inside = { kind: "lathe", material: "bell-inside", at, segs: 12, under: true, profile: [[throat * 0.8, neck - 0.05], [exit * 0.96, y + 0.01]] };
|
|
12
|
+
const head = { kind: "lathe", material: "engine-metal", at, segs: 8, under: true, only: "mesh", profile: [[throat, neck], [exit * 0.5, y + length * 0.82], [exit * 0.4, y + length]] };
|
|
13
|
+
return [bell, inside, head];
|
|
14
|
+
}
|
|
15
|
+
const R = 1.85;
|
|
16
|
+
/** Each part's height is the sourced figure where there is one; the others share out what the sourced whole leaves, so the parts add up to it. */
|
|
17
|
+
const falconOf = (first, interstage, secondHeight, joint, fairing, fairingRadius, engines, legs, fins) => ({
|
|
18
|
+
first: { height: first, interstage, radius: R, engines, legs, fins },
|
|
19
|
+
second: { height: secondHeight, joint, radius: R, nozzle: joint * 0.9 },
|
|
20
|
+
fairing: { height: fairing, radius: fairingRadius },
|
|
21
|
+
});
|
|
22
|
+
export const FALCON_DRAWS = {
|
|
23
|
+
// 54.9 m in all: 39.6 m of stage. The interstage, second stage and fairing are shared out: thin evidence
|
|
24
|
+
"v1.0": falconOf(44.5, 4.9, 7, 3, 6.4, 2.6, "grid", false, false),
|
|
25
|
+
// 68.4 m in all: 41.2 m of stage
|
|
26
|
+
"v1.1": falconOf(46, 4.8, 13.8, 4.5, 13.1, 2.6, "octaweb", true, false),
|
|
27
|
+
ft: falconOf(47.7, 6.5, 13.8, 4.8, 13.1, 2.6, "octaweb", true, true),
|
|
28
|
+
"block-5": falconOf(47.7, 6.5, 13.8, 4.8, 13.1, 2.6, "octaweb", true, true),
|
|
29
|
+
};
|
|
30
|
+
function firstStage(d, withTop) {
|
|
31
|
+
const { height: H, interstage, radius: r } = d;
|
|
32
|
+
const tank = H - interstage;
|
|
33
|
+
const pieces = [];
|
|
34
|
+
const skirt = 1.2;
|
|
35
|
+
const hull = { kind: "lathe", material: "white", segs: 48, uv: false, body: true, outline: true, profile: [[r, skirt], [r, tank]], rings: [skirt + 0.02, tank * 0.28, tank * 0.74], welds: [tank * 0.5, tank * 0.62, tank * 0.86] };
|
|
36
|
+
pieces.push(hull);
|
|
37
|
+
if (withTop)
|
|
38
|
+
pieces.push({ kind: "lathe", material: "black", segs: 48, body: true, outline: true, profile: [[r, tank], [r, H]], rings: [tank + 0.02, H - 0.02], welds: [tank + interstage * 0.5] });
|
|
39
|
+
pieces.push({ kind: "lathe", material: "soot", segs: 36, under: true, profile: [[0, skirt + 0.5], [r - 0.1, skirt + 0.5], [r, skirt]], rings: [skirt + 0.5] });
|
|
40
|
+
if (d.engines === "grid") {
|
|
41
|
+
for (let i = -1; i <= 1; i++)
|
|
42
|
+
for (let j = -1; j <= 1; j++)
|
|
43
|
+
pieces.push(...merlin([i * 1.05, j * 1.05], 0, 0.42, 2));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
pieces.push(...merlin([0, 0], 0, 0.46, 2));
|
|
47
|
+
for (let i = 0; i < 8; i++) {
|
|
48
|
+
const a = deg(22.5 + i * 45);
|
|
49
|
+
pieces.push(...merlin([1.32 * Math.sin(a), 1.32 * Math.cos(a)], 0.1, 0.46, 1.9));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// a raceway up the back
|
|
53
|
+
pieces.push(strut("black", pointOf(radial(Math.PI), r + 0.06, skirt + 1), pointOf(radial(Math.PI), r + 0.06, tank + 0.5), 0.28, 0.14, { plain: false }));
|
|
54
|
+
if (d.legs)
|
|
55
|
+
for (const az of [45, 135, 225, 315]) {
|
|
56
|
+
const frame = radial(deg(az));
|
|
57
|
+
pieces.push({ kind: "plate", material: "black", frame, outline: [[r - 0.05, 0.8], [r + 0.5, 0.8], [r + 0.55, 3], [r + 0.15, 10.5], [r - 0.05, 11]], thickness: 1.2 });
|
|
58
|
+
}
|
|
59
|
+
if (d.fins)
|
|
60
|
+
for (const az of [45, 135, 225, 315])
|
|
61
|
+
pieces.push(...gridFin(az, tank + 0.1, r, r + 1.4, 1.5, 0.3, false));
|
|
62
|
+
return pieces;
|
|
63
|
+
}
|
|
64
|
+
function secondStage(d) {
|
|
65
|
+
const { height, joint, radius: r, nozzle } = d;
|
|
66
|
+
const pieces = [];
|
|
67
|
+
pieces.push({ kind: "lathe", material: "white", segs: 48, body: true, outline: true, profile: [[r * 0.6, joint - 0.5], [r, joint], [r, height]], rings: [joint + 0.5, height - 0.05], welds: [(joint + height) / 2] });
|
|
68
|
+
pieces.push({ kind: "lathe", material: "soot", segs: 36, under: true, only: "mesh", profile: [[0, joint - 0.5], [r * 0.6, joint - 0.5]] });
|
|
69
|
+
pieces.push(...merlin([0, 0], 0, r * 0.62, nozzle * 1.06));
|
|
70
|
+
return pieces;
|
|
71
|
+
}
|
|
72
|
+
function fairing(d, jointRadius) {
|
|
73
|
+
const { height: H, radius: r } = d;
|
|
74
|
+
const profile = [[jointRadius, 0], [r, H * 0.16]];
|
|
75
|
+
const steps = 10;
|
|
76
|
+
const start = H * 0.4;
|
|
77
|
+
for (let i = 0; i <= steps; i++) {
|
|
78
|
+
const t = 1 - (1 - i / steps) ** 1.7;
|
|
79
|
+
profile.push([r * Math.sqrt(Math.max(0, 1 - t ** 2.1)), start + (H - start) * t]);
|
|
80
|
+
}
|
|
81
|
+
profile[profile.length - 1] = [0.001, H];
|
|
82
|
+
const seam = (a) => profile.slice(1).map(([rr, y]) => [(rr + 0.01) * Math.sin(a), y, (rr + 0.01) * Math.cos(a)]);
|
|
83
|
+
return [
|
|
84
|
+
{ kind: "lathe", material: "white", segs: 48, body: true, outline: true, profile, rings: [H * 0.16] },
|
|
85
|
+
{ kind: "lathe", material: "soot", segs: 24, under: true, only: "mesh", profile: [[0, 0], [jointRadius, 0]] },
|
|
86
|
+
{ kind: "lines", weight: "feature", paths: [seam(deg(90)), seam(deg(-90))] },
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
export const falconFirstStage = (id) => firstStage(FALCON_DRAWS[id].first, true);
|
|
90
|
+
export const falconSecondStage = (id) => secondStage(FALCON_DRAWS[id].second);
|
|
91
|
+
export const falconFairing = (id) => fairing(FALCON_DRAWS[id].fairing, R);
|
|
92
|
+
export const DRAGON_DRAWS = {
|
|
93
|
+
"dragon-1": { capsule: 3.8, nest: 0, trunk: 2.3, baseRadius: 1.85, trunkRadius: 1.8, windows: false, wings: true, fins: false },
|
|
94
|
+
// the capsule's 4.5 m includes its heat shield, which nests 0.34 m into the trunk: 3.94 m of trunk and that make the sourced 8.1 m
|
|
95
|
+
"dragon-2": { capsule: 4.5, nest: 0.342, trunk: 3.94, baseRadius: 2, trunkRadius: 1.92, windows: true, wings: false, fins: true },
|
|
96
|
+
};
|
|
97
|
+
export function capsule(d) {
|
|
98
|
+
const H = d.capsule;
|
|
99
|
+
const r = d.baseRadius;
|
|
100
|
+
const shoulder = H * 0.74;
|
|
101
|
+
const n = d.nest;
|
|
102
|
+
const profile = [[r * 0.9, n], [r, n + 0.3], [r * 0.93, n + 0.7], [r * 0.5, shoulder], [r * 0.47, H * 0.84], [r * 0.3, H * 0.95], [0.001, H]];
|
|
103
|
+
const pieces = [{ kind: "lathe", material: "white", segs: 48, body: true, outline: true, profile, rings: [n + 0.3, shoulder], welds: [H * 0.3] }];
|
|
104
|
+
pieces.push({ kind: "lathe", material: "soot", segs: 40, under: true, profile: [[0, 0], [r * 0.72, 0], [r * 0.9, n]], rings: [n] });
|
|
105
|
+
if (d.windows)
|
|
106
|
+
for (const az of [38, -38, 142, -142]) {
|
|
107
|
+
const frame = radial(deg(az));
|
|
108
|
+
pieces.push({ kind: "plate", material: "glass", frame, outline: [[r * 0.76, H * 0.31], [r * 0.73, H * 0.39], [r * 0.64, H * 0.43], [r * 0.6, H * 0.36]], thickness: 0.36 });
|
|
109
|
+
}
|
|
110
|
+
return pieces;
|
|
111
|
+
}
|
|
112
|
+
export function trunk(d) {
|
|
113
|
+
const H = d.trunk;
|
|
114
|
+
const r = d.trunkRadius;
|
|
115
|
+
const pieces = [];
|
|
116
|
+
if (d.fins) {
|
|
117
|
+
// half of it is solar cells, half white, with four fins at the foot
|
|
118
|
+
const half = (y) => { void y; return [-Math.PI / 2, Math.PI / 2]; };
|
|
119
|
+
pieces.push({ kind: "lathe", material: "solar", segs: 40, body: true, profile: [[r, 0], [r, H]], arc: half, rings: [H - 0.02] });
|
|
120
|
+
pieces.push({ kind: "lathe", material: "white", segs: 40, body: true, profile: [[r, 0], [r, H]], arc: () => [Math.PI / 2, (3 * Math.PI) / 2], rings: [H - 0.02] });
|
|
121
|
+
for (const az of [45, 135, 225, 315])
|
|
122
|
+
pieces.push({ kind: "plate", material: "white", frame: radial(deg(az)), outline: [[r - 0.05, 0], [r + 0.6, 0], [r + 0.25, 1.4], [r - 0.05, 1.8]], thickness: 0.12 });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
pieces.push({ kind: "lathe", material: "white", segs: 40, body: true, outline: true, profile: [[r, 0], [r, H]], rings: [0.02, H - 0.02], welds: [H * 0.5] });
|
|
126
|
+
}
|
|
127
|
+
pieces.push({ kind: "lathe", material: "soot", segs: 30, under: true, only: "mesh", profile: [[0, 0.02], [r, 0.02]] });
|
|
128
|
+
if (d.wings) {
|
|
129
|
+
for (const side of [1, -1]) {
|
|
130
|
+
const frame = radial(deg(90 * side), H * 0.5);
|
|
131
|
+
pieces.push({ kind: "plate", material: "solar", frame, outline: [[r + 0.5, -0.75], [r + 5.8, -0.75], [r + 5.8, 0.75], [r + 0.5, 0.75]], thickness: 0.08, ribs: [1.5, 2.5, 3.5, 4.5].map((u) => [[r + u, -0.75], [r + u, 0.75]]) });
|
|
132
|
+
pieces.push(strut("gold", pointOf(frame, r - 0.05, 0), pointOf(frame, r + 0.55, 0), 0.14, 0.14));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return pieces;
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------------------------------------------------------------
|
|
138
|
+
// Falcon Heavy
|
|
139
|
+
/** A side booster, with its nose cone in place of an upper stage. */
|
|
140
|
+
export function sideBooster(height, r, side) {
|
|
141
|
+
const nose = 5.5;
|
|
142
|
+
const tank = height - nose;
|
|
143
|
+
const pieces = firstStage({ height: tank, interstage: 0.001, radius: r, engines: "octaweb", legs: true, fins: false }, false);
|
|
144
|
+
const profile = [[r, tank]];
|
|
145
|
+
for (let i = 1; i <= 8; i++) {
|
|
146
|
+
const t = i / 8;
|
|
147
|
+
profile.push([r * Math.sqrt(Math.max(0, 1 - t ** 1.9)), tank + nose * t]);
|
|
148
|
+
}
|
|
149
|
+
profile[profile.length - 1] = [0.001, height];
|
|
150
|
+
pieces.push({ kind: "lathe", material: "white", segs: 40, body: true, outline: true, profile, rings: [tank + 0.02] });
|
|
151
|
+
// where it holds the centre core
|
|
152
|
+
const frame = radial(deg(90 * -side));
|
|
153
|
+
pieces.push(strut("black", pointOf(frame, r, tank - 1), pointOf(frame, r + 0.5, tank - 1), 0.4, 0.4));
|
|
154
|
+
for (const az of [0, 180])
|
|
155
|
+
pieces.push(...gridFin(az + 45, tank - 0.3, r, r + 1.4, 1.5, 0.3, false));
|
|
156
|
+
return pieces;
|
|
157
|
+
}
|
|
158
|
+
export function centreCore(height, r) {
|
|
159
|
+
const first = 47.7;
|
|
160
|
+
const pieces = firstStage({ height: first, interstage: 6.5, radius: r, engines: "octaweb", legs: true, fins: false }, true);
|
|
161
|
+
const secondTop = height - 13.1;
|
|
162
|
+
pieces.push({ kind: "lathe", material: "white", segs: 40, body: true, outline: true, profile: [[r, first], [r, secondTop]], rings: [secondTop - 0.02] });
|
|
163
|
+
const fair = fairing({ height: 13.1, radius: 2.6 }, r);
|
|
164
|
+
for (const piece of fair) {
|
|
165
|
+
if (piece.kind === "lathe")
|
|
166
|
+
pieces.push({ ...piece, profile: piece.profile.map(([a, b]) => [a, b + secondTop]), rings: piece.rings?.map((y) => y + secondTop), under: false, body: true });
|
|
167
|
+
else if (piece.kind === "lines")
|
|
168
|
+
pieces.push({ ...piece, paths: piece.paths.map((path) => path.map(([x, y, z]) => [x, y + secondTop, z])) });
|
|
169
|
+
}
|
|
170
|
+
return pieces;
|
|
171
|
+
}
|
|
172
|
+
// ---------------------------------------------------------------------------------------------------------------------------------
|
|
173
|
+
// Grasshopper
|
|
174
|
+
export function grasshopper(height) {
|
|
175
|
+
const hullBottom = height * 0.17;
|
|
176
|
+
const r = 1.57;
|
|
177
|
+
const frameTop = hullBottom + 0.2;
|
|
178
|
+
const pieces = [];
|
|
179
|
+
const profile = [[r, hullBottom], [r, height - 2.4], [r * 0.7, height - 1], [r * 0.32, height - 0.2], [0.001, height]];
|
|
180
|
+
pieces.push({ kind: "lathe", material: "white", segs: 40, body: true, outline: true, profile, rings: [height - 2.4, hullBottom + 0.05], welds: [height * 0.4, height * 0.6, height * 0.8] });
|
|
181
|
+
pieces.push({ kind: "lathe", material: "soot", segs: 30, under: true, only: "mesh", profile: [[0, hullBottom], [r, hullBottom]] });
|
|
182
|
+
pieces.push(...merlin([0, 0], 1.2, 0.5, 3.2));
|
|
183
|
+
// the blue frame
|
|
184
|
+
const low = hullBottom - 2.8;
|
|
185
|
+
for (const a of [45, 135, 225, 315]) {
|
|
186
|
+
const f = radial(deg(a));
|
|
187
|
+
pieces.push(strut("blue", pointOf(f, 2.2, low), pointOf(f, 2.2, frameTop), 0.2, 0.2, { plain: false }));
|
|
188
|
+
pieces.push(strut("blue", pointOf(f, 2.2, low), pointOf(f, 0.5, low + 1), 0.16, 0.16, { plain: false }));
|
|
189
|
+
// the leg: from the frame out to a foot, and a strut back up
|
|
190
|
+
pieces.push(strut("engine-pale", pointOf(f, 2.2, low + 0.5), pointOf(f, 5.3, 0.1), 0.2, 0.2, { plain: false }));
|
|
191
|
+
pieces.push(strut("engine-pale", pointOf(f, 2.2, frameTop - 0.5), pointOf(f, 5.3, 0.1), 0.14, 0.14, { plain: false }));
|
|
192
|
+
pieces.push({ kind: "plate", material: "dark", frame: { o: [f.u[0] * 5.3, 0.05, f.u[2] * 5.3], u: f.u, v: f.w, w: [0, 1, 0] }, outline: [[-0.45, -0.45], [0.45, -0.45], [0.45, 0.45], [-0.45, 0.45]], thickness: 0.1, plain: true });
|
|
193
|
+
}
|
|
194
|
+
for (let i = 0; i < 4; i++) {
|
|
195
|
+
const a = (i * 90 + 45) * (Math.PI / 180);
|
|
196
|
+
const b = ((i + 1) * 90 + 45) * (Math.PI / 180);
|
|
197
|
+
const p = (ang, y) => [2.2 * Math.sin(ang), y, 2.2 * Math.cos(ang)];
|
|
198
|
+
for (const y of [low, frameTop])
|
|
199
|
+
pieces.push(strut("blue", p(a, y), p(b, y), 0.18, 0.18, { plain: false }));
|
|
200
|
+
pieces.push(strut("blue", p(a, low), p(b, frameTop), 0.12, 0.12, { plain: true }));
|
|
201
|
+
}
|
|
202
|
+
// the bottles round the frame
|
|
203
|
+
for (const a of [0, 90, 180, 270])
|
|
204
|
+
pieces.push({ kind: "plate", material: "dark", frame: radial(deg(a)), outline: [[2.05, frameTop - 1.6], [2.28, frameTop - 1.6], [2.28, frameTop - 0.5], [2.05, frameTop - 0.5]], thickness: 2.4, plain: false });
|
|
205
|
+
return pieces;
|
|
206
|
+
}
|