@johnmorrisdotca/uchusen-rockets 0.4.0 → 0.6.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 +25 -0
- package/NOTICE.md +2 -0
- package/README.md +80 -5
- 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.0-dragon-apart.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-apart.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-apart.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-capsule.glb +0 -0
- package/dist/assets/falcon-9-v1.0-dragon-capsule.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-capsule.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-capsule.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-first-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-dragon-first-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-first-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-first-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-second-stage.glb +0 -0
- package/dist/assets/falcon-9-v1.0-dragon-second-stage.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-second-stage.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-second-stage.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-trunk.glb +0 -0
- package/dist/assets/falcon-9-v1.0-dragon-trunk.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-trunk.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon-trunk.thick.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon.glb +0 -0
- package/dist/assets/falcon-9-v1.0-dragon.painted.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon.svg +1 -0
- package/dist/assets/falcon-9-v1.0-dragon.thick.svg +1 -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/build/glb.js +15 -7
- package/dist/build/index.d.ts +5 -0
- package/dist/build/index.js +16 -3
- package/dist/data/falcon.js +18 -0
- package/dist/data/flights.d.ts +7 -0
- package/dist/data/flights.js +204 -13
- package/dist/data/propellants.d.ts +59 -0
- package/dist/data/propellants.js +43 -0
- package/dist/data/sources.d.ts +92 -0
- package/dist/data/sources.js +12 -0
- 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.d.ts +6 -1
- package/dist/element/scene.js +71 -7
- package/dist/element.d.ts +1 -0
- package/dist/element.js +18 -6
- package/dist/flight.d.ts +2 -0
- package/dist/flight.js +8 -1
- package/dist/flightbar.d.ts +30 -0
- package/dist/flightbar.js +46 -9
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/model/falcon.js +2 -2
- 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.js +2 -2
- package/dist/model/starship.d.ts +6 -3
- package/dist/model/starship.js +12 -7
- package/dist/react.d.ts +3 -3
- package/package.json +18 -3
package/dist/data/flights.js
CHANGED
|
@@ -36,7 +36,7 @@ function starshipFlight(options) {
|
|
|
36
36
|
{ id: "boostback-end", name: "Boostback ends", what: "The booster falls back, steering with its grid fins.", at: time(times.boostbackEnd, source), burns: { ship: six }, pose: { booster: { ...away, pitch: 180 }, ship: { dx: 0.2, dy: 0.08 } }, ground: 1.6, air: 1 },
|
|
37
37
|
{ id: "booster-landing", name: "Booster's landing burn", what: "The booster lights its inner thirteen, then keeps only the inner three.", at: time(times.landing, source), burns: { booster: landingBurn, ship: six }, pose: { booster: { dx: -0.23, dy: -0.1 }, ship: { dx: 0.2, dy: 0.09 } }, ground: 0.45, air: 0.1 },
|
|
38
38
|
{ id: "booster-three", name: "Inner three", what: "The last of the landing burn, on the three middle engines.", at: blank("The page gives no time for this step."), burns: { booster: count(3, SH), ship: six }, pose: { booster: { dx: -0.23, dy: -0.32 }, ship: { dx: 0.2, dy: 0.1 } }, ground: 0.3, air: 0 },
|
|
39
|
-
{ id: "booster-down", name: "Booster down", what: "The landing burn ends.", at: time(times.landingEnd, source), burns: { ship: six }, pose: { booster: { dx: -0.23, rest: true }, ship: { dx: 0.2, dy: 0.11 } }, ground: 0, air: 0.8 },
|
|
39
|
+
{ id: "booster-down", name: "Booster down", what: "The landing burn ends.", at: time(times.landingEnd, source), burns: { ship: six }, pose: { booster: { dx: -0.23, rest: true }, ship: { dx: 0.2, dy: 0.11 } }, ground: 0, air: 0.8, ...(options.boosterSpeed ? { speed: options.boosterSpeed } : {}) },
|
|
40
40
|
{ id: "seco", name: "Ship's engines cut off", what: "The Ship coasts on, above the air.", at: time(times.seco, source), burns: {}, pose: { booster: { dx: -0.23, rest: true }, ship: { dx: 0.2, dy: 0.12 } }, ground: 0, ...(options.apogee ? { altitude: options.apogee } : {}) },
|
|
41
41
|
{ id: "entry", name: "Entry", what: "The Ship comes back into the air, belly first on its tiles.", at: time(times.entry, source), burns: {}, pose: { booster: { dx: -0.23, rest: true }, ship: { dx: 0.2, dy: 0.12, pitch: -80 } }, ground: 0 },
|
|
42
42
|
...(seconds(times.flip) <= seconds(times.shipBurn)
|
|
@@ -64,18 +64,19 @@ const FLIGHT_12 = "wikipedia-flight-12";
|
|
|
64
64
|
const FH = "wikipedia-fh-test-flight";
|
|
65
65
|
const STARLINK = "sfn-starlink-2019";
|
|
66
66
|
const CRS16 = "sfn-crs-16";
|
|
67
|
+
const FLIGHT_1 = "wikipedia-flight-1";
|
|
68
|
+
const CASSIOPE = "sfn-cassiope";
|
|
69
|
+
const ABS = "sfn-abs-eutelsat";
|
|
70
|
+
const F9 = "wikipedia-falcon-9";
|
|
71
|
+
const GRASSHOPPER = "wikipedia-grasshopper";
|
|
72
|
+
const COTS1_TIMELINE = "sfn-cots1-timeline";
|
|
73
|
+
const COTS1_PRESSKIT = "sfn-cots1-presskit";
|
|
74
|
+
const nineMerlin = (note) => count(9, F9, note);
|
|
75
|
+
const oneMerlin = (note) => count(1, F9, note);
|
|
76
|
+
/** The second stage and Dragon on it move as one. */
|
|
77
|
+
const onTop = (dx, dy) => ({ "second-stage": { dx, dy }, trunk: { dx, dy }, capsule: { dx, dy } });
|
|
67
78
|
const lower = (dy, extra = {}) => ({ dx: -0.26, dy, ...extra });
|
|
68
|
-
|
|
69
|
-
* Every flight the package can play, one for each variant that has a timeline. Use `flightOf` to find one.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```ts
|
|
73
|
-
* import { FLIGHTS } from "@johnmorrisdotca/uchusen-rockets";
|
|
74
|
-
*
|
|
75
|
-
* console.log(FLIGHTS.map((flight) => `${flight.rocket}:${flight.variant}`));
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export const FLIGHTS = [
|
|
79
|
+
const BASE_FLIGHTS = [
|
|
79
80
|
starshipFlight({
|
|
80
81
|
id: "starship-flight-5",
|
|
81
82
|
variant: "v1",
|
|
@@ -100,6 +101,7 @@ export const FLIGHTS = [
|
|
|
100
101
|
stageEngines: count(3, SH, "The article's mission profile: all but three of its centre engines."),
|
|
101
102
|
backEngines: count(13, SH, "The article's mission profile: ten more lit beside the three. On this flight nine of the ten lit."),
|
|
102
103
|
landingBurn: count(13, FLIGHT_7, "All 13 engines ignited."),
|
|
104
|
+
apogee: { value: "146 km: where the Ship was when it was lost, 27 seconds before the planned cutoff", source: FLIGHT_7 },
|
|
103
105
|
shipLanding: [],
|
|
104
106
|
}),
|
|
105
107
|
starshipFlight({
|
|
@@ -112,6 +114,8 @@ export const FLIGHTS = [
|
|
|
112
114
|
stageEngines: count(5, SH, "The article's interstage section, for this booster: all but five inner engines. Its older mission profile says three."),
|
|
113
115
|
backEngines: count(33, SH, "The article's propulsion section: the ascent and boostback burns use all thirty-three. The flight's page counts 28 to relight, beside the five already burning."),
|
|
114
116
|
landingBurn: count(13, SH, "The inner thirteen."),
|
|
117
|
+
apogee: { value: "195 km, the highest of the Ship's orbit", source: FLIGHT_12 },
|
|
118
|
+
boosterSpeed: { value: "1,450 km/h when the booster hit the water in the Gulf, which it did not land from", source: FLIGHT_12 },
|
|
115
119
|
shipLanding: [
|
|
116
120
|
{ id: "ship-two", name: "Three to two engines", what: "The Ship's landing burn drops to two.", at: time("+01:05:17", FLIGHT_12), burn: count(2, FLIGHT_12) },
|
|
117
121
|
{ id: "ship-one", name: "Two to one engine", what: "And to one, for the last seconds.", at: time("+01:05:24", FLIGHT_12), burn: count(1, FLIGHT_12) },
|
|
@@ -158,7 +162,7 @@ export const FLIGHTS = [
|
|
|
158
162
|
{ id: "entry-burn", name: "Entry burn", what: "Three engines again, to slow the fall.", at: time("+00:06:35", CRS16), burns: { "first-stage": THREE, "second-stage": UPPER }, pose: { "first-stage": lower(-0.14), "second-stage": { dx: 0.22, dy: 0.08 }, trunk: { dx: 0.22, dy: 0.08 }, capsule: { dx: 0.22, dy: 0.08 } }, ground: 0.8, air: 0.5, altitude: { value: "from about 70 to 40 km", source: "wikipedia-landing-tests", note: "The landing tests article, for the burn in general." } },
|
|
159
163
|
{ id: "landing-burn", name: "Landing burn", what: "Only the centre engine, just before touchdown.", at: blank("This timeline gives no time for its start."), burns: { "first-stage": ONE, "second-stage": UPPER }, pose: { "first-stage": lower(-0.3), "second-stage": { dx: 0.22, dy: 0.17 }, trunk: { dx: 0.22, dy: 0.17 }, capsule: { dx: 0.22, dy: 0.17 } }, ground: 0.22, air: 0 },
|
|
160
164
|
{ id: "landing", name: "Landing", what: "The planned landing, on plain ground here.", at: time("+00:08:17", CRS16), burns: { "second-stage": UPPER }, pose: { "first-stage": { dx: -0.26, rest: true }, "second-stage": { dx: 0.22, dy: 0.19 }, trunk: { dx: 0.22, dy: 0.19 }, capsule: { dx: 0.22, dy: 0.19 } }, ground: 0, air: 1 },
|
|
161
|
-
{ id: "seco", name: "SECO", what: "The second stage shuts down.", at: time("+00:08:51", CRS16), burns: {}, pose: { "first-stage": { dx: -0.26, rest: true }, "second-stage": { dx: 0.22, dy: 0.11 }, trunk: { dx: 0.22, dy: 0.11 }, capsule: { dx: 0.22, dy: 0.11 } }, ground: 0 },
|
|
165
|
+
{ id: "seco", name: "SECO", what: "The second stage shuts down.", at: time("+00:08:51", CRS16), burns: {}, pose: { "first-stage": { dx: -0.26, rest: true }, "second-stage": { dx: 0.22, dy: 0.11 }, trunk: { dx: 0.22, dy: 0.11 }, capsule: { dx: 0.22, dy: 0.11 } }, ground: 0, altitude: { value: "an orbit with a low point of about 124 miles (200 km) and a high point of about 223 miles (360 km), as planned", source: CRS16 } },
|
|
162
166
|
{ id: "dragon", name: "Dragon separates", what: "The spacecraft comes away from the second stage.", at: time("+00:09:51", CRS16), burns: {}, pose: { "first-stage": { dx: -0.26, rest: true }, "second-stage": { dx: 0.22, dy: 0.17 }, trunk: { dx: 0.22, dy: 0.25 }, capsule: { dx: 0.22, dy: 0.25 } }, ground: 0 },
|
|
163
167
|
],
|
|
164
168
|
},
|
|
@@ -184,4 +188,191 @@ export const FLIGHTS = [
|
|
|
184
188
|
{ id: "side-landing", name: "Side cores land", what: "Both side cores down, side by side.", at: time("+00:07:58", FH), burns: {}, pose: { "side-left": { dx: -0.23, rest: true }, "side-right": { dx: 0.34, rest: true } }, ground: 0 },
|
|
185
189
|
],
|
|
186
190
|
},
|
|
191
|
+
{
|
|
192
|
+
id: "starship-flight-1",
|
|
193
|
+
rocket: "starship",
|
|
194
|
+
variant: "v1-flight1",
|
|
195
|
+
name: "Starship flight test 1",
|
|
196
|
+
basis: "SpaceX's timeline as Wikipedia gives it, for the flight of 20 April 2023, the first of Starship. It is what happened, not a plan: the stages never separated, and the flight termination system destroyed the stack. The planned steps after that (the boostback, the landing burn, the Ship's burn and its entry) were never reached, so they are not here.",
|
|
197
|
+
source: FLIGHT_1,
|
|
198
|
+
note: "No separation, tower or flaps and fins to move: the stack stays one piece to the end. Which of the booster's engines burned after the first minute, the page does not say, so the flames drawn there are a picture.",
|
|
199
|
+
phases: [
|
|
200
|
+
{ id: "ignition", name: "Ignition", what: "The booster's engines start while it is held on the pad. Three of them were stopped at ignition because the flight software did not find them healthy enough.", at: time("-00:00:06", FLIGHT_1), burns: { booster: count(30, FLIGHT_1, "Thirty-three engines, three of whose ignition was terminated: the page's words, and the sum is this package's.") }, pose: { booster: {}, ship: {} }, ground: 0, smoke: 0.7 },
|
|
201
|
+
{ id: "liftoff", name: "Liftoff", what: "A successful liftoff that significantly damaged the launch pad, the page says.", at: time("+00:00:05", FLIGHT_1), burns: { booster: count(30, FLIGHT_1, "The thirty that lit.") }, pose: { booster: {}, ship: {} }, ground: 0.02, smoke: 1 },
|
|
202
|
+
{ id: "max-q", name: "Max q", what: "The throttle goes down for max q, later and lower than planned.", at: time("+00:00:55", FLIGHT_1), burns: { booster: count(30, FLIGHT_1, "The thirty that lit; the page does not count the failures that came during the climb.") }, pose: { booster: {}, ship: {} }, ground: 1.6, air: 0.25 },
|
|
203
|
+
{ id: "meco", name: "Most engines cut off", what: "Fires in the booster's aft end led to a loss of communication with most of its engines.", at: time("+00:02:49", FLIGHT_1), burns: { booster: { engines: blank("The page gives no count of the engines that burned here."), drawn: 3 } }, pose: { booster: {}, ship: {} }, ground: 1.6, air: 0.7 },
|
|
204
|
+
{ id: "separation", name: "Separation fails", what: "The stages did not separate. The vehicle lost control and began spinning.", at: time("+00:02:52", FLIGHT_1), burns: { booster: { engines: blank("The page gives no count of the engines that burned here."), drawn: 3 } }, pose: { booster: {}, ship: {} }, ground: 1.6, air: 0.85, altitude: { value: "about 39 km, the highest it rose", source: FLIGHT_1 } },
|
|
205
|
+
{ id: "termination", name: "Flight termination", what: "The flight termination system was triggered.", at: time("+00:03:10", FLIGHT_1), burns: { booster: { engines: blank("The page gives no count of the engines that burned here."), drawn: 3 } }, pose: { booster: {}, ship: {} }, ground: 1.6, air: 1 },
|
|
206
|
+
{ id: "destroyed", name: "Destroyed", what: "The stack was destroyed. The booster's engines had kept firing until about four minutes in.", at: time("+00:04:01", FLIGHT_1), burns: {}, pose: { booster: {}, ship: {} }, ground: 1.6, air: 1, altitude: { value: "29 km: the height at which the page says the booster's engines were still firing, at about four minutes", source: FLIGHT_1 } },
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: "falcon-9-cassiope",
|
|
211
|
+
rocket: "falcon-9",
|
|
212
|
+
variant: "v1.1",
|
|
213
|
+
name: "Falcon 9 v1.1, CASSIOPE",
|
|
214
|
+
basis: "Spaceflight Now's live coverage of 29 September 2013, the first launch of the Falcon 9 v1.1. The minutes are the live coverage's own, rounded as it said them, and they are what happened, not a plan.",
|
|
215
|
+
source: CASSIOPE,
|
|
216
|
+
note: "The page does not follow the first stage after it separates, so it leaves the picture. A fairing is drawn though the satellites went inside it: that is this version's drawing. Altitudes are the live coverage's words, in the units it used.",
|
|
217
|
+
phases: [
|
|
218
|
+
{ id: "ignition", name: "Ignition", what: "The nine engines start on the pad.", at: blank("The live coverage starts at liftoff."), burns: { "first-stage": nineMerlin("The nine Merlin 1D of this version, from its page.") }, pose: {}, ground: 0, smoke: 0.7 },
|
|
219
|
+
{ id: "liftoff", name: "Liftoff", what: "Liftoff of the next-generation Falcon 9, at 1600 GMT.", at: time("+00:00:00", CASSIOPE), burns: { "first-stage": nineMerlin("The nine Merlin 1D of this version, from its page.") }, pose: {}, ground: 0.02, smoke: 1 },
|
|
220
|
+
{ id: "max-q", name: "Max q", what: "The rocket nears the speed of sound, in the phase of maximum aerodynamic pressure.", at: time("+00:01:00", CASSIOPE), burns: { "first-stage": nineMerlin("The nine Merlin 1D of this version, from its page.") }, pose: {}, ground: 1.6, air: 0.25 },
|
|
221
|
+
{ id: "climb", name: "Two minutes", what: "Climbing, with the first stage still burning.", at: time("+00:02:00", CASSIOPE), burns: { "first-stage": nineMerlin("The nine Merlin 1D of this version, from its page.") }, pose: {}, ground: 1.6, air: 0.6, altitude: { value: "more than 20 miles", source: CASSIOPE } },
|
|
222
|
+
{ id: "separation", name: "Stage separation", what: "The first stage's engines cut off and the stages separate; the second stage's engine lights for about six minutes.", at: time("+00:03:20", CASSIOPE), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": lower(-0.05), "second-stage": { dx: 0.14, dy: 0.04 }, fairing: { dx: 0.14, dy: 0.04 } }, ground: 1.6, air: 0.9 },
|
|
223
|
+
{ id: "fairing", name: "Fairing separation", what: "The payload fairing has separated as planned.", at: time("+00:04:00", CASSIOPE), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": lower(-0.1, { gone: true }), "second-stage": { dx: 0.22, dy: 0.08 }, fairing: { dx: 0.4, dy: 0.2, gone: true } }, ground: 1.6, air: 1 },
|
|
224
|
+
{ id: "altitude", name: "Seven minutes twenty", what: "Still burning, high above the air.", at: time("+00:07:20", CASSIOPE), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.1 }, fairing: { gone: true } }, ground: 1.6, air: 1, altitude: { value: "310 kilometers", source: CASSIOPE } },
|
|
225
|
+
{ id: "seco", name: "Second stage cutoff", what: "The second stage's engine shuts down, confirmed at 1609 GMT.", at: time("+00:09:00", CASSIOPE, { note: "From the page's GMT clock: liftoff at 1600, cutoff confirmed at 1609." }), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.12 }, fairing: { gone: true } }, ground: 1.6 },
|
|
226
|
+
{ id: "deploy", name: "CASSIOPE deploys", what: "The satellite is released first, at T+14:15.", at: time("+00:14:15", CASSIOPE), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.16 }, fairing: { gone: true } }, ground: 1.6, altitude: { value: "a planned orbit between 155 miles and 900 miles high", source: CASSIOPE } },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: "falcon-9-abs-eutelsat",
|
|
231
|
+
rocket: "falcon-9",
|
|
232
|
+
variant: "ft",
|
|
233
|
+
name: "Falcon 9 Full Thrust, ABS-2A and Eutelsat 117 West B",
|
|
234
|
+
basis: "Spaceflight Now's timeline for the flight of 15 June 2016, the plan as it was published before the launch. Its page does not name the version; Wikipedia's article on ABS-2A says it was a Falcon 9 Full Thrust.",
|
|
235
|
+
source: ABS,
|
|
236
|
+
note: "The page says only that the first stage would try to land on a barge in the Atlantic and gives no time for it, so the stage is not followed after it separates. The second stage burns twice, and the page gives each time.",
|
|
237
|
+
phases: [
|
|
238
|
+
{ id: "ignition", name: "Ignition", what: "The nine engines start on the pad.", at: blank("The timeline starts at liftoff."), burns: { "first-stage": nineMerlin("The nine Merlin 1D+ of this version, from its page.") }, pose: {}, ground: 0, smoke: 0.7 },
|
|
239
|
+
{ id: "liftoff", name: "Liftoff", what: "Nine Merlin engines at full thrust.", at: time("+00:00:00", ABS), burns: { "first-stage": nineMerlin("The nine Merlin 1D+ of this version, from its page.") }, pose: {}, ground: 0.02, smoke: 1 },
|
|
240
|
+
{ id: "mach-1", name: "Mach 1", what: "The rocket reaches the speed of sound.", at: time("+00:01:13", ABS), burns: { "first-stage": nineMerlin("The nine Merlin 1D+ of this version, from its page.") }, pose: {}, ground: 1.2, air: 0.1, speed: { value: "Mach 1", source: ABS } },
|
|
241
|
+
{ id: "max-q", name: "Max Q", what: "The point of maximum aerodynamic pressure.", at: time("+00:01:17", ABS), burns: { "first-stage": nineMerlin("The nine Merlin 1D+ of this version, from its page.") }, pose: {}, ground: 1.6, air: 0.25 },
|
|
242
|
+
{ id: "meco", name: "MECO", what: "The nine first stage engines shut down.", at: time("+00:02:36", ABS), burns: {}, pose: {}, ground: 1.6, air: 0.8 },
|
|
243
|
+
{ id: "separation", name: "Stage separation", what: "The first stage falls away. It is not followed from here.", at: time("+00:02:39", ABS), burns: {}, pose: { "first-stage": lower(-0.05), "second-stage": { dx: 0.14, dy: 0.04 }, fairing: { dx: 0.14, dy: 0.04 } }, ground: 1.6, air: 0.9 },
|
|
244
|
+
{ id: "second-stage", name: "Second stage ignition", what: "The vacuum Merlin lights for about six and a half minutes, to a parking orbit.", at: time("+00:02:47", ABS), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": lower(-0.1, { gone: true }), "second-stage": { dx: 0.22, dy: 0.08 }, fairing: { dx: 0.22, dy: 0.08 } }, ground: 1.6, air: 1 },
|
|
245
|
+
{ id: "fairing", name: "Fairing jettison", what: "The payload fairing comes away.", at: time("+00:03:34", ABS), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.1 }, fairing: { dx: 0.4, dy: 0.2, gone: true } }, ground: 1.6, air: 1 },
|
|
246
|
+
{ id: "seco-1", name: "SECO 1", what: "The second stage shuts down in its parking orbit.", at: time("+00:09:22", ABS), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.12 }, fairing: { gone: true } }, ground: 1.6 },
|
|
247
|
+
{ id: "restart", name: "Second burn", what: "The second stage's engine restarts for the burn to the transfer orbit.", at: time("+00:25:54", ABS), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.14 }, fairing: { gone: true } }, ground: 1.6, air: 1 },
|
|
248
|
+
{ id: "seco-2", name: "SECO 2", what: "The second stage shuts down again.", at: time("+00:26:58", ABS), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.16 }, fairing: { gone: true } }, ground: 1.6 },
|
|
249
|
+
{ id: "eutelsat", name: "Eutelsat 117 West B separates", what: "The first of the two satellites is released.", at: time("+00:30:29", ABS), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.18 }, fairing: { gone: true } }, ground: 1.6 },
|
|
250
|
+
{ id: "abs", name: "ABS-2A separates", what: "The second satellite is released.", at: time("+00:35:29", ABS), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.2 }, fairing: { gone: true } }, ground: 1.6 },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "falcon-9-cots-1",
|
|
255
|
+
rocket: "falcon-9",
|
|
256
|
+
variant: "v1.0-dragon",
|
|
257
|
+
name: "Falcon 9 v1.0 with Dragon 1, COTS Demo Flight 1",
|
|
258
|
+
basis: "SpaceX's COTS Demo Flight 1 press kit and Spaceflight Now's launch timeline, both from before the launch of 8 December 2010: the plan, which the kit says will vary.",
|
|
259
|
+
source: COTS1_TIMELINE,
|
|
260
|
+
note: "Two engines shut down before the rest, with no time given. The first stage is not followed after it separates, and the trunk stays with the second stage, as the timeline says. Dragon 1 is drawn with solar wings the kit does not mention.",
|
|
261
|
+
phases: [
|
|
262
|
+
{ id: "ignition", name: "Ignition", what: "The engine controller starts the ignition sequence on the pad.", at: time("-00:00:03", COTS1_PRESSKIT), burns: { "first-stage": nineMerlin("The nine Merlin 1C of this version, from its page.") }, pose: {}, ground: 0, smoke: 0.7 },
|
|
263
|
+
{ id: "liftoff", name: "Liftoff", what: "The four hold-down clamps release the booster.", at: time("+00:00:00", COTS1_TIMELINE), burns: { "first-stage": nineMerlin("The nine Merlin 1C of this version, from its page.") }, pose: {}, ground: 0.02, smoke: 1 },
|
|
264
|
+
{ id: "max-q", name: "Max q", what: "The rocket reaches the point of maximum aerodynamic pressure.", at: time("+00:01:16", COTS1_TIMELINE), burns: { "first-stage": nineMerlin("The nine Merlin 1C of this version, from its page.") }, pose: {}, ground: 1.6, air: 0.25 },
|
|
265
|
+
{ id: "two-down", name: "Two engines shut down", what: "Two of the first stage's engines shut down before the other seven.", at: blank("The timeline says two engines shut down before the rest and gives no time."), burns: { "first-stage": count(7, COTS1_TIMELINE, "The page says the remaining seven cut off at MECO.") }, pose: {}, ground: 1.6, air: 0.7 },
|
|
266
|
+
{ id: "meco", name: "MECO", what: "The remaining seven Merlin engines cut off.", at: time("+00:02:58", COTS1_TIMELINE), burns: {}, pose: {}, ground: 1.6, air: 0.8 },
|
|
267
|
+
{ id: "separation", name: "Stage separation", what: "The first stage separates from the second, four seconds after MECO. It is not followed from here.", at: time("+00:03:02", COTS1_TIMELINE), burns: {}, pose: { "first-stage": lower(-0.05), ...onTop(0.14, 0.04) }, ground: 1.6, air: 0.9 },
|
|
268
|
+
{ id: "second-stage", name: "Second stage ignition", what: "The second stage's Merlin vacuum engine ignites for a six-minute burn to put Dragon in orbit.", at: time("+00:03:09", COTS1_TIMELINE), burns: { "second-stage": oneMerlin("The one vacuum Merlin of this version, from its page.") }, pose: { "first-stage": lower(-0.1, { gone: true }), ...onTop(0.2, 0.08) }, ground: 1.6, air: 1 },
|
|
269
|
+
{ id: "seco", name: "Second stage cutoff", what: "The vacuum engine shuts down in orbit.", at: time("+00:09:00", COTS1_TIMELINE), burns: {}, pose: { "first-stage": { gone: true }, ...onTop(0.22, 0.12) }, ground: 1.6, altitude: { value: "a 186-mile-high orbit, inclined 34.5 degrees", source: COTS1_TIMELINE } },
|
|
270
|
+
{ id: "dragon-separates", name: "Dragon separates", what: "The Dragon capsule separates from the second stage; the trunk stays behind with the second stage.", at: time("+00:09:35", COTS1_TIMELINE), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { dx: 0.22, dy: 0.12 }, trunk: { dx: 0.22, dy: 0.12 }, capsule: { dx: 0.25, dy: 0.2 } }, ground: 1.6 },
|
|
271
|
+
{ id: "operations", name: "On-orbit operations begin", what: "Dragon begins a series of demonstrations in space lasting about three and a half hours.", at: time("+00:13:00", COTS1_TIMELINE), burns: {}, pose: { "first-stage": { gone: true }, "second-stage": { gone: true }, trunk: { gone: true }, capsule: { dx: 0.25, dy: 0.24 } }, ground: 1.6 },
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: "dragon-cots-1",
|
|
276
|
+
rocket: "dragon",
|
|
277
|
+
variant: "dragon-1",
|
|
278
|
+
name: "Dragon 1, COTS Demo Flight 1",
|
|
279
|
+
basis: "SpaceX's COTS Demo Flight 1 press kit, from before the launch of 8 December 2010: the plan, which the kit says will vary. Its later times read as hours and minutes from launch (the kit says about three and a half hours to splashdown).",
|
|
280
|
+
source: COTS1_PRESSKIT,
|
|
281
|
+
note: "Dragon's thrusters and parachutes are not drawn: the burn and the descent are in words. The trunk stays with the second stage, as Spaceflight Now's timeline says. Dragon 1 is drawn with solar wings; the kit says this Dragon ran on a battery.",
|
|
282
|
+
phases: [
|
|
283
|
+
{ id: "separation", name: "Dragon separates", what: "Dragon separates from Falcon 9 and starts its propulsion. The trunk stays with the second stage.", at: time("+00:09:35", COTS1_PRESSKIT), burns: {}, pose: { trunk: { dx: -0.25, dy: -0.2 }, capsule: {} }, ground: 1.6, altitude: { value: "a 300 kilometers circular orbit", source: COTS1_PRESSKIT } },
|
|
284
|
+
{ id: "operations", name: "On-orbit operations", what: "Dragon begins its demonstrations: its thrusters, telemetry, guidance and control.", at: time("+00:13:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: {} }, ground: 1.6, speed: { value: "greater than 17,000 mph, an orbit in 90 minutes", source: COTS1_PRESSKIT } },
|
|
285
|
+
{ id: "deorbit-start", name: "Deorbit burn begins", what: "The Draco thrusters fire to bring Dragon down.", at: time("+02:32:00", COTS1_PRESSKIT, { note: "The kit's 2:32, read as hours and minutes." }), burns: {}, pose: { trunk: { gone: true }, capsule: { pitch: 20 } }, ground: 1.6 },
|
|
286
|
+
{ id: "deorbit-end", name: "Deorbit burn ends", what: "The burn ends.", at: time("+02:38:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: { pitch: 20 } }, ground: 1.6 },
|
|
287
|
+
{ id: "entry", name: "Entry interface", what: "The reentry phase begins; the heat shield faces the air.", at: time("+02:58:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: { pitch: 30 } }, ground: 0.8, air: 1 },
|
|
288
|
+
{ id: "drogue", name: "Drogue parachutes", what: "Dual drogue parachutes deploy to stabilize and slow Dragon.", at: time("+03:09:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: {} }, ground: 0.5, air: 0.4, altitude: { value: "45,000 feet", source: COTS1_PRESSKIT } },
|
|
289
|
+
{ id: "main", name: "Main parachutes", what: "The drogues release the main parachutes, which slow Dragon to a gentle landing.", at: time("+03:10:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: {} }, ground: 0.2, air: 0.1, altitude: { value: "about 10,000 feet", source: COTS1_PRESSKIT } },
|
|
290
|
+
{ id: "splashdown", name: "Water landing", what: "Dragon lands in the Pacific Ocean, roughly 500 miles west of the coast of Mexico, drawn here on plain ground.", at: time("+03:19:00", COTS1_PRESSKIT), burns: {}, pose: { trunk: { gone: true }, capsule: { rest: true } }, ground: 0, speed: { value: "approximately 16 to 18 ft/sec under the main parachutes", source: COTS1_PRESSKIT } },
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "grasshopper-hop-8",
|
|
295
|
+
rocket: "grasshopper",
|
|
296
|
+
variant: "grasshopper",
|
|
297
|
+
name: "Grasshopper's highest hop",
|
|
298
|
+
basis: "Wikipedia's table of Grasshopper's eight hops: the last, on 7 October 2013, rose 744 m in 79 seconds. The page gives the height and the length of each hop and nothing between, so the steps between are a picture.",
|
|
299
|
+
source: GRASSHOPPER,
|
|
300
|
+
note: "The page gives a hop's height and duration only, so the engine's start, its throttling and the descent have no times here. The seven earlier hops were lower (the first rose 1.8 m in 3 s) and are not played.",
|
|
301
|
+
phases: [
|
|
302
|
+
{ id: "ignition", name: "Ignition", what: "The one Merlin 1D starts.", at: blank("The page gives each hop's height and length, not when its engine started."), burns: { vehicle: count(1, GRASSHOPPER, "A single Merlin-1D engine, the page says.") }, pose: {}, ground: 0, smoke: 0.5 },
|
|
303
|
+
{ id: "liftoff", name: "Liftoff", what: "The hop begins.", at: time("+00:00:00", GRASSHOPPER, { note: "The start of the hop's 79 seconds." }), burns: { vehicle: count(1, GRASSHOPPER, "A single Merlin-1D engine, the page says.") }, pose: {}, ground: 0.02, smoke: 0.8 },
|
|
304
|
+
{ id: "top", name: "The top of the hop", what: "The highest point.", at: blank("The page gives the height, not the moment."), burns: { vehicle: count(1, GRASSHOPPER, "A single Merlin-1D engine, the page says.") }, pose: {}, ground: 1.6, air: 0.2, altitude: { value: "744 m, the highest of its eight hops", source: GRASSHOPPER } },
|
|
305
|
+
{ id: "landing", name: "Landing", what: "It comes down on its fixed legs, 79 seconds after the start.", at: time("+00:01:19", GRASSHOPPER, { note: "The hop's duration, 79 s, from its start." }), burns: {}, pose: { vehicle: { rest: true } }, ground: 0 },
|
|
306
|
+
],
|
|
307
|
+
},
|
|
187
308
|
];
|
|
309
|
+
const MOVE_SOURCES = {
|
|
310
|
+
falcon: { source: "wikipedia-landing-tests", says: "The grid fins are \"stowed on ascent and deployed on reentry\"; the landing burn comes, then the legs open, then the touchdown." },
|
|
311
|
+
superHeavy: { source: "wikipedia-super-heavy", says: "After the boostback the booster descends \"using its grid fins for minor course corrections\"." },
|
|
312
|
+
ship: { source: "wikipedia-ship", says: "The Ship \"controls its reentry with four flaps\", two aft and two forward." },
|
|
313
|
+
};
|
|
314
|
+
const MOVES_OF = {
|
|
315
|
+
"falcon-9-starlink-2019": ["falcon"],
|
|
316
|
+
"falcon-9-crs-16": ["falcon"],
|
|
317
|
+
"falcon-heavy-test-flight": ["falcon"],
|
|
318
|
+
"starship-flight-5": ["superHeavy", "ship"],
|
|
319
|
+
"starship-flight-7": ["superHeavy", "ship"],
|
|
320
|
+
"starship-flight-12": ["superHeavy", "ship"],
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* What turns in each flight. The pages say the Falcon 9's grid fins are "stowed on ascent and deployed on reentry" and that its legs open in the landing burn, before the touch down (Falcon 9 first-stage landing tests);
|
|
324
|
+
* that Super Heavy steers with its grid fins for minor course corrections after the boostback (Super Heavy); and that the Ship controls its reentry with four flaps, two aft and two forward (Starship). How far a group turns,
|
|
325
|
+
* and the moment inside a phase, are a picture. A painted model that is one shell has nothing here to turn: nothing is cut to fake a move.
|
|
326
|
+
*/
|
|
327
|
+
const ARTICULATIONS = {
|
|
328
|
+
"falcon-9-starlink-2019": { "first-stage": [["ignition", { "grid-fins": 85, legs: 0 }], ["entry-burn", { "grid-fins": 0, legs: 0 }], ["landing-burn", { "grid-fins": 0, legs: 60 }]] },
|
|
329
|
+
"falcon-9-crs-16": { "first-stage": [["ignition", { "grid-fins": 85, legs: 0 }], ["entry-burn", { "grid-fins": 0, legs: 0 }], ["landing-burn", { "grid-fins": 0, legs: 60 }]] },
|
|
330
|
+
"falcon-heavy-test-flight": { "side-left": [["side-start", { legs: 0 }], ["side-burn", { legs: 60 }]], "side-right": [["side-start", { legs: 0 }], ["side-burn", { legs: 60 }]] },
|
|
331
|
+
"starship-flight-5": {
|
|
332
|
+
booster: [["boostback-end", { "grid-fins": 10 }], ["booster-landing", { "grid-fins": -10 }], ["booster-three", { "grid-fins": 6 }], ["booster-down", { "grid-fins": 0 }]],
|
|
333
|
+
ship: [["entry", { "forward-flaps": 8, "aft-flaps": 8 }], ["flip", { "forward-flaps": 60, "aft-flaps": 20 }]],
|
|
334
|
+
},
|
|
335
|
+
"starship-flight-7": {
|
|
336
|
+
booster: [["boostback-end", { "grid-fins": 10 }], ["booster-landing", { "grid-fins": -10 }], ["booster-three", { "grid-fins": 6 }], ["booster-down", { "grid-fins": 0 }]],
|
|
337
|
+
ship: [["entry", { "forward-flaps": 8, "aft-flaps": 8 }], ["flip", { "forward-flaps": 60, "aft-flaps": 20 }]],
|
|
338
|
+
},
|
|
339
|
+
"starship-flight-12": {
|
|
340
|
+
booster: [["boostback-end", { "grid-fins": 10 }], ["booster-landing", { "grid-fins": -10 }], ["booster-three", { "grid-fins": 6 }], ["booster-down", { "grid-fins": 0 }]],
|
|
341
|
+
ship: [["entry", { "forward-flaps": 8, "aft-flaps": 8 }], ["ship-burn", { "forward-flaps": 60, "aft-flaps": 20 }]],
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
function articulated(flight) {
|
|
345
|
+
const rules = ARTICULATIONS[flight.id];
|
|
346
|
+
if (!rules)
|
|
347
|
+
return flight;
|
|
348
|
+
let note = flight.note ?? "";
|
|
349
|
+
note = note.replace(/No tower, catch arms or moving flaps and fins yet: /, "No tower or catch arms yet: ").replace(/No drone ship or moving legs yet: /, "No drone ship yet: ").replace(/No moving legs or fins yet: /, "").replace(/No tower, catch arms or moving flaps and fins yet\b/, "No tower or catch arms yet");
|
|
350
|
+
return {
|
|
351
|
+
...flight,
|
|
352
|
+
note: `${note}${note ? " " : ""}Its moving parts turn where the pages say they do: how far, and the moment inside a phase, are a picture.`.trim(),
|
|
353
|
+
moves: (MOVES_OF[flight.id] ?? []).map((key) => MOVE_SOURCES[key]),
|
|
354
|
+
phases: flight.phases.map((phase) => {
|
|
355
|
+
const pose = { ...phase.pose };
|
|
356
|
+
for (const [slot, steps] of Object.entries(rules)) {
|
|
357
|
+
let angles = {};
|
|
358
|
+
for (const [id, at] of steps) {
|
|
359
|
+
if (flight.phases.findIndex((one) => one.id === id) <= flight.phases.indexOf(phase))
|
|
360
|
+
angles = at;
|
|
361
|
+
}
|
|
362
|
+
pose[slot] = { ...(pose[slot] ?? {}), moves: angles };
|
|
363
|
+
}
|
|
364
|
+
return { ...phase, pose };
|
|
365
|
+
}),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Every flight the package can play, one for each variant that has a timeline. Use `flightOf` to find one.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* import { FLIGHTS } from "@johnmorrisdotca/uchusen-rockets";
|
|
374
|
+
*
|
|
375
|
+
* console.log(FLIGHTS.map((flight) => `${flight.rocket}:${flight.variant}`));
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
export const FLIGHTS = BASE_FLIGHTS.map(articulated);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Figure } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The kinds of flame the pictures have a colour for. `neutral` is for an engine whose propellant no page read here names.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import type { FlameKind } from "@johnmorrisdotca/uchusen-rockets";
|
|
8
|
+
*
|
|
9
|
+
* const kinds: FlameKind[] = ["kerosene", "methane", "hydrogen", "solid", "hypergolic", "neutral"];
|
|
10
|
+
* console.log(kinds.length);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export type FlameKind = "kerosene" | "methane" | "hydrogen" | "solid" | "hypergolic" | "neutral";
|
|
14
|
+
/**
|
|
15
|
+
* What one engine burns: its name as the variants' data spells it, the propellant as its page words it (a figure with its source), and the kind of flame that picks the colour.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { PROPELLANTS, type EnginePropellant } from "@johnmorrisdotca/uchusen-rockets";
|
|
20
|
+
*
|
|
21
|
+
* const merlin: EnginePropellant | undefined = PROPELLANTS.find((one) => one.id === "merlin");
|
|
22
|
+
* console.log(merlin?.propellant.value, merlin?.flame);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface EnginePropellant {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
/** Words that, found in a variant's engine text, say this is the engine. */
|
|
29
|
+
names: readonly string[];
|
|
30
|
+
/** Left out: any rocket. A rocket's id when the same words mean another engine elsewhere (a "solid motor"). */
|
|
31
|
+
rocket?: string;
|
|
32
|
+
propellant: Figure<string>;
|
|
33
|
+
flame: Exclude<FlameKind, "neutral">;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The engines whose propellant a page read here names. Each propellant is a figure with its page; the flame kind follows from it (a test holds the two together).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { PROPELLANTS } from "@johnmorrisdotca/uchusen-rockets";
|
|
41
|
+
*
|
|
42
|
+
* console.log(PROPELLANTS.map((one) => `${one.name}: ${one.propellant.value}`));
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const PROPELLANTS: readonly EnginePropellant[];
|
|
46
|
+
/**
|
|
47
|
+
* The record of what an engine burns, found by the engine's name in a variant's engine text, or null when no page read here names it. A rocket's `rocket` makes "solid motor" mean its own.
|
|
48
|
+
*
|
|
49
|
+
* @param rocket A rocket's id.
|
|
50
|
+
* @param engineText The engine text of a part, as a variant's data words it ("9 Merlin 1D"), or null or undefined.
|
|
51
|
+
* @returns What that engine burns, or null.
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { propellantOf } from "@johnmorrisdotca/uchusen-rockets";
|
|
55
|
+
*
|
|
56
|
+
* console.log(propellantOf("falcon-9", "9 Merlin 1D")?.propellant.value, propellantOf("starship", "3 sea-level and 3 vacuum"));
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function propellantOf(rocket: string, engineText: string | null | undefined): EnginePropellant | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engines whose propellant a page read here names. Each propellant is a figure with its page; the flame kind follows from it (a test holds the two together).
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { PROPELLANTS } from "@johnmorrisdotca/uchusen-rockets";
|
|
7
|
+
*
|
|
8
|
+
* console.log(PROPELLANTS.map((one) => `${one.name}: ${one.propellant.value}`));
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export const PROPELLANTS = [
|
|
12
|
+
{ id: "merlin", name: "Merlin", names: ["Merlin"], propellant: { value: "LOX / RP-1", source: "wikipedia-merlin", date: "2026-11-03", note: "The article's lead: \"Merlin engines use RP-1 and liquid oxygen as rocket propellants in a gas-generator power cycle.\"" }, flame: "kerosene" },
|
|
13
|
+
{ id: "kestrel", name: "Kestrel", names: ["Kestrel"], propellant: { value: "LOX / RP-1", source: "wikipedia-kestrel", date: "2026-11-03", note: "The infobox, and the lead: \"an LOX/RP-1 pressure-fed rocket engine\"." }, flame: "kerosene" },
|
|
14
|
+
{ id: "raptor", name: "Raptor", names: ["Raptor"], propellant: { value: "LOX / CH4", source: "wikipedia-raptor", date: "2026-11-03", note: "The infobox, and the lead: \"cryogenic liquid methane and liquid oxygen, a combination known as methalox\"." }, flame: "methane" },
|
|
15
|
+
{ id: "f-1", name: "F-1", names: ["F-1"], propellant: { value: "LOX / RP-1", source: "wikipedia-f-1", date: "2026-11-03", note: "The infobox." }, flame: "kerosene" },
|
|
16
|
+
{ id: "j-2", name: "J-2", names: ["J-2"], propellant: { value: "Liquid oxygen / Liquid hydrogen", source: "wikipedia-j-2", date: "2026-11-03", note: "The infobox, and the lead: \"burned cryogenic liquid hydrogen (LH2) and liquid oxygen (LOX) propellants\"." }, flame: "hydrogen" },
|
|
17
|
+
{ id: "rs-25", name: "RS-25", names: ["RS-25"], propellant: { value: "LOX / LH2", source: "wikipedia-rs-25", date: "2026-11-03", note: "The infobox, and the lead: \"burns cryogenic (very low temperature) liquid hydrogen and liquid oxygen propellants\"." }, flame: "hydrogen" },
|
|
18
|
+
{ id: "shuttle-srb", name: "Space Shuttle solid rocket booster", names: ["solid motor"], rocket: "space-shuttle", propellant: { value: "Ammonium perchlorate, aluminium powder and PBAN (APCP)", source: "wikipedia-shuttle-srb", date: "2026-11-03", note: "\"The primary propellants were ammonium perchlorate as the oxidizer along with aluminum powder and PBAN as fuel.\"" }, flame: "solid" },
|
|
19
|
+
{ id: "sls-srb", name: "SLS solid rocket booster", names: ["solid motor"], rocket: "sls", propellant: { value: "PBAN-APCP", source: "wikipedia-sls-srb", date: "2026-11-03", note: "The infobox." }, flame: "solid" },
|
|
20
|
+
{ id: "be-4", name: "BE-4", names: ["BE-4"], propellant: { value: "LOX / CH4", source: "wikipedia-be-4", date: "2026-11-03", note: "The infobox, and the lead: \"It uses liquefied methane fuel\"." }, flame: "methane" },
|
|
21
|
+
{ id: "be-3", name: "BE-3", names: ["BE-3"], propellant: { value: "LOX / LH2", source: "wikipedia-be-3", date: "2026-11-03", note: "The infobox of the BE-3 and of the BE-3U." }, flame: "hydrogen" },
|
|
22
|
+
{ id: "rutherford", name: "Rutherford", names: ["Rutherford"], propellant: { value: "LOX / RP-1", source: "wikipedia-rutherford", date: "2026-11-03", note: "The infobox, and the lead: \"LOX (liquid oxygen) and RP-1 (refined kerosene)\"." }, flame: "kerosene" },
|
|
23
|
+
{ id: "rd-107", name: "RD-107 and RD-108", names: ["RD-107", "RD-108"], propellant: { value: "LOX / RG-1", source: "wikipedia-rd-107", date: "2026-11-03", note: "The infobox; its design section: \"liquid oxygen and highly refined kerosene (RG-1)\"." }, flame: "kerosene" },
|
|
24
|
+
{ id: "lm-descent", name: "Lunar module descent engine", names: ["LM descent engine"], propellant: { value: "Aerozine 50 fuel / nitrogen tetroxide oxidizer", source: "wikipedia-lunar-module", date: "2026-11-03", note: "The article's specifications: \"DPS propellants: Aerozine 50 fuel / nitrogen tetroxide oxidizer\"." }, flame: "hypergolic" },
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* The record of what an engine burns, found by the engine's name in a variant's engine text, or null when no page read here names it. A rocket's `rocket` makes "solid motor" mean its own.
|
|
28
|
+
*
|
|
29
|
+
* @param rocket A rocket's id.
|
|
30
|
+
* @param engineText The engine text of a part, as a variant's data words it ("9 Merlin 1D"), or null or undefined.
|
|
31
|
+
* @returns What that engine burns, or null.
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { propellantOf } from "@johnmorrisdotca/uchusen-rockets";
|
|
35
|
+
*
|
|
36
|
+
* console.log(propellantOf("falcon-9", "9 Merlin 1D")?.propellant.value, propellantOf("starship", "3 sea-level and 3 vacuum"));
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function propellantOf(rocket, engineText) {
|
|
40
|
+
if (!engineText)
|
|
41
|
+
return null;
|
|
42
|
+
return PROPELLANTS.find((one) => (one.rocket === undefined || one.rocket === rocket) && one.names.some((name) => engineText.includes(name))) ?? null;
|
|
43
|
+
}
|
package/dist/data/sources.d.ts
CHANGED
|
@@ -269,6 +269,76 @@ export declare const SOURCES: {
|
|
|
269
269
|
readonly read: "direct";
|
|
270
270
|
readonly licence: "CC BY-SA 4.0";
|
|
271
271
|
};
|
|
272
|
+
readonly "sfn-cassiope": {
|
|
273
|
+
readonly title: "The launch of the first Falcon 9 v1.1, with CASSIOPE (Spaceflight Now's live coverage, its status page)";
|
|
274
|
+
readonly publisher: "Spaceflight Now";
|
|
275
|
+
readonly url: "https://spaceflightnow.com/falcon9/006/cassiopestatus.html";
|
|
276
|
+
readonly date: "2013-09-29";
|
|
277
|
+
readonly read: "direct";
|
|
278
|
+
};
|
|
279
|
+
readonly "sfn-abs-eutelsat": {
|
|
280
|
+
readonly title: "Mission events timeline for Falcon 9's launch for ABS and Eutelsat";
|
|
281
|
+
readonly publisher: "Spaceflight Now";
|
|
282
|
+
readonly url: "https://spaceflightnow.com/2016/06/14/mission-events-timeline-for-falcon-9s-launch-for-abs-and-eutelsat/";
|
|
283
|
+
readonly date: "2016-06-14";
|
|
284
|
+
readonly read: "direct";
|
|
285
|
+
};
|
|
286
|
+
readonly "wikipedia-abs-2a": {
|
|
287
|
+
readonly title: "ABS-2A (its infobox: the rocket was a Falcon 9 Full Thrust)";
|
|
288
|
+
readonly publisher: "Wikipedia";
|
|
289
|
+
readonly url: "https://en.wikipedia.org/wiki/ABS-2A";
|
|
290
|
+
readonly date: "2026-11-03";
|
|
291
|
+
readonly read: "direct";
|
|
292
|
+
readonly licence: "CC BY-SA 4.0";
|
|
293
|
+
};
|
|
294
|
+
readonly "wikipedia-be-4": {
|
|
295
|
+
readonly title: "Blue Origin BE-4 (the infobox)";
|
|
296
|
+
readonly publisher: "Wikipedia";
|
|
297
|
+
readonly url: "https://en.wikipedia.org/wiki/Blue_Origin_BE-4";
|
|
298
|
+
readonly date: "2026-11-03";
|
|
299
|
+
readonly read: "direct";
|
|
300
|
+
readonly licence: "CC BY-SA 4.0";
|
|
301
|
+
};
|
|
302
|
+
readonly "wikipedia-be-3": {
|
|
303
|
+
readonly title: "Blue Origin BE-3 (the infobox, for the BE-3 and the BE-3U)";
|
|
304
|
+
readonly publisher: "Wikipedia";
|
|
305
|
+
readonly url: "https://en.wikipedia.org/wiki/Blue_Origin_BE-3";
|
|
306
|
+
readonly date: "2026-11-03";
|
|
307
|
+
readonly read: "direct";
|
|
308
|
+
readonly licence: "CC BY-SA 4.0";
|
|
309
|
+
};
|
|
310
|
+
readonly "wikipedia-rutherford": {
|
|
311
|
+
readonly title: "Rutherford (rocket engine) (the infobox and the lead)";
|
|
312
|
+
readonly publisher: "Wikipedia";
|
|
313
|
+
readonly url: "https://en.wikipedia.org/wiki/Rutherford_(rocket_engine)";
|
|
314
|
+
readonly date: "2026-11-03";
|
|
315
|
+
readonly read: "direct";
|
|
316
|
+
readonly licence: "CC BY-SA 4.0";
|
|
317
|
+
};
|
|
318
|
+
readonly "wikipedia-rd-107": {
|
|
319
|
+
readonly title: "RD-107 (the infobox and its design section, for the RD-107 and the RD-108)";
|
|
320
|
+
readonly publisher: "Wikipedia";
|
|
321
|
+
readonly url: "https://en.wikipedia.org/wiki/RD-107";
|
|
322
|
+
readonly date: "2026-11-03";
|
|
323
|
+
readonly read: "direct";
|
|
324
|
+
readonly licence: "CC BY-SA 4.0";
|
|
325
|
+
};
|
|
326
|
+
readonly "wikipedia-kestrel": {
|
|
327
|
+
readonly title: "SpaceX Kestrel (the infobox and the lead)";
|
|
328
|
+
readonly publisher: "Wikipedia";
|
|
329
|
+
readonly url: "https://en.wikipedia.org/wiki/SpaceX_Kestrel";
|
|
330
|
+
readonly date: "2026-11-03";
|
|
331
|
+
readonly read: "direct";
|
|
332
|
+
readonly licence: "CC BY-SA 4.0";
|
|
333
|
+
};
|
|
334
|
+
readonly "wikipedia-sls-srb": {
|
|
335
|
+
readonly title: "Space Launch System Solid Rocket Booster (the infobox)";
|
|
336
|
+
readonly publisher: "Wikipedia";
|
|
337
|
+
readonly url: "https://en.wikipedia.org/wiki/Space_Launch_System_Solid_Rocket_Booster";
|
|
338
|
+
readonly date: "2026-11-03";
|
|
339
|
+
readonly read: "direct";
|
|
340
|
+
readonly licence: "CC BY-SA 4.0";
|
|
341
|
+
};
|
|
272
342
|
readonly "wikipedia-shuttle-srb": {
|
|
273
343
|
readonly title: "Space Shuttle Solid Rocket Booster (the infobox)";
|
|
274
344
|
readonly publisher: "Wikipedia";
|
|
@@ -537,6 +607,28 @@ export declare const SOURCES: {
|
|
|
537
607
|
readonly date: "2016-04-07";
|
|
538
608
|
readonly read: "direct";
|
|
539
609
|
};
|
|
610
|
+
readonly "wikipedia-falcon-9-v1-0": {
|
|
611
|
+
readonly title: "Falcon 9 v1.0 (the infobox)";
|
|
612
|
+
readonly publisher: "Wikipedia";
|
|
613
|
+
readonly url: "https://en.wikipedia.org/wiki/Falcon_9_v1.0";
|
|
614
|
+
readonly date: "2026-11-03";
|
|
615
|
+
readonly read: "direct";
|
|
616
|
+
readonly licence: "CC BY-SA 4.0";
|
|
617
|
+
};
|
|
618
|
+
readonly "sfn-cots1-timeline": {
|
|
619
|
+
readonly title: "Falcon 9/COTS 1 launch timeline";
|
|
620
|
+
readonly publisher: "Spaceflight Now";
|
|
621
|
+
readonly url: "https://spaceflightnow.com/falcon9/002/launchtimeline.html";
|
|
622
|
+
readonly date: "2010-12-06";
|
|
623
|
+
readonly read: "direct";
|
|
624
|
+
};
|
|
625
|
+
readonly "sfn-cots1-presskit": {
|
|
626
|
+
readonly title: "SpaceX COTS Demo Flight 1 press kit (its mission timeline, page 7)";
|
|
627
|
+
readonly publisher: "SpaceX, as hosted by Spaceflight Now";
|
|
628
|
+
readonly url: "https://spaceflightnow.com/falcon9/002/cots1_presskit.pdf";
|
|
629
|
+
readonly date: "2026-11-03";
|
|
630
|
+
readonly read: "direct";
|
|
631
|
+
};
|
|
540
632
|
readonly "sfn-flight-35": {
|
|
541
633
|
readonly title: "Timeline of SpaceX's 35th Falcon 9 launch";
|
|
542
634
|
readonly publisher: "Spaceflight Now";
|
package/dist/data/sources.js
CHANGED
|
@@ -42,6 +42,15 @@ export const SOURCES = {
|
|
|
42
42
|
"wikipedia-space-shuttle": { title: "Space Shuttle (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Shuttle", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
43
43
|
"wikipedia-shuttle-orbiter": { title: "Space Shuttle orbiter (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Shuttle_orbiter", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
44
44
|
"wikipedia-shuttle-tank": { title: "Space Shuttle external tank (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Shuttle_external_tank", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
45
|
+
"sfn-cassiope": { title: "The launch of the first Falcon 9 v1.1, with CASSIOPE (Spaceflight Now's live coverage, its status page)", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/falcon9/006/cassiopestatus.html", date: "2013-09-29", read: "direct" },
|
|
46
|
+
"sfn-abs-eutelsat": { title: "Mission events timeline for Falcon 9's launch for ABS and Eutelsat", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/2016/06/14/mission-events-timeline-for-falcon-9s-launch-for-abs-and-eutelsat/", date: "2016-06-14", read: "direct" },
|
|
47
|
+
"wikipedia-abs-2a": { title: "ABS-2A (its infobox: the rocket was a Falcon 9 Full Thrust)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/ABS-2A", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
48
|
+
"wikipedia-be-4": { title: "Blue Origin BE-4 (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Blue_Origin_BE-4", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
49
|
+
"wikipedia-be-3": { title: "Blue Origin BE-3 (the infobox, for the BE-3 and the BE-3U)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Blue_Origin_BE-3", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
50
|
+
"wikipedia-rutherford": { title: "Rutherford (rocket engine) (the infobox and the lead)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Rutherford_(rocket_engine)", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
51
|
+
"wikipedia-rd-107": { title: "RD-107 (the infobox and its design section, for the RD-107 and the RD-108)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/RD-107", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
52
|
+
"wikipedia-kestrel": { title: "SpaceX Kestrel (the infobox and the lead)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/SpaceX_Kestrel", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
53
|
+
"wikipedia-sls-srb": { title: "Space Launch System Solid Rocket Booster (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Launch_System_Solid_Rocket_Booster", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
45
54
|
"wikipedia-shuttle-srb": { title: "Space Shuttle Solid Rocket Booster (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Shuttle_Solid_Rocket_Booster", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
46
55
|
"wikipedia-rs-25": { title: "RS-25 (the infobox and the nozzle's sizes)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/RS-25", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
47
56
|
"wikipedia-sls": { title: "Space Launch System (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Space_Launch_System", date: "2026-11-02", read: "direct", licence: "CC BY-SA 4.0" },
|
|
@@ -76,5 +85,8 @@ export const SOURCES = {
|
|
|
76
85
|
"sfn-crs-16": { title: "Launch timeline for SpaceX's 16th resupply launch to the space station", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/2018/12/05/launch-timeline-for-spacexs-16th-resupply-launch-to-the-space-station/", date: "2018-12-05", read: "direct" },
|
|
77
86
|
"sfn-starlink-2019": { title: "Timeline for Falcon 9 launch of Starlink satellites", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/2019/11/11/launch-timeline-for-falcon-9-launch-of-starlink-satellites/", date: "2019-11-11", read: "direct" },
|
|
78
87
|
"sfn-flight-23": { title: "Timeline for the Falcon 9 rocket's 23rd flight", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/2016/04/07/timeline-for-the-falcon-9-rockets-23rd-flight/", date: "2016-04-07", read: "direct" },
|
|
88
|
+
"wikipedia-falcon-9-v1-0": { title: "Falcon 9 v1.0 (the infobox)", publisher: "Wikipedia", url: "https://en.wikipedia.org/wiki/Falcon_9_v1.0", date: "2026-11-03", read: "direct", licence: "CC BY-SA 4.0" },
|
|
89
|
+
"sfn-cots1-timeline": { title: "Falcon 9/COTS 1 launch timeline", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/falcon9/002/launchtimeline.html", date: "2010-12-06", read: "direct" },
|
|
90
|
+
"sfn-cots1-presskit": { title: "SpaceX COTS Demo Flight 1 press kit (its mission timeline, page 7)", publisher: "SpaceX, as hosted by Spaceflight Now", url: "https://spaceflightnow.com/falcon9/002/cots1_presskit.pdf", date: "2026-11-03", read: "direct" },
|
|
79
91
|
"sfn-flight-35": { title: "Timeline of SpaceX's 35th Falcon 9 launch", publisher: "Spaceflight Now", url: "https://spaceflightnow.com/?p=24998", date: "2017-05-31", read: "direct" },
|
|
80
92
|
};
|
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>`);
|