@johnmorrisdotca/uchusen-rockets 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/LICENSES/CC-BY-4.0.txt +396 -0
- package/LICENSES/MIT.txt +21 -0
- package/NOTICE.md +66 -0
- package/README.md +171 -2
- package/dist/assets/dragon-dragon-1-apart.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-apart.svg +1 -0
- package/dist/assets/dragon-dragon-1-apart.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.glb +0 -0
- package/dist/assets/dragon-dragon-1-capsule.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.svg +1 -0
- package/dist/assets/dragon-dragon-1-capsule.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.glb +0 -0
- package/dist/assets/dragon-dragon-1-trunk.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.svg +1 -0
- package/dist/assets/dragon-dragon-1-trunk.thick.svg +1 -0
- package/dist/assets/dragon-dragon-1.glb +0 -0
- package/dist/assets/dragon-dragon-1.painted.svg +1 -0
- package/dist/assets/dragon-dragon-1.svg +1 -0
- package/dist/assets/dragon-dragon-1.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.svg +1 -0
- package/dist/assets/dragon-dragon-2-apart.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.glb +0 -0
- package/dist/assets/dragon-dragon-2-capsule.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.svg +1 -0
- package/dist/assets/dragon-dragon-2-capsule.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.glb +0 -0
- package/dist/assets/dragon-dragon-2-trunk.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.svg +1 -0
- package/dist/assets/dragon-dragon-2-trunk.thick.svg +1 -0
- package/dist/assets/dragon-dragon-2.glb +0 -0
- package/dist/assets/dragon-dragon-2.painted.svg +1 -0
- package/dist/assets/dragon-dragon-2.svg +1 -0
- package/dist/assets/dragon-dragon-2.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.svg +1 -0
- package/dist/assets/falcon-9-block-5-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-capsule.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2-trunk.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.svg +1 -0
- package/dist/assets/falcon-9-block-5-dragon-2.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.glb +0 -0
- package/dist/assets/falcon-9-block-5-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.svg +1 -0
- package/dist/assets/falcon-9-block-5-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.svg +1 -0
- package/dist/assets/falcon-9-block-5-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-block-5.glb +0 -0
- package/dist/assets/falcon-9-block-5.painted.svg +1 -0
- package/dist/assets/falcon-9-block-5.svg +1 -0
- package/dist/assets/falcon-9-block-5.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.svg +1 -0
- package/dist/assets/falcon-9-ft-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.glb +0 -0
- package/dist/assets/falcon-9-ft-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.svg +1 -0
- package/dist/assets/falcon-9-ft-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.glb +0 -0
- package/dist/assets/falcon-9-ft-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.svg +1 -0
- package/dist/assets/falcon-9-ft-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.glb +0 -0
- package/dist/assets/falcon-9-ft-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.svg +1 -0
- package/dist/assets/falcon-9-ft-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-ft.glb +0 -0
- package/dist/assets/falcon-9-ft.painted.svg +1 -0
- package/dist/assets/falcon-9-ft.svg +1 -0
- package/dist/assets/falcon-9-ft.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.svg +1 -0
- package/dist/assets/falcon-9-v1.0-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.glb +0 -0
- package/dist/assets/falcon-9-v1.0-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.svg +1 -0
- package/dist/assets/falcon-9-v1.0-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0.glb +0 -0
- package/dist/assets/falcon-9-v1.0.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0.svg +1 -0
- package/dist/assets/falcon-9-v1.0.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.svg +1 -0
- package/dist/assets/falcon-9-v1.1-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.glb +0 -0
- package/dist/assets/falcon-9-v1.1-fairing.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.svg +1 -0
- package/dist/assets/falcon-9-v1.1-fairing.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.1-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.1-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.1-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.1-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.1.glb +0 -0
- package/dist/assets/falcon-9-v1.1.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.1.svg +1 -0
- package/dist/assets/falcon-9-v1.1.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-apart.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.thick.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy.painted.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.svg +1 -0
- package/dist/assets/falcon-heavy-falcon-heavy.thick.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.glb +0 -0
- package/dist/assets/grasshopper-grasshopper.painted.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.svg +1 -0
- package/dist/assets/grasshopper-grasshopper.thick.svg +1 -0
- package/dist/assets/models/dragon-dragon-1-capsule.glb +0 -0
- package/dist/assets/models/dragon-dragon-1-trunk.glb +0 -0
- package/dist/assets/models/dragon-dragon-2-capsule.glb +0 -0
- package/dist/assets/models/dragon-dragon-2-trunk.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-fairing.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-first-stage.glb +0 -0
- package/dist/assets/models/falcon-9-block-5-second-stage.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-centre.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-side-left.glb +0 -0
- package/dist/assets/models/falcon-heavy-falcon-heavy-side-right.glb +0 -0
- package/dist/assets/models/grasshopper-grasshopper-vehicle.glb +0 -0
- package/dist/assets/models/starship-2019-ship.glb +0 -0
- package/dist/assets/models/starship-v1-flight1-booster.glb +0 -0
- package/dist/assets/models/starship-v1-flight1-ship.glb +0 -0
- package/dist/assets/models/starship-v3-booster.glb +0 -0
- package/dist/assets/models/starship-v3-ship.glb +0 -0
- package/dist/assets/parts/flap-aft.glb +0 -0
- package/dist/assets/parts/flap-aft.painted.svg +1 -0
- package/dist/assets/parts/flap-aft.svg +1 -0
- package/dist/assets/parts/flap-aft.thick.svg +1 -0
- package/dist/assets/parts/flap-forward.glb +0 -0
- package/dist/assets/parts/flap-forward.painted.svg +1 -0
- package/dist/assets/parts/flap-forward.svg +1 -0
- package/dist/assets/parts/flap-forward.thick.svg +1 -0
- package/dist/assets/parts/grid-fin-four.glb +0 -0
- package/dist/assets/parts/grid-fin-four.painted.svg +1 -0
- package/dist/assets/parts/grid-fin-four.svg +1 -0
- package/dist/assets/parts/grid-fin-four.thick.svg +1 -0
- package/dist/assets/parts/grid-fin-three.glb +0 -0
- package/dist/assets/parts/grid-fin-three.painted.svg +1 -0
- package/dist/assets/parts/grid-fin-three.svg +1 -0
- package/dist/assets/parts/grid-fin-three.thick.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.glb +0 -0
- package/dist/assets/parts/merlin-sea-level.painted.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.svg +1 -0
- package/dist/assets/parts/merlin-sea-level.thick.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.glb +0 -0
- package/dist/assets/parts/raptor-sea-level.painted.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.svg +1 -0
- package/dist/assets/parts/raptor-sea-level.thick.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.glb +0 -0
- package/dist/assets/parts/raptor-vacuum.painted.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.svg +1 -0
- package/dist/assets/parts/raptor-vacuum.thick.svg +1 -0
- package/dist/assets/starship-2019.glb +0 -0
- package/dist/assets/starship-2019.painted.svg +1 -0
- package/dist/assets/starship-2019.svg +1 -0
- package/dist/assets/starship-2019.thick.svg +1 -0
- package/dist/assets/starship-v1-apart.painted.svg +1 -0
- package/dist/assets/starship-v1-apart.svg +1 -0
- package/dist/assets/starship-v1-apart.thick.svg +1 -0
- package/dist/assets/starship-v1-booster.glb +0 -0
- package/dist/assets/starship-v1-booster.painted.svg +1 -0
- package/dist/assets/starship-v1-booster.svg +1 -0
- package/dist/assets/starship-v1-booster.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.svg +1 -0
- package/dist/assets/starship-v1-flight1-apart.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.glb +0 -0
- package/dist/assets/starship-v1-flight1-booster.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.svg +1 -0
- package/dist/assets/starship-v1-flight1-booster.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.glb +0 -0
- package/dist/assets/starship-v1-flight1-ship.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.svg +1 -0
- package/dist/assets/starship-v1-flight1-ship.thick.svg +1 -0
- package/dist/assets/starship-v1-flight1.glb +0 -0
- package/dist/assets/starship-v1-flight1.painted.svg +1 -0
- package/dist/assets/starship-v1-flight1.svg +1 -0
- package/dist/assets/starship-v1-flight1.thick.svg +1 -0
- package/dist/assets/starship-v1-ship.glb +0 -0
- package/dist/assets/starship-v1-ship.painted.svg +1 -0
- package/dist/assets/starship-v1-ship.svg +1 -0
- package/dist/assets/starship-v1-ship.thick.svg +1 -0
- package/dist/assets/starship-v1.glb +0 -0
- package/dist/assets/starship-v1.painted.svg +1 -0
- package/dist/assets/starship-v1.svg +1 -0
- package/dist/assets/starship-v1.thick.svg +1 -0
- package/dist/assets/starship-v2-apart.painted.svg +1 -0
- package/dist/assets/starship-v2-apart.svg +1 -0
- package/dist/assets/starship-v2-apart.thick.svg +1 -0
- package/dist/assets/starship-v2-booster.glb +0 -0
- package/dist/assets/starship-v2-booster.painted.svg +1 -0
- package/dist/assets/starship-v2-booster.svg +1 -0
- package/dist/assets/starship-v2-booster.thick.svg +1 -0
- package/dist/assets/starship-v2-ship.glb +0 -0
- package/dist/assets/starship-v2-ship.painted.svg +1 -0
- package/dist/assets/starship-v2-ship.svg +1 -0
- package/dist/assets/starship-v2-ship.thick.svg +1 -0
- package/dist/assets/starship-v2.glb +0 -0
- package/dist/assets/starship-v2.painted.svg +1 -0
- package/dist/assets/starship-v2.svg +1 -0
- package/dist/assets/starship-v2.thick.svg +1 -0
- package/dist/assets/starship-v3-apart.painted.svg +1 -0
- package/dist/assets/starship-v3-apart.svg +1 -0
- package/dist/assets/starship-v3-apart.thick.svg +1 -0
- package/dist/assets/starship-v3-booster.glb +0 -0
- package/dist/assets/starship-v3-booster.painted.svg +1 -0
- package/dist/assets/starship-v3-booster.svg +1 -0
- package/dist/assets/starship-v3-booster.thick.svg +1 -0
- package/dist/assets/starship-v3-ship.glb +0 -0
- package/dist/assets/starship-v3-ship.painted.svg +1 -0
- package/dist/assets/starship-v3-ship.svg +1 -0
- package/dist/assets/starship-v3-ship.thick.svg +1 -0
- package/dist/assets/starship-v3.glb +0 -0
- package/dist/assets/starship-v3.painted.svg +1 -0
- package/dist/assets/starship-v3.svg +1 -0
- package/dist/assets/starship-v3.thick.svg +1 -0
- package/dist/assets/starship-v4-apart.painted.svg +1 -0
- package/dist/assets/starship-v4-apart.svg +1 -0
- package/dist/assets/starship-v4-apart.thick.svg +1 -0
- package/dist/assets/starship-v4-booster.glb +0 -0
- package/dist/assets/starship-v4-booster.painted.svg +1 -0
- package/dist/assets/starship-v4-booster.svg +1 -0
- package/dist/assets/starship-v4-booster.thick.svg +1 -0
- package/dist/assets/starship-v4-ship.glb +0 -0
- package/dist/assets/starship-v4-ship.painted.svg +1 -0
- package/dist/assets/starship-v4-ship.svg +1 -0
- package/dist/assets/starship-v4-ship.thick.svg +1 -0
- package/dist/assets/starship-v4.glb +0 -0
- package/dist/assets/starship-v4.painted.svg +1 -0
- package/dist/assets/starship-v4.svg +1 -0
- package/dist/assets/starship-v4.thick.svg +1 -0
- package/dist/build/glb.d.ts +12 -0
- package/dist/build/glb.js +157 -0
- package/dist/build/index.d.ts +59 -0
- package/dist/build/index.js +118 -0
- package/dist/build/lineup.d.ts +40 -0
- package/dist/build/lineup.js +97 -0
- package/dist/crossfade.d.ts +6 -0
- package/dist/crossfade.js +11 -0
- package/dist/data/credits.d.ts +87 -0
- package/dist/data/credits.js +82 -0
- package/dist/data/dragon.d.ts +2 -0
- package/dist/data/dragon.js +45 -0
- package/dist/data/falcon.d.ts +3 -0
- package/dist/data/falcon.js +121 -0
- package/dist/data/grasshopper.d.ts +2 -0
- package/dist/data/grasshopper.js +21 -0
- package/dist/data/sources.d.ts +173 -0
- package/dist/data/sources.js +25 -0
- package/dist/data/starship.d.ts +2 -0
- package/dist/data/starship.js +183 -0
- package/dist/data/types.d.ts +48 -0
- package/dist/data/types.js +8 -0
- package/dist/draw/svg.d.ts +26 -0
- package/dist/draw/svg.js +262 -0
- package/dist/element/paint.d.ts +9 -0
- package/dist/element/paint.js +138 -0
- package/dist/element/scene.d.ts +104 -0
- package/dist/element/scene.js +758 -0
- package/dist/element.d.ts +74 -0
- package/dist/element.js +488 -0
- package/dist/fit.d.ts +43 -0
- package/dist/fit.js +93 -0
- package/dist/framing.d.ts +32 -0
- package/dist/framing.js +52 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +13 -0
- package/dist/layout.d.ts +17 -0
- package/dist/layout.js +33 -0
- package/dist/looks.d.ts +25 -0
- package/dist/looks.js +36 -0
- package/dist/model/falcon.d.ts +48 -0
- package/dist/model/falcon.js +206 -0
- package/dist/model/materials.d.ts +113 -0
- package/dist/model/materials.js +19 -0
- package/dist/model/pieces.d.ts +91 -0
- package/dist/model/pieces.js +223 -0
- package/dist/model/registry.d.ts +8 -0
- package/dist/model/registry.js +104 -0
- package/dist/model/starship.d.ts +67 -0
- package/dist/model/starship.js +503 -0
- package/dist/parts.d.ts +44 -0
- package/dist/parts.js +1 -0
- package/dist/react.d.ts +314 -0
- package/dist/react.js +8 -0
- package/dist/rockets.d.ts +27 -0
- package/dist/rockets.js +36 -0
- package/dist/tilt.d.ts +21 -0
- package/dist/tilt.js +23 -0
- package/package.json +65 -4
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Description, FitReport } from "./fit.ts";
|
|
2
|
+
import { type Angle } from "./framing.ts";
|
|
3
|
+
import { ROCKETS } from "./rockets.ts";
|
|
4
|
+
export type RocketState = "picture" | "loading" | "3d" | "fallback" | "error";
|
|
5
|
+
declare const HTMLBase: typeof HTMLElement;
|
|
6
|
+
/** Where the licensed models are, for every element with no `assets` attribute of its own. Left unset: beside this module, in ./assets/. */
|
|
7
|
+
export declare function setAssetBase(url: string): void;
|
|
8
|
+
export declare class UchusenRocket extends HTMLBase {
|
|
9
|
+
static readonly observedAttributes: string[];
|
|
10
|
+
private descriptionValue;
|
|
11
|
+
private picture;
|
|
12
|
+
private holder;
|
|
13
|
+
private drawing;
|
|
14
|
+
private viewer;
|
|
15
|
+
private watch;
|
|
16
|
+
private cleanups;
|
|
17
|
+
private releaseTimer;
|
|
18
|
+
private revealTimer;
|
|
19
|
+
private generation;
|
|
20
|
+
private builtNow;
|
|
21
|
+
private queued;
|
|
22
|
+
private stateNow;
|
|
23
|
+
private shownAt;
|
|
24
|
+
private starting;
|
|
25
|
+
private key;
|
|
26
|
+
/** What is showing now. */
|
|
27
|
+
get state(): RocketState;
|
|
28
|
+
/** What the fit check said about what is showing: its label, parts and credits. */
|
|
29
|
+
get report(): FitReport | null;
|
|
30
|
+
/** A rocket from parts, as a property: the same as the `build` attribute's JSON. */
|
|
31
|
+
get description(): Description | null;
|
|
32
|
+
set description(value: Description | null);
|
|
33
|
+
/** Where the camera is looking from, while there is a 3D view. */
|
|
34
|
+
get angle(): Angle | null;
|
|
35
|
+
connectedCallback(): void;
|
|
36
|
+
disconnectedCallback(): void;
|
|
37
|
+
attributeChangedCallback(name: string, before: string | null, after: string | null): void;
|
|
38
|
+
private spin;
|
|
39
|
+
private descriptionNow;
|
|
40
|
+
private queue;
|
|
41
|
+
private teardown;
|
|
42
|
+
private setState;
|
|
43
|
+
private emit;
|
|
44
|
+
/** What to draw from the attributes, every value checked. */
|
|
45
|
+
private settings;
|
|
46
|
+
private update;
|
|
47
|
+
private renderShell;
|
|
48
|
+
/** The drawing for what the attributes say now. It is made from code, so it needs no file. */
|
|
49
|
+
private redraw;
|
|
50
|
+
private mark;
|
|
51
|
+
private applyRocket;
|
|
52
|
+
private lastView;
|
|
53
|
+
private applySettings;
|
|
54
|
+
private startWatching;
|
|
55
|
+
private fallBack;
|
|
56
|
+
private start;
|
|
57
|
+
private startTilt;
|
|
58
|
+
}
|
|
59
|
+
/** Rockets side by side on one baseline at one scale: `<uchusen-lineup items="starship:v1,starship:v3,person,building" unit="4" ruler labels>`. */
|
|
60
|
+
export declare class UchusenLineup extends HTMLBase {
|
|
61
|
+
static readonly observedAttributes: string[];
|
|
62
|
+
connectedCallback(): void;
|
|
63
|
+
attributeChangedCallback(name: string, before: string | null, after: string | null): void;
|
|
64
|
+
private render;
|
|
65
|
+
}
|
|
66
|
+
/** Defines both elements, once. Importing this module does it. */
|
|
67
|
+
export declare function defineUchusenRocket(): void;
|
|
68
|
+
export { ROCKETS };
|
|
69
|
+
declare global {
|
|
70
|
+
interface HTMLElementTagNameMap {
|
|
71
|
+
"uchusen-rocket": UchusenRocket;
|
|
72
|
+
"uchusen-lineup": UchusenLineup;
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/element.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
// The viewer: `<uchusen-rocket rocket="starship" variant="v3" show="together" look="line">` in any page. The drawing first, then, when it comes on screen and the browser can draw WebGL, the 3D view
|
|
2
|
+
// crossfading in over it; drag to turn, keyboard, tilt on phones, a slow spin, reduced motion; the drawing stays on no WebGL, Save-Data or any failure. Importing this file defines the elements. three.js is
|
|
3
|
+
// loaded only when a 3D view is wanted, and a licensed model only when the painted look asks for one.
|
|
4
|
+
import { build, FitError } from "./build/index.js";
|
|
5
|
+
import { lineupSvg } from "./build/lineup.js";
|
|
6
|
+
import { CROSSFADE_MS, REDUCED_FADE_MS, revealDelay } from "./crossfade.js";
|
|
7
|
+
import { cleanView, VIEWS } from "./framing.js";
|
|
8
|
+
import { cleanShow } from "./layout.js";
|
|
9
|
+
import { cleanLook } from "./looks.js";
|
|
10
|
+
import { ROCKETS } from "./rockets.js";
|
|
11
|
+
import { tiltMode, tiltTurn } from "./tilt.js";
|
|
12
|
+
const HTMLBase = typeof HTMLElement === "undefined" ? class {
|
|
13
|
+
} : HTMLElement;
|
|
14
|
+
/** A view out of sight keeps its WebGL canvas this long before letting it go (a browser allows only a few at a time). */
|
|
15
|
+
const RELEASE_MS = 30_000;
|
|
16
|
+
const DEFAULT_SPIN = { seconds: 180, way: "cw" };
|
|
17
|
+
let assetBase = null;
|
|
18
|
+
/** Where the licensed models are, for every element with no `assets` attribute of its own. Left unset: beside this module, in ./assets/. */
|
|
19
|
+
export function setAssetBase(url) {
|
|
20
|
+
assetBase = url.endsWith("/") ? url : `${url}/`;
|
|
21
|
+
}
|
|
22
|
+
const defaultBase = () => assetBase ?? new URL("./assets/", import.meta.url).href;
|
|
23
|
+
const CSS = `
|
|
24
|
+
:host { display: block; position: relative; color: inherit; }
|
|
25
|
+
:host([hidden]) { display: none; }
|
|
26
|
+
.picture { position: relative; width: 100%; aspect-ratio: var(--uchusen-aspect, 3 / 4); margin: 0; border-radius: inherit; }
|
|
27
|
+
.drawing, .drawing svg { display: block; width: 100%; height: 100%; }
|
|
28
|
+
.drawing, .canvas { transition: opacity var(--uchusen-fade, 1500ms) ease-in-out, filter var(--uchusen-fade, 1500ms) ease-in-out; }
|
|
29
|
+
.stage { position: absolute; inset: 0; }
|
|
30
|
+
.canvas { display: block; width: 100%; height: 100%; opacity: 0; filter: blur(3px); cursor: grab; border-radius: inherit; }
|
|
31
|
+
.canvas:active { cursor: grabbing; }
|
|
32
|
+
.canvas:focus-visible { outline: 2px solid currentColor; outline-offset: -2px; }
|
|
33
|
+
.picture[data-state="3d"] .drawing { opacity: 0; filter: blur(2px); }
|
|
34
|
+
.picture[data-state="3d"] .canvas { opacity: 1; filter: none; }
|
|
35
|
+
.tilt { position: absolute; right: 0.5rem; bottom: 0.5rem; min-height: 2rem; padding: 0.25rem 0.75rem; border: 1px solid currentColor; border-radius: 1rem; background: transparent; color: inherit; font: inherit; font-size: 0.8rem; cursor: pointer; }
|
|
36
|
+
@media (prefers-reduced-motion: reduce) {
|
|
37
|
+
.drawing, .canvas { filter: none !important; transition: opacity var(--uchusen-fade, 400ms) ease; }
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
function adopt(root, css, holder) {
|
|
41
|
+
try {
|
|
42
|
+
if (!holder.sheet) {
|
|
43
|
+
holder.sheet = new CSSStyleSheet();
|
|
44
|
+
holder.sheet.replaceSync(css);
|
|
45
|
+
}
|
|
46
|
+
root.adoptedStyleSheets = [holder.sheet];
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
const style = document.createElement("style");
|
|
50
|
+
style.textContent = css;
|
|
51
|
+
root.appendChild(style);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const rocketSheet = { sheet: null };
|
|
55
|
+
const lineupSheet = { sheet: null };
|
|
56
|
+
const media = (query) => typeof matchMedia === "function" && matchMedia(query).matches;
|
|
57
|
+
let webgl = null;
|
|
58
|
+
function webglWorks() {
|
|
59
|
+
if (webgl !== null)
|
|
60
|
+
return webgl;
|
|
61
|
+
try {
|
|
62
|
+
const probe = document.createElement("canvas");
|
|
63
|
+
const gl = probe.getContext("webgl2") ?? probe.getContext("webgl");
|
|
64
|
+
webgl = Boolean(gl);
|
|
65
|
+
gl?.getExtension("WEBGL_lose_context")?.loseContext();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
webgl = false;
|
|
69
|
+
}
|
|
70
|
+
return webgl;
|
|
71
|
+
}
|
|
72
|
+
function whyNot(mode) {
|
|
73
|
+
if (mode === "drawing")
|
|
74
|
+
return "off";
|
|
75
|
+
const connection = navigator.connection;
|
|
76
|
+
if (connection?.saveData || media("(prefers-reduced-data: reduce)"))
|
|
77
|
+
return "reduced-data";
|
|
78
|
+
if (!webglWorks())
|
|
79
|
+
return "no-webgl";
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
export class UchusenRocket extends HTMLBase {
|
|
83
|
+
static observedAttributes = ["rocket", "variant", "show", "look", "view", "build", "mode", "assets", "spin", "way", "label"];
|
|
84
|
+
descriptionValue = null;
|
|
85
|
+
picture = null;
|
|
86
|
+
holder = null;
|
|
87
|
+
drawing = null;
|
|
88
|
+
viewer = null;
|
|
89
|
+
watch = null;
|
|
90
|
+
cleanups = [];
|
|
91
|
+
releaseTimer = 0;
|
|
92
|
+
revealTimer = 0;
|
|
93
|
+
generation = 0;
|
|
94
|
+
builtNow = null;
|
|
95
|
+
queued = false;
|
|
96
|
+
stateNow = "picture";
|
|
97
|
+
shownAt = 0;
|
|
98
|
+
starting = false;
|
|
99
|
+
key = "";
|
|
100
|
+
/** What is showing now. */
|
|
101
|
+
get state() {
|
|
102
|
+
return this.stateNow;
|
|
103
|
+
}
|
|
104
|
+
/** What the fit check said about what is showing: its label, parts and credits. */
|
|
105
|
+
get report() {
|
|
106
|
+
return this.builtNow?.report ?? null;
|
|
107
|
+
}
|
|
108
|
+
/** A rocket from parts, as a property: the same as the `build` attribute's JSON. */
|
|
109
|
+
get description() {
|
|
110
|
+
return this.descriptionValue;
|
|
111
|
+
}
|
|
112
|
+
set description(value) {
|
|
113
|
+
this.descriptionValue = value;
|
|
114
|
+
this.queue();
|
|
115
|
+
}
|
|
116
|
+
/** Where the camera is looking from, while there is a 3D view. */
|
|
117
|
+
get angle() {
|
|
118
|
+
return this.viewer?.currentAngle() ?? null;
|
|
119
|
+
}
|
|
120
|
+
connectedCallback() {
|
|
121
|
+
if (!this.shadowRoot)
|
|
122
|
+
adopt(this.attachShadow({ mode: "open" }), CSS, rocketSheet);
|
|
123
|
+
this.queue();
|
|
124
|
+
}
|
|
125
|
+
disconnectedCallback() {
|
|
126
|
+
this.teardown();
|
|
127
|
+
this.key = "";
|
|
128
|
+
}
|
|
129
|
+
attributeChangedCallback(name, before, after) {
|
|
130
|
+
if (before === after)
|
|
131
|
+
return;
|
|
132
|
+
if (name === "spin" || name === "way")
|
|
133
|
+
this.viewer?.setSpin(this.spin());
|
|
134
|
+
else
|
|
135
|
+
this.queue();
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------------------------------------------------------------
|
|
138
|
+
spin() {
|
|
139
|
+
const raw = this.getAttribute("spin");
|
|
140
|
+
const named = { off: 0, slow: 180, medium: 67, fast: 20 };
|
|
141
|
+
const seconds = raw === null ? DEFAULT_SPIN.seconds : (named[raw] ?? (Number.isFinite(Number(raw)) ? Math.max(0, Number(raw)) : DEFAULT_SPIN.seconds));
|
|
142
|
+
return { seconds, way: this.getAttribute("way") === "ccw" ? "ccw" : "cw" };
|
|
143
|
+
}
|
|
144
|
+
descriptionNow() {
|
|
145
|
+
if (this.descriptionValue)
|
|
146
|
+
return this.descriptionValue;
|
|
147
|
+
const raw = this.getAttribute("build");
|
|
148
|
+
if (raw) {
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(raw);
|
|
151
|
+
return { rocket: String(parsed.rocket ?? ""), ...(parsed.variant ? { variant: String(parsed.variant) } : {}), ...(parsed.parts ? { parts: parsed.parts } : {}) };
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return { rocket: "" };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const variant = this.getAttribute("variant");
|
|
158
|
+
return { rocket: this.getAttribute("rocket") ?? "starship", ...(variant ? { variant } : {}) };
|
|
159
|
+
}
|
|
160
|
+
queue() {
|
|
161
|
+
if (this.queued)
|
|
162
|
+
return;
|
|
163
|
+
this.queued = true;
|
|
164
|
+
queueMicrotask(() => {
|
|
165
|
+
this.queued = false;
|
|
166
|
+
if (this.isConnected)
|
|
167
|
+
this.update();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
teardown() {
|
|
171
|
+
this.generation++;
|
|
172
|
+
this.watch?.disconnect();
|
|
173
|
+
this.watch = null;
|
|
174
|
+
window.clearTimeout(this.releaseTimer);
|
|
175
|
+
window.clearTimeout(this.revealTimer);
|
|
176
|
+
for (const clean of this.cleanups.splice(0))
|
|
177
|
+
clean();
|
|
178
|
+
this.viewer?.dispose();
|
|
179
|
+
this.viewer = null;
|
|
180
|
+
}
|
|
181
|
+
setState(state, why = "") {
|
|
182
|
+
this.stateNow = state;
|
|
183
|
+
if (this.picture)
|
|
184
|
+
this.picture.dataset.state = state;
|
|
185
|
+
this.dataset.state = state;
|
|
186
|
+
if (why)
|
|
187
|
+
this.dataset.why = why;
|
|
188
|
+
else
|
|
189
|
+
delete this.dataset.why;
|
|
190
|
+
}
|
|
191
|
+
emit(name, detail) {
|
|
192
|
+
this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
|
|
193
|
+
}
|
|
194
|
+
/** What to draw from the attributes, every value checked. */
|
|
195
|
+
settings(built) {
|
|
196
|
+
const look = cleanLook(this.getAttribute("look"));
|
|
197
|
+
const show = cleanShow(this.getAttribute("show"), built.parts.map((part) => part.slot));
|
|
198
|
+
const viewName = cleanView(this.getAttribute("view"));
|
|
199
|
+
return { look, show, viewName, angle: VIEWS[viewName] };
|
|
200
|
+
}
|
|
201
|
+
update() {
|
|
202
|
+
const description = this.descriptionNow();
|
|
203
|
+
const key = JSON.stringify(description);
|
|
204
|
+
if (key !== this.key || !this.builtNow) {
|
|
205
|
+
this.key = key;
|
|
206
|
+
try {
|
|
207
|
+
this.builtNow = build(description);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
this.builtNow = null;
|
|
211
|
+
this.renderShell();
|
|
212
|
+
this.setState("error", error instanceof FitError ? error.report.issues[0]?.code ?? "error" : "error");
|
|
213
|
+
this.emit("uchusen-error", { error });
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!this.picture)
|
|
217
|
+
this.renderShell();
|
|
218
|
+
void this.applyRocket();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
this.applySettings();
|
|
222
|
+
}
|
|
223
|
+
renderShell() {
|
|
224
|
+
const root = this.shadowRoot;
|
|
225
|
+
if (!root)
|
|
226
|
+
return;
|
|
227
|
+
for (const child of [...root.childNodes])
|
|
228
|
+
child.remove();
|
|
229
|
+
const reduced = media("(prefers-reduced-motion: reduce)");
|
|
230
|
+
const picture = document.createElement("figure");
|
|
231
|
+
picture.className = "picture";
|
|
232
|
+
picture.setAttribute("part", "figure");
|
|
233
|
+
picture.style.setProperty("--uchusen-fade", `${reduced ? REDUCED_FADE_MS : CROSSFADE_MS}ms`);
|
|
234
|
+
const drawing = document.createElement("div");
|
|
235
|
+
drawing.className = "drawing";
|
|
236
|
+
drawing.setAttribute("part", "drawing");
|
|
237
|
+
const holder = document.createElement("div");
|
|
238
|
+
holder.className = "stage";
|
|
239
|
+
picture.append(drawing, holder);
|
|
240
|
+
root.appendChild(picture);
|
|
241
|
+
this.picture = picture;
|
|
242
|
+
this.drawing = drawing;
|
|
243
|
+
this.holder = holder;
|
|
244
|
+
this.startWatching();
|
|
245
|
+
}
|
|
246
|
+
/** The drawing for what the attributes say now. It is made from code, so it needs no file. */
|
|
247
|
+
redraw() {
|
|
248
|
+
if (!this.builtNow || !this.drawing)
|
|
249
|
+
return;
|
|
250
|
+
const { look, show, angle } = this.settings(this.builtNow);
|
|
251
|
+
// when a 3D view is showing, a drawing of the same angle is ready behind it
|
|
252
|
+
const from = this.viewer?.currentAngle() ?? angle;
|
|
253
|
+
const svg = this.builtNow.svg({ look, show, view: from });
|
|
254
|
+
const template = document.createElement("template");
|
|
255
|
+
template.innerHTML = svg;
|
|
256
|
+
const node = template.content.firstElementChild;
|
|
257
|
+
if (!node)
|
|
258
|
+
return;
|
|
259
|
+
node.setAttribute("aria-label", this.getAttribute("label") ?? this.builtNow.report.label);
|
|
260
|
+
this.drawing.replaceChildren(node);
|
|
261
|
+
}
|
|
262
|
+
mark() {
|
|
263
|
+
if (!this.builtNow)
|
|
264
|
+
return;
|
|
265
|
+
const { look, show, viewName } = this.settings(this.builtNow);
|
|
266
|
+
this.dataset.look = look;
|
|
267
|
+
this.dataset.show = show;
|
|
268
|
+
this.dataset.viewAngle = viewName;
|
|
269
|
+
this.dataset.variant = this.builtNow.report.variant?.id ?? "";
|
|
270
|
+
this.dataset.custom = String(this.builtNow.report.custom);
|
|
271
|
+
}
|
|
272
|
+
async applyRocket() {
|
|
273
|
+
this.mark();
|
|
274
|
+
this.redraw();
|
|
275
|
+
if (!this.builtNow)
|
|
276
|
+
return;
|
|
277
|
+
if (this.viewer) {
|
|
278
|
+
const { look, show, viewName, angle } = this.settings(this.builtNow);
|
|
279
|
+
this.viewer.setLabel(this.getAttribute("label") ?? this.builtNow.report.label);
|
|
280
|
+
// a new rocket keeps the angle, unless the same change named another view
|
|
281
|
+
const from = viewName !== this.lastView ? angle : this.viewer.currentAngle();
|
|
282
|
+
this.lastView = viewName;
|
|
283
|
+
await this.viewer.load(this.builtNow, show, look, from);
|
|
284
|
+
this.emit("uchusen-change", { report: this.builtNow.report });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
lastView = "";
|
|
288
|
+
applySettings() {
|
|
289
|
+
if (!this.builtNow)
|
|
290
|
+
return;
|
|
291
|
+
this.mark();
|
|
292
|
+
const { look, show, viewName, angle } = this.settings(this.builtNow);
|
|
293
|
+
if (this.viewer) {
|
|
294
|
+
this.viewer.setLook(look);
|
|
295
|
+
this.viewer.setShow(show);
|
|
296
|
+
if (viewName !== this.lastView)
|
|
297
|
+
this.viewer.setAngle(angle, true);
|
|
298
|
+
this.viewer.setHome(angle);
|
|
299
|
+
}
|
|
300
|
+
this.lastView = viewName;
|
|
301
|
+
this.redraw();
|
|
302
|
+
}
|
|
303
|
+
startWatching() {
|
|
304
|
+
this.watch?.disconnect();
|
|
305
|
+
const reason = whyNot(this.getAttribute("mode") ?? "auto");
|
|
306
|
+
if (reason) {
|
|
307
|
+
this.setState(reason === "off" ? "picture" : "fallback", reason);
|
|
308
|
+
if (reason !== "off")
|
|
309
|
+
this.emit("uchusen-fallback", { reason });
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.setState("picture");
|
|
313
|
+
this.watch = new IntersectionObserver(([seen]) => {
|
|
314
|
+
if (seen.isIntersecting) {
|
|
315
|
+
this.shownAt ||= performance.now();
|
|
316
|
+
window.clearTimeout(this.releaseTimer);
|
|
317
|
+
if (this.viewer)
|
|
318
|
+
this.viewer.setVisible(true);
|
|
319
|
+
else if (!this.starting)
|
|
320
|
+
void this.start();
|
|
321
|
+
}
|
|
322
|
+
else if (this.viewer) {
|
|
323
|
+
this.viewer.setVisible(false);
|
|
324
|
+
const generation = this.generation;
|
|
325
|
+
this.releaseTimer = window.setTimeout(() => {
|
|
326
|
+
this.viewer?.dispose();
|
|
327
|
+
this.viewer = null;
|
|
328
|
+
if (generation === this.generation)
|
|
329
|
+
this.setState("picture");
|
|
330
|
+
}, RELEASE_MS);
|
|
331
|
+
}
|
|
332
|
+
}, { rootMargin: "200px 0px" });
|
|
333
|
+
this.watch.observe(this);
|
|
334
|
+
}
|
|
335
|
+
fallBack(why) {
|
|
336
|
+
this.viewer?.dispose();
|
|
337
|
+
this.viewer = null;
|
|
338
|
+
this.setState("fallback", why);
|
|
339
|
+
this.emit("uchusen-fallback", { reason: why });
|
|
340
|
+
}
|
|
341
|
+
async start() {
|
|
342
|
+
const generation = this.generation;
|
|
343
|
+
const built = this.builtNow;
|
|
344
|
+
const holder = this.holder;
|
|
345
|
+
if (!built || !holder)
|
|
346
|
+
return;
|
|
347
|
+
this.starting = true;
|
|
348
|
+
this.setState("loading");
|
|
349
|
+
const reduced = media("(prefers-reduced-motion: reduce)");
|
|
350
|
+
try {
|
|
351
|
+
const { RocketViewer } = await import("./element/scene.js");
|
|
352
|
+
if (generation !== this.generation)
|
|
353
|
+
return;
|
|
354
|
+
const assets = this.getAttribute("assets");
|
|
355
|
+
const made = new RocketViewer(holder, {
|
|
356
|
+
label: this.getAttribute("label") ?? built.report.label,
|
|
357
|
+
reducedMotion: reduced,
|
|
358
|
+
onLost: () => this.fallBack("context-lost"),
|
|
359
|
+
assets: assets ? `${assets.replace(/\/$/, "")}/` : defaultBase(),
|
|
360
|
+
onPainted: (state) => {
|
|
361
|
+
this.dataset.painted = state;
|
|
362
|
+
this.emit("uchusen-painted", { state });
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
this.viewer = made;
|
|
366
|
+
made.setVisible(true);
|
|
367
|
+
const { look, show, viewName, angle } = this.settings(this.builtNow ?? built);
|
|
368
|
+
this.lastView = viewName;
|
|
369
|
+
await made.load(this.builtNow ?? built, show, look, angle);
|
|
370
|
+
if (generation !== this.generation)
|
|
371
|
+
return;
|
|
372
|
+
made.setSpin(this.spin());
|
|
373
|
+
const wait = revealDelay(this.shownAt, performance.now(), reduced);
|
|
374
|
+
this.revealTimer = window.setTimeout(() => {
|
|
375
|
+
if (generation === this.generation && this.viewer === made) {
|
|
376
|
+
this.setState("3d");
|
|
377
|
+
this.emit("uchusen-ready", { frames: made.frames });
|
|
378
|
+
this.startTilt();
|
|
379
|
+
}
|
|
380
|
+
}, wait);
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
if (generation === this.generation) {
|
|
384
|
+
this.emit("uchusen-error", { error });
|
|
385
|
+
this.fallBack("error");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
this.starting = false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
startTilt() {
|
|
393
|
+
const Orientation = (typeof DeviceOrientationEvent === "undefined" ? undefined : DeviceOrientationEvent);
|
|
394
|
+
const mode = tiltMode({
|
|
395
|
+
reducedMotion: media("(prefers-reduced-motion: reduce)"),
|
|
396
|
+
coarsePointer: media("(pointer: coarse)"),
|
|
397
|
+
hasOrientation: Orientation !== undefined,
|
|
398
|
+
needsPermission: typeof Orientation?.requestPermission === "function",
|
|
399
|
+
});
|
|
400
|
+
const listen = () => {
|
|
401
|
+
let rest = null;
|
|
402
|
+
const onTilt = (event) => {
|
|
403
|
+
const reading = { beta: event.beta, gamma: event.gamma };
|
|
404
|
+
rest ??= reading;
|
|
405
|
+
const turn = tiltTurn(reading, rest, window.screen.orientation?.angle ?? 0);
|
|
406
|
+
if (turn)
|
|
407
|
+
this.viewer?.setTilt(turn.azimuth, turn.polar);
|
|
408
|
+
};
|
|
409
|
+
window.addEventListener("deviceorientation", onTilt);
|
|
410
|
+
this.cleanups.push(() => window.removeEventListener("deviceorientation", onTilt));
|
|
411
|
+
};
|
|
412
|
+
if (mode === "on")
|
|
413
|
+
listen();
|
|
414
|
+
if (mode === "ask" && this.picture) {
|
|
415
|
+
const button = document.createElement("button");
|
|
416
|
+
button.type = "button";
|
|
417
|
+
button.className = "tilt";
|
|
418
|
+
button.setAttribute("part", "tilt");
|
|
419
|
+
button.textContent = "Tilt to turn";
|
|
420
|
+
button.addEventListener("click", async () => {
|
|
421
|
+
button.remove();
|
|
422
|
+
try {
|
|
423
|
+
if ((await Orientation?.requestPermission?.()) === "granted")
|
|
424
|
+
listen();
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// no tilt, then: the view still turns by finger
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
this.picture.appendChild(button);
|
|
431
|
+
this.cleanups.push(() => button.remove());
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const LINEUP_CSS = `
|
|
436
|
+
:host { display: block; overflow-x: auto; color: inherit; }
|
|
437
|
+
svg { display: block; }
|
|
438
|
+
`;
|
|
439
|
+
/** Rockets side by side on one baseline at one scale: `<uchusen-lineup items="starship:v1,starship:v3,person,building" unit="4" ruler labels>`. */
|
|
440
|
+
export class UchusenLineup extends HTMLBase {
|
|
441
|
+
static observedAttributes = ["items", "unit", "look", "view", "show", "ruler", "labels"];
|
|
442
|
+
connectedCallback() {
|
|
443
|
+
if (!this.shadowRoot)
|
|
444
|
+
adopt(this.attachShadow({ mode: "open" }), LINEUP_CSS, lineupSheet);
|
|
445
|
+
this.render();
|
|
446
|
+
}
|
|
447
|
+
attributeChangedCallback(name, before, after) {
|
|
448
|
+
if (before !== after && this.shadowRoot)
|
|
449
|
+
this.render();
|
|
450
|
+
}
|
|
451
|
+
render() {
|
|
452
|
+
const root = this.shadowRoot;
|
|
453
|
+
if (!root)
|
|
454
|
+
return;
|
|
455
|
+
const raw = Number.parseFloat(this.getAttribute("unit") ?? "3");
|
|
456
|
+
const unit = Number.isFinite(raw) && raw > 0 && raw <= 100 ? raw : 3;
|
|
457
|
+
const items = (this.getAttribute("items") ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
458
|
+
const { svg, items: shown } = lineupSvg({
|
|
459
|
+
items,
|
|
460
|
+
unit,
|
|
461
|
+
look: cleanLook(this.getAttribute("look")),
|
|
462
|
+
view: cleanView(this.getAttribute("view") ?? "front"),
|
|
463
|
+
show: this.getAttribute("show") ?? undefined,
|
|
464
|
+
ruler: this.hasAttribute("ruler"),
|
|
465
|
+
labels: this.hasAttribute("labels"),
|
|
466
|
+
});
|
|
467
|
+
const template = document.createElement("template");
|
|
468
|
+
template.innerHTML = svg;
|
|
469
|
+
const node = template.content.firstElementChild;
|
|
470
|
+
if (!node)
|
|
471
|
+
return;
|
|
472
|
+
for (const child of [...root.childNodes])
|
|
473
|
+
child.remove();
|
|
474
|
+
root.appendChild(node);
|
|
475
|
+
this.dataset.count = String(shown.length);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/** Defines both elements, once. Importing this module does it. */
|
|
479
|
+
export function defineUchusenRocket() {
|
|
480
|
+
if (typeof customElements === "undefined")
|
|
481
|
+
return;
|
|
482
|
+
if (!customElements.get("uchusen-rocket"))
|
|
483
|
+
customElements.define("uchusen-rocket", UchusenRocket);
|
|
484
|
+
if (!customElements.get("uchusen-lineup"))
|
|
485
|
+
customElements.define("uchusen-lineup", UchusenLineup);
|
|
486
|
+
}
|
|
487
|
+
defineUchusenRocket();
|
|
488
|
+
export { ROCKETS };
|
package/dist/fit.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type CreditId, type ModelCredit } from "./data/credits.ts";
|
|
2
|
+
import type { Variant } from "./data/types.ts";
|
|
3
|
+
import type { PartRecord } from "./parts.ts";
|
|
4
|
+
export interface Description {
|
|
5
|
+
rocket: string;
|
|
6
|
+
/** Left out: the rocket's default variant. */
|
|
7
|
+
variant?: string;
|
|
8
|
+
/** Another part in a slot, by its id. */
|
|
9
|
+
parts?: Readonly<Record<string, string>>;
|
|
10
|
+
}
|
|
11
|
+
export type FitCode = "unknown-rocket" | "unknown-variant" | "unknown-slot" | "unknown-part" | "wrong-kind" | "no-joint" | "joint-kind" | "joint-diameter" | "custom";
|
|
12
|
+
export interface FitIssue {
|
|
13
|
+
code: FitCode;
|
|
14
|
+
/** "error" stops the build. "note" says what the check decided. */
|
|
15
|
+
level: "error" | "note";
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SlotPart {
|
|
19
|
+
slot: string;
|
|
20
|
+
part: PartRecord;
|
|
21
|
+
}
|
|
22
|
+
export interface FitReport {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
issues: readonly FitIssue[];
|
|
25
|
+
/** What to call it: a real variant's name, or a plain words label for a mix. */
|
|
26
|
+
label: string;
|
|
27
|
+
/** True when any part is not true for the named variant: never presented as a vehicle that flew. */
|
|
28
|
+
custom: boolean;
|
|
29
|
+
variant: Variant | null;
|
|
30
|
+
/** The parts, bottom up. */
|
|
31
|
+
parts: readonly SlotPart[];
|
|
32
|
+
/** Metres, as drawn: the parts' heights added, the widest diameter. Null when it does not fit. */
|
|
33
|
+
size: {
|
|
34
|
+
height: number;
|
|
35
|
+
diameter: number;
|
|
36
|
+
} | null;
|
|
37
|
+
/** Whose work each licensed part is. Empty when only this package's code is used. */
|
|
38
|
+
credits: readonly (ModelCredit & {
|
|
39
|
+
id: CreditId;
|
|
40
|
+
usedFor: readonly string[];
|
|
41
|
+
})[];
|
|
42
|
+
}
|
|
43
|
+
export declare function check(description: Description): FitReport;
|