@johnmorrisdotca/uchusen-rockets 0.19.1 → 0.20.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSES/Orbiter-MIT.txt +21 -0
  3. package/NOTICE.md +37 -1
  4. package/README.md +13 -6
  5. package/dist/assets/falcon-heavy-test-flight-apart.painted.svg +1 -0
  6. package/dist/assets/falcon-heavy-test-flight-apart.svg +1 -0
  7. package/dist/assets/falcon-heavy-test-flight-apart.thick.svg +1 -0
  8. package/dist/assets/falcon-heavy-test-flight-car.glb +0 -0
  9. package/dist/assets/falcon-heavy-test-flight-car.painted.svg +1 -0
  10. package/dist/assets/falcon-heavy-test-flight-car.svg +1 -0
  11. package/dist/assets/falcon-heavy-test-flight-car.thick.svg +1 -0
  12. package/dist/assets/falcon-heavy-test-flight-centre.glb +0 -0
  13. package/dist/assets/falcon-heavy-test-flight-centre.painted.svg +1 -0
  14. package/dist/assets/falcon-heavy-test-flight-centre.svg +1 -0
  15. package/dist/assets/falcon-heavy-test-flight-centre.thick.svg +1 -0
  16. package/dist/assets/falcon-heavy-test-flight-side-left.glb +0 -0
  17. package/dist/assets/falcon-heavy-test-flight-side-left.painted.svg +1 -0
  18. package/dist/assets/falcon-heavy-test-flight-side-left.svg +1 -0
  19. package/dist/assets/falcon-heavy-test-flight-side-left.thick.svg +1 -0
  20. package/dist/assets/falcon-heavy-test-flight-side-right.glb +0 -0
  21. package/dist/assets/falcon-heavy-test-flight-side-right.painted.svg +1 -0
  22. package/dist/assets/falcon-heavy-test-flight-side-right.svg +1 -0
  23. package/dist/assets/falcon-heavy-test-flight-side-right.thick.svg +1 -0
  24. package/dist/assets/falcon-heavy-test-flight-starman.glb +0 -0
  25. package/dist/assets/falcon-heavy-test-flight-starman.painted.svg +1 -0
  26. package/dist/assets/falcon-heavy-test-flight-starman.svg +1 -0
  27. package/dist/assets/falcon-heavy-test-flight-starman.thick.svg +1 -0
  28. package/dist/assets/falcon-heavy-test-flight.glb +0 -0
  29. package/dist/assets/falcon-heavy-test-flight.painted.svg +1 -0
  30. package/dist/assets/falcon-heavy-test-flight.svg +1 -0
  31. package/dist/assets/falcon-heavy-test-flight.thick.svg +1 -0
  32. package/dist/assets/models/roadster-first-generation-car.glb +0 -0
  33. package/dist/assets/models/roadster-second-generation-2-car.glb +0 -0
  34. package/dist/assets/models/roadster-second-generation-car.glb +0 -0
  35. package/dist/assets/roadster-first-generation-apart.painted.svg +1 -0
  36. package/dist/assets/roadster-first-generation-apart.svg +1 -0
  37. package/dist/assets/roadster-first-generation-apart.thick.svg +1 -0
  38. package/dist/assets/roadster-first-generation-car.glb +0 -0
  39. package/dist/assets/roadster-first-generation-car.painted.svg +1 -0
  40. package/dist/assets/roadster-first-generation-car.svg +1 -0
  41. package/dist/assets/roadster-first-generation-car.thick.svg +1 -0
  42. package/dist/assets/roadster-first-generation-starman.glb +0 -0
  43. package/dist/assets/roadster-first-generation-starman.painted.svg +1 -0
  44. package/dist/assets/roadster-first-generation-starman.svg +1 -0
  45. package/dist/assets/roadster-first-generation-starman.thick.svg +1 -0
  46. package/dist/assets/roadster-first-generation.glb +0 -0
  47. package/dist/assets/roadster-first-generation.painted.svg +1 -0
  48. package/dist/assets/roadster-first-generation.svg +1 -0
  49. package/dist/assets/roadster-first-generation.thick.svg +1 -0
  50. package/dist/assets/roadster-second-generation-2-apart.painted.svg +1 -0
  51. package/dist/assets/roadster-second-generation-2-apart.svg +1 -0
  52. package/dist/assets/roadster-second-generation-2-apart.thick.svg +1 -0
  53. package/dist/assets/roadster-second-generation-2-car.glb +0 -0
  54. package/dist/assets/roadster-second-generation-2-car.painted.svg +1 -0
  55. package/dist/assets/roadster-second-generation-2-car.svg +1 -0
  56. package/dist/assets/roadster-second-generation-2-car.thick.svg +1 -0
  57. package/dist/assets/roadster-second-generation-2-starman.glb +0 -0
  58. package/dist/assets/roadster-second-generation-2-starman.painted.svg +1 -0
  59. package/dist/assets/roadster-second-generation-2-starman.svg +1 -0
  60. package/dist/assets/roadster-second-generation-2-starman.thick.svg +1 -0
  61. package/dist/assets/roadster-second-generation-2.glb +0 -0
  62. package/dist/assets/roadster-second-generation-2.painted.svg +1 -0
  63. package/dist/assets/roadster-second-generation-2.svg +1 -0
  64. package/dist/assets/roadster-second-generation-2.thick.svg +1 -0
  65. package/dist/assets/roadster-second-generation-apart.painted.svg +1 -0
  66. package/dist/assets/roadster-second-generation-apart.svg +1 -0
  67. package/dist/assets/roadster-second-generation-apart.thick.svg +1 -0
  68. package/dist/assets/roadster-second-generation-car.glb +0 -0
  69. package/dist/assets/roadster-second-generation-car.painted.svg +1 -0
  70. package/dist/assets/roadster-second-generation-car.svg +1 -0
  71. package/dist/assets/roadster-second-generation-car.thick.svg +1 -0
  72. package/dist/assets/roadster-second-generation-starman.glb +0 -0
  73. package/dist/assets/roadster-second-generation-starman.painted.svg +1 -0
  74. package/dist/assets/roadster-second-generation-starman.svg +1 -0
  75. package/dist/assets/roadster-second-generation-starman.thick.svg +1 -0
  76. package/dist/assets/roadster-second-generation.glb +0 -0
  77. package/dist/assets/roadster-second-generation.painted.svg +1 -0
  78. package/dist/assets/roadster-second-generation.svg +1 -0
  79. package/dist/assets/roadster-second-generation.thick.svg +1 -0
  80. package/dist/data/countries.d.ts +51 -0
  81. package/dist/data/countries.js +91 -0
  82. package/dist/data/credits.d.ts +36 -0
  83. package/dist/data/credits.js +36 -0
  84. package/dist/data/falcon.js +26 -14
  85. package/dist/data/makers.js +1 -0
  86. package/dist/data/roadster.d.ts +13 -0
  87. package/dist/data/roadster.js +73 -0
  88. package/dist/data/sources.d.ts +56 -0
  89. package/dist/data/sources.js +7 -0
  90. package/dist/element/flag.d.ts +30 -0
  91. package/dist/element/flag.js +23 -0
  92. package/dist/element/scene.d.ts +7 -0
  93. package/dist/element/scene.js +35 -1
  94. package/dist/element.d.ts +5 -0
  95. package/dist/element.js +58 -2
  96. package/dist/index.d.ts +3 -0
  97. package/dist/index.js +2 -0
  98. package/dist/inspace.js +1 -0
  99. package/dist/model/registry.js +6 -4
  100. package/dist/model/roadster.d.ts +31 -0
  101. package/dist/model/roadster.js +112 -0
  102. package/dist/parts.d.ts +1 -1
  103. package/dist/physics/atmosphere.d.ts +182 -0
  104. package/dist/physics/atmosphere.js +246 -0
  105. package/dist/physics/attitude.d.ts +342 -0
  106. package/dist/physics/attitude.js +351 -0
  107. package/dist/physics/burns.d.ts +194 -0
  108. package/dist/physics/burns.js +157 -0
  109. package/dist/physics/elements.d.ts +133 -0
  110. package/dist/physics/elements.js +239 -0
  111. package/dist/physics/gravity.d.ts +53 -0
  112. package/dist/physics/gravity.js +78 -0
  113. package/dist/physics/index.d.ts +17 -0
  114. package/dist/physics/index.js +8 -0
  115. package/dist/physics/propagate.d.ts +110 -0
  116. package/dist/physics/propagate.js +122 -0
  117. package/dist/physics/rocket.d.ts +105 -0
  118. package/dist/physics/rocket.js +102 -0
  119. package/dist/physics/transfers.d.ts +128 -0
  120. package/dist/physics/transfers.js +155 -0
  121. package/dist/physics/vector.d.ts +20 -0
  122. package/dist/physics/vector.js +9 -0
  123. package/dist/react.d.ts +1 -1
  124. package/dist/rockets.d.ts +5 -0
  125. package/dist/rockets.js +9 -4
  126. package/dist/sun.d.ts +13 -0
  127. package/dist/sun.js +25 -0
  128. package/package.json +11 -1
package/dist/element.d.ts CHANGED
@@ -63,6 +63,8 @@ export declare class UchusenRocket extends HTMLBase {
63
63
  private builtNow;
64
64
  private queued;
65
65
  private stateNow;
66
+ /** What the flag in the corner is for: a country's code, "" for none, "off", or "-" before the first look. */
67
+ private flagKey;
66
68
  private shownAt;
67
69
  private starting;
68
70
  /** Whether this element has asked the page's WebGL answer since it started watching. */
@@ -201,6 +203,8 @@ export declare class UchusenRocket extends HTMLBase {
201
203
  /** The drawing for what the attributes say now. It is made from code, so it needs no file. A zoom or a move only changes which part of it the box shows (its viewBox), so the drawing is as sharp as the box and never grows past it. */
202
204
  private redraw;
203
205
  private mark;
206
+ /** `flags`: the rocket's country's flag in the picture's corner, from hata (an optional peer) when the page has it. `data-flag` is the code shown, or empty once it is settled that none is; it is absent while one is loading or the attribute is off. */
207
+ private syncFlag;
204
208
  private applyRocket;
205
209
  /** A fleet ship is built coarse first (the first picture and the far view) and fine when the camera comes near, an inset opens or one part is shown alone: once per visit, never on a turn or a look change. */
206
210
  private fine;
@@ -257,6 +261,7 @@ export declare class UchusenLineup extends HTMLBase {
257
261
  */
258
262
  export declare function defineUchusenRocket(): void;
259
263
  export { ROCKETS, UchusenFlight };
264
+ export { setFlagLoader, type FlagSource } from "./element/flag.ts";
260
265
  export type { FlightWords } from "./flightbar.ts";
261
266
  declare global {
262
267
  interface HTMLElementTagNameMap {
package/dist/element.js CHANGED
@@ -6,6 +6,7 @@ import { buildOf } from "./buildPlay.js";
6
6
  import { bellsOf, enginesWanted, pickBells } from "./engines.js";
7
7
  import { lightMode } from "./model/fleet/lights.js";
8
8
  import { thrusterMode } from "./thrusters.js";
9
+ import { parseSun } from "./sun.js";
9
10
  import { motionsOf, motionSpeed, motionWanted } from "./model/fleet/motion.js";
10
11
  import { engineChart } from "./draw/plume.js";
11
12
  import { burnsOf, FlightClock, flightOf, frameAt, phaseAt, phasePosition } from "./flight.js";
@@ -23,6 +24,7 @@ import { skyQuery } from "./element/solarGroups.js";
23
24
  import { Panel } from "./element/panel.js";
24
25
  import { glProbe } from "./element/probe.js";
25
26
  import { spinOf } from "./spin.js";
27
+ import { flagSvg } from "./element/flag.js";
26
28
  const HTMLBase = typeof HTMLElement === "undefined" ? class {
27
29
  } : HTMLElement;
28
30
  /** A view out of sight keeps its WebGL canvas this long before letting it go (a browser allows only a few at a time). */
@@ -47,6 +49,15 @@ export function setAssetBase(url) {
47
49
  assetBase = url.endsWith("/") ? url : `${url}/`;
48
50
  }
49
51
  const defaultBase = () => assetBase ?? new URL("./assets/", import.meta.url).href;
52
+ /** A country's name in the page's language, or its code where the browser has no name for it. */
53
+ function regionName(code, lang) {
54
+ try {
55
+ return new Intl.DisplayNames([lang || "en"], { type: "region" }).of(code) ?? code;
56
+ }
57
+ catch {
58
+ return code;
59
+ }
60
+ }
50
61
  const CSS = `
51
62
  :host { display: block; position: relative; color: inherit; }
52
63
  :host([hidden]) { display: none; }
@@ -73,6 +84,8 @@ const CSS = `
73
84
  .panel table { border-collapse: collapse; width: 100%; }
74
85
  .panel th, .panel td { text-align: left; padding: 0.15rem 0.4rem; border-bottom: 1px solid color-mix(in srgb, currentColor 25%, transparent); vertical-align: top; }
75
86
  .panel p { margin: 0 0 0.5rem; }
87
+ .flag { position: absolute; top: 0.5rem; left: 0.5rem; display: block; width: 1.5rem; aspect-ratio: 4 / 3; line-height: 0; pointer-events: none; box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 35%, transparent); }
88
+ .flag svg { display: block; width: 100%; height: 100%; }
76
89
  .tilt { position: absolute; right: 0.5rem; bottom: 0.5rem; min-height: 2rem; padding: 0.25rem 0.75rem; border: 1px solid currentColor; border-radius: 1rem; background: transparent; color: inherit; font: inherit; font-size: 0.8rem; cursor: pointer; }
77
90
  @media (prefers-reduced-motion: reduce) {
78
91
  .drawing, .canvas { filter: none !important; transition: opacity var(--uchusen-fade, 400ms) ease; }
@@ -119,7 +132,7 @@ function whyNot(mode) {
119
132
  * ```
120
133
  */
121
134
  export class UchusenRocket extends HTMLBase {
122
- static observedAttributes = ["rocket", "variant", "show", "look", "view", "build", "mode", "assets", "spin", "way", "label", "engines", "lights", "thrusters", "phase", "motion", "labels", "inspector", "beside", "captions", "scale-note", "off-screen", "scale", "progress", "float", "names", "leave", "docked", "edges", "world", "world-turn", "form"];
135
+ static observedAttributes = ["rocket", "variant", "show", "look", "view", "build", "mode", "assets", "spin", "way", "label", "engines", "lights", "thrusters", "phase", "motion", "labels", "inspector", "beside", "captions", "scale-note", "off-screen", "scale", "progress", "float", "names", "leave", "docked", "edges", "world", "world-turn", "form", "sun", "flags"];
123
136
  descriptionValue = null;
124
137
  picture = null;
125
138
  holder = null;
@@ -137,6 +150,8 @@ export class UchusenRocket extends HTMLBase {
137
150
  builtNow = null;
138
151
  queued = false;
139
152
  stateNow = "picture";
153
+ /** What the flag in the corner is for: a country's code, "" for none, "off", or "-" before the first look. */
154
+ flagKey = "-";
140
155
  shownAt = 0;
141
156
  starting = false;
142
157
  /** Whether this element has asked the page's WebGL answer since it started watching. */
@@ -321,7 +336,9 @@ export class UchusenRocket extends HTMLBase {
321
336
  attributeChangedCallback(name, before, after) {
322
337
  if (before === after)
323
338
  return;
324
- if (name === "scale") {
339
+ if (name === "flags")
340
+ this.syncFlag();
341
+ else if (name === "scale") {
325
342
  this.viewer?.setScaleMode(after === "true");
326
343
  // `scale="true"` is the true-scale view. A number is a Kaminari size, which rebuilds the craft.
327
344
  if (after !== "true" && after !== "false")
@@ -346,6 +363,8 @@ export class UchusenRocket extends HTMLBase {
346
363
  this.viewer?.setFloating(after !== null);
347
364
  else if (name === "edges")
348
365
  this.viewer?.setEdges(after === "open");
366
+ else if (name === "sun")
367
+ this.viewer?.setSun(parseSun(after));
349
368
  else if (name === "world")
350
369
  this.viewer?.setOrbit(after === "earth");
351
370
  else if (name === "world-turn")
@@ -664,6 +683,7 @@ export class UchusenRocket extends HTMLBase {
664
683
  holder.className = "stage";
665
684
  picture.append(drawing, holder);
666
685
  root.appendChild(picture);
686
+ this.flagKey = "-";
667
687
  this.picture = picture;
668
688
  this.drawing = drawing;
669
689
  this.holder = holder;
@@ -734,6 +754,40 @@ export class UchusenRocket extends HTMLBase {
734
754
  else
735
755
  delete this.dataset.docked;
736
756
  this.dataset.custom = String(this.builtNow.report.custom);
757
+ this.syncFlag();
758
+ }
759
+ /** `flags`: the rocket's country's flag in the picture's corner, from hata (an optional peer) when the page has it. `data-flag` is the code shown, or empty once it is settled that none is; it is absent while one is loading or the attribute is off. */
760
+ syncFlag() {
761
+ const picture = this.picture;
762
+ if (!picture)
763
+ return;
764
+ const on = this.hasAttribute("flags") && this.getAttribute("flags") !== "off";
765
+ const code = on && this.builtNow ? (ROCKETS[this.descriptionNow().rocket]?.country.value ?? "") : "";
766
+ const key = on ? code : "off";
767
+ if (key === this.flagKey)
768
+ return;
769
+ this.flagKey = key;
770
+ picture.querySelector(".flag")?.remove();
771
+ if (!on)
772
+ return void delete this.dataset.flag;
773
+ if (!code)
774
+ return void (this.dataset.flag = "");
775
+ delete this.dataset.flag;
776
+ void flagSvg(code).then((svg) => {
777
+ if (this.flagKey !== key || this.picture !== picture)
778
+ return;
779
+ if (svg) {
780
+ const badge = document.createElement("span");
781
+ badge.className = "flag";
782
+ badge.setAttribute("part", "flag");
783
+ badge.setAttribute("role", "img");
784
+ badge.setAttribute("aria-label", regionName(code, this.closest("[lang]")?.getAttribute("lang")));
785
+ badge.innerHTML = svg;
786
+ badge.firstElementChild?.setAttribute("aria-hidden", "true");
787
+ picture.append(badge);
788
+ }
789
+ this.dataset.flag = svg ? code : "";
790
+ });
737
791
  }
738
792
  async applyRocket() {
739
793
  this.mark();
@@ -1001,6 +1055,7 @@ export class UchusenRocket extends HTMLBase {
1001
1055
  made.drive(this.driven);
1002
1056
  made.setFloating(this.hasAttribute("float"));
1003
1057
  made.setEdges(this.getAttribute("edges") === "open");
1058
+ made.setSun(parseSun(this.getAttribute("sun")));
1004
1059
  made.setNameLabels(this.getAttribute("names") !== "off");
1005
1060
  made.setVisible(true);
1006
1061
  const { look, show, viewName, angle } = this.settings(this.builtNow ?? built);
@@ -1193,3 +1248,4 @@ export function defineUchusenRocket() {
1193
1248
  }
1194
1249
  defineUchusenRocket();
1195
1250
  export { ROCKETS, UchusenFlight };
1251
+ export { setFlagLoader } from "./element/flag.js";
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export { blank, figureWords } from "./data/types.ts";
4
4
  export type { Disagreement, Figure, Variant, VariantFacts, VariantStatus } from "./data/types.ts";
5
5
  export { MAKERS } from "./data/makers.ts";
6
6
  export type { MakerId } from "./data/makers.ts";
7
+ export { COUNTRY_CODES, countryOf, MAKER_COUNTRIES } from "./data/countries.ts";
8
+ export type { CountryCode } from "./data/countries.ts";
7
9
  export { FLIGHT_LOG, flightLogOf } from "./data/flight-log.ts";
8
10
  export type { LoggedFlight } from "./data/flight-log.ts";
9
11
  export { moved, movingGroupsOf } from "./model/moves.ts";
@@ -57,6 +59,7 @@ export { beaconLamp, dutyOf, lampFactor, lightMode, lightPool, sternLamp, workLa
57
59
  export type { Motion, MotionKind, MotionSpeed } from "./model/fleet/motion.ts";
58
60
  export { bellsOf, enginesWanted, FLAME_SETTLE_MS, pickBells, plumeLength, ringsOf } from "./engines.ts";
59
61
  export type { Bell } from "./engines.ts";
62
+ export { parseSun } from "./sun.ts";
60
63
  export { OPTION_ATTRIBUTES, attributesFromOptions, optionsFromAttributes, rocketFromParams, rocketToParams } from "./attrs.ts";
61
64
  export type { RocketOptions } from "./attrs.ts";
62
65
  export { assetPathOf, CDN, EMBED_KINDS, SITE, THREE_VERSION, embedCode, embedNote, paintedCredit } from "./embed.ts";
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  export { PLANNED_ROCKETS, ROCKETS, partIdOf, SLOT_NAMES, variantOf } from "./rockets.js";
3
3
  export { blank, figureWords } from "./data/types.js";
4
4
  export { MAKERS } from "./data/makers.js";
5
+ export { COUNTRY_CODES, countryOf, MAKER_COUNTRIES } from "./data/countries.js";
5
6
  export { FLIGHT_LOG, flightLogOf } from "./data/flight-log.js";
6
7
  export { moved, movingGroupsOf } from "./model/moves.js";
7
8
  export { PROPELLANTS, propellantOf } from "./data/propellants.js";
@@ -34,6 +35,7 @@ export { beamLight } from "./draw/beamLight.js";
34
35
  export { angleAt, MOTIONS, motionNote, motionSpeed, motionsOf, motionWanted, slideAt } from "./model/fleet/motion.js";
35
36
  export { beaconLamp, dutyOf, lampFactor, lightMode, lightPool, sternLamp, workLamp } from "./model/fleet/lights.js";
36
37
  export { bellsOf, enginesWanted, FLAME_SETTLE_MS, pickBells, plumeLength, ringsOf } from "./engines.js";
38
+ export { parseSun } from "./sun.js";
37
39
  export { OPTION_ATTRIBUTES, attributesFromOptions, optionsFromAttributes, rocketFromParams, rocketToParams } from "./attrs.js";
38
40
  export { assetPathOf, CDN, EMBED_KINDS, SITE, THREE_VERSION, embedCode, embedNote, paintedCredit } from "./embed.js";
39
41
  export { COMPANIES, COMPANY_KINDS, companyOf } from "./model/fleet/company.js";
package/dist/inspace.js CHANGED
@@ -7,6 +7,7 @@ const SPACE = {
7
7
  sls: ["upper-stage", "service-module", "capsule"],
8
8
  "falcon-9": ["second-stage", "fairing", "trunk", "capsule"],
9
9
  dragon: ["trunk", "capsule"],
10
+ "falcon-heavy": ["car", "starman"],
10
11
  };
11
12
  /**
12
13
  * The slots to draw when the craft is in space, or null when the picture stays the whole variant.
@@ -1,6 +1,6 @@
1
1
  // Every part there is, with its record and how to make its pieces. A new rocket adds its rows here and nothing else changes.
2
2
  import { DRAGON_VARIANTS } from "../data/dragon.js";
3
- import { FALCON_9_VARIANTS } from "../data/falcon.js";
3
+ import { FALCON_9_VARIANTS, FALCON_HEAVY_VARIANTS } from "../data/falcon.js";
4
4
  import { STARSHIP_VARIANTS } from "../data/starship.js";
5
5
  import { capsule, centreCore, DRAGON_DRAWS, falconFairing, falconFirstStage, FALCON_DRAWS, falconSecondStage, grasshopper, merlin, sideBooster, trunk } from "./falcon.js";
6
6
  import { FALCON_1_VARIANTS } from "../data/falcon1.js";
@@ -37,6 +37,8 @@ import { HUBBLE_VARIANTS } from "../data/hubble.js";
37
37
  import { HUBBLE_BUILDS } from "./hubble.js";
38
38
  import { TIANGONG_VARIANTS } from "../data/tiangong.js";
39
39
  import { TIANGONG_BUILDS } from "./tiangong.js";
40
+ import { ROADSTER_VARIANTS } from "../data/roadster.js";
41
+ import { ROADSTER_BUILDS } from "./roadster.js";
40
42
  import { DRONE_VARIANTS } from "../data/drone.js";
41
43
  import { DRONE_BUILDS } from "./drone.js";
42
44
  import { DRAGON_INSIDE_BUILDS } from "./dragoninside.js";
@@ -145,11 +147,11 @@ const FH_FROM = "This package's code for the two line looks, proportions taken f
145
147
  const FH_SIDE_HEIGHT = 45.6;
146
148
  const FH_OFFSET = 4.25;
147
149
  const figure = (value, source) => ({ value, source });
148
- entries.push({ record: { id: "falcon-heavy-falcon-heavy-centre", kind: "first-stage", name: "Falcon Heavy centre core, with its upper stage and fairing", rocket: "falcon-heavy", trueTo: ["falcon-heavy"], height: 70, diameter: 5.2, sourced: { height: figure(70, "wikipedia-falcon-heavy"), diameter: { ...blankFigure, note: "3.7 m the core; 5.2 m the fairing." } }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-centre"], drawnFrom: FH_FROM }, pieces: () => centreCore(70, 1.85) }, { record: { id: "falcon-heavy-falcon-heavy-side-left", kind: "first-stage", name: "Falcon Heavy side booster, left", rocket: "falcon-heavy", trueTo: ["falcon-heavy"], height: FH_SIDE_HEIGHT, diameter: 3.7, strapOn: { x: -FH_OFFSET, z: 0 }, sourced: { height: blankFigure, diameter: figure(3.7, "wikipedia-falcon-heavy") }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-side-left"], drawnFrom: FH_FROM }, pieces: () => sideBooster(FH_SIDE_HEIGHT, 1.85, -1) }, { record: { id: "falcon-heavy-falcon-heavy-side-right", kind: "first-stage", name: "Falcon Heavy side booster, right", rocket: "falcon-heavy", trueTo: ["falcon-heavy"], height: FH_SIDE_HEIGHT, diameter: 3.7, strapOn: { x: FH_OFFSET, z: 0 }, sourced: { height: blankFigure, diameter: figure(3.7, "wikipedia-falcon-heavy") }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-side-right"], drawnFrom: FH_FROM }, pieces: () => sideBooster(FH_SIDE_HEIGHT, 1.85, 1) }, { record: { id: "grasshopper-grasshopper-vehicle", kind: "test-vehicle", name: "Grasshopper, test vehicle", rocket: "grasshopper", trueTo: ["grasshopper"], height: 32, diameter: 3.2, sourced: { height: figure(32, "wikipedia-grasshopper"), diameter: blankFigure }, joints: {}, painted: PAINTED["grasshopper-grasshopper-vehicle"], drawnFrom: "This package's code for the two line looks, proportions taken from Echo_3's low-poly model at the sourced height; that model for the painted look." }, pieces: () => grasshopper(32) });
150
+ entries.push({ record: { id: "falcon-heavy-falcon-heavy-centre", kind: "first-stage", name: "Falcon Heavy centre core, with its upper stage and fairing", rocket: "falcon-heavy", trueTo: ["falcon-heavy"], height: 70, diameter: 5.2, sourced: { height: figure(70, "wikipedia-falcon-heavy"), diameter: { ...blankFigure, note: "3.7 m the core; 5.2 m the fairing." } }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-centre"], drawnFrom: FH_FROM }, pieces: () => centreCore(70, 1.85) }, { record: { id: "falcon-heavy-falcon-heavy-side-left", kind: "first-stage", name: "Falcon Heavy side booster, left", rocket: "falcon-heavy", trueTo: ["falcon-heavy", "test-flight"], height: FH_SIDE_HEIGHT, diameter: 3.7, strapOn: { x: -FH_OFFSET, z: 0 }, sourced: { height: blankFigure, diameter: figure(3.7, "wikipedia-falcon-heavy") }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-side-left"], drawnFrom: FH_FROM }, pieces: () => sideBooster(FH_SIDE_HEIGHT, 1.85, -1) }, { record: { id: "falcon-heavy-falcon-heavy-side-right", kind: "first-stage", name: "Falcon Heavy side booster, right", rocket: "falcon-heavy", trueTo: ["falcon-heavy", "test-flight"], height: FH_SIDE_HEIGHT, diameter: 3.7, strapOn: { x: FH_OFFSET, z: 0 }, sourced: { height: blankFigure, diameter: figure(3.7, "wikipedia-falcon-heavy") }, joints: {}, painted: PAINTED["falcon-heavy-falcon-heavy-side-right"], drawnFrom: FH_FROM }, pieces: () => sideBooster(FH_SIDE_HEIGHT, 1.85, 1) }, { record: { id: "grasshopper-grasshopper-vehicle", kind: "test-vehicle", name: "Grasshopper, test vehicle", rocket: "grasshopper", trueTo: ["grasshopper"], height: 32, diameter: 3.2, sourced: { height: figure(32, "wikipedia-grasshopper"), diameter: blankFigure }, joints: {}, painted: PAINTED["grasshopper-grasshopper-vehicle"], drawnFrom: "This package's code for the two line looks, proportions taken from Echo_3's low-poly model at the sourced height; that model for the painted look." }, pieces: () => grasshopper(32) });
149
151
  // ---------------------------------------------------------------------------------------------------------------------------------
150
152
  // Rockets drawn wholly by this package's code, from their tables
151
- const stackFacts = [...DRONE_VARIANTS, ...HUBBLE_VARIANTS, ...ISS_VARIANTS, ...TIANGONG_VARIANTS, ...JUPITER_C_VARIANTS, ...MERCURY_VARIANTS, ...GEMINI_VARIANTS, ...APOLLO_SOYUZ_VARIANTS, ...LUNAR_MODULE_VARIANTS, ...STARBASE_VARIANTS, ...DRAGON_VARIANTS, ...TOWER_VARIANTS, ...DRONESHIP_VARIANTS, ...LANDING_ZONE_VARIANTS, ...SOYUZ_VARIANTS, ...NEW_GLENN_VARIANTS, ...ELECTRON_VARIANTS, ...SLS_VARIANTS, ...FALCON_1_VARIANTS, ...SATURN_V_VARIANTS, ...SHUTTLE_VARIANTS, ...SHIP_02_VARIANTS];
152
- const stacks = [...DRONE_BUILDS, ...HUBBLE_BUILDS, ...ISS_BUILDS, ...TIANGONG_BUILDS, ...JUPITER_C_BUILDS, ...MERCURY_BUILDS, ...GEMINI_BUILDS, ...APOLLO_SOYUZ_BUILDS, ...LUNAR_MODULE_BUILDS, ...STARBASE_BUILDS, ...DRAGON_INSIDE_BUILDS, ...TOWER_BUILDS, ...DRONESHIP_BUILDS, ...LANDING_ZONE_BUILDS, ...SOYUZ_BUILDS, ...NEW_GLENN_BUILDS, ...ELECTRON_BUILDS, ...SLS_BUILDS, ...FALCON_1_BUILDS, ...SATURN_V_BUILDS, ...SHUTTLE_BUILDS, ...SHIP_02_BUILDS];
153
+ const stackFacts = [...ROADSTER_VARIANTS, ...FALCON_HEAVY_VARIANTS, ...DRONE_VARIANTS, ...HUBBLE_VARIANTS, ...ISS_VARIANTS, ...TIANGONG_VARIANTS, ...JUPITER_C_VARIANTS, ...MERCURY_VARIANTS, ...GEMINI_VARIANTS, ...APOLLO_SOYUZ_VARIANTS, ...LUNAR_MODULE_VARIANTS, ...STARBASE_VARIANTS, ...DRAGON_VARIANTS, ...TOWER_VARIANTS, ...DRONESHIP_VARIANTS, ...LANDING_ZONE_VARIANTS, ...SOYUZ_VARIANTS, ...NEW_GLENN_VARIANTS, ...ELECTRON_VARIANTS, ...SLS_VARIANTS, ...FALCON_1_VARIANTS, ...SATURN_V_VARIANTS, ...SHUTTLE_VARIANTS, ...SHIP_02_VARIANTS];
154
+ const stacks = [...ROADSTER_BUILDS, ...DRONE_BUILDS, ...HUBBLE_BUILDS, ...ISS_BUILDS, ...TIANGONG_BUILDS, ...JUPITER_C_BUILDS, ...MERCURY_BUILDS, ...GEMINI_BUILDS, ...APOLLO_SOYUZ_BUILDS, ...LUNAR_MODULE_BUILDS, ...STARBASE_BUILDS, ...DRAGON_INSIDE_BUILDS, ...TOWER_BUILDS, ...DRONESHIP_BUILDS, ...LANDING_ZONE_BUILDS, ...SOYUZ_BUILDS, ...NEW_GLENN_BUILDS, ...ELECTRON_BUILDS, ...SLS_BUILDS, ...FALCON_1_BUILDS, ...SATURN_V_BUILDS, ...SHUTTLE_BUILDS, ...SHIP_02_BUILDS];
153
155
  for (const build of stacks)
154
156
  for (const part of build.parts) {
155
157
  const { height, diameter } = facts(stackFacts, build.variant, part.slot);
@@ -0,0 +1,31 @@
1
+ import type { Piece, Vec3 } from "./pieces.ts";
2
+ import type { StackBuild } from "./stacks.ts";
3
+ /** One car as measured from its model, in metres: nose towards +Z, on the ground at y = 0. */
4
+ export interface CarDraw {
5
+ /** Nose to tail, and across. */
6
+ length: number;
7
+ width: number;
8
+ /** The top of the windscreen. */
9
+ height: number;
10
+ /** The wheels: their middles along the car, their radius and how far each stands from the middle. */
11
+ front: number;
12
+ rear: number;
13
+ wheel: number;
14
+ track: number;
15
+ /** The top of the doors. */
16
+ belt: number;
17
+ /** Where the windscreen meets the body, along the car. */
18
+ screen: number;
19
+ /** The driver's hip, in the left seat: across, up and along. */
20
+ seat: Vec3;
21
+ /** The middle of the steering wheel. */
22
+ wheelAt: Vec3;
23
+ }
24
+ export declare const ROADSTER_DRAWS: Readonly<Record<string, CarDraw>>;
25
+ /** The car: its body seen from the side and pressed to its width, its cockpit open, the windscreen and the four wheels. */
26
+ export declare function carPieces(d: CarDraw): Piece[];
27
+ /** Starman in the driver's seat: legs forward to the pedals, his back against the seat, his right hand on the wheel, his left arm along the top of the door. */
28
+ export declare function starmanPieces(d: CarDraw): Piece[];
29
+ /** Where the payload stands in Falcon Heavy: 0.8 m above where its fairing begins (the fairing's sourced 13.1 m under the 70 m top), clear of the fairing's walls. The 0.8 m is a placing, not a figure. */
30
+ export declare const FALCON_HEAVY_PAYLOAD_Y: number;
31
+ export declare const ROADSTER_BUILDS: readonly StackBuild[];
@@ -0,0 +1,112 @@
1
+ // The Roadster in plain shapes for the line looks, and Starman in every look. The car's length, wheelbase, wheels and seat are measured from each licensed model at its sourced scale; the painted look is the
2
+ // model. Starman is this package's own: a seated figure in a white suit with dark trim, a white helmet with a dark visor, his right hand on the wheel and his left arm on the door. He shares the car's origin
3
+ // (the middle of the car on the ground), so he sits in its seat in any build that places the two together.
4
+ import { ROADSTER_FLIGHT } from "../data/roadster.js";
5
+ import { blank } from "../data/types.js";
6
+ import { centreCore } from "./falcon.js";
7
+ import { strut } from "./pieces.js";
8
+ export const ROADSTER_DRAWS = {
9
+ "first-generation": { length: 3.946, width: 1.85, height: 1.03, front: 1.114, rear: -1.258, wheel: 0.3, track: 1.46, belt: 0.8, screen: 0.95, seat: [0.29, 0.34, -0.22], wheelAt: [0.29, 0.62, 0.37] },
10
+ "second-generation": { length: 4.47, width: 1.83, height: 1.14, front: 1.417, rear: -1.408, wheel: 0.31, track: 1.52, belt: 0.84, screen: 0.75, seat: [0.32, 0.22, -0.18], wheelAt: [0.32, 0.58, 0.3] },
11
+ "second-generation-2": { length: 4.28, width: 1.86, height: 1.07, front: 1.398, rear: -1.429, wheel: 0.31, track: 1.58, belt: 0.8, screen: 0.7, seat: [0.32, 0.17, -0.2], wheelAt: [0.32, 0.55, 0.28] },
12
+ };
13
+ const side = (d) => {
14
+ const nose = d.length / 2;
15
+ return [
16
+ [-nose, 0.22],
17
+ [-d.length * 0.16, 0.14],
18
+ [nose * 0.8, 0.14],
19
+ [nose, 0.32],
20
+ [nose * 0.92, 0.6],
21
+ [d.screen, d.belt - 0.02],
22
+ [-nose * 0.9, d.belt + 0.04],
23
+ [-nose, 0.62],
24
+ ];
25
+ };
26
+ const octagon = (r) => Array.from({ length: 12 }, (_, i) => [Math.cos((i * Math.PI) / 6) * r, Math.sin((i * Math.PI) / 6) * r]);
27
+ /** The car: its body seen from the side and pressed to its width, its cockpit open, the windscreen and the four wheels. */
28
+ export function carPieces(d) {
29
+ const half = d.width / 2;
30
+ const pieces = [{ kind: "plate", material: "red", frame: { o: [0, 0, 0], u: [0, 0, 1], v: [0, 1, 0], w: [1, 0, 0] }, outline: side(d), thickness: d.width - 0.06 }];
31
+ // the open cockpit, dark, sunk into the top of the body
32
+ pieces.push({ kind: "plate", material: "dark", frame: { o: [0, d.belt + 0.005, 0], u: [1, 0, 0], v: [0, 0, 1], w: [0, 1, 0] }, outline: [[-half + 0.2, -0.75], [half - 0.2, -0.75], [half - 0.2, d.screen - 0.05], [-half + 0.2, d.screen - 0.05]], thickness: 0.01, plain: true });
33
+ // the windscreen, leaning back from the scuttle
34
+ const lean = 0.55;
35
+ const rise = d.height - d.belt;
36
+ const up = [0, Math.cos(lean), -Math.sin(lean)];
37
+ pieces.push({ kind: "plate", material: "glass", frame: { o: [0, d.belt, d.screen], u: [1, 0, 0], v: up, w: [0, Math.sin(lean), Math.cos(lean)] }, outline: [[-half + 0.18, 0], [half - 0.18, 0], [half - 0.26, rise / Math.cos(lean)], [-half + 0.26, rise / Math.cos(lean)]], thickness: 0.012 });
38
+ for (const z of [d.front, d.rear])
39
+ for (const s of [1, -1]) {
40
+ const x = (s * d.track) / 2;
41
+ pieces.push({ kind: "plate", material: "black", frame: { o: [x, d.wheel, z], u: [0, 0, 1], v: [0, 1, 0], w: [1, 0, 0] }, outline: octagon(d.wheel), thickness: 0.24 });
42
+ pieces.push({ kind: "plate", material: "fin-dark", frame: { o: [x + s * 0.121, d.wheel, z], u: [0, 0, 1], v: [0, 1, 0], w: [1, 0, 0] }, outline: octagon(d.wheel * 0.62), thickness: 0.01, plain: true });
43
+ }
44
+ return pieces;
45
+ }
46
+ const at = (p, dx, dy, dz) => [p[0] + dx, p[1] + dy, p[2] + dz];
47
+ /** Starman in the driver's seat: legs forward to the pedals, his back against the seat, his right hand on the wheel, his left arm along the top of the door. */
48
+ export function starmanPieces(d) {
49
+ const hip = d.seat;
50
+ const shoulder = at(hip, 0, 0.52, -0.1);
51
+ const knee = at(hip, 0, 0.12, 0.44);
52
+ // the feet on the floor, which is never under the ground
53
+ const foot = [hip[0], Math.max(0.12, hip[1] - 0.18), hip[2] + 0.86];
54
+ const right = at(shoulder, -0.19, 0, 0);
55
+ const left = at(shoulder, 0.19, 0, 0);
56
+ const door = [Math.min(d.width / 2 - 0.08, hip[0] + 0.42), d.belt + 0.05, hip[2] + 0.12];
57
+ const pieces = [
58
+ // the torso, leaning back with the seat
59
+ { kind: "lathe", material: "white", segs: 16, at: [hip[0], hip[2]], lean: [0, -0.19], profile: [[0.14, hip[1]], [0.18, hip[1] + 0.22], [0.19, shoulder[1] - 0.06], [0.1, shoulder[1] + 0.04]] },
60
+ // the helmet, white, round, with its dark visor facing the road
61
+ { kind: "lathe", material: "white", segs: 20, at: [shoulder[0], shoulder[2] - 0.02], profile: [[0.07, shoulder[1] + 0.04], [0.15, shoulder[1] + 0.13], [0.155, shoulder[1] + 0.22], [0.12, shoulder[1] + 0.31], [0.001, shoulder[1] + 0.35]] },
62
+ { kind: "plate", material: "glass", frame: { o: [shoulder[0], shoulder[1] + 0.2, shoulder[2] + 0.12], u: [1, 0, 0], v: [0, 1, 0], w: [0, 0, 1] }, outline: [[-0.1, -0.06], [0.1, -0.06], [0.11, 0.05], [-0.11, 0.05]], thickness: 0.04, plain: true },
63
+ // a dark ring where the helmet meets the suit
64
+ { kind: "lathe", material: "black", segs: 16, at: [shoulder[0], shoulder[2] - 0.02], profile: [[0.12, shoulder[1] + 0.01], [0.12, shoulder[1] + 0.06]], only: "mesh" },
65
+ ];
66
+ for (const s of [-1, 1]) {
67
+ const h = at(hip, s * 0.1, 0, 0);
68
+ const k = at(knee, s * 0.1, 0, 0);
69
+ const f = at(foot, s * 0.1, 0, 0);
70
+ pieces.push(strut("white", h, k, 0.15, 0.15), strut("white", k, f, 0.13, 0.13), strut("black", f, at(f, 0, 0.02, 0.1), 0.12, 0.14));
71
+ }
72
+ // his right hand on the wheel's rim, his left arm on the door top
73
+ const hand = at(d.wheelAt, -0.12, 0.04, -0.03);
74
+ const elbowR = at(right, -0.02, -0.22, 0.2);
75
+ const elbowL = at(left, 0.12, -0.2, 0.05);
76
+ pieces.push(strut("white", right, elbowR, 0.11, 0.11), strut("white", elbowR, hand, 0.1, 0.1), strut("black", hand, at(hand, 0, 0, 0.06), 0.09, 0.09));
77
+ pieces.push(strut("white", left, elbowL, 0.11, 0.11), strut("white", elbowL, door, 0.1, 0.1), strut("black", door, at(door, 0, 0, 0.07), 0.09, 0.09));
78
+ return pieces;
79
+ }
80
+ const FILES = {
81
+ "first-generation": { file: "roadster-first-generation-car.glb", credit: "the86guy-roadster" },
82
+ "second-generation": { file: "roadster-second-generation-car.glb", credit: "metarex4d-roadster" },
83
+ "second-generation-2": { file: "roadster-second-generation-2-car.glb", credit: "gmmichaelonearth-roadster", also: ["minchou19-wheels"] },
84
+ };
85
+ const STARMAN_NAME = "Starman, a mannequin in a pressure suit";
86
+ const carPart = (variant, place) => {
87
+ const d = ROADSTER_DRAWS[variant];
88
+ return { slot: "car", kind: "payload", name: variant === "first-generation" ? "The Roadster, first generation" : "The Roadster, second generation", height: d.height, diameter: d.width, sourcedDiameter: blank(`Its width as drawn: the model at its sourced scale is ${d.width} m across.`), painted: FILES[variant], ...(place ? { place } : {}), pieces: () => carPieces(d) };
89
+ };
90
+ const starmanPart = (variant, place) => {
91
+ const d = ROADSTER_DRAWS[variant];
92
+ return { slot: "starman", kind: "payload", name: STARMAN_NAME, height: d.seat[1] + 0.87, diameter: 0.6, sourcedDiameter: blank("No source read here gives Starman's size."), place, pieces: () => starmanPieces(d) };
93
+ };
94
+ const FROM = (variant) => variant === "first-generation"
95
+ ? "The 86 guy's model for the painted look, with its badges and roof left off and its bonnet made bare carbon; this package's plain shapes for the line looks, measured from it at the sourced length. Starman is this package's own in every look."
96
+ : "Its author's model for the painted look, with its badges left off, painted the first generation's red; this package's plain shapes for the line looks, measured from it at the sourced wheelbase. Starman is this package's own in every look.";
97
+ /** Where the payload stands in Falcon Heavy: 0.8 m above where its fairing begins (the fairing's sourced 13.1 m under the 70 m top), clear of the fairing's walls. The 0.8 m is a placing, not a figure. */
98
+ export const FALCON_HEAVY_PAYLOAD_Y = 70 - 13.1 + 0.8;
99
+ export const ROADSTER_BUILDS = [
100
+ ...Object.keys(ROADSTER_DRAWS).map((variant) => ({ rocket: "roadster", variant, drawnFrom: FROM(variant), parts: [carPart(variant), starmanPart(variant, { x: 0, z: 0 })] })),
101
+ {
102
+ rocket: "falcon-heavy",
103
+ variant: ROADSTER_FLIGHT.id,
104
+ drawnFrom: "Antonio_Adams's low-poly Falcon Heavy and the 86 guy's Roadster for the painted look, this package's plain shapes for the line looks. Starman is this package's own in every look.",
105
+ parts: [
106
+ // the same vehicle as Falcon Heavy's own, on its first flight: its centre core is its own part here, so the test flight is reached from its own foot
107
+ { slot: "centre", kind: "first-stage", name: "Falcon Heavy centre core, with its upper stage and fairing", height: 70, diameter: 5.2, sourcedDiameter: blank("3.7 m the core; 5.2 m the fairing."), painted: { file: "falcon-heavy-falcon-heavy-centre.glb", credit: "antonio-falcon-heavy" }, pieces: () => centreCore(70, 1.85) },
108
+ carPart("first-generation", { x: 0, z: 0, y: FALCON_HEAVY_PAYLOAD_Y }),
109
+ starmanPart("first-generation", { x: 0, z: 0, y: FALCON_HEAVY_PAYLOAD_Y }),
110
+ ],
111
+ },
112
+ ];
package/dist/parts.d.ts CHANGED
@@ -11,7 +11,7 @@ import type { Figure } from "./data/types.ts";
11
11
  * console.log(kind);
12
12
  * ```
13
13
  */
14
- export type PartKind = "booster" | "first-stage" | "ship" | "second-stage" | "fairing" | "capsule" | "trunk" | "engine" | "fin" | "flap" | "ring" | "test-vehicle" | "third-stage" | "fourth-stage" | "tail" | "adapter" | "service-module" | "tower" | "tank" | "solid-booster" | "liquid-booster" | "orbiter" | "deck" | "mount" | "arms" | "site" | "spacecraft" | "module" | "array" | "antenna";
14
+ export type PartKind = "booster" | "first-stage" | "ship" | "second-stage" | "fairing" | "capsule" | "trunk" | "engine" | "fin" | "flap" | "ring" | "test-vehicle" | "third-stage" | "fourth-stage" | "tail" | "adapter" | "service-module" | "tower" | "tank" | "solid-booster" | "liquid-booster" | "orbiter" | "deck" | "mount" | "arms" | "site" | "spacecraft" | "module" | "array" | "antenna" | "payload";
15
15
  /**
16
16
  * Where a part mates. Every part's origin is the middle of its base on its axis, at its lowest point; Y is up and the windward side faces +Z, in metres.
17
17
  *
@@ -0,0 +1,182 @@
1
+ import { type Vec3 } from "./vector.ts";
2
+ /**
3
+ * One layer of a layered atmosphere: from its base up, the temperature changes at a fixed rate with geopotential height (or stays the same), and the pressure and density follow.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { EARTH_ATMOSPHERE, type AtmosphereLayer } from "@johnmorrisdotca/uchusen-rockets/physics";
8
+ *
9
+ * const ground: AtmosphereLayer = EARTH_ATMOSPHERE.layers[0]!;
10
+ * console.log(ground.temperature, ground.lapse);
11
+ * ```
12
+ */
13
+ export interface AtmosphereLayer {
14
+ /** The layer's base, geopotential metres. */
15
+ base: number;
16
+ /** Temperature at the base, kelvin. */
17
+ temperature: number;
18
+ /** How the temperature changes going up, kelvin a geopotential metre (0 for a layer of one temperature). */
19
+ lapse: number;
20
+ /** Pressure at the base, pascals. */
21
+ pressure: number;
22
+ /** Density at the base, kg/m^3. */
23
+ density: number;
24
+ }
25
+ /**
26
+ * A layered model of a planet's air, as Orbiter's 2006 models are.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { MARS_ATMOSPHERE, type AtmosphereModel } from "@johnmorrisdotca/uchusen-rockets/physics";
31
+ *
32
+ * const mars: AtmosphereModel = MARS_ATMOSPHERE;
33
+ * console.log(mars.name, mars.limit);
34
+ * ```
35
+ */
36
+ export interface AtmosphereModel {
37
+ name: string;
38
+ /** The radius the geopotential height is worked from, metres. */
39
+ radius: number;
40
+ /** Standard gravity over the gas constant, g0 / R, kelvin a metre. */
41
+ g0R: number;
42
+ /** The specific gas constant, J/(kg K). */
43
+ gasConstant: number;
44
+ /** The ratio of specific heats. */
45
+ gamma: number;
46
+ /** Above this height (geometric metres) the model has no air. */
47
+ limit: number;
48
+ /** The layers, lowest first. */
49
+ layers: readonly AtmosphereLayer[];
50
+ }
51
+ /**
52
+ * The air at a height: temperature (K), pressure (Pa), density (kg/m^3) and the speed of sound (m/s). All zero where there is no air.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { atmosphereAt, EARTH_ATMOSPHERE, type Air } from "@johnmorrisdotca/uchusen-rockets/physics";
57
+ *
58
+ * const air: Air = atmosphereAt(EARTH_ATMOSPHERE, 10_000);
59
+ * console.log(air.density.toFixed(3));
60
+ * ```
61
+ */
62
+ export interface Air {
63
+ temperature: number;
64
+ pressure: number;
65
+ density: number;
66
+ speedOfSound: number;
67
+ }
68
+ /**
69
+ * The Earth's air as Orbiter 2006 models it (Orbiter's EarthAtm2006: "a simple static atmosphere model", its own description, ending at 200 km).
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * import { atmosphereAt, EARTH_ATMOSPHERE } from "@johnmorrisdotca/uchusen-rockets/physics";
74
+ *
75
+ * console.log(atmosphereAt(EARTH_ATMOSPHERE, 0).pressure);
76
+ * ```
77
+ */
78
+ export declare const EARTH_ATMOSPHERE: AtmosphereModel;
79
+ /**
80
+ * Mars's air as Orbiter 2006 models it (Orbiter's MarsAtm2006), ending at 100 km.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { atmosphereAt, MARS_ATMOSPHERE } from "@johnmorrisdotca/uchusen-rockets/physics";
85
+ *
86
+ * console.log(atmosphereAt(MARS_ATMOSPHERE, 0).pressure);
87
+ * ```
88
+ */
89
+ export declare const MARS_ATMOSPHERE: AtmosphereModel;
90
+ /**
91
+ * Venus's air as Orbiter 2006 models it (Orbiter's VenusAtm2006), ending at 200 km.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { atmosphereAt, VENUS_ATMOSPHERE } from "@johnmorrisdotca/uchusen-rockets/physics";
96
+ *
97
+ * console.log(atmosphereAt(VENUS_ATMOSPHERE, 0).temperature);
98
+ * ```
99
+ */
100
+ export declare const VENUS_ATMOSPHERE: AtmosphereModel;
101
+ /**
102
+ * The air of a layered model at a height above the surface (Orbiter's `clbkParams`): the geometric height is turned into geopotential height, the layer below it is found, and the temperature, pressure and density follow from that layer.
103
+ * @param model The model.
104
+ * @param altitude The geometric height above the reference radius, metres (below 0 the lowest layer goes on down).
105
+ * @returns The air there; all zero above the model's limit, as Orbiter has no air there.
106
+ * @example
107
+ * ```ts
108
+ * import { atmosphereAt, EARTH_ATMOSPHERE } from "@johnmorrisdotca/uchusen-rockets/physics";
109
+ *
110
+ * const air = atmosphereAt(EARTH_ATMOSPHERE, 30_000);
111
+ * console.log(air.temperature.toFixed(1), "K,", air.pressure.toFixed(0), "Pa,", air.density.toFixed(4), "kg/m^3");
112
+ * ```
113
+ */
114
+ export declare function atmosphereAt(model: AtmosphereModel, altitude: number): Air;
115
+ /**
116
+ * The 1976 U.S. Standard Atmosphere from 5 km below sea level to 86 km, worked from its defining constants and equations (its tables 2, 3 and 4): seven layers of fixed temperature gradient in geopotential height, the sea-level air's mean molecular weight from its table 3, the hydrostatic equation and the gas law.
117
+ * @param altitude The geometric height above sea level, metres, from -5,000 to 86,000.
118
+ * @returns The air there (the temperature is the molecular-scale temperature, the same as the kinetic temperature below 80 km), or null outside those heights.
119
+ * @example
120
+ * ```ts
121
+ * import { standardAtmosphere } from "@johnmorrisdotca/uchusen-rockets/physics";
122
+ *
123
+ * const air = standardAtmosphere(11_000);
124
+ * console.log(air?.temperature.toFixed(3), "K,", air?.pressure.toFixed(0), "Pa");
125
+ * ```
126
+ */
127
+ export declare function standardAtmosphere(altitude: number): Air | null;
128
+ /**
129
+ * A craft's speed through the air, for air that turns with its planet and no wind (Orbiter's airspeed): its speed relative to the planet less the speed of the air where it is, `v - w x r`.
130
+ * @param position The craft's position from the planet's centre, metres.
131
+ * @param velocity Its speed relative to the planet's centre, metres a second.
132
+ * @param spin The planet's turn rate as a vector along its pole, radians a second (`[0, 0, 7.292115e-5]` for the Earth in an equatorial frame).
133
+ * @returns The velocity through the air, metres a second.
134
+ * @example
135
+ * ```ts
136
+ * import { airVelocity } from "@johnmorrisdotca/uchusen-rockets/physics";
137
+ *
138
+ * console.log(airVelocity([6_778_000, 0, 0], [0, 7_669, 0], [0, 0, 7.292115e-5]));
139
+ * ```
140
+ */
141
+ export declare const airVelocity: (position: Vec3, velocity: Vec3, spin: Vec3) => Vec3;
142
+ /**
143
+ * The dynamic pressure of the air on a craft, `rho v^2 / 2` (Orbiter's `dynp`).
144
+ * @param density The air's density, kg/m^3.
145
+ * @param airspeed The speed through the air, metres a second.
146
+ * @returns The dynamic pressure, pascals.
147
+ * @example
148
+ * ```ts
149
+ * import { dynamicPressure } from "@johnmorrisdotca/uchusen-rockets/physics";
150
+ *
151
+ * console.log(dynamicPressure(0.4, 400));
152
+ * ```
153
+ */
154
+ export declare const dynamicPressure: (density: number, airspeed: number) => number;
155
+ /**
156
+ * The Mach number: the speed through the air over the speed of sound there (Orbiter's `atmM`).
157
+ * @param airspeed The speed through the air, metres a second.
158
+ * @param air The air, from {@link atmosphereAt} or {@link standardAtmosphere}.
159
+ * @returns The Mach number; 0 where there is no air.
160
+ * @example
161
+ * ```ts
162
+ * import { atmosphereAt, EARTH_ATMOSPHERE, machNumber } from "@johnmorrisdotca/uchusen-rockets/physics";
163
+ *
164
+ * console.log(machNumber(600, atmosphereAt(EARTH_ATMOSPHERE, 11_000)).toFixed(2));
165
+ * ```
166
+ */
167
+ export declare const machNumber: (airspeed: number, air: Air) => number;
168
+ /**
169
+ * The drag on a craft as an acceleration: `-(rho |v| v / 2) Cd A / m`, against its velocity through the air (Orbiter's drag element: its drag coefficient times the dynamic pressure, along the airflow).
170
+ * @param density The air's density, kg/m^3.
171
+ * @param air The velocity through the air, from {@link airVelocity}, metres a second.
172
+ * @param dragArea The drag coefficient times the reference area, `Cd A`, m^2.
173
+ * @param mass The craft's mass, kilograms.
174
+ * @returns The acceleration, metres a second squared.
175
+ * @example
176
+ * ```ts
177
+ * import { atmosphereAt, dragAcceleration, EARTH_ATMOSPHERE } from "@johnmorrisdotca/uchusen-rockets/physics";
178
+ *
179
+ * console.log(dragAcceleration(atmosphereAt(EARTH_ATMOSPHERE, 60_000).density, [0, 7_000, 0], 1.3 * 12, 9_000));
180
+ * ```
181
+ */
182
+ export declare function dragAcceleration(density: number, air: Vec3, dragArea: number, mass: number): Vec3;