@johnmorrisdotca/wakusei-planets 0.0.0-stage → 0.3.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 (114) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +48 -0
  4. package/README.md +214 -2
  5. package/dist/bodies/all.d.ts +16 -0
  6. package/dist/bodies/all.js +16 -0
  7. package/dist/bodies/earth.d.ts +1 -0
  8. package/dist/bodies/earth.js +23 -0
  9. package/dist/bodies/jupiter.d.ts +1 -0
  10. package/dist/bodies/jupiter.js +22 -0
  11. package/dist/bodies/mars.d.ts +1 -0
  12. package/dist/bodies/mars.js +25 -0
  13. package/dist/bodies/mercury.d.ts +1 -0
  14. package/dist/bodies/mercury.js +9 -0
  15. package/dist/bodies/moons-earth.d.ts +1 -0
  16. package/dist/bodies/moons-earth.js +37 -0
  17. package/dist/bodies/moons-jupiter.d.ts +1 -0
  18. package/dist/bodies/moons-jupiter.js +29 -0
  19. package/dist/bodies/moons-mars.d.ts +1 -0
  20. package/dist/bodies/moons-mars.js +4 -0
  21. package/dist/bodies/moons-neptune.d.ts +1 -0
  22. package/dist/bodies/moons-neptune.js +11 -0
  23. package/dist/bodies/moons-pluto.d.ts +1 -0
  24. package/dist/bodies/moons-pluto.js +5 -0
  25. package/dist/bodies/moons-saturn.d.ts +1 -0
  26. package/dist/bodies/moons-saturn.js +34 -0
  27. package/dist/bodies/moons-uranus.d.ts +1 -0
  28. package/dist/bodies/moons-uranus.js +7 -0
  29. package/dist/bodies/neptune.d.ts +1 -0
  30. package/dist/bodies/neptune.js +19 -0
  31. package/dist/bodies/pluto.d.ts +1 -0
  32. package/dist/bodies/pluto.js +22 -0
  33. package/dist/bodies/saturn.d.ts +1 -0
  34. package/dist/bodies/saturn.js +14 -0
  35. package/dist/bodies/sun.d.ts +1 -0
  36. package/dist/bodies/sun.js +20 -0
  37. package/dist/bodies/uranus.d.ts +1 -0
  38. package/dist/bodies/uranus.js +15 -0
  39. package/dist/bodies/venus.d.ts +1 -0
  40. package/dist/bodies/venus.js +10 -0
  41. package/dist/data/orbits.d.ts +20 -0
  42. package/dist/data/orbits.js +27 -0
  43. package/dist/element.d.ts +88 -0
  44. package/dist/element.js +603 -0
  45. package/dist/ephemeris.d.ts +189 -0
  46. package/dist/ephemeris.js +340 -0
  47. package/dist/facts.d.ts +213 -0
  48. package/dist/facts.js +198 -0
  49. package/dist/gate.d.ts +37 -0
  50. package/dist/gate.js +54 -0
  51. package/dist/index.d.ts +5 -0
  52. package/dist/index.js +5 -0
  53. package/dist/line.d.ts +82 -0
  54. package/dist/line.js +280 -0
  55. package/dist/live.d.ts +42 -0
  56. package/dist/live.js +62 -0
  57. package/dist/pose.d.ts +62 -0
  58. package/dist/pose.js +69 -0
  59. package/dist/scene/body.d.ts +68 -0
  60. package/dist/scene/body.js +337 -0
  61. package/dist/scene/glsl.d.ts +13 -0
  62. package/dist/scene/glsl.js +186 -0
  63. package/dist/scene/look.d.ts +106 -0
  64. package/dist/scene/look.js +35 -0
  65. package/dist/scene/recipes.d.ts +49 -0
  66. package/dist/scene/recipes.js +76 -0
  67. package/dist/scene/rings.d.ts +34 -0
  68. package/dist/scene/rings.js +64 -0
  69. package/dist/scene/shared.d.ts +24 -0
  70. package/dist/scene/shared.js +55 -0
  71. package/dist/scene/store.d.ts +19 -0
  72. package/dist/scene/store.js +8 -0
  73. package/dist/textures/callisto-1024.webp +0 -0
  74. package/dist/textures/callisto-512.webp +0 -0
  75. package/dist/textures/enceladus-1024.webp +0 -0
  76. package/dist/textures/enceladus-512.webp +0 -0
  77. package/dist/textures/europa-1024.webp +0 -0
  78. package/dist/textures/europa-512.webp +0 -0
  79. package/dist/textures/ganymede-1024.webp +0 -0
  80. package/dist/textures/ganymede-512.webp +0 -0
  81. package/dist/textures/io-1024.webp +0 -0
  82. package/dist/textures/io-512.webp +0 -0
  83. package/dist/textures/jupiter-1024.webp +0 -0
  84. package/dist/textures/jupiter-512.webp +0 -0
  85. package/dist/textures/mars-1024.webp +0 -0
  86. package/dist/textures/mars-512.webp +0 -0
  87. package/dist/textures/mercury-1024.webp +0 -0
  88. package/dist/textures/mercury-512.webp +0 -0
  89. package/dist/textures/mimas-1024.webp +0 -0
  90. package/dist/textures/mimas-512.webp +0 -0
  91. package/dist/textures/moon-1024.webp +0 -0
  92. package/dist/textures/moon-512.webp +0 -0
  93. package/dist/textures/neptune-1024.webp +0 -0
  94. package/dist/textures/neptune-512.webp +0 -0
  95. package/dist/textures/pluto-1024.webp +0 -0
  96. package/dist/textures/pluto-512.webp +0 -0
  97. package/dist/textures/rhea-1024.webp +0 -0
  98. package/dist/textures/rhea-512.webp +0 -0
  99. package/dist/textures/saturn-1024.webp +0 -0
  100. package/dist/textures/saturn-512.webp +0 -0
  101. package/dist/textures/saturn-ring-2048.webp +0 -0
  102. package/dist/textures/sun-1024.webp +0 -0
  103. package/dist/textures/sun-512.webp +0 -0
  104. package/dist/textures/titan-1024.webp +0 -0
  105. package/dist/textures/titan-512.webp +0 -0
  106. package/dist/textures/uranus-1024.webp +0 -0
  107. package/dist/textures/uranus-512.webp +0 -0
  108. package/dist/textures/venus-1024.webp +0 -0
  109. package/dist/textures/venus-512.webp +0 -0
  110. package/dist/vec.d.ts +19 -0
  111. package/dist/vec.js +32 -0
  112. package/dist/version.d.ts +1 -0
  113. package/dist/version.js +1 -0
  114. package/package.json +236 -4
@@ -0,0 +1,35 @@
1
+ import { looks, waiting } from "./store.js";
2
+ /**
3
+ * Teach the package how to draw a body in three.js. Each body's entry point (`@johnmorrisdotca/wakusei-planets/jupiter`) calls this once as it loads; call it yourself to add or replace a look.
4
+ *
5
+ * @param look - The body's name and its shader code, atmosphere and map.
6
+ * @returns The look, as it was given.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { registerLook, lookOf } from "@johnmorrisdotca/wakusei-planets/look";
11
+ *
12
+ * registerLook({ id: "moon", surface: "vec4 surface(vec3 p) { return vec4(vec3(0.6), 0.0); }" });
13
+ * console.log(lookOf("moon")?.id);
14
+ * ```
15
+ */
16
+ export function registerLook(look) {
17
+ looks.set(look.id, look);
18
+ for (const wake of waiting)
19
+ wake();
20
+ return look;
21
+ }
22
+ /**
23
+ * The look registered for a body, or `undefined` when its entry point has not been imported.
24
+ *
25
+ * @param id - The body's name.
26
+ * @returns The registered look, or `undefined` for a body with none.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { lookOf } from "@johnmorrisdotca/wakusei-planets/look";
31
+ *
32
+ * console.log(lookOf("jupiter") === undefined);
33
+ * ```
34
+ */
35
+ export const lookOf = (id) => looks.get(id);
@@ -0,0 +1,49 @@
1
+ /** A `#rrggbb` colour as GLSL. */
2
+ export declare const vec: (hex: string) => string;
3
+ /** A latitude-to-colour function from stops (latitude in degrees, south to north). `plateau` (0 to 0.5) keeps each band flat and its edge sharp. */
4
+ export declare function bandFn(stops: readonly (readonly [number, string])[], plateau?: number): string;
5
+ export interface GasOptions {
6
+ stops: readonly (readonly [number, string])[];
7
+ /** How much the bands wobble (0 to 0.1). */
8
+ turbulence?: number;
9
+ /** How strong the streaks along the bands are (0 to 0.5). */
10
+ streaks?: number;
11
+ plateau?: number;
12
+ /** GLSL that may change `col` and read `p` and `la`. */
13
+ extra?: string;
14
+ }
15
+ /** A banded atmosphere: colour by latitude, wobbled by noise, streaked along the bands. */
16
+ export declare function gas(options: GasOptions): string;
17
+ export interface RockOptions {
18
+ /** The darker and the lighter ground colour. */
19
+ dark: string;
20
+ light: string;
21
+ /** How many craters (1 is a lot). */
22
+ craters?: number;
23
+ /** How dark the bowls are (0 to 2). */
24
+ contrast?: number;
25
+ seed?: number;
26
+ /** How large the patches of dark and light ground are (the noise's frequency). */
27
+ patches?: number;
28
+ /** GLSL that may change `col` and read `p`, `h` (the crater height) and `tone`. */
29
+ extra?: string;
30
+ }
31
+ /** A cratered world: two ground colours in patches, with craters that are dark in the bowl and bright on the rim. */
32
+ export declare function rock(options: RockOptions): string;
33
+ export interface CloudOptions {
34
+ light: string;
35
+ dark: string;
36
+ /** How much swirl (0 to 1). */
37
+ swirl?: number;
38
+ extra?: string;
39
+ }
40
+ /** A world wrapped in cloud: pale and smooth, streaked and swirled. */
41
+ export declare function cloud(options: CloudOptions): string;
42
+ /** An icy world: bright ground with a few dark stains and faint cracks. */
43
+ export declare function ice(options: {
44
+ base: string;
45
+ stain: string;
46
+ cracks?: number;
47
+ craters?: number;
48
+ extra?: string;
49
+ }): string;
@@ -0,0 +1,76 @@
1
+ // Builders for the surface code of a body: a gas giant's bands, a cratered rock, a cloud-wrapped world. Each returns the GLSL text of `vec4 surface(vec3 p)`; a body's own entry
2
+ // point adds what is only its own (a spot, a cap, a heart) as a few lines of `extra`.
3
+ /** A `#rrggbb` colour as GLSL. */
4
+ export const vec = (hex) => {
5
+ const c = [1, 3, 5].map((i) => (parseInt(hex.slice(i, i + 2), 16) / 255).toFixed(3));
6
+ return `vec3(${c.join(", ")})`;
7
+ };
8
+ /** A latitude-to-colour function from stops (latitude in degrees, south to north). `plateau` (0 to 0.5) keeps each band flat and its edge sharp. */
9
+ export function bandFn(stops, plateau = 0.3) {
10
+ let code = `vec3 bandCol(float la) { float d = degrees(la); vec3 c = ${vec(stops[0][1])};\n`;
11
+ for (let i = 1; i < stops.length; i += 1) {
12
+ const a = stops[i - 1][0];
13
+ const b = stops[i][0];
14
+ const w = (b - a) * plateau;
15
+ code += ` c = mix(c, ${vec(stops[i][1])}, smoothstep(${(a + w).toFixed(2)}, ${(b - w).toFixed(2)}, d));\n`;
16
+ }
17
+ return `${code} return c; }\n`;
18
+ }
19
+ /** A banded atmosphere: colour by latitude, wobbled by noise, streaked along the bands. */
20
+ export function gas(options) {
21
+ const t = options.turbulence ?? 0.04;
22
+ const k = options.streaks ?? 0.2;
23
+ return `${bandFn(options.stops, options.plateau)}
24
+ vec4 surface(vec3 p) {
25
+ float la = lat(p);
26
+ float warp = (fbm(vec3(p.xy * 4.5, p.z * 9.0)) - 0.5) * ${t.toFixed(3)} + (fbm(vec3(p.xy * 11.0, p.z * 22.0) + 5.0) - 0.5) * ${(t * 0.45).toFixed(3)};
27
+ vec3 col = bandCol(la + warp);
28
+ float streak = fbm(vec3(p.xy * 3.0, p.z * 36.0) + 2.0);
29
+ col *= 1.0 + ${k.toFixed(3)} * (streak - 0.5) * 2.0;
30
+ ${options.extra ?? ""}
31
+ return vec4(col, 0.0);
32
+ }`;
33
+ }
34
+ /** A cratered world: two ground colours in patches, with craters that are dark in the bowl and bright on the rim. */
35
+ export function rock(options) {
36
+ const c = options.craters ?? 1;
37
+ const seed = (options.seed ?? 0).toFixed(1);
38
+ return `
39
+ vec4 surface(vec3 p) {
40
+ float h = craters(p, 3.0, ${(c * 1.0).toFixed(2)}, ${seed}) + craters(p, 8.0, ${(c * 0.7).toFixed(2)}, ${seed} + 7.0) * 0.6 + craters(p, 20.0, ${(c * 0.6).toFixed(2)}, ${seed} + 3.0) * 0.3;
41
+ float tone = fbm(p * ${(options.patches ?? 2.3).toFixed(2)} + ${seed});
42
+ vec3 col = mix(${vec(options.dark)}, ${vec(options.light)}, smoothstep(0.25, 0.75, tone));
43
+ col *= 1.0 + ${(options.contrast ?? 1).toFixed(2)} * 1.15 * h;
44
+ col *= 0.94 + 0.12 * fbm(p * 60.0);
45
+ ${options.extra ?? ""}
46
+ return vec4(col, h);
47
+ }`;
48
+ }
49
+ /** A world wrapped in cloud: pale and smooth, streaked and swirled. */
50
+ export function cloud(options) {
51
+ return `
52
+ vec4 surface(vec3 p) {
53
+ vec3 q = p * 2.2;
54
+ float w = fbm(q * 1.4 + 3.0);
55
+ float n = fbm(q + vec3(w * 2.6, w * 1.4, -w * 2.0));
56
+ float streak = fbm(vec3(p.xy * 2.2, p.z * 7.0) + w);
57
+ float m = clamp(mix(n, streak, 0.4) * 1.3 - 0.12, 0.0, 1.0);
58
+ vec3 col = mix(${vec(options.dark)}, ${vec(options.light)}, mix(0.55, m, ${(options.swirl ?? 0.8).toFixed(2)}));
59
+ ${options.extra ?? ""}
60
+ return vec4(col, 0.0);
61
+ }`;
62
+ }
63
+ /** An icy world: bright ground with a few dark stains and faint cracks. */
64
+ export function ice(options) {
65
+ return `
66
+ vec4 surface(vec3 p) {
67
+ float h = craters(p, 4.0, ${(options.craters ?? 0.5).toFixed(2)}, 11.0) + craters(p, 12.0, ${((options.craters ?? 0.5) * 0.8).toFixed(2)}, 5.0) * 0.6;
68
+ float tone = fbm(p * 3.0 + 9.0);
69
+ vec3 col = mix(${vec(options.base)}, ${vec(options.stain)}, smoothstep(0.45, 0.8, tone) * 0.7);
70
+ float ridge = 1.0 - smoothstep(0.0, 0.02, abs(fbm(p * 7.0 + 4.0) - 0.5));
71
+ col = mix(col, ${vec(options.stain)}, ridge * ${(options.cracks ?? 0.0).toFixed(2)});
72
+ col *= 1.0 + 0.7 * h;
73
+ ${options.extra ?? ""}
74
+ return vec4(col, h);
75
+ }`;
76
+ }
@@ -0,0 +1,34 @@
1
+ import type { BodyFacts } from "../facts.ts";
2
+ /**
3
+ * The strip of a body's rings: RGBA bytes `width` pixels across, and the inner and outer radii in body radii.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { BODIES, ringStrip, type RingStrip } from "@johnmorrisdotca/wakusei-planets";
8
+ *
9
+ * const strip: RingStrip | null = ringStrip(BODIES.saturn, 64);
10
+ * console.log(strip?.data.length);
11
+ * ```
12
+ */
13
+ export interface RingStrip {
14
+ data: Uint8Array;
15
+ width: number;
16
+ inner: number;
17
+ outer: number;
18
+ }
19
+ /**
20
+ * The strip of a body's rings, made from its ring bands: where each band is it stops light and has its tone, with fine ringlets in it and soft edges.
21
+ *
22
+ * @param facts - A body that has rings.
23
+ * @param width - The strip's width in pixels. Default 1024.
24
+ * @returns The RGBA bytes with the inner and outer radii in body radii, or `null` for a body with no rings.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { BODIES, ringStrip } from "@johnmorrisdotca/wakusei-planets";
29
+ *
30
+ * const strip = ringStrip(BODIES.saturn, 256);
31
+ * console.log(strip?.width, strip?.inner.toFixed(2), strip?.outer.toFixed(2));
32
+ * ```
33
+ */
34
+ export declare function ringStrip(facts: BodyFacts, width?: number): RingStrip | null;
@@ -0,0 +1,64 @@
1
+ const PALETTE = {
2
+ saturn: [[104, 86, 66], [243, 230, 200]],
3
+ uranus: [[58, 62, 66], [150, 158, 162]],
4
+ };
5
+ const hash = (n) => {
6
+ const s = Math.sin(n * 127.1 + 311.7) * 43758.5453;
7
+ return s - Math.floor(s);
8
+ };
9
+ const smooth = (a, b, x) => {
10
+ const t = Math.min(1, Math.max(0, (x - a) / (b - a)));
11
+ return t * t * (3 - 2 * t);
12
+ };
13
+ const ripple = (x) => {
14
+ const i = Math.floor(x);
15
+ const f = x - i;
16
+ return hash(i) * (1 - f) + hash(i + 1) * f;
17
+ };
18
+ /**
19
+ * The strip of a body's rings, made from its ring bands: where each band is it stops light and has its tone, with fine ringlets in it and soft edges.
20
+ *
21
+ * @param facts - A body that has rings.
22
+ * @param width - The strip's width in pixels. Default 1024.
23
+ * @returns The RGBA bytes with the inner and outer radii in body radii, or `null` for a body with no rings.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { BODIES, ringStrip } from "@johnmorrisdotca/wakusei-planets";
28
+ *
29
+ * const strip = ringStrip(BODIES.saturn, 256);
30
+ * console.log(strip?.width, strip?.inner.toFixed(2), strip?.outer.toFixed(2));
31
+ * ```
32
+ */
33
+ export function ringStrip(facts, width = 1024) {
34
+ const bands = facts.rings;
35
+ if (bands === undefined || bands.length === 0)
36
+ return null;
37
+ const from = Math.min(...bands.map((band) => band.from)) - 300;
38
+ const to = Math.max(...bands.map((band) => band.to)) + 300;
39
+ const [dark, light] = PALETTE[facts.id] ?? [[90, 90, 90], [170, 170, 170]];
40
+ const data = new Uint8Array(width * 4);
41
+ const km = (to - from) / width;
42
+ for (let x = 0; x < width; x += 1) {
43
+ const r = from + (x + 0.5) * km;
44
+ let alpha = 0;
45
+ let tone = 0;
46
+ let weight = 0;
47
+ for (const band of bands) {
48
+ const soft = Math.max(km * 1.2, 120);
49
+ const inside = smooth(band.from - soft, band.from + soft, r) * (1 - smooth(band.to - soft, band.to + soft, r));
50
+ if (inside <= 0)
51
+ continue;
52
+ const fine = 0.78 + 0.22 * ripple(r / 380) + 0.12 * (ripple(r / 90) - 0.5);
53
+ alpha += inside * band.alpha * Math.min(1.15, fine);
54
+ tone += inside * band.tone * (0.9 + 0.2 * ripple(r / 650 + 7));
55
+ weight += inside;
56
+ }
57
+ const t = weight > 0 ? Math.min(1, tone / weight) : 0;
58
+ data[x * 4] = Math.round(dark[0] + (light[0] - dark[0]) * t);
59
+ data[x * 4 + 1] = Math.round(dark[1] + (light[1] - dark[1]) * t);
60
+ data[x * 4 + 2] = Math.round(dark[2] + (light[2] - dark[2]) * t);
61
+ data[x * 4 + 3] = Math.round(Math.min(1, alpha) * 255);
62
+ }
63
+ return { data, width, inner: from / facts.radiusKm, outer: to / facts.radiusKm };
64
+ }
@@ -0,0 +1,24 @@
1
+ import { type SharedRenderer } from "@johnmorrisdotca/kazoku-ui/renderer";
2
+ import type { ThreeParts } from "./body.ts";
3
+ export type ContextState = "lost" | "restored" | "unavailable";
4
+ /**
5
+ * Where three.js comes from, if a page wants to hand it over itself (before the first element connects). A page that hands over a module that re-exports only the parts the bodies use
6
+ * ships a few hundred kilobytes of three.js instead of all of it, and a page with no bundler gives the address of a module (an import map is the other way).
7
+ *
8
+ * @param load - Returns the three.js module, as a promise.
9
+ *
10
+ * @example
11
+ * ```ts no-run
12
+ * import { setThreeLoader } from "@johnmorrisdotca/wakusei-planets/element";
13
+ *
14
+ * setThreeLoader(() => import("three"));
15
+ * ```
16
+ */
17
+ export declare function setThreeLoader(load: () => Promise<ThreeParts>): void;
18
+ /** The page's renderer, made on first use. Call `release()` (from the result) when the element leaves. */
19
+ export declare function takeRenderer(onContext: (state: ContextState) => void): {
20
+ renderer: SharedRenderer<ThreeParts>;
21
+ release(): void;
22
+ };
23
+ /** The renderer now, if one is made: for tests and for a page that wants its numbers. */
24
+ export declare const currentRenderer: () => SharedRenderer<ThreeParts> | undefined;
@@ -0,0 +1,55 @@
1
+ // One shared renderer for the page, whichever element draws with it: every `<wakusei-body>` takes it from here, so the page keeps one WebGL context. The last element to
2
+ // leave lets it go (after a second, in case it is only moving).
3
+ import { createSharedRenderer } from "@johnmorrisdotca/kazoku-ui/renderer";
4
+ let shared;
5
+ let loader = () => import("three");
6
+ const listeners = new Set();
7
+ let elements = 0;
8
+ let releaseTimer;
9
+ /**
10
+ * Where three.js comes from, if a page wants to hand it over itself (before the first element connects). A page that hands over a module that re-exports only the parts the bodies use
11
+ * ships a few hundred kilobytes of three.js instead of all of it, and a page with no bundler gives the address of a module (an import map is the other way).
12
+ *
13
+ * @param load - Returns the three.js module, as a promise.
14
+ *
15
+ * @example
16
+ * ```ts no-run
17
+ * import { setThreeLoader } from "@johnmorrisdotca/wakusei-planets/element";
18
+ *
19
+ * setThreeLoader(() => import("three"));
20
+ * ```
21
+ */
22
+ export function setThreeLoader(load) {
23
+ loader = load;
24
+ }
25
+ /** The page's renderer, made on first use. Call `release()` (from the result) when the element leaves. */
26
+ export function takeRenderer(onContext) {
27
+ clearTimeout(releaseTimer);
28
+ elements += 1;
29
+ listeners.add(onContext);
30
+ shared ??= createSharedRenderer({
31
+ load: () => loader(),
32
+ stillSize: 480,
33
+ onContext: (state) => {
34
+ for (const listener of listeners)
35
+ listener(state);
36
+ },
37
+ });
38
+ return {
39
+ renderer: shared,
40
+ release() {
41
+ listeners.delete(onContext);
42
+ elements -= 1;
43
+ if (elements <= 0) {
44
+ releaseTimer = setTimeout(() => {
45
+ if (elements <= 0) {
46
+ shared?.dispose();
47
+ shared = undefined;
48
+ }
49
+ }, 1000);
50
+ }
51
+ },
52
+ };
53
+ }
54
+ /** The renderer now, if one is made: for tests and for a page that wants its numbers. */
55
+ export const currentRenderer = () => shared;
@@ -0,0 +1,19 @@
1
+ import type { BodyId } from "../facts.ts";
2
+ import type { BodyLook } from "./look.ts";
3
+ /** A body drawn by another package's element (the Earth, by chikyu-earth) instead of by this one: the element asks it for what to show. */
4
+ export interface Delegate {
5
+ /** The line look: an element for the body, centred on a longitude and latitude (`"lon,lat"`). */
6
+ line(options: {
7
+ center: string;
8
+ size: number;
9
+ }): Promise<HTMLElement>;
10
+ /** The shaded and painted looks, drawn live: an element lit by the Sun at `time` (`"now"` or an ISO date). */
11
+ live(options: {
12
+ center: string;
13
+ time: string;
14
+ textureBase: string | null;
15
+ }): Promise<HTMLElement>;
16
+ }
17
+ export declare const looks: Map<BodyId, BodyLook>, waiting: Set<() => void>, delegates: Map<BodyId, Delegate>;
18
+ /** Call `wake` whenever a look is registered; returns a function that stops it. */
19
+ export declare function onRegister(wake: () => void): () => void;
@@ -0,0 +1,8 @@
1
+ const KEY = Symbol.for("johnmorrisdotca.wakusei-planets.looks");
2
+ const store = (globalThis[KEY] ??= { looks: new Map(), waiting: new Set(), delegates: new Map() });
3
+ export const { looks, waiting, delegates } = store;
4
+ /** Call `wake` whenever a look is registered; returns a function that stops it. */
5
+ export function onRegister(wake) {
6
+ waiting.add(wake);
7
+ return () => waiting.delete(wake);
8
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/vec.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ export type Vec3 = readonly [number, number, number];
2
+ export declare const RAD: number;
3
+ export declare const DEG: number;
4
+ export declare const add: (a: Vec3, b: Vec3) => Vec3;
5
+ export declare const sub: (a: Vec3, b: Vec3) => Vec3;
6
+ export declare const scale: (a: Vec3, k: number) => Vec3;
7
+ export declare const dot: (a: Vec3, b: Vec3) => number;
8
+ export declare const cross: (a: Vec3, b: Vec3) => Vec3;
9
+ export declare const length: (a: Vec3) => number;
10
+ export declare const unit: (a: Vec3) => Vec3;
11
+ /** A direction from right ascension and declination in degrees. */
12
+ export declare const fromRaDec: (ra: number, dec: number) => Vec3;
13
+ /** The mean obliquity of the ecliptic at J2000, in degrees. */
14
+ export declare const OBLIQUITY = 23.4392911;
15
+ /** An equatorial vector (J2000) in ecliptic coordinates. */
16
+ export declare const toEcliptic: (v: Vec3) => Vec3;
17
+ /** Turn `v` about the unit axis `k` by `angle` degrees (Rodrigues). */
18
+ export declare const turn: (v: Vec3, k: Vec3, angle: number) => Vec3;
19
+ export declare const wrap360: (a: number) => number;
package/dist/vec.js ADDED
@@ -0,0 +1,32 @@
1
+ // Three-number vectors and the few turns the drawings need. Pure, no allocation beyond the arrays themselves.
2
+ export const RAD = Math.PI / 180;
3
+ export const DEG = 180 / Math.PI;
4
+ export const add = (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
5
+ export const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
6
+ export const scale = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
7
+ export const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
8
+ export const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
9
+ export const length = (a) => Math.hypot(a[0], a[1], a[2]);
10
+ export const unit = (a) => {
11
+ const n = length(a);
12
+ return n === 0 ? [0, 0, 0] : [a[0] / n, a[1] / n, a[2] / n];
13
+ };
14
+ /** A direction from right ascension and declination in degrees. */
15
+ export const fromRaDec = (ra, dec) => [Math.cos(dec * RAD) * Math.cos(ra * RAD), Math.cos(dec * RAD) * Math.sin(ra * RAD), Math.sin(dec * RAD)];
16
+ /** The mean obliquity of the ecliptic at J2000, in degrees. */
17
+ export const OBLIQUITY = 23.4392911;
18
+ /** An equatorial vector (J2000) in ecliptic coordinates. */
19
+ export const toEcliptic = (v) => {
20
+ const c = Math.cos(OBLIQUITY * RAD);
21
+ const s = Math.sin(OBLIQUITY * RAD);
22
+ return [v[0], v[1] * c + v[2] * s, -v[1] * s + v[2] * c];
23
+ };
24
+ /** Turn `v` about the unit axis `k` by `angle` degrees (Rodrigues). */
25
+ export const turn = (v, k, angle) => {
26
+ const c = Math.cos(angle * RAD);
27
+ const s = Math.sin(angle * RAD);
28
+ const kv = cross(k, v);
29
+ const d = dot(k, v) * (1 - c);
30
+ return [v[0] * c + kv[0] * s + k[0] * d, v[1] * c + kv[1] * s + k[1] * d, v[2] * c + kv[2] * s + k[2] * d];
31
+ };
32
+ export const wrap360 = (a) => ((a % 360) + 360) % 360;
@@ -0,0 +1 @@
1
+ export declare const VERSION = "0.3.0";
@@ -0,0 +1 @@
1
+ export const VERSION = "0.3.0";