@johnmorrisdotca/hoshi-stars 0.0.0-stage → 0.2.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 (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +146 -0
  4. package/README.md +337 -2
  5. package/dist/boundaries.d.ts +95 -0
  6. package/dist/boundaries.js +195 -0
  7. package/dist/constellations.d.ts +72 -0
  8. package/dist/constellations.js +105 -0
  9. package/dist/data/boundaries.d.ts +4 -0
  10. package/dist/data/boundaries.js +449 -0
  11. package/dist/data/constellations.d.ts +1 -0
  12. package/dist/data/constellations.js +91 -0
  13. package/dist/data/lines.d.ts +2 -0
  14. package/dist/data/lines.js +92 -0
  15. package/dist/data/messier.d.ts +2 -0
  16. package/dist/data/messier.js +116 -0
  17. package/dist/data/milky-way.d.ts +5 -0
  18. package/dist/data/milky-way.js +7 -0
  19. package/dist/data/ngc-ic.d.ts +2 -0
  20. package/dist/data/ngc-ic.js +11794 -0
  21. package/dist/data/star-names-ja.d.ts +2 -0
  22. package/dist/data/star-names-ja.js +4 -0
  23. package/dist/data/stars.d.ts +4 -0
  24. package/dist/data/stars.js +5088 -0
  25. package/dist/deep-sky-core.d.ts +123 -0
  26. package/dist/deep-sky-core.js +139 -0
  27. package/dist/deep-sky-ngc-ic.d.ts +34 -0
  28. package/dist/deep-sky-ngc-ic.js +43 -0
  29. package/dist/deep-sky.d.ts +49 -0
  30. package/dist/deep-sky.js +59 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +2 -0
  33. package/dist/lines.d.ts +49 -0
  34. package/dist/lines.js +64 -0
  35. package/dist/milky-way.d.ts +144 -0
  36. package/dist/milky-way.js +242 -0
  37. package/dist/projection.d.ts +127 -0
  38. package/dist/projection.js +284 -0
  39. package/dist/sky.d.ts +202 -0
  40. package/dist/sky.js +283 -0
  41. package/dist/stars.d.ts +162 -0
  42. package/dist/stars.js +195 -0
  43. package/dist/time-scale.d.ts +14 -0
  44. package/dist/time-scale.js +71 -0
  45. package/dist/when.d.ts +16 -0
  46. package/dist/when.js +18 -0
  47. package/package.json +184 -4
@@ -0,0 +1,144 @@
1
+ import type { SkyPoint } from "./projection.ts";
2
+ export type { SkyPoint };
3
+ /**
4
+ * A point in galactic coordinates, in degrees.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import type { GalacticPoint } from "@johnmorrisdotca/hoshi-stars/milky-way";
9
+ *
10
+ * const centre: GalacticPoint = { longitude: 0, latitude: 0 };
11
+ * console.log(centre.longitude);
12
+ * ```
13
+ */
14
+ export interface GalacticPoint {
15
+ /** Galactic longitude, 0 (toward the Galactic centre in Sagittarius) up to but not including 360. */
16
+ longitude: number;
17
+ /** Galactic latitude, -90 to 90: 0 is the plane of the Milky Way. */
18
+ latitude: number;
19
+ }
20
+ /**
21
+ * A ribbon of the Milky Way at one brightness level: two chains of sky points of the same length, the upper edge and the lower, each pair at the same galactic longitude. Draw the ribbon as
22
+ * the polygon `upper` forward then `lower` backward. A strip stops where the band splits or ends (a dust lane, the end of a bright patch), so one level is several strips.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { milkyWayBands, type MilkyWayStrip } from "@johnmorrisdotca/hoshi-stars/milky-way";
27
+ *
28
+ * const strip: MilkyWayStrip = milkyWayBands()[0]!.strips[0]!;
29
+ * console.log(strip.upper.length === strip.lower.length);
30
+ * ```
31
+ */
32
+ export interface MilkyWayStrip {
33
+ /** The edge toward the galactic north, from low longitude to high. */
34
+ upper: SkyPoint[];
35
+ /** The edge toward the galactic south, at the same longitudes as `upper`. */
36
+ lower: SkyPoint[];
37
+ }
38
+ /**
39
+ * Everywhere the Milky Way is brighter than one level.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { milkyWayBands, type MilkyWayBand } from "@johnmorrisdotca/hoshi-stars/milky-way";
44
+ *
45
+ * const band: MilkyWayBand = milkyWayBands([0.5])[0]!;
46
+ * console.log(band.level, band.strips.length);
47
+ * ```
48
+ */
49
+ export interface MilkyWayBand {
50
+ /** The level, 0 (dark sky) to 1 (the brightest part of the core), as `milkyWayBrightness` gives it. */
51
+ level: number;
52
+ /** The ribbons where the brightness is above the level. */
53
+ strips: MilkyWayStrip[];
54
+ }
55
+ /**
56
+ * Where to cut the bands, and how finely.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import { milkyWayBands, type MilkyWayBandsOptions } from "@johnmorrisdotca/hoshi-stars/milky-way";
61
+ *
62
+ * const options: MilkyWayBandsOptions = { step: 2 };
63
+ * console.log(milkyWayBands([0.3, 0.6], options).length);
64
+ * ```
65
+ */
66
+ export interface MilkyWayBandsOptions {
67
+ /** Degrees of galactic longitude between points along a strip; a multiple of the grid's 0.5, default 1. */
68
+ step?: number;
69
+ }
70
+ /**
71
+ * The galactic longitude and latitude of a point of the sky (J2000, the IAU's galactic frame).
72
+ *
73
+ * @param point - A right ascension and declination in degrees.
74
+ * @returns Longitude 0 up to but not including 360, latitude -90 to 90.
75
+ * @throws TypeError if the right ascension or declination is not a finite number.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { toGalactic } from "@johnmorrisdotca/hoshi-stars/milky-way";
80
+ *
81
+ * // Sagittarius A*, the centre of the Galaxy, is at the origin
82
+ * console.log(toGalactic({ rightAscension: 266.4168, declination: -29.0078 })); // about { longitude: 0, latitude: 0 }
83
+ * ```
84
+ */
85
+ export declare function toGalactic(point: SkyPoint): GalacticPoint;
86
+ /**
87
+ * The right ascension and declination (J2000) of a point given in galactic coordinates.
88
+ *
89
+ * @param point - A galactic longitude and latitude in degrees.
90
+ * @returns Right ascension 0 up to but not including 360, declination -90 to 90.
91
+ * @throws TypeError if the longitude or latitude is not a finite number.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { fromGalactic } from "@johnmorrisdotca/hoshi-stars/milky-way";
96
+ *
97
+ * console.log(fromGalactic({ longitude: 0, latitude: 0 })); // about { rightAscension: 266.4, declination: -28.9 }
98
+ * ```
99
+ */
100
+ export declare function fromGalactic(point: GalacticPoint): SkyPoint;
101
+ /**
102
+ * How bright the Milky Way is toward a point, as a display value from 0 (dark sky) to 1 (the brightest part of the core, in Sagittarius). It follows the band along the galactic equator, is brightest
103
+ * toward the Galactic centre, and is dimmer where the dust lanes cross it (the Great Rift, the Coalsack, the Cygnus Rift). The grid is 0.5 degrees on a side near the plane and holds thirty-two levels, so
104
+ * it is a smooth map for shading a chart or a texture, not a measurement; beyond 30 degrees of the plane it is 0. The values are already shaped for the eye (a gamma of about 0.45), so use them as a pixel's
105
+ * brightness directly.
106
+ *
107
+ * @param point - A right ascension and declination in degrees (J2000).
108
+ * @returns A number from 0 to 1.
109
+ * @throws TypeError if the right ascension or declination is not a finite number.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { milkyWayBrightness } from "@johnmorrisdotca/hoshi-stars/milky-way";
114
+ *
115
+ * const sagittarius = milkyWayBrightness({ rightAscension: 266.4, declination: -28.9 });
116
+ * const pole = milkyWayBrightness({ rightAscension: 192.9, declination: 27.1 });
117
+ * console.log(sagittarius > pole); // true
118
+ * ```
119
+ */
120
+ export declare function milkyWayBrightness(point: SkyPoint): number;
121
+ /**
122
+ * The Milky Way cut at some brightness levels, as ribbons of sky points for drawing soft bands (a polygon per strip, filled with a low opacity so the levels build up). Each strip runs along
123
+ * the galactic equator; a dust lane that splits the band ends the strips on either side of it, so the lane shows as a gap. The points are in right ascension and declination, ready for
124
+ * `project` from `@johnmorrisdotca/hoshi-stars/projection`; a strip crossing a map's seam or the far side is the caller's to cut (compare the longitudes of neighbouring points).
125
+ *
126
+ * @param levels - Levels from 0 to 1, as `milkyWayBrightness` measures them. Default `[0.2, 0.42, 0.68]`: the glow, the band and the core. Each must be a number above 0 and below 1.
127
+ * @param options - `step`: degrees of longitude between points along a strip (a multiple of 0.5, default 1).
128
+ * @returns One band per level, in the order given.
129
+ * @throws RangeError if a level is not between 0 and 1 (exclusive) or `step` is not a positive multiple of 0.5. TypeError if a level is not a number.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * import { milkyWayBands } from "@johnmorrisdotca/hoshi-stars/milky-way";
134
+ * import { project } from "@johnmorrisdotca/hoshi-stars/projection";
135
+ *
136
+ * for (const band of milkyWayBands()) {
137
+ * for (const strip of band.strips) {
138
+ * const polygon = [...strip.upper, ...strip.lower.slice().reverse()].map((p) => project("hammer", p, { centre: { rightAscension: 0, declination: 0 } }));
139
+ * console.log(band.level, polygon.length);
140
+ * }
141
+ * }
142
+ * ```
143
+ */
144
+ export declare function milkyWayBands(levels?: readonly number[], options?: MilkyWayBandsOptions): MilkyWayBand[];
@@ -0,0 +1,242 @@
1
+ // The Milky Way as a small brightness grid in galactic coordinates (the milky-way tier, `@johnmorrisdotca/hoshi-stars/milky-way`): about 5 kB of data, made by
2
+ // scripts/make-milky-way-data.py from SAO star counts, the IRAS 100 micron dust map and a bulge of our own (see NOTICE.md). Pure: no DOM, no clock, no state beyond the unpacked grid.
3
+ import { MILKY_WAY_COLUMNS, MILKY_WAY_LATITUDE, MILKY_WAY_LEVELS, MILKY_WAY_PACKED, MILKY_WAY_ROWS } from "./data/milky-way.js";
4
+ const RAD = Math.PI / 180;
5
+ const DEG = 180 / Math.PI;
6
+ // The J2000 galactic frame: the north galactic pole's right ascension and declination, and the galactic longitude of the north celestial pole (IAU 1958 as set in ICRS by the Hipparcos catalogue's documentation, 192.85948, 27.12825 and 122.93192 degrees).
7
+ const POLE_RA = 192.85948 * RAD;
8
+ const POLE_DEC = 27.12825 * RAD;
9
+ const POLE_L = 122.93192 * RAD;
10
+ const COLUMNS = MILKY_WAY_COLUMNS;
11
+ const ROWS = MILKY_WAY_ROWS;
12
+ const STEP_L = 360 / COLUMNS;
13
+ const STEP_B = (2 * MILKY_WAY_LATITUDE) / ROWS;
14
+ let grid;
15
+ function unpack() {
16
+ if (grid === undefined) {
17
+ const text = atob(MILKY_WAY_PACKED);
18
+ const out = new Float32Array(COLUMNS * ROWS);
19
+ let at = 0;
20
+ for (let i = 0; i < text.length; i++) {
21
+ const byte = text.charCodeAt(i);
22
+ const value = (byte >> 3) / MILKY_WAY_LEVELS;
23
+ for (let n = (byte & 7) + 1; n > 0; n--)
24
+ out[at++] = value;
25
+ }
26
+ grid = out;
27
+ }
28
+ return grid;
29
+ }
30
+ /**
31
+ * The galactic longitude and latitude of a point of the sky (J2000, the IAU's galactic frame).
32
+ *
33
+ * @param point - A right ascension and declination in degrees.
34
+ * @returns Longitude 0 up to but not including 360, latitude -90 to 90.
35
+ * @throws TypeError if the right ascension or declination is not a finite number.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { toGalactic } from "@johnmorrisdotca/hoshi-stars/milky-way";
40
+ *
41
+ * // Sagittarius A*, the centre of the Galaxy, is at the origin
42
+ * console.log(toGalactic({ rightAscension: 266.4168, declination: -29.0078 })); // about { longitude: 0, latitude: 0 }
43
+ * ```
44
+ */
45
+ export function toGalactic(point) {
46
+ finite("rightAscension", point?.rightAscension);
47
+ finite("declination", point?.declination);
48
+ const ra = point.rightAscension * RAD;
49
+ const dec = point.declination * RAD;
50
+ const sinB = Math.sin(dec) * Math.sin(POLE_DEC) + Math.cos(dec) * Math.cos(POLE_DEC) * Math.cos(ra - POLE_RA);
51
+ const y = Math.cos(dec) * Math.sin(ra - POLE_RA);
52
+ const x = Math.sin(dec) * Math.cos(POLE_DEC) - Math.cos(dec) * Math.sin(POLE_DEC) * Math.cos(ra - POLE_RA);
53
+ const longitude = (((POLE_L - Math.atan2(y, x)) * DEG) % 360 + 360) % 360;
54
+ return { longitude: longitude >= 360 ? 0 : longitude, latitude: Math.asin(Math.max(-1, Math.min(1, sinB))) * DEG };
55
+ }
56
+ /**
57
+ * The right ascension and declination (J2000) of a point given in galactic coordinates.
58
+ *
59
+ * @param point - A galactic longitude and latitude in degrees.
60
+ * @returns Right ascension 0 up to but not including 360, declination -90 to 90.
61
+ * @throws TypeError if the longitude or latitude is not a finite number.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { fromGalactic } from "@johnmorrisdotca/hoshi-stars/milky-way";
66
+ *
67
+ * console.log(fromGalactic({ longitude: 0, latitude: 0 })); // about { rightAscension: 266.4, declination: -28.9 }
68
+ * ```
69
+ */
70
+ export function fromGalactic(point) {
71
+ finite("longitude", point?.longitude);
72
+ finite("latitude", point?.latitude);
73
+ const b = point.latitude * RAD;
74
+ const dl = POLE_L - point.longitude * RAD;
75
+ const sinDec = Math.sin(b) * Math.sin(POLE_DEC) + Math.cos(b) * Math.cos(POLE_DEC) * Math.cos(dl);
76
+ const y = Math.cos(b) * Math.sin(dl);
77
+ const x = Math.sin(b) * Math.cos(POLE_DEC) - Math.cos(b) * Math.sin(POLE_DEC) * Math.cos(dl);
78
+ const rightAscension = ((POLE_RA + Math.atan2(y, x)) * DEG % 360 + 360) % 360;
79
+ return { rightAscension: rightAscension >= 360 ? 0 : rightAscension, declination: Math.asin(Math.max(-1, Math.min(1, sinDec))) * DEG };
80
+ }
81
+ function finite(name, value) {
82
+ if (typeof value !== "number" || !Number.isFinite(value))
83
+ throw new TypeError(`${name} must be a finite number, got ${String(value)}`);
84
+ }
85
+ function cell(column, row) {
86
+ return unpack()[(((column % COLUMNS) + COLUMNS) % COLUMNS) + Math.max(0, Math.min(ROWS - 1, row)) * COLUMNS];
87
+ }
88
+ function brightnessAt(longitude, latitude) {
89
+ if (latitude <= -MILKY_WAY_LATITUDE || latitude >= MILKY_WAY_LATITUDE)
90
+ return 0;
91
+ const x = longitude / STEP_L - 0.5;
92
+ const y = (latitude + MILKY_WAY_LATITUDE) / STEP_B - 0.5;
93
+ const c = Math.floor(x);
94
+ const r = Math.floor(y);
95
+ const fx = x - c;
96
+ const fy = y - r;
97
+ const top = cell(c, r + 1) * (1 - fx) + cell(c + 1, r + 1) * fx;
98
+ const bottom = cell(c, r) * (1 - fx) + cell(c + 1, r) * fx;
99
+ return bottom * (1 - fy) + top * fy;
100
+ }
101
+ /**
102
+ * How bright the Milky Way is toward a point, as a display value from 0 (dark sky) to 1 (the brightest part of the core, in Sagittarius). It follows the band along the galactic equator, is brightest
103
+ * toward the Galactic centre, and is dimmer where the dust lanes cross it (the Great Rift, the Coalsack, the Cygnus Rift). The grid is 0.5 degrees on a side near the plane and holds thirty-two levels, so
104
+ * it is a smooth map for shading a chart or a texture, not a measurement; beyond 30 degrees of the plane it is 0. The values are already shaped for the eye (a gamma of about 0.45), so use them as a pixel's
105
+ * brightness directly.
106
+ *
107
+ * @param point - A right ascension and declination in degrees (J2000).
108
+ * @returns A number from 0 to 1.
109
+ * @throws TypeError if the right ascension or declination is not a finite number.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { milkyWayBrightness } from "@johnmorrisdotca/hoshi-stars/milky-way";
114
+ *
115
+ * const sagittarius = milkyWayBrightness({ rightAscension: 266.4, declination: -28.9 });
116
+ * const pole = milkyWayBrightness({ rightAscension: 192.9, declination: 27.1 });
117
+ * console.log(sagittarius > pole); // true
118
+ * ```
119
+ */
120
+ export function milkyWayBrightness(point) {
121
+ const g = toGalactic(point);
122
+ return brightnessAt(g.longitude, g.latitude);
123
+ }
124
+ /** The latitudes where one column of the grid is above a level, as spans, with the crossing between two rows found by a straight line. */
125
+ function spans(column, level) {
126
+ const out = [];
127
+ let start;
128
+ const latitude = (row) => -MILKY_WAY_LATITUDE + (row + 0.5) * STEP_B;
129
+ for (let row = 0; row < ROWS; row++) {
130
+ const v = cell(column, row);
131
+ const above = v > level;
132
+ if (above && start === undefined) {
133
+ if (row === 0)
134
+ start = -MILKY_WAY_LATITUDE;
135
+ else {
136
+ const u = cell(column, row - 1);
137
+ start = latitude(row - 1) + ((level - u) / (v - u)) * STEP_B;
138
+ }
139
+ }
140
+ else if (!above && start !== undefined) {
141
+ const u = cell(column, row - 1);
142
+ out.push({ low: start, high: latitude(row - 1) + ((u - level) / (u - v)) * STEP_B });
143
+ start = undefined;
144
+ }
145
+ }
146
+ if (start !== undefined)
147
+ out.push({ low: start, high: MILKY_WAY_LATITUDE });
148
+ return out;
149
+ }
150
+ const overlaps = (a, b) => a.low < b.high && b.low < a.high;
151
+ /**
152
+ * The Milky Way cut at some brightness levels, as ribbons of sky points for drawing soft bands (a polygon per strip, filled with a low opacity so the levels build up). Each strip runs along
153
+ * the galactic equator; a dust lane that splits the band ends the strips on either side of it, so the lane shows as a gap. The points are in right ascension and declination, ready for
154
+ * `project` from `@johnmorrisdotca/hoshi-stars/projection`; a strip crossing a map's seam or the far side is the caller's to cut (compare the longitudes of neighbouring points).
155
+ *
156
+ * @param levels - Levels from 0 to 1, as `milkyWayBrightness` measures them. Default `[0.2, 0.42, 0.68]`: the glow, the band and the core. Each must be a number above 0 and below 1.
157
+ * @param options - `step`: degrees of longitude between points along a strip (a multiple of 0.5, default 1).
158
+ * @returns One band per level, in the order given.
159
+ * @throws RangeError if a level is not between 0 and 1 (exclusive) or `step` is not a positive multiple of 0.5. TypeError if a level is not a number.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * import { milkyWayBands } from "@johnmorrisdotca/hoshi-stars/milky-way";
164
+ * import { project } from "@johnmorrisdotca/hoshi-stars/projection";
165
+ *
166
+ * for (const band of milkyWayBands()) {
167
+ * for (const strip of band.strips) {
168
+ * const polygon = [...strip.upper, ...strip.lower.slice().reverse()].map((p) => project("hammer", p, { centre: { rightAscension: 0, declination: 0 } }));
169
+ * console.log(band.level, polygon.length);
170
+ * }
171
+ * }
172
+ * ```
173
+ */
174
+ export function milkyWayBands(levels = [0.2, 0.42, 0.68], options = {}) {
175
+ const step = options.step ?? 1;
176
+ if (typeof step !== "number" || !(step >= STEP_L) || Math.abs(step / STEP_L - Math.round(step / STEP_L)) > 1e-9)
177
+ throw new RangeError(`step must be a positive multiple of ${STEP_L}, got ${String(step)}`);
178
+ const stride = Math.round(step / STEP_L);
179
+ for (const level of levels) {
180
+ finite("level", level);
181
+ if (!(level > 0 && level < 1))
182
+ throw new RangeError(`a level must be above 0 and below 1, got ${level}`);
183
+ }
184
+ return levels.map((level) => ({ level, strips: stripsAt(level, stride) }));
185
+ }
186
+ function stripsAt(level, stride) {
187
+ const count = COLUMNS / stride;
188
+ const longitude = (k) => (k * stride + 0.5) * STEP_L;
189
+ // columns 0 to count: the last is the first again, so that the band is cut once, at longitude 0
190
+ const columns = Array.from({ length: count + 1 }, (_, k) => spans((k % count) * stride, level));
191
+ const forward = columns.map((c, k) => c.map((a) => (k < count ? columns[k + 1].flatMap((b, i) => (overlaps(a, b) ? [i] : [])) : [])));
192
+ const backward = columns.map((c, k) => c.map((b) => (k > 0 ? columns[k - 1].flatMap((a, i) => (overlaps(a, b) ? [i] : [])) : [])));
193
+ const joined = (k, a) => {
194
+ const next = forward[k][a];
195
+ return next.length === 1 && backward[k + 1][next[0]].length === 1 ? next[0] : undefined;
196
+ };
197
+ const strips = [];
198
+ const make = (members, pinchStart, pinchEnd) => {
199
+ const upper = [];
200
+ const lower = [];
201
+ const half = (stride * STEP_L) / 2;
202
+ const first = members[0];
203
+ const last = members[members.length - 1];
204
+ const mid = (s) => (s.low + s.high) / 2;
205
+ if (pinchStart) {
206
+ const p = fromGalactic({ longitude: longitude(first[0]) - half, latitude: mid(first[1]) });
207
+ upper.push(p);
208
+ lower.push(p);
209
+ }
210
+ for (const [k, s] of members) {
211
+ upper.push(fromGalactic({ longitude: longitude(k), latitude: s.high }));
212
+ lower.push(fromGalactic({ longitude: longitude(k), latitude: s.low }));
213
+ }
214
+ if (pinchEnd) {
215
+ const p = fromGalactic({ longitude: longitude(last[0]) + half, latitude: mid(last[1]) });
216
+ upper.push(p);
217
+ lower.push(p);
218
+ }
219
+ strips.push({ upper, lower });
220
+ };
221
+ for (let k = 0; k <= count; k++) {
222
+ for (let a = 0; a < columns[k].length; a++) {
223
+ const before = backward[k][a];
224
+ if (k > 0 && before.length === 1 && joined(k - 1, before[0]) === a)
225
+ continue;
226
+ const members = [[k, columns[k][a]]];
227
+ let ci = k;
228
+ let ai = a;
229
+ for (let next = joined(ci, ai); next !== undefined; next = joined(ci, ai)) {
230
+ ci += 1;
231
+ ai = next;
232
+ members.push([ci, columns[ci][ai]]);
233
+ }
234
+ make(members, k > 0 && before.length === 0, ci < count && forward[ci][ai].length === 0);
235
+ // where the band branches or merges, the pairs that are not joined get a strip of their own
236
+ for (const d of forward[ci][ai])
237
+ if (joined(ci, ai) !== d)
238
+ make([[ci, columns[ci][ai]], [ci + 1, columns[ci + 1][d]]], false, false);
239
+ }
240
+ }
241
+ return strips;
242
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The projections: the five that show a hemisphere or less, then the two that show the whole sky.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { PROJECTIONS } from "@johnmorrisdotca/hoshi-stars/projection";
7
+ *
8
+ * console.log(PROJECTIONS.join(", "));
9
+ * ```
10
+ */
11
+ export declare const PROJECTIONS: readonly ["stereographic", "orthographic", "gnomonic", "equidistant", "equirectangular", "mollweide", "hammer"];
12
+ /**
13
+ * The kinds of projection.
14
+ *
15
+ * - `stereographic`: keeps shapes (conformal); the whole sphere but the antipode, stretching to the edge. Radius 2 at 90 degrees from the centre.
16
+ * - `orthographic`: the sky as a ball seen from far away; the near hemisphere only. Radius 1 at 90 degrees.
17
+ * - `gnomonic`: great circles are straight lines; less than 90 degrees from the centre.
18
+ * - `equidistant`: the azimuthal equidistant; distance from the centre is true, the whole sphere is a disc of radius pi.
19
+ * - `equirectangular`: the plain right ascension and declination plot; the centre's declination moves the chart up or down.
20
+ * - `mollweide`: an equal-area oval of the whole sky, turned so the centre is its middle.
21
+ * - `hammer`: Hammer's equal-area oval of the whole sky (often called Hammer-Aitoff), turned so the centre is its middle.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { PROJECTIONS, type Projection } from "@johnmorrisdotca/hoshi-stars/projection";
26
+ *
27
+ * const kind: Projection = PROJECTIONS[0];
28
+ * console.log(kind, PROJECTIONS.length);
29
+ * ```
30
+ */
31
+ export type Projection = (typeof PROJECTIONS)[number];
32
+ /**
33
+ * A point on the celestial sphere, in degrees. Right ascension may be any number (it wraps at 360); declination is -90 to 90.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import type { SkyPoint } from "@johnmorrisdotca/hoshi-stars/projection";
38
+ *
39
+ * const polaris: SkyPoint = { rightAscension: 37.95, declination: 89.26 };
40
+ * console.log(polaris.declination);
41
+ * ```
42
+ */
43
+ export interface SkyPoint {
44
+ /** Right ascension in degrees. */
45
+ rightAscension: number;
46
+ /** Declination in degrees, -90 to 90. */
47
+ declination: number;
48
+ }
49
+ /**
50
+ * A point on the chart. `x` runs to the right and `y` UP (north above the centre), in the units of `scale`; flip `y` when you draw on a screen.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import type { MapPoint } from "@johnmorrisdotca/hoshi-stars/projection";
55
+ *
56
+ * const middle: MapPoint = { x: 0, y: 0 };
57
+ * console.log(middle.x + middle.y);
58
+ * ```
59
+ */
60
+ export interface MapPoint {
61
+ /** Across the chart, to the right. East is to the left unless `mirror` is set. */
62
+ x: number;
63
+ /** Up the chart. */
64
+ y: number;
65
+ }
66
+ /**
67
+ * How to place and size the chart.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import type { ProjectionOptions } from "@johnmorrisdotca/hoshi-stars/projection";
72
+ *
73
+ * const options: ProjectionOptions = { centre: { rightAscension: 83.8, declination: -5.4 }, scale: 300 };
74
+ * console.log(options.scale);
75
+ * ```
76
+ */
77
+ export interface ProjectionOptions {
78
+ /** The point of the sky that lands at (0, 0). With north up, it is the middle of the chart. */
79
+ centre: SkyPoint;
80
+ /** The radius of the celestial sphere in drawing units; default 1. `stereographic` puts 90 degrees from the centre at 2 times this, `orthographic` at exactly this. For `equirectangular` to give degrees, pass 180 / Math.PI. */
81
+ scale?: number;
82
+ /** Turns the finished chart anticlockwise by this many degrees (as drawn with y up); default 0. */
83
+ rotation?: number;
84
+ /** True for the map convention, east to the right. Default false: the sky seen looking up, east to the left. */
85
+ mirror?: boolean;
86
+ }
87
+ /**
88
+ * Where a point of the sky lands on the chart.
89
+ *
90
+ * With the default `mirror: false` the chart is the sky seen looking up: north is up and east is to the LEFT, so `x` grows toward decreasing right ascension. Set `mirror: true` for the map convention (east right).
91
+ * `y` grows toward the north (up the chart).
92
+ *
93
+ * @param kind - The projection.
94
+ * @param point - The sky position in degrees (`rightAscension` wraps at 360; `declination` is -90 to 90).
95
+ * @param options - The `centre` that lands at (0, 0), and optionally `scale`, `rotation` and `mirror`.
96
+ * @returns The point on the chart, or `undefined` when the sky position is not on the visible side: the far hemisphere of `orthographic` and `gnomonic` (90 degrees or more from the centre for `gnomonic`), and the antipode of the centre for `stereographic`.
97
+ * @throws RangeError if `kind` is unknown, or a number is not finite, or a declination is outside -90 to 90, or `scale` is not above 0.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * import { project } from "@johnmorrisdotca/hoshi-stars/projection";
102
+ *
103
+ * const orion = { centre: { rightAscension: 83.8, declination: -5.4 }, scale: 300 };
104
+ * const betelgeuse = project("stereographic", { rightAscension: 88.79, declination: 7.41 }, orion);
105
+ * console.log(betelgeuse); // left of the middle (it is east of the centre) and above it
106
+ * ```
107
+ */
108
+ export declare function project(kind: Projection, point: SkyPoint, options: ProjectionOptions): MapPoint | undefined;
109
+ /**
110
+ * The sky position under a point of the chart: the inverse of `project`, with the same `options`.
111
+ *
112
+ * @param kind - The projection.
113
+ * @param point - A point of the chart, in the same units and convention as `project` gives.
114
+ * @param options - The same options that drew the chart.
115
+ * @returns Right ascension (0 up to 360) and declination in degrees, or `undefined` when the point is outside the map: beyond the edge of the orthographic disc, the equidistant disc (radius pi times `scale`), the Mollweide and Hammer ovals, or the equirectangular rectangle.
116
+ * @throws RangeError if `kind` is unknown or a number is not finite.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * import { project, unproject } from "@johnmorrisdotca/hoshi-stars/projection";
121
+ *
122
+ * const options = { centre: { rightAscension: 279.2, declination: 38.8 }, scale: 200 };
123
+ * const spot = project("orthographic", { rightAscension: 290, declination: 45 }, options)!;
124
+ * console.log(unproject("orthographic", spot, options)); // { rightAscension: 290, declination: 45 }, to rounding
125
+ * ```
126
+ */
127
+ export declare function unproject(kind: Projection, point: MapPoint, options: ProjectionOptions): SkyPoint | undefined;