@johnmorrisdotca/uchusen-rockets 0.3.0 → 0.5.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 +31 -5
- package/NOTICE.md +2 -0
- package/README.md +316 -4
- package/dist/assets/droneship-asog-landed.glb +0 -0
- package/dist/assets/droneship-asog.glb +0 -0
- package/dist/assets/droneship-jrti-landed.glb +0 -0
- package/dist/assets/droneship-jrti.glb +0 -0
- package/dist/assets/droneship-ocisly-landed.glb +0 -0
- package/dist/assets/droneship-ocisly.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5-dragon-2.glb +0 -0
- package/dist/assets/falcon-9-block-5-first-stage.glb +0 -0
- package/dist/assets/falcon-9-block-5.glb +0 -0
- package/dist/assets/falcon-9-ft-first-stage.glb +0 -0
- package/dist/assets/falcon-9-ft.glb +0 -0
- package/dist/assets/falcon-9-v1.1-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.1.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-centre.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-left.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy-side-right.glb +0 -0
- package/dist/assets/falcon-heavy-falcon-heavy.glb +0 -0
- package/dist/assets/landing-zone-lz-1-landed.glb +0 -0
- package/dist/assets/landing-zone-lz-1.glb +0 -0
- package/dist/assets/landing-zone-lz-4-landed.glb +0 -0
- package/dist/assets/landing-zone-lz-4.glb +0 -0
- package/dist/assets/launch-tower-catch-booster.glb +0 -0
- package/dist/assets/launch-tower-catch.glb +0 -0
- package/dist/assets/launch-tower-stack-booster.glb +0 -0
- package/dist/assets/launch-tower-stack-ship.glb +0 -0
- package/dist/assets/launch-tower-stack.glb +0 -0
- package/dist/assets/starship-2019.glb +0 -0
- package/dist/assets/starship-v1-booster.glb +0 -0
- package/dist/assets/starship-v1-flight1-booster.glb +0 -0
- package/dist/assets/starship-v1-flight1-ship.glb +0 -0
- package/dist/assets/starship-v1-flight1.glb +0 -0
- package/dist/assets/starship-v1-ship.glb +0 -0
- package/dist/assets/starship-v1.glb +0 -0
- package/dist/assets/starship-v2-booster.glb +0 -0
- package/dist/assets/starship-v2-ship.glb +0 -0
- package/dist/assets/starship-v2.glb +0 -0
- package/dist/assets/starship-v3-booster.glb +0 -0
- package/dist/assets/starship-v3-ship.glb +0 -0
- package/dist/assets/starship-v3.glb +0 -0
- package/dist/assets/starship-v4-booster.glb +0 -0
- package/dist/assets/starship-v4-ship.glb +0 -0
- package/dist/assets/starship-v4.glb +0 -0
- package/dist/attrs.d.ts +70 -4
- package/dist/attrs.js +59 -4
- package/dist/build/glb.d.ts +25 -0
- package/dist/build/glb.js +29 -7
- package/dist/build/index.d.ts +94 -4
- package/dist/build/index.js +71 -7
- package/dist/build/lineup.d.ts +56 -2
- package/dist/build/lineup.js +23 -2
- package/dist/builder.d.ts +47 -1
- package/dist/builder.js +14 -1
- package/dist/crossfade.d.ts +44 -2
- package/dist/crossfade.js +44 -2
- package/dist/data/credits.d.ts +44 -1
- package/dist/data/credits.js +22 -1
- package/dist/data/flight-log.d.ts +33 -1
- package/dist/data/flight-log.js +22 -1
- package/dist/data/flights.d.ts +61 -2
- package/dist/data/flights.js +160 -3
- package/dist/data/makers.d.ts +21 -0
- package/dist/data/makers.js +10 -0
- package/dist/data/propellants.d.ts +59 -0
- package/dist/data/propellants.js +43 -0
- package/dist/data/sources.d.ts +102 -0
- package/dist/data/sources.js +19 -0
- package/dist/data/types.d.ts +81 -1
- package/dist/data/types.js +26 -1
- package/dist/draw/plume.d.ts +10 -1
- package/dist/draw/plume.js +15 -5
- package/dist/element/plumes.d.ts +7 -8
- package/dist/element/plumes.js +36 -21
- package/dist/element/scene.js +30 -5
- package/dist/element.d.ts +61 -3
- package/dist/element.js +53 -5
- package/dist/embed.d.ts +110 -5
- package/dist/embed.js +88 -5
- package/dist/engines.d.ts +86 -4
- package/dist/engines.js +75 -4
- package/dist/fit.d.ts +68 -0
- package/dist/fit.js +13 -0
- package/dist/flight.d.ts +136 -9
- package/dist/flight.js +130 -10
- package/dist/flightbar.d.ts +12 -0
- package/dist/flightbar.js +13 -1
- package/dist/framing.d.ts +182 -8
- package/dist/framing.js +127 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/layout.d.ts +65 -1
- package/dist/layout.js +43 -1
- package/dist/looks.d.ts +64 -1
- package/dist/looks.js +42 -1
- package/dist/model/falcon.js +2 -2
- package/dist/model/materials.d.ts +32 -0
- package/dist/model/materials.js +10 -0
- package/dist/model/moves.d.ts +36 -0
- package/dist/model/moves.js +70 -0
- package/dist/model/pieces.d.ts +31 -0
- package/dist/model/pieces.js +51 -9
- package/dist/model/registry.d.ts +22 -0
- package/dist/model/registry.js +22 -0
- package/dist/model/starship.d.ts +6 -3
- package/dist/model/starship.js +12 -7
- package/dist/parts.d.ts +33 -1
- package/dist/pixels.d.ts +44 -4
- package/dist/pixels.js +44 -4
- package/dist/react.d.ts +26 -2
- package/dist/react.js +13 -0
- package/dist/rockets.d.ts +91 -3
- package/dist/rockets.js +58 -2
- package/dist/spin.d.ts +57 -2
- package/dist/spin.js +35 -2
- package/dist/tilt.d.ts +48 -4
- package/dist/tilt.js +26 -4
- package/package.json +27 -7
package/dist/data/types.d.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import type { SourceId } from "./sources.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Another value a source gave for a figure, with the source and the reason it was not the one chosen.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { variantOf, type Disagreement } from "@johnmorrisdotca/uchusen-rockets";
|
|
8
|
+
*
|
|
9
|
+
* const others: readonly Disagreement<number>[] = variantOf("starship", "v3")?.facts.parts.booster?.others ?? [];
|
|
10
|
+
* console.log(others.map((other) => other.value));
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
2
13
|
export interface Disagreement<T> {
|
|
3
14
|
value: T;
|
|
4
15
|
source: SourceId;
|
|
5
16
|
note: string;
|
|
6
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* A number or a text with where it came from. A figure with no source is blank (`value` is null) and is never estimated. Where sources disagree, the figure keeps the one chosen and `others` holds the rest.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { variantOf, type Figure } from "@johnmorrisdotca/uchusen-rockets";
|
|
24
|
+
*
|
|
25
|
+
* const height: Figure | undefined = variantOf("starship", "v3")?.facts.height;
|
|
26
|
+
* console.log(height?.value, height?.source);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
7
29
|
export interface Figure<T = number> {
|
|
8
30
|
value: T | null;
|
|
9
31
|
source: SourceId | null;
|
|
@@ -14,10 +36,57 @@ export interface Figure<T = number> {
|
|
|
14
36
|
brief?: string;
|
|
15
37
|
others?: readonly Disagreement<T>[];
|
|
16
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A figure with no value: the shape of "no source gives this".
|
|
41
|
+
*
|
|
42
|
+
* @param note Why there is no value. Optional.
|
|
43
|
+
* @param brief What to say in place of a value wherever it is shown. Optional.
|
|
44
|
+
* @returns A figure whose value and source are null.
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { blank } from "@johnmorrisdotca/uchusen-rockets";
|
|
48
|
+
*
|
|
49
|
+
* console.log(blank("No source read gives it.", "not given"));
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
17
52
|
export declare const blank: <T = number>(note?: string, brief?: string) => Figure<T>;
|
|
18
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* A figure as words for a table: its value and whatever must stay beside it; a blank says why, or that no source gives it.
|
|
55
|
+
*
|
|
56
|
+
* @param figure A figure, or undefined.
|
|
57
|
+
* @param unit Text to put after a number, such as " m". Optional.
|
|
58
|
+
* @returns The figure as words for a table. A blank gives its `brief`, or "No source" when it has none.
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { figureWords, variantOf } from "@johnmorrisdotca/uchusen-rockets";
|
|
62
|
+
*
|
|
63
|
+
* console.log(figureWords(variantOf("starship", "v3")?.facts.height, " m"));
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
19
66
|
export declare function figureWords(figure: Figure<number | string> | undefined, unit?: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* What kind of thing a variant is: flown, announced (not built), an artist's model, a test vehicle or a support item.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { variantOf, type VariantStatus } from "@johnmorrisdotca/uchusen-rockets";
|
|
73
|
+
*
|
|
74
|
+
* const status: VariantStatus | undefined = variantOf("starship", "v4")?.status;
|
|
75
|
+
* console.log(status);
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
20
78
|
export type VariantStatus = "flown" | "announced" | "artist-model" | "test-vehicle" | "support";
|
|
79
|
+
/**
|
|
80
|
+
* The sourced numbers of a variant: its height and diameter, each part's height by slot, its engines, other facts and any sizes that are not a height or a width.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { variantOf, type VariantFacts } from "@johnmorrisdotca/uchusen-rockets";
|
|
85
|
+
*
|
|
86
|
+
* const facts: VariantFacts | undefined = variantOf("falcon-9", "block-5")?.facts;
|
|
87
|
+
* console.log(facts?.height.value, Object.keys(facts?.parts ?? {}));
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
21
90
|
export interface VariantFacts {
|
|
22
91
|
/** Overall height with every part in place, metres. */
|
|
23
92
|
height: Figure;
|
|
@@ -31,6 +100,17 @@ export interface VariantFacts {
|
|
|
31
100
|
/** Sizes that are not a height or a width, shown as they are named here: "Deck length". */
|
|
32
101
|
sizes?: Readonly<Record<string, Figure>>;
|
|
33
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* One version of a rocket as data: its id, name, status, first flight, its parts from the bottom up, and the sourced facts about it.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { variantOf, type Variant } from "@johnmorrisdotca/uchusen-rockets";
|
|
109
|
+
*
|
|
110
|
+
* const variant: Variant | undefined = variantOf("starship", "v3");
|
|
111
|
+
* console.log(variant?.name, variant?.slots);
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
34
114
|
export interface Variant {
|
|
35
115
|
/** "v1", "v2" and so on; a label for what is not a numbered version. */
|
|
36
116
|
id: string;
|
package/dist/data/types.js
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A figure with no value: the shape of "no source gives this".
|
|
3
|
+
*
|
|
4
|
+
* @param note Why there is no value. Optional.
|
|
5
|
+
* @param brief What to say in place of a value wherever it is shown. Optional.
|
|
6
|
+
* @returns A figure whose value and source are null.
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { blank } from "@johnmorrisdotca/uchusen-rockets";
|
|
10
|
+
*
|
|
11
|
+
* console.log(blank("No source read gives it.", "not given"));
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
1
14
|
export const blank = (note, brief) => ({ value: null, source: null, ...(note ? { note } : {}), ...(brief ? { brief } : {}) });
|
|
2
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* A figure as words for a table: its value and whatever must stay beside it; a blank says why, or that no source gives it.
|
|
17
|
+
*
|
|
18
|
+
* @param figure A figure, or undefined.
|
|
19
|
+
* @param unit Text to put after a number, such as " m". Optional.
|
|
20
|
+
* @returns The figure as words for a table. A blank gives its `brief`, or "No source" when it has none.
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { figureWords, variantOf } from "@johnmorrisdotca/uchusen-rockets";
|
|
24
|
+
*
|
|
25
|
+
* console.log(figureWords(variantOf("starship", "v3")?.facts.height, " m"));
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
3
28
|
export function figureWords(figure, unit = "") {
|
|
4
29
|
if (!figure || figure.value === null)
|
|
5
30
|
return figure?.brief ?? "No source";
|
package/dist/draw/plume.d.ts
CHANGED
|
@@ -2,7 +2,16 @@ import type { Camera } from "./svg.ts";
|
|
|
2
2
|
import type { Vec3 } from "../framing.ts";
|
|
3
3
|
import { type Bell } from "../engines.ts";
|
|
4
4
|
import type { LookName } from "../looks.ts";
|
|
5
|
+
import type { FlameKind } from "../data/propellants.ts";
|
|
6
|
+
/** A flame's colours in a picture, by the kind its engine's propellant gives: the hot core, the middle and the cool edge, and the line looks' stroke. A picture, not a measurement of a flame. */
|
|
7
|
+
export declare const FLAME_COLOURS: Readonly<Record<FlameKind, {
|
|
8
|
+
core: string;
|
|
9
|
+
mid: string;
|
|
10
|
+
edge: string;
|
|
11
|
+
line: string;
|
|
12
|
+
inner: string;
|
|
13
|
+
}>>;
|
|
5
14
|
/** The flames for these bells, in picture coordinates. Empty when there are none. */
|
|
6
|
-
export declare function plumeMarkup(bells: readonly Bell[], at: Vec3, cam: Camera, look: LookName, prefix: string): string;
|
|
15
|
+
export declare function plumeMarkup(bells: readonly Bell[], at: Vec3, cam: Camera, look: LookName, prefix: string, kind?: FlameKind): string;
|
|
7
16
|
/** The engines of a part seen from below, each in its own place, the burning ones filled: `engine by engine`. Its words are for a screen reader. */
|
|
8
17
|
export declare function engineChart(bells: readonly Bell[], burning: readonly Bell[], label: string): string;
|
package/dist/draw/plume.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { PLUME_DIAMONDS, plumeLength } from "../engines.js";
|
|
2
|
+
/** A flame's colours in a picture, by the kind its engine's propellant gives: the hot core, the middle and the cool edge, and the line looks' stroke. A picture, not a measurement of a flame. */
|
|
3
|
+
export const FLAME_COLOURS = {
|
|
4
|
+
kerosene: { core: "#fff4d2", mid: "#ff9a3a", edge: "#ff4a12", line: "#e7a24a", inner: "#ffd7a1" },
|
|
5
|
+
methane: { core: "#fff1e6", mid: "#9aa5ff", edge: "#5d6dff", line: "#8f9bf0", inner: "#d9ddff" },
|
|
6
|
+
hydrogen: { core: "#ffffff", mid: "#b8dcff", edge: "#6fa8ff", line: "#9cc7f5", inner: "#e4f1ff" },
|
|
7
|
+
solid: { core: "#fff1c4", mid: "#ffb02e", edge: "#ff3d0a", line: "#f0a030", inner: "#ffe0a0" },
|
|
8
|
+
hypergolic: { core: "#ffffff", mid: "#e6ecff", edge: "#b8c4ee", line: "#c3cbe3", inner: "#f2f5ff" },
|
|
9
|
+
neutral: { core: "#ffffff", mid: "#d5dae3", edge: "#9aa3b2", line: "#b4bcc9", inner: "#e6e9ef" },
|
|
10
|
+
};
|
|
2
11
|
const n = (v) => (Math.round(v * 100) / 100).toString();
|
|
3
12
|
function hull(points) {
|
|
4
13
|
const p = [...points].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
@@ -19,9 +28,10 @@ function hull(points) {
|
|
|
19
28
|
}
|
|
20
29
|
const pathOf = (pts) => `M${pts.map(([x, y]) => `${n(x)} ${n(y)}`).join("L")}Z`;
|
|
21
30
|
/** The flames for these bells, in picture coordinates. Empty when there are none. */
|
|
22
|
-
export function plumeMarkup(bells, at, cam, look, prefix) {
|
|
31
|
+
export function plumeMarkup(bells, at, cam, look, prefix, kind = "neutral") {
|
|
23
32
|
if (bells.length === 0)
|
|
24
33
|
return "";
|
|
34
|
+
const colour = FLAME_COLOURS[kind];
|
|
25
35
|
const painted = look === "painted";
|
|
26
36
|
const stroke = look === "thick" ? 2.4 : 1.2;
|
|
27
37
|
const parts = [];
|
|
@@ -45,17 +55,17 @@ export function plumeMarkup(bells, at, cam, look, prefix) {
|
|
|
45
55
|
const rx = Math.max(1.2, (Math.max(...xs) - Math.min(...xs)) / 2);
|
|
46
56
|
const ry = Math.max(1.2, (Math.max(...ys) - Math.min(...ys)) / 2);
|
|
47
57
|
const defs = painted
|
|
48
|
-
? `<defs><linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${n(mouthPoint[0])}" y1="${n(mouthPoint[1])}" x2="${n(tipPoint[0])}" y2="${n(tipPoint[1])}"><stop offset="0" stop-color="
|
|
58
|
+
? `<defs><linearGradient id="${id}" gradientUnits="userSpaceOnUse" x1="${n(mouthPoint[0])}" y1="${n(mouthPoint[1])}" x2="${n(tipPoint[0])}" y2="${n(tipPoint[1])}"><stop offset="0" stop-color="${colour.core}"/><stop offset="0.45" stop-color="${colour.mid}"/><stop offset="1" stop-color="${colour.edge}" stop-opacity="0"/></linearGradient><radialGradient id="${glow}" gradientUnits="userSpaceOnUse" cx="${n(cx)}" cy="${n(cy)}" r="${n(Math.max(rx, ry))}"><stop offset="0" stop-color="${colour.core}"/><stop offset="1" stop-color="${colour.mid}" stop-opacity="0"/></radialGradient></defs>`
|
|
49
59
|
: "";
|
|
50
|
-
const flame = shape.length >= 3 ? (painted ? `<path data-plume="1" d="${pathOf(shape)}" fill="url(#${id})" stroke="none"/>` : `<path data-plume="1" d="${pathOf(shape)}" fill="none" stroke="
|
|
51
|
-
const ring = painted ? `<ellipse data-glow="1" cx="${n(cx)}" cy="${n(cy)}" rx="${n(rx * 1.35)}" ry="${n(ry * 1.35)}" fill="url(#${glow})"/>` : `<ellipse data-glow="1" cx="${n(cx)}" cy="${n(cy)}" rx="${n(rx * 1.2)}" ry="${n(ry * 1.2)}" fill="none" stroke="
|
|
60
|
+
const flame = shape.length >= 3 ? (painted ? `<path data-plume="1" d="${pathOf(shape)}" fill="url(#${id})" stroke="none"/>` : `<path data-plume="1" d="${pathOf(shape)}" fill="none" stroke="${colour.line}" stroke-width="${stroke}"/><path d="M${n(mouthPoint[0])} ${n(mouthPoint[1])}L${n(tipPoint[0])} ${n(tipPoint[1])}" fill="none" stroke="${colour.inner}" stroke-width="${stroke * 0.6}"/>`) : "";
|
|
61
|
+
const ring = painted ? `<ellipse data-glow="1" cx="${n(cx)}" cy="${n(cy)}" rx="${n(rx * 1.35)}" ry="${n(ry * 1.35)}" fill="url(#${glow})"/>` : `<ellipse data-glow="1" cx="${n(cx)}" cy="${n(cy)}" rx="${n(rx * 1.2)}" ry="${n(ry * 1.2)}" fill="none" stroke="${colour.line}" stroke-width="${stroke}"/>`;
|
|
52
62
|
// the first few shock diamonds, as small rings down the flame: a picture
|
|
53
63
|
let diamonds = "";
|
|
54
64
|
for (let k = 1; k <= 3; k++) {
|
|
55
65
|
const along = k / PLUME_DIAMONDS;
|
|
56
66
|
const [dx, dy] = cam.project([mouth[0], mouth[1] - plumeLength(bell.exit) * along, mouth[2]]);
|
|
57
67
|
const size = n(Math.max(0.9, rx * 0.5 * (1 - along)));
|
|
58
|
-
diamonds += painted ? `<ellipse data-diamond="1" cx="${n(dx)}" cy="${n(dy)}" rx="${size}" ry="${size}" fill="
|
|
68
|
+
diamonds += painted ? `<ellipse data-diamond="1" cx="${n(dx)}" cy="${n(dy)}" rx="${size}" ry="${size}" fill="${colour.core}" fill-opacity="0.7"/>` : `<ellipse data-diamond="1" cx="${n(dx)}" cy="${n(dy)}" rx="${size}" ry="${size}" fill="none" stroke="${colour.line}" stroke-width="${stroke * 0.6}"/>`;
|
|
59
69
|
}
|
|
60
70
|
if (flame !== "")
|
|
61
71
|
parts.push(`<g>${defs}${ring}${flame}${diamonds}</g>`);
|
package/dist/element/plumes.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import type { FlameKind } from "../data/propellants.ts";
|
|
1
2
|
import { BufferGeometry, Group, PointLight, ShaderMaterial } from "three";
|
|
2
3
|
import { type Bell } from "../engines.ts";
|
|
3
|
-
/** What burns in each engine's flame, as a picture: a fuel's flame is drawn in its own colours. */
|
|
4
|
-
export type Fuel = "methane" | "kerosene";
|
|
5
4
|
/** One part's flames: at most as many as it has bells. Its buffers are made once and written when the engines that burn change. */
|
|
6
5
|
export declare class PartPlumes {
|
|
7
6
|
readonly group: Group<import("three").Object3DEventMap>;
|
|
@@ -9,23 +8,23 @@ export declare class PartPlumes {
|
|
|
9
8
|
private flames;
|
|
10
9
|
private glows;
|
|
11
10
|
private key;
|
|
12
|
-
constructor(kit: PlumeKit, bells: readonly Bell[]);
|
|
11
|
+
constructor(kit: PlumeKit, bells: readonly Bell[], kind: FlameKind);
|
|
13
12
|
/** The bells that burn now. Nothing is written when they are the ones that burned last. */
|
|
14
13
|
light(burning: readonly Bell[]): void;
|
|
15
14
|
dispose(): void;
|
|
16
15
|
}
|
|
17
|
-
/** One set of everything a flame needs, for one viewer. */
|
|
16
|
+
/** One set of everything a flame needs, for one viewer. The materials of each kind of flame share one program and one set of clocks; only the two colours differ. */
|
|
18
17
|
export declare class PlumeKit {
|
|
19
18
|
readonly shape: BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
20
19
|
readonly quad: BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
21
|
-
readonly flame: ShaderMaterial;
|
|
22
|
-
readonly glow: ShaderMaterial;
|
|
23
20
|
/** The hull's own glow in the painted look. Always in the scene, at no strength when no engine burns, so lighting engines never makes the materials build again. */
|
|
24
21
|
readonly light: PointLight;
|
|
25
22
|
private uniforms;
|
|
26
|
-
|
|
23
|
+
private kinds;
|
|
24
|
+
private kind;
|
|
25
|
+
flameFor(kind: FlameKind): ShaderMaterial;
|
|
26
|
+
glowFor(kind: FlameKind): ShaderMaterial;
|
|
27
27
|
setLook(painted: boolean): void;
|
|
28
|
-
setFuel(fuel: Fuel): void;
|
|
29
28
|
/** 0 near the ground, 1 high up where a flame fans out. */
|
|
30
29
|
setAir(air: number): void;
|
|
31
30
|
/** Where the ground stands, for a flame to stop at. */
|
package/dist/element/plumes.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Flames for the 3D view, made here and loaded only with it. One shape, one material and one instanced draw for every engine of a part, however many burn, so lighting and dousing engines changes numbers, never what the graphics card holds. The look of a flame (its shock diamonds, its length, its glow) is a picture, not a measurement.
|
|
2
1
|
import { AdditiveBlending, BufferGeometry, Color, DoubleSide, DynamicDrawUsage, Float32BufferAttribute, Group, InstancedMesh, Matrix4, PointLight, Quaternion, ShaderMaterial, Vector3 } from "three";
|
|
3
2
|
import { PLUME_DIAMONDS, plumeLength } from "../engines.js";
|
|
4
3
|
const STEPS = 28;
|
|
@@ -100,9 +99,14 @@ function glowShape() {
|
|
|
100
99
|
geometry.setIndex([0, 1, 2, 0, 2, 3]);
|
|
101
100
|
return geometry;
|
|
102
101
|
}
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
/** What a flame is drawn in: the kind of flame its engine's propellant gives (`src/data/propellants.ts`), and a neutral grey-white where no page read says. A picture, not a measurement. */
|
|
103
|
+
const FLAME_TONES = {
|
|
105
104
|
kerosene: { hot: "#fff4d2", cool: "#ff7a1a" },
|
|
105
|
+
methane: { hot: "#fff1e6", cool: "#6f7dff" },
|
|
106
|
+
hydrogen: { hot: "#ffffff", cool: "#6fa8ff" },
|
|
107
|
+
solid: { hot: "#fff1c4", cool: "#ff4a12" },
|
|
108
|
+
hypergolic: { hot: "#ffffff", cool: "#b8c4ee" },
|
|
109
|
+
neutral: { hot: "#ffffff", cool: "#9aa3b2" },
|
|
106
110
|
};
|
|
107
111
|
const place = new Matrix4();
|
|
108
112
|
const still = new Quaternion();
|
|
@@ -115,11 +119,11 @@ export class PartPlumes {
|
|
|
115
119
|
flames;
|
|
116
120
|
glows;
|
|
117
121
|
key = "";
|
|
118
|
-
constructor(kit, bells) {
|
|
122
|
+
constructor(kit, bells, kind) {
|
|
119
123
|
this.bells = bells;
|
|
120
124
|
this.group.name = "plumes";
|
|
121
|
-
this.flames = new InstancedMesh(kit.shape, kit.
|
|
122
|
-
this.glows = new InstancedMesh(kit.quad, kit.
|
|
125
|
+
this.flames = new InstancedMesh(kit.shape, kit.flameFor(kind), bells.length);
|
|
126
|
+
this.glows = new InstancedMesh(kit.quad, kit.glowFor(kind), bells.length);
|
|
123
127
|
for (const mesh of [this.flames, this.glows]) {
|
|
124
128
|
mesh.instanceMatrix.setUsage(DynamicDrawUsage);
|
|
125
129
|
mesh.frustumCulled = false;
|
|
@@ -149,12 +153,10 @@ export class PartPlumes {
|
|
|
149
153
|
this.glows.dispose();
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
|
-
/** One set of everything a flame needs, for one viewer. */
|
|
156
|
+
/** One set of everything a flame needs, for one viewer. The materials of each kind of flame share one program and one set of clocks; only the two colours differ. */
|
|
153
157
|
export class PlumeKit {
|
|
154
158
|
shape = flameShape();
|
|
155
159
|
quad = glowShape();
|
|
156
|
-
flame;
|
|
157
|
-
glow;
|
|
158
160
|
/** The hull's own glow in the painted look. Always in the scene, at no strength when no engine burns, so lighting engines never makes the materials build again. */
|
|
159
161
|
light = new PointLight("#ffb56b", 0, 0, 1.6);
|
|
160
162
|
uniforms = {
|
|
@@ -164,21 +166,31 @@ export class PlumeKit {
|
|
|
164
166
|
uLine: { value: 1 },
|
|
165
167
|
uFloor: { value: -1e6 },
|
|
166
168
|
uGlow: { value: 0.75 },
|
|
167
|
-
uHot: { value: new Color(FUEL_COLOURS.kerosene.hot) },
|
|
168
|
-
uCool: { value: new Color(FUEL_COLOURS.kerosene.cool) },
|
|
169
169
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
kinds = new Map();
|
|
171
|
+
kind(kind) {
|
|
172
|
+
const known = this.kinds.get(kind);
|
|
173
|
+
if (known)
|
|
174
|
+
return known;
|
|
175
|
+
const tone = FLAME_TONES[kind];
|
|
176
|
+
const uniforms = { ...this.uniforms, uHot: { value: new Color(tone.hot) }, uCool: { value: new Color(tone.cool) } };
|
|
177
|
+
const common = { transparent: true, depthWrite: false, blending: AdditiveBlending, toneMapped: false, uniforms };
|
|
178
|
+
const made = {
|
|
179
|
+
flame: new ShaderMaterial({ ...common, vertexShader: FLAME_VERTEX, fragmentShader: FLAME_FRAGMENT, side: DoubleSide }),
|
|
180
|
+
glow: new ShaderMaterial({ ...common, vertexShader: GLOW_VERTEX, fragmentShader: GLOW_FRAGMENT }),
|
|
181
|
+
};
|
|
182
|
+
this.kinds.set(kind, made);
|
|
183
|
+
return made;
|
|
184
|
+
}
|
|
185
|
+
flameFor(kind) {
|
|
186
|
+
return this.kind(kind).flame;
|
|
187
|
+
}
|
|
188
|
+
glowFor(kind) {
|
|
189
|
+
return this.kind(kind).glow;
|
|
174
190
|
}
|
|
175
191
|
setLook(painted) {
|
|
176
192
|
this.uniforms.uLine.value = painted ? 0 : 1;
|
|
177
193
|
}
|
|
178
|
-
setFuel(fuel) {
|
|
179
|
-
this.uniforms.uHot.value.set(FUEL_COLOURS[fuel].hot);
|
|
180
|
-
this.uniforms.uCool.value.set(FUEL_COLOURS[fuel].cool);
|
|
181
|
-
}
|
|
182
194
|
/** 0 near the ground, 1 high up where a flame fans out. */
|
|
183
195
|
setAir(air) {
|
|
184
196
|
this.uniforms.uAir.value = air;
|
|
@@ -200,8 +212,11 @@ export class PlumeKit {
|
|
|
200
212
|
dispose() {
|
|
201
213
|
this.shape.dispose();
|
|
202
214
|
this.quad.dispose();
|
|
203
|
-
this.
|
|
204
|
-
|
|
215
|
+
for (const { flame, glow } of this.kinds.values()) {
|
|
216
|
+
flame.dispose();
|
|
217
|
+
glow.dispose();
|
|
218
|
+
}
|
|
219
|
+
this.kinds.clear();
|
|
205
220
|
this.light.dispose();
|
|
206
221
|
this.light.removeFromParent();
|
|
207
222
|
}
|
package/dist/element/scene.js
CHANGED
|
@@ -77,8 +77,6 @@ void main() {
|
|
|
77
77
|
/** The materials of an engine's bell, in this package's own meshes. */
|
|
78
78
|
const NOZZLE_MATERIALS = new Set(["bell", "bell-inside", "engine-metal"]);
|
|
79
79
|
const asVec = (v) => [v.x, v.y, v.z];
|
|
80
|
-
/** A flame's colours, a picture: Raptor burns methane, Merlin kerosene. */
|
|
81
|
-
const fuelOf = (built) => (built.report.parts.some(({ part }) => part.rocket === "starship") ? "methane" : "kerosene");
|
|
82
80
|
/** The pictures a licensed model's material may hold (the shared sky it reflects is not one of them). */
|
|
83
81
|
const MODEL_PICTURES = ["map", "normalMap", "roughnessMap", "metalnessMap", "aoMap", "emissiveMap"];
|
|
84
82
|
export class RocketViewer {
|
|
@@ -242,8 +240,20 @@ export class RocketViewer {
|
|
|
242
240
|
to: [0, 0, 0],
|
|
243
241
|
hideAtEnd: false,
|
|
244
242
|
plumes: null,
|
|
243
|
+
moves: [],
|
|
245
244
|
};
|
|
246
245
|
this.takeMeshes(source, code, view);
|
|
246
|
+
for (const node of source.children.slice(1)) {
|
|
247
|
+
const axis = node.userData.axis;
|
|
248
|
+
if (!axis)
|
|
249
|
+
continue;
|
|
250
|
+
const group = new Group();
|
|
251
|
+
group.name = node.name;
|
|
252
|
+
group.position.copy(node.position);
|
|
253
|
+
code.add(group);
|
|
254
|
+
this.takeMeshes(node, group, view);
|
|
255
|
+
view.moves.push({ name: String(node.userData.group), group, axis: new Vector3(axis[0], axis[1], axis[2]).normalize(), angle: 0 });
|
|
256
|
+
}
|
|
247
257
|
const place = places.find((p) => p.slot === part.slot);
|
|
248
258
|
holder.position.set(...(place?.at ?? [0, 0, 0]));
|
|
249
259
|
holder.visible = place?.shown ?? false;
|
|
@@ -252,7 +262,6 @@ export class RocketViewer {
|
|
|
252
262
|
}
|
|
253
263
|
this.addGlow();
|
|
254
264
|
this.setLook(look);
|
|
255
|
-
this.plumes.setFuel(fuelOf(built));
|
|
256
265
|
this.frameTo(angle ?? this.currentAngle(), false);
|
|
257
266
|
if (this.clockNow) {
|
|
258
267
|
this.flightKey = "";
|
|
@@ -455,10 +464,11 @@ export class RocketViewer {
|
|
|
455
464
|
for (const view of this.parts) {
|
|
456
465
|
const count = counts.get(view.slot) ?? 0;
|
|
457
466
|
if (count > 0 && !view.plumes) {
|
|
458
|
-
const
|
|
467
|
+
const built = this.built.parts.find((part) => part.slot === view.slot);
|
|
468
|
+
const bells = bellsOf(built?.pieces ?? []);
|
|
459
469
|
if (bells.length === 0)
|
|
460
470
|
continue;
|
|
461
|
-
view.plumes = new PartPlumes(this.plumes, bells);
|
|
471
|
+
view.plumes = new PartPlumes(this.plumes, bells, built?.flame ?? "neutral");
|
|
462
472
|
view.holder.add(view.plumes.group);
|
|
463
473
|
}
|
|
464
474
|
if (!view.plumes)
|
|
@@ -507,7 +517,12 @@ export class RocketViewer {
|
|
|
507
517
|
part.holder.position.set(...(place?.at ?? [0, 0, 0]));
|
|
508
518
|
part.holder.rotation.set(0, 0, 0);
|
|
509
519
|
part.holder.visible = place?.shown ?? false;
|
|
520
|
+
for (const move of part.moves) {
|
|
521
|
+
move.angle = 0;
|
|
522
|
+
move.group.quaternion.identity();
|
|
523
|
+
}
|
|
510
524
|
}
|
|
525
|
+
this.canvas.dataset.turned = "";
|
|
511
526
|
this.plumes.setAir(0);
|
|
512
527
|
this.plumes.setFloor(-1e6);
|
|
513
528
|
this.reframe();
|
|
@@ -583,10 +598,20 @@ export class RocketViewer {
|
|
|
583
598
|
continue;
|
|
584
599
|
const turn = MathUtils.degToRad(pose.pitch);
|
|
585
600
|
const middle = (parts.find((one) => one.slot === part.slot)?.height ?? 0) / 2;
|
|
601
|
+
for (const move of part.moves) {
|
|
602
|
+
const angle = pose.moves[move.name] ?? 0;
|
|
603
|
+
if (angle === move.angle)
|
|
604
|
+
continue;
|
|
605
|
+
move.angle = angle;
|
|
606
|
+
move.group.quaternion.setFromAxisAngle(move.axis, MathUtils.degToRad(angle));
|
|
607
|
+
}
|
|
586
608
|
part.holder.visible = !pose.gone;
|
|
587
609
|
part.holder.rotation.set(0, 0, turn);
|
|
588
610
|
part.holder.position.set(place.at[0] + pose.dx + Math.sin(turn) * middle, place.at[1] + pose.dy + middle - Math.cos(turn) * middle, place.at[2]);
|
|
589
611
|
}
|
|
612
|
+
const turned = this.parts.flatMap((part) => part.moves.filter((move) => move.angle !== 0).map((move) => `${part.slot}:${move.group.name.split("~").slice(1).join("~")}=${Math.round(move.angle)}`)).join(" ");
|
|
613
|
+
if (turned !== this.canvas.dataset.turned)
|
|
614
|
+
this.canvas.dataset.turned = turned;
|
|
590
615
|
this.plumes.setAir(frame.air);
|
|
591
616
|
this.plumes.setFloor(frame.groundY);
|
|
592
617
|
this.ground?.set(frame.groundY, this.options.reducedMotion ? 0 : frame.smoke, performance.now() / 1000);
|
package/dist/element.d.ts
CHANGED
|
@@ -3,10 +3,45 @@ import { UchusenFlight } from "./flightbar.ts";
|
|
|
3
3
|
import type { Description, FitReport } from "./fit.ts";
|
|
4
4
|
import { type Angle, type ViewChoice } from "./framing.ts";
|
|
5
5
|
import { ROCKETS } from "./rockets.ts";
|
|
6
|
+
/**
|
|
7
|
+
* What a `<uchusen-rocket>` is doing, as its `data-state` attribute says: `picture` (the drawing), `loading` (the 3D view is on its way), `3d`, `fallback` (no WebGL, so the drawing stays) or `error`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import type { RocketState } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
12
|
+
*
|
|
13
|
+
* const states: RocketState[] = ["picture", "loading", "3d", "fallback", "error"];
|
|
14
|
+
* console.log(states.length);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
6
17
|
export type RocketState = "picture" | "loading" | "3d" | "fallback" | "error";
|
|
7
18
|
declare const HTMLBase: typeof HTMLElement;
|
|
8
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Where the licensed models are, for every element with no `assets` attribute of its own. Left unset: beside this module, in ./assets/.
|
|
21
|
+
*
|
|
22
|
+
* @param url The folder the licensed models are in. A trailing slash is added if it has none.
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts no-run
|
|
25
|
+
* import { setAssetBase } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
26
|
+
*
|
|
27
|
+
* setAssetBase("/static/uchusen-assets/");
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
9
30
|
export declare function setAssetBase(url: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* The `<uchusen-rocket>` element: a rocket as a drawing that a 3D view takes over from when the browser can draw WebGL. Its attributes are the words of `RocketOptions`. It exposes its state as `data-state` and loads three.js only for a 3D view.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts no-run
|
|
36
|
+
* import { UchusenRocket } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
37
|
+
*
|
|
38
|
+
* const rocket: UchusenRocket = document.createElement("uchusen-rocket") as UchusenRocket;
|
|
39
|
+
* rocket.setAttribute("rocket", "starship");
|
|
40
|
+
* rocket.setAttribute("variant", "v3");
|
|
41
|
+
* rocket.setAttribute("look", "painted");
|
|
42
|
+
* document.body.append(rocket);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
10
45
|
export declare class UchusenRocket extends HTMLBase {
|
|
11
46
|
static readonly observedAttributes: string[];
|
|
12
47
|
private descriptionValue;
|
|
@@ -117,14 +152,37 @@ export declare class UchusenRocket extends HTMLBase {
|
|
|
117
152
|
private start;
|
|
118
153
|
private startTilt;
|
|
119
154
|
}
|
|
120
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Rockets side by side on one baseline at one scale: `<uchusen-lineup items="starship:v1,starship:v3,person,building" unit="4" ruler labels>`.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts no-run
|
|
160
|
+
* import { UchusenLineup } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
161
|
+
*
|
|
162
|
+
* const lineup: UchusenLineup = document.createElement("uchusen-lineup") as UchusenLineup;
|
|
163
|
+
* lineup.setAttribute("items", "starship:v2,starship:v3,person");
|
|
164
|
+
* lineup.setAttribute("unit", "3");
|
|
165
|
+
* lineup.toggleAttribute("ruler", true);
|
|
166
|
+
* document.body.append(lineup);
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
121
169
|
export declare class UchusenLineup extends HTMLBase {
|
|
122
170
|
static readonly observedAttributes: string[];
|
|
123
171
|
connectedCallback(): void;
|
|
124
172
|
attributeChangedCallback(name: string, before: string | null, after: string | null): void;
|
|
125
173
|
private render;
|
|
126
174
|
}
|
|
127
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* Defines both elements, once. Importing this module does it.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts no-run
|
|
180
|
+
* import { defineUchusenRocket } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
181
|
+
*
|
|
182
|
+
* defineUchusenRocket();
|
|
183
|
+
* console.log(customElements.get("uchusen-rocket") !== undefined);
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
128
186
|
export declare function defineUchusenRocket(): void;
|
|
129
187
|
export { ROCKETS, UchusenFlight };
|
|
130
188
|
declare global {
|
package/dist/element.js
CHANGED
|
@@ -21,7 +21,17 @@ const RELEASE_MS = 30_000;
|
|
|
21
21
|
/** The drawing, followed along a turn, is made again at most this often. */
|
|
22
22
|
const PLACE_MS = 50;
|
|
23
23
|
let assetBase = null;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Where the licensed models are, for every element with no `assets` attribute of its own. Left unset: beside this module, in ./assets/.
|
|
26
|
+
*
|
|
27
|
+
* @param url The folder the licensed models are in. A trailing slash is added if it has none.
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts no-run
|
|
30
|
+
* import { setAssetBase } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
31
|
+
*
|
|
32
|
+
* setAssetBase("/static/uchusen-assets/");
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
25
35
|
export function setAssetBase(url) {
|
|
26
36
|
assetBase = url.endsWith("/") ? url : `${url}/`;
|
|
27
37
|
}
|
|
@@ -86,6 +96,20 @@ function whyNot(mode) {
|
|
|
86
96
|
return "no-webgl";
|
|
87
97
|
return "";
|
|
88
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* The `<uchusen-rocket>` element: a rocket as a drawing that a 3D view takes over from when the browser can draw WebGL. Its attributes are the words of `RocketOptions`. It exposes its state as `data-state` and loads three.js only for a 3D view.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts no-run
|
|
104
|
+
* import { UchusenRocket } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
105
|
+
*
|
|
106
|
+
* const rocket: UchusenRocket = document.createElement("uchusen-rocket") as UchusenRocket;
|
|
107
|
+
* rocket.setAttribute("rocket", "starship");
|
|
108
|
+
* rocket.setAttribute("variant", "v3");
|
|
109
|
+
* rocket.setAttribute("look", "painted");
|
|
110
|
+
* document.body.append(rocket);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
89
113
|
export class UchusenRocket extends HTMLBase {
|
|
90
114
|
static observedAttributes = ["rocket", "variant", "show", "look", "view", "build", "mode", "assets", "spin", "way", "label", "engines", "phase"];
|
|
91
115
|
descriptionValue = null;
|
|
@@ -479,11 +503,12 @@ export class UchusenRocket extends HTMLBase {
|
|
|
479
503
|
const from = this.driven ? turnedBy(angle, this.driven.yaw, this.driven.tilt) : (this.viewer?.currentAngle() ?? angle);
|
|
480
504
|
const label = this.getAttribute("label") ?? this.builtNow.report.label;
|
|
481
505
|
const lit = flying ? (media("(prefers-reduced-motion: reduce)") ? false : flying.burns) : this.engineLights();
|
|
482
|
-
const
|
|
506
|
+
const moves = flying ? Object.fromEntries(Object.entries(flying.phase.pose).flatMap(([slot, pose]) => (pose.moves ? [[slot, pose.moves]] : []))) : undefined;
|
|
507
|
+
const key = `${this.key}|${look}|${show}|${from.az.toFixed(2)}|${from.el.toFixed(2)}|${label}|${flying ? flying.index : ""}|${JSON.stringify(lit)}|${JSON.stringify(moves ?? {})}`;
|
|
483
508
|
let node = this.drawing.firstElementChild;
|
|
484
509
|
if (key !== this.drawn.key || !node) {
|
|
485
510
|
const template = document.createElement("template");
|
|
486
|
-
template.innerHTML = this.builtNow.svg({ look, show, view: from, engines: lit });
|
|
511
|
+
template.innerHTML = this.builtNow.svg({ look, show, view: from, engines: lit, ...(moves ? { moves } : {}) });
|
|
487
512
|
node = template.content.firstElementChild;
|
|
488
513
|
if (!node)
|
|
489
514
|
return;
|
|
@@ -715,7 +740,20 @@ const LINEUP_CSS = `
|
|
|
715
740
|
:host { display: block; overflow-x: auto; color: inherit; }
|
|
716
741
|
svg { display: block; }
|
|
717
742
|
`;
|
|
718
|
-
/**
|
|
743
|
+
/**
|
|
744
|
+
* Rockets side by side on one baseline at one scale: `<uchusen-lineup items="starship:v1,starship:v3,person,building" unit="4" ruler labels>`.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts no-run
|
|
748
|
+
* import { UchusenLineup } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
749
|
+
*
|
|
750
|
+
* const lineup: UchusenLineup = document.createElement("uchusen-lineup") as UchusenLineup;
|
|
751
|
+
* lineup.setAttribute("items", "starship:v2,starship:v3,person");
|
|
752
|
+
* lineup.setAttribute("unit", "3");
|
|
753
|
+
* lineup.toggleAttribute("ruler", true);
|
|
754
|
+
* document.body.append(lineup);
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
719
757
|
export class UchusenLineup extends HTMLBase {
|
|
720
758
|
static observedAttributes = ["items", "unit", "look", "view", "show", "ruler", "labels"];
|
|
721
759
|
connectedCallback() {
|
|
@@ -754,7 +792,17 @@ export class UchusenLineup extends HTMLBase {
|
|
|
754
792
|
this.dataset.count = String(shown.length);
|
|
755
793
|
}
|
|
756
794
|
}
|
|
757
|
-
/**
|
|
795
|
+
/**
|
|
796
|
+
* Defines both elements, once. Importing this module does it.
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* ```ts no-run
|
|
800
|
+
* import { defineUchusenRocket } from "@johnmorrisdotca/uchusen-rockets/element";
|
|
801
|
+
*
|
|
802
|
+
* defineUchusenRocket();
|
|
803
|
+
* console.log(customElements.get("uchusen-rocket") !== undefined);
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
758
806
|
export function defineUchusenRocket() {
|
|
759
807
|
if (typeof customElements === "undefined")
|
|
760
808
|
return;
|