spacemap 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/spacemap.iife.js +122 -122
- package/dist/spacemap.js +122 -122
- package/index.d.ts +8 -2
- package/index.js +404 -142
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2623,7 +2623,7 @@ function hydrate(component, options) {
|
|
|
2623
2623
|
set_hydrate_node(previous_hydrate_node);
|
|
2624
2624
|
}
|
|
2625
2625
|
}
|
|
2626
|
-
var listeners = /* @__PURE__ */ new Map();
|
|
2626
|
+
var listeners$1 = /* @__PURE__ */ new Map();
|
|
2627
2627
|
function _mount(Component, { target, anchor, props = {}, events, context, intro = true, transformError }) {
|
|
2628
2628
|
init_operations();
|
|
2629
2629
|
var component = void 0;
|
|
@@ -2655,10 +2655,10 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
|
|
|
2655
2655
|
registered_events.add(event_name);
|
|
2656
2656
|
var passive = is_passive_event(event_name);
|
|
2657
2657
|
for (const node of [target, document]) {
|
|
2658
|
-
var counts = listeners.get(node);
|
|
2658
|
+
var counts = listeners$1.get(node);
|
|
2659
2659
|
if (counts === void 0) {
|
|
2660
2660
|
counts = /* @__PURE__ */ new Map();
|
|
2661
|
-
listeners.set(node, counts);
|
|
2661
|
+
listeners$1.set(node, counts);
|
|
2662
2662
|
}
|
|
2663
2663
|
var count = counts.get(event_name);
|
|
2664
2664
|
if (count === void 0) {
|
|
@@ -2672,12 +2672,12 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
|
|
|
2672
2672
|
root_event_handles.add(event_handle);
|
|
2673
2673
|
return () => {
|
|
2674
2674
|
for (var event_name of registered_events) for (const node of [target, document]) {
|
|
2675
|
-
var counts = listeners.get(node);
|
|
2675
|
+
var counts = listeners$1.get(node);
|
|
2676
2676
|
var count = counts.get(event_name);
|
|
2677
2677
|
if (--count == 0) {
|
|
2678
2678
|
node.removeEventListener(event_name, handle_event_propagation);
|
|
2679
2679
|
counts.delete(event_name);
|
|
2680
|
-
if (counts.size === 0) listeners.delete(node);
|
|
2680
|
+
if (counts.size === 0) listeners$1.delete(node);
|
|
2681
2681
|
} else counts.set(event_name, count);
|
|
2682
2682
|
}
|
|
2683
2683
|
root_event_handles.delete(event_handle);
|
|
@@ -6709,6 +6709,7 @@ var BodyIndex = class {
|
|
|
6709
6709
|
focusFeature = null;
|
|
6710
6710
|
extrasolar = /* @__PURE__ */ new Map();
|
|
6711
6711
|
pinnedExtrasolar = /* @__PURE__ */ new Set();
|
|
6712
|
+
tripCentreId = null;
|
|
6712
6713
|
exoplanets = /* @__PURE__ */ new Map();
|
|
6713
6714
|
exoplanetAliases = /* @__PURE__ */ new Map();
|
|
6714
6715
|
dirtyAsteroidZones = /* @__PURE__ */ new Set();
|
|
@@ -6730,6 +6731,7 @@ var BodyIndex = class {
|
|
|
6730
6731
|
set(this.#orientationVersion, value, true);
|
|
6731
6732
|
}
|
|
6732
6733
|
orientationRead = /* @__PURE__ */ new Set();
|
|
6734
|
+
nutationAwaited = /* @__PURE__ */ new Map();
|
|
6733
6735
|
childrenByParent = /* @__PURE__ */ new Map();
|
|
6734
6736
|
moonMaxAByParent = /* @__PURE__ */ new Map();
|
|
6735
6737
|
addListeners = /* @__PURE__ */ new Set();
|
|
@@ -6769,10 +6771,13 @@ var BodyIndex = class {
|
|
|
6769
6771
|
this.pinnedExtrasolar = ids;
|
|
6770
6772
|
this.trimExtrasolar();
|
|
6771
6773
|
}
|
|
6774
|
+
pinTripCentre(id) {
|
|
6775
|
+
this.tripCentreId = id;
|
|
6776
|
+
}
|
|
6772
6777
|
trimExtrasolar() {
|
|
6773
6778
|
for (const id of this.extrasolar.keys()) {
|
|
6774
6779
|
if (this.extrasolar.size - this.pinnedExtrasolar.size <= EXTRASOLAR_KEPT) break;
|
|
6775
|
-
if (!this.pinnedExtrasolar.has(id)) this.extrasolar.delete(id);
|
|
6780
|
+
if (id !== this.tripCentreId && !this.pinnedExtrasolar.has(id)) this.extrasolar.delete(id);
|
|
6776
6781
|
}
|
|
6777
6782
|
}
|
|
6778
6783
|
addExoplanets(bodies) {
|
|
@@ -7094,15 +7099,70 @@ async function fetchWithTimeout(input, init = {}, timeoutMs = BOOT_FETCH_TIMEOUT
|
|
|
7094
7099
|
});
|
|
7095
7100
|
}
|
|
7096
7101
|
|
|
7102
|
+
// src/lib/fetch/online.ts
|
|
7103
|
+
function untilOnline() {
|
|
7104
|
+
if (typeof navigator === "undefined" || navigator.onLine !== false) return Promise.resolve();
|
|
7105
|
+
return new Promise((resolve) => window.addEventListener("online", () => resolve(), { once: true }));
|
|
7106
|
+
}
|
|
7107
|
+
|
|
7108
|
+
// src/lib/fetch/retry-backoff.ts
|
|
7109
|
+
var FIRST_DELAY_MS = 2e3;
|
|
7110
|
+
var MAX_DELAY_MS = 6e4;
|
|
7111
|
+
var JITTER = .25;
|
|
7112
|
+
var RetryBackoff = class {
|
|
7113
|
+
onDue;
|
|
7114
|
+
random;
|
|
7115
|
+
failures = /* @__PURE__ */ new Map();
|
|
7116
|
+
constructor(onDue = () => {}, random = Math.random) {
|
|
7117
|
+
this.onDue = onDue;
|
|
7118
|
+
this.random = random;
|
|
7119
|
+
}
|
|
7120
|
+
waiting(key) {
|
|
7121
|
+
return this.failures.get(key)?.timer !== void 0;
|
|
7122
|
+
}
|
|
7123
|
+
fail(key) {
|
|
7124
|
+
const last = this.failures.get(key);
|
|
7125
|
+
clearTimeout(last?.timer);
|
|
7126
|
+
const failure = {
|
|
7127
|
+
count: (last?.count ?? 0) + 1,
|
|
7128
|
+
timer: void 0
|
|
7129
|
+
};
|
|
7130
|
+
const delay = Math.min(FIRST_DELAY_MS * 2 ** (failure.count - 1), MAX_DELAY_MS);
|
|
7131
|
+
const spread = 1 + JITTER * (2 * this.random() - 1);
|
|
7132
|
+
failure.timer = setTimeout(() => {
|
|
7133
|
+
failure.timer = void 0;
|
|
7134
|
+
this.onDue(key);
|
|
7135
|
+
}, delay * spread);
|
|
7136
|
+
this.failures.set(key, failure);
|
|
7137
|
+
}
|
|
7138
|
+
clear(key) {
|
|
7139
|
+
clearTimeout(this.failures.get(key)?.timer);
|
|
7140
|
+
this.failures.delete(key);
|
|
7141
|
+
}
|
|
7142
|
+
};
|
|
7143
|
+
|
|
7097
7144
|
// src/lib/fetch/systems-global.ts
|
|
7098
7145
|
var phases = /* @__PURE__ */ new Map();
|
|
7099
7146
|
var gmKm3s2 = /* @__PURE__ */ new Map();
|
|
7100
7147
|
var loadPromise$1 = null;
|
|
7148
|
+
var loaded = false;
|
|
7149
|
+
var absent = false;
|
|
7150
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
7151
|
+
var RETRY_KEY = "global";
|
|
7152
|
+
var retry = new RetryBackoff(() => void askAgain());
|
|
7153
|
+
var StatusError = class extends Error {
|
|
7154
|
+
status;
|
|
7155
|
+
constructor(status) {
|
|
7156
|
+
super(`systems/global.json: ${status}`);
|
|
7157
|
+
this.status = status;
|
|
7158
|
+
}
|
|
7159
|
+
};
|
|
7160
|
+
var permanent = (e) => e instanceof StatusError && e.status >= 400 && e.status < 500 && e.status !== 408 && e.status !== 429;
|
|
7101
7161
|
function loadSystemsGlobal() {
|
|
7102
7162
|
if (loadPromise$1) return loadPromise$1;
|
|
7103
7163
|
const p = (async () => {
|
|
7104
7164
|
const r = await fetchWithTimeout(`${dataBase()}/v1/systems/global.json`);
|
|
7105
|
-
if (!r.ok) throw new
|
|
7165
|
+
if (!r.ok) throw new StatusError(r.status);
|
|
7106
7166
|
const raw = await r.json();
|
|
7107
7167
|
if (raw.gm) for (const [k, v] of Object.entries(raw.gm)) gmKm3s2.set(parseInt(k, 10), v);
|
|
7108
7168
|
if (raw.nut_prec_angles) for (const [k, v] of Object.entries(raw.nut_prec_angles)) {
|
|
@@ -7112,13 +7172,51 @@ function loadSystemsGlobal() {
|
|
|
7112
7172
|
phaseDegree
|
|
7113
7173
|
});
|
|
7114
7174
|
}
|
|
7175
|
+
loaded = true;
|
|
7115
7176
|
})();
|
|
7116
|
-
p.
|
|
7177
|
+
p.then(() => {
|
|
7178
|
+
retry.clear(RETRY_KEY);
|
|
7179
|
+
settle();
|
|
7180
|
+
}, (e) => {
|
|
7117
7181
|
if (loadPromise$1 === p) loadPromise$1 = null;
|
|
7182
|
+
if (!permanent(e)) {
|
|
7183
|
+
retry.fail(RETRY_KEY);
|
|
7184
|
+
return;
|
|
7185
|
+
}
|
|
7186
|
+
absent = true;
|
|
7187
|
+
retry.clear(RETRY_KEY);
|
|
7188
|
+
settle();
|
|
7118
7189
|
});
|
|
7119
7190
|
loadPromise$1 = p;
|
|
7120
7191
|
return p;
|
|
7121
7192
|
}
|
|
7193
|
+
function settle() {
|
|
7194
|
+
const told = [...listeners];
|
|
7195
|
+
listeners.clear();
|
|
7196
|
+
for (const listener of told) try {
|
|
7197
|
+
listener();
|
|
7198
|
+
} catch (e) {
|
|
7199
|
+
console.error("systems-global: a listener failed:", e);
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
function onSystemsGlobalSettled(listener) {
|
|
7203
|
+
if (loaded || absent) {
|
|
7204
|
+
listener();
|
|
7205
|
+
return () => {};
|
|
7206
|
+
}
|
|
7207
|
+
listeners.add(listener);
|
|
7208
|
+
askAgain();
|
|
7209
|
+
return () => listeners.delete(listener);
|
|
7210
|
+
}
|
|
7211
|
+
async function askAgain() {
|
|
7212
|
+
if (loaded || absent || listeners.size === 0 || retry.waiting(RETRY_KEY)) return;
|
|
7213
|
+
await untilOnline();
|
|
7214
|
+
if (listeners.size === 0) return;
|
|
7215
|
+
await loadSystemsGlobal().catch(() => {});
|
|
7216
|
+
}
|
|
7217
|
+
function nutPrecAwaited(id, sums) {
|
|
7218
|
+
return !!sums && !loaded && !absent && naifId(id) !== null;
|
|
7219
|
+
}
|
|
7122
7220
|
function nutPrecFor(id, sums) {
|
|
7123
7221
|
const naif = naifId(id);
|
|
7124
7222
|
if (naif === null || !sums) return void 0;
|
|
@@ -9046,14 +9144,14 @@ function writeTrailAlphas(fullArr, trailArr, posArr, isOpenCurve, useTrail) {
|
|
|
9046
9144
|
// src/lib/fetch/position/chunk.ts
|
|
9047
9145
|
var SSB_NAIF_ID = 0;
|
|
9048
9146
|
var SUN_NAIF_ID = 10;
|
|
9049
|
-
function chebyshevOsculatingElements(body, parentId, jd, origin) {
|
|
9147
|
+
function chebyshevOsculatingElements(body, parentId, jd, origin, ownGm = 0) {
|
|
9050
9148
|
const gmKm3s2 = getGmKm3s2(parentId);
|
|
9051
9149
|
if (!gmKm3s2) return null;
|
|
9052
9150
|
const state = chebyshevStateKm(body, jd);
|
|
9053
9151
|
if (!state) return null;
|
|
9054
9152
|
const from = origin ? chebyshevStateKm(origin, jd) : null;
|
|
9055
9153
|
if (origin && !from) return null;
|
|
9056
|
-
const muAuDay2 = gmKm3s2 * KM3_S2_TO_AU3_DAY2;
|
|
9154
|
+
const muAuDay2 = (gmKm3s2 + ownGm) * KM3_S2_TO_AU3_DAY2;
|
|
9057
9155
|
const r = [
|
|
9058
9156
|
(state.position[0] - (from?.position[0] ?? 0)) / AU_KM,
|
|
9059
9157
|
(state.position[1] - (from?.position[1] ?? 0)) / AU_KM,
|
|
@@ -9068,7 +9166,7 @@ function chebyshevOsculatingElements(body, parentId, jd, origin) {
|
|
|
9068
9166
|
}
|
|
9069
9167
|
function chebyshevHelioElements(body, jd, sun) {
|
|
9070
9168
|
if (!sun || body.parentId !== SSB_NAIF_ID || body.naifId === SUN_NAIF_ID) return null;
|
|
9071
|
-
return chebyshevOsculatingElements(body, SUN_NAIF_ID, jd, sun);
|
|
9169
|
+
return chebyshevOsculatingElements(body, SUN_NAIF_ID, jd, sun, getGmKm3s2(body.naifId));
|
|
9072
9170
|
}
|
|
9073
9171
|
function elementsUrl(zone, zoom, part, time) {
|
|
9074
9172
|
return time ? chunkedPartedUrl(zone, zoom, time, part) : partedUrl(zone, zoom, part);
|
|
@@ -9716,42 +9814,6 @@ async function fetchChebyshev(zone, zoom, chunk, priority) {
|
|
|
9716
9814
|
};
|
|
9717
9815
|
}
|
|
9718
9816
|
|
|
9719
|
-
// src/lib/fetch/retry-backoff.ts
|
|
9720
|
-
var FIRST_DELAY_MS = 2e3;
|
|
9721
|
-
var MAX_DELAY_MS = 6e4;
|
|
9722
|
-
var JITTER = .25;
|
|
9723
|
-
var RetryBackoff = class {
|
|
9724
|
-
onDue;
|
|
9725
|
-
random;
|
|
9726
|
-
failures = /* @__PURE__ */ new Map();
|
|
9727
|
-
constructor(onDue = () => {}, random = Math.random) {
|
|
9728
|
-
this.onDue = onDue;
|
|
9729
|
-
this.random = random;
|
|
9730
|
-
}
|
|
9731
|
-
waiting(key) {
|
|
9732
|
-
return this.failures.get(key)?.timer !== void 0;
|
|
9733
|
-
}
|
|
9734
|
-
fail(key) {
|
|
9735
|
-
const last = this.failures.get(key);
|
|
9736
|
-
clearTimeout(last?.timer);
|
|
9737
|
-
const failure = {
|
|
9738
|
-
count: (last?.count ?? 0) + 1,
|
|
9739
|
-
timer: void 0
|
|
9740
|
-
};
|
|
9741
|
-
const delay = Math.min(FIRST_DELAY_MS * 2 ** (failure.count - 1), MAX_DELAY_MS);
|
|
9742
|
-
const spread = 1 + JITTER * (2 * this.random() - 1);
|
|
9743
|
-
failure.timer = setTimeout(() => {
|
|
9744
|
-
failure.timer = void 0;
|
|
9745
|
-
this.onDue(key);
|
|
9746
|
-
}, delay * spread);
|
|
9747
|
-
this.failures.set(key, failure);
|
|
9748
|
-
}
|
|
9749
|
-
clear(key) {
|
|
9750
|
-
clearTimeout(this.failures.get(key)?.timer);
|
|
9751
|
-
this.failures.delete(key);
|
|
9752
|
-
}
|
|
9753
|
-
};
|
|
9754
|
-
|
|
9755
9817
|
// src/lib/fetch/position/chunk-window.ts
|
|
9756
9818
|
var NEIGHBOR_WINDOW = 1;
|
|
9757
9819
|
var RESOLVED = Promise.resolve();
|
|
@@ -10743,7 +10805,7 @@ function nodeInfo(container) {
|
|
|
10743
10805
|
var EXTRASOLAR_FORMAT = 1;
|
|
10744
10806
|
var LITERATURE_PAGE = "/constants/stars/literature.py";
|
|
10745
10807
|
function isWikidataCredit(credit) {
|
|
10746
|
-
return credit.source
|
|
10808
|
+
return credit.source === "https://www.wikidata.org/";
|
|
10747
10809
|
}
|
|
10748
10810
|
function isPaperCredit(credit) {
|
|
10749
10811
|
return /\b\d{4}$/.test(credit.organisation) || credit.source.includes("/constants/stars/literature.py");
|
|
@@ -15927,7 +15989,7 @@ function typePriority(type) {
|
|
|
15927
15989
|
}
|
|
15928
15990
|
}
|
|
15929
15991
|
function isDrawn(bo) {
|
|
15930
|
-
return bo.body.position !== null && !bo.carried;
|
|
15992
|
+
return bo.body.position !== null && !bo.carried && !bo.body.nutPrecAwaited;
|
|
15931
15993
|
}
|
|
15932
15994
|
|
|
15933
15995
|
// src/lib/scene/label/culling.ts
|
|
@@ -16707,16 +16769,33 @@ function shapeModelSkipReason(global) {
|
|
|
16707
16769
|
|
|
16708
16770
|
// src/lib/scene/objects/body/orientation-apply.ts
|
|
16709
16771
|
function applyBodyOrientation(body, orientation, nutPrec, ctx, systemId) {
|
|
16772
|
+
const id = body.data.id;
|
|
16710
16773
|
body.orientation = orientation;
|
|
16711
|
-
body.nutPrec = nutPrecFor(
|
|
16774
|
+
body.nutPrec = nutPrecFor(id, nutPrec);
|
|
16775
|
+
body.nutPrecAwaited = nutPrecAwaited(id, nutPrec) ? nutPrec : void 0;
|
|
16776
|
+
if (body.nutPrecAwaited) ctx?.bodies.nutationAwaited.set(id, body);
|
|
16777
|
+
else ctx?.bodies.nutationAwaited.delete(id);
|
|
16712
16778
|
if (ctx) ctx.bodies.dataVersion++;
|
|
16713
16779
|
ctx?.credits.registerOrientation({
|
|
16714
|
-
bodyId:
|
|
16715
|
-
systemId: systemId ??
|
|
16780
|
+
bodyId: id,
|
|
16781
|
+
systemId: systemId ?? id,
|
|
16716
16782
|
source: orientation.source,
|
|
16717
16783
|
reference: orientation.reference
|
|
16718
16784
|
});
|
|
16719
16785
|
}
|
|
16786
|
+
function joinAwaitedNutation(ctx) {
|
|
16787
|
+
const awaited = ctx.bodies.nutationAwaited;
|
|
16788
|
+
const turned = [];
|
|
16789
|
+
for (const body of awaited.values()) {
|
|
16790
|
+
body.nutPrec = nutPrecFor(body.data.id, body.nutPrecAwaited);
|
|
16791
|
+
body.nutPrecAwaited = void 0;
|
|
16792
|
+
if (body.nutPrec) turned.push(body);
|
|
16793
|
+
}
|
|
16794
|
+
const settled = awaited.size > 0;
|
|
16795
|
+
awaited.clear();
|
|
16796
|
+
if (settled) ctx.bodies.dataVersion++;
|
|
16797
|
+
return turned;
|
|
16798
|
+
}
|
|
16720
16799
|
|
|
16721
16800
|
// src/lib/scene/objects/body/model-radial.ts
|
|
16722
16801
|
var LAT_CELLS = 48;
|
|
@@ -16979,7 +17058,9 @@ function modelUnitScene(bo) {
|
|
|
16979
17058
|
return bo.modelUnitOverride ?? bo.radiusScene;
|
|
16980
17059
|
}
|
|
16981
17060
|
var attachedModelRoots = /* @__PURE__ */ new Set();
|
|
16982
|
-
function mountModel(bo, root) {
|
|
17061
|
+
function mountModel(bo, root, jd) {
|
|
17062
|
+
if (bo.mesh) root.quaternion.copy(bo.mesh.quaternion);
|
|
17063
|
+
else if (bo.body.orientation && Number.isFinite(jd)) applyOrientation(root, bo.body.orientation, jd, bo.body.nutPrec);
|
|
16983
17064
|
const wrapper = new Group();
|
|
16984
17065
|
wrapper.scale.setScalar(modelUnitScene(bo));
|
|
16985
17066
|
wrapper.userData.pickBody = bo.body;
|
|
@@ -17055,6 +17136,7 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
17055
17136
|
root.visible = !bo.isCrashed;
|
|
17056
17137
|
bo.model = root;
|
|
17057
17138
|
bo.modelName = slug;
|
|
17139
|
+
if (ctx) ctx.bodies.dataVersion++;
|
|
17058
17140
|
setHaloLoading(bo, false);
|
|
17059
17141
|
const spanM = meta.exports[tier]?.scale_meters;
|
|
17060
17142
|
if (spanM) {
|
|
@@ -17101,6 +17183,10 @@ async function loadNaturalBodyModel(bo, ctx) {
|
|
|
17101
17183
|
console.warn(`Failed to load shape-model metadata for ${slug}:`, e);
|
|
17102
17184
|
return;
|
|
17103
17185
|
}
|
|
17186
|
+
if ((bo.modelLoadEpoch ?? 0) !== epoch) {
|
|
17187
|
+
disposeGltf(gltf.scene);
|
|
17188
|
+
return;
|
|
17189
|
+
}
|
|
17104
17190
|
if (meta.kind !== "shape_model") {
|
|
17105
17191
|
disposeGltf(gltf.scene);
|
|
17106
17192
|
return;
|
|
@@ -17128,7 +17214,7 @@ async function loadNaturalBodyModel(bo, ctx) {
|
|
|
17128
17214
|
setLabelAnnotation(bo, "missing", null);
|
|
17129
17215
|
}
|
|
17130
17216
|
}
|
|
17131
|
-
mountModel(bo, root);
|
|
17217
|
+
mountModel(bo, root, ctx?.placer?.jd ?? NaN);
|
|
17132
17218
|
bo.modelName = slug;
|
|
17133
17219
|
if (bo.mesh) bo.mesh.visible = false;
|
|
17134
17220
|
const credit = shapeModelCredit(meta);
|
|
@@ -20456,6 +20542,54 @@ function planetBody(planet, elements, orbit, file, hostRecord) {
|
|
|
20456
20542
|
};
|
|
20457
20543
|
}
|
|
20458
20544
|
|
|
20545
|
+
// src/lib/math/travel/vec3.ts
|
|
20546
|
+
function add(a, b) {
|
|
20547
|
+
return [
|
|
20548
|
+
a[0] + b[0],
|
|
20549
|
+
a[1] + b[1],
|
|
20550
|
+
a[2] + b[2]
|
|
20551
|
+
];
|
|
20552
|
+
}
|
|
20553
|
+
function sub(a, b) {
|
|
20554
|
+
return [
|
|
20555
|
+
a[0] - b[0],
|
|
20556
|
+
a[1] - b[1],
|
|
20557
|
+
a[2] - b[2]
|
|
20558
|
+
];
|
|
20559
|
+
}
|
|
20560
|
+
function scale(a, k) {
|
|
20561
|
+
return [
|
|
20562
|
+
a[0] * k,
|
|
20563
|
+
a[1] * k,
|
|
20564
|
+
a[2] * k
|
|
20565
|
+
];
|
|
20566
|
+
}
|
|
20567
|
+
function dot$1(a, b) {
|
|
20568
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
20569
|
+
}
|
|
20570
|
+
function cross$1(a, b) {
|
|
20571
|
+
return [
|
|
20572
|
+
a[1] * b[2] - a[2] * b[1],
|
|
20573
|
+
a[2] * b[0] - a[0] * b[2],
|
|
20574
|
+
a[0] * b[1] - a[1] * b[0]
|
|
20575
|
+
];
|
|
20576
|
+
}
|
|
20577
|
+
function norm(a) {
|
|
20578
|
+
return Math.hypot(a[0], a[1], a[2]);
|
|
20579
|
+
}
|
|
20580
|
+
function normalize(a) {
|
|
20581
|
+
const n = norm(a);
|
|
20582
|
+
return n === 0 ? [
|
|
20583
|
+
0,
|
|
20584
|
+
0,
|
|
20585
|
+
0
|
|
20586
|
+
] : [
|
|
20587
|
+
a[0] / n,
|
|
20588
|
+
a[1] / n,
|
|
20589
|
+
a[2] / n
|
|
20590
|
+
];
|
|
20591
|
+
}
|
|
20592
|
+
|
|
20459
20593
|
// src/lib/scene/exoplanets/pair-orbits.ts
|
|
20460
20594
|
var MAX_PERIOD_DAYS = 36525e3;
|
|
20461
20595
|
var POINT_ALONE_PX = 2;
|
|
@@ -20486,6 +20620,20 @@ function barycentreOf(first, second, share) {
|
|
|
20486
20620
|
first[2] + share * (second[2] * depth - first[2])
|
|
20487
20621
|
];
|
|
20488
20622
|
}
|
|
20623
|
+
function nodePlace(id, pairs, sceneOf) {
|
|
20624
|
+
const pair = pairs.find((p) => p.node.id === id);
|
|
20625
|
+
if (!pair || pair.node.point) {
|
|
20626
|
+
const at = sceneOf(id);
|
|
20627
|
+
return at && [
|
|
20628
|
+
at[0],
|
|
20629
|
+
at[1],
|
|
20630
|
+
at[2]
|
|
20631
|
+
];
|
|
20632
|
+
}
|
|
20633
|
+
const first = nodePlace(pair.first, pairs, sceneOf);
|
|
20634
|
+
const second = nodePlace(pair.second, pairs, sceneOf);
|
|
20635
|
+
return first && second && barycentreOf(first, second, pair.orbit.barycentre);
|
|
20636
|
+
}
|
|
20489
20637
|
function drawnPairs(nodes) {
|
|
20490
20638
|
const draws = ({ orbit, members }) => orbit !== null && orbit.period <= MAX_PERIOD_DAYS && members.length === 2 && members.every((member) => {
|
|
20491
20639
|
if (member.id === null) return false;
|
|
@@ -20686,13 +20834,7 @@ function samePlace(a, b) {
|
|
|
20686
20834
|
async function holdStarSystem(ctx, plan) {
|
|
20687
20835
|
await Promise.all(plan.ids.map((id) => ensureExtrasolarBody(ctx, extrasolarObjectId(id))));
|
|
20688
20836
|
const anchor = (id) => ctx.getBody(extrasolarObjectId(id))?.extrasolar;
|
|
20689
|
-
const placeOf = (id) =>
|
|
20690
|
-
const pair = plan.pairs.find((p) => p.node.id === id);
|
|
20691
|
-
if (!pair || pair.node.point) return anchor(id)?.scene ?? null;
|
|
20692
|
-
const first = placeOf(pair.first);
|
|
20693
|
-
const second = placeOf(pair.second);
|
|
20694
|
-
return first && second && barycentreOf(first, second, pair.orbit.barycentre);
|
|
20695
|
-
};
|
|
20837
|
+
const placeOf = (id) => nodePlace(id, plan.pairs, (at) => anchor(at)?.scene ?? null);
|
|
20696
20838
|
const pairs = plan.pairs.filter(({ node, first, second }) => placeOf(node.id) !== null && anchor(first) !== void 0 && anchor(second) !== void 0);
|
|
20697
20839
|
const insidePoints = /* @__PURE__ */ new Map();
|
|
20698
20840
|
for (const pair of pairs) {
|
|
@@ -21207,6 +21349,13 @@ var ExoplanetStore = class {
|
|
|
21207
21349
|
this.trim();
|
|
21208
21350
|
}
|
|
21209
21351
|
view(objectId) {
|
|
21352
|
+
const frame = this.frameOf(objectId);
|
|
21353
|
+
return frame && systemView(frame.at, frame.normal, frame.outer || 1);
|
|
21354
|
+
}
|
|
21355
|
+
planeNormal(objectId) {
|
|
21356
|
+
return this.frameOf(objectId)?.normal ?? null;
|
|
21357
|
+
}
|
|
21358
|
+
frameOf(objectId) {
|
|
21210
21359
|
const planet = planetOfObjectId(objectId);
|
|
21211
21360
|
const hostId = planet?.host ?? extrasolarIdOf(objectId);
|
|
21212
21361
|
if (hostId === null) return null;
|
|
@@ -21217,13 +21366,15 @@ var ExoplanetStore = class {
|
|
|
21217
21366
|
if (!held && !pair || !at) return null;
|
|
21218
21367
|
const planets = held?.file.planets.filter((p) => p.host === hostId) ?? [];
|
|
21219
21368
|
const plane = planets.find((p) => p.number === planet?.n)?.elements ?? pair?.orbit ?? outermostElements(planets);
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21369
|
+
return {
|
|
21370
|
+
at,
|
|
21371
|
+
normal: plane ? orbitNormal(plane.i, plane.om) : [
|
|
21372
|
+
0,
|
|
21373
|
+
1,
|
|
21374
|
+
0
|
|
21375
|
+
],
|
|
21376
|
+
outer: Math.max(pair?.orbit.a ?? 0, outerOrbitAU(planets) ?? 0)
|
|
21377
|
+
};
|
|
21227
21378
|
}
|
|
21228
21379
|
pairOf(star) {
|
|
21229
21380
|
const root = star && systemRoot(star.record);
|
|
@@ -27778,54 +27929,6 @@ function refreshTrail(body, line, basisPos, jd, view) {
|
|
|
27778
27929
|
recordRewrite(ud, curve, offX, offY, offZ, headX, headY, headZ, posArr, n, view);
|
|
27779
27930
|
}
|
|
27780
27931
|
|
|
27781
|
-
// src/lib/math/travel/vec3.ts
|
|
27782
|
-
function add(a, b) {
|
|
27783
|
-
return [
|
|
27784
|
-
a[0] + b[0],
|
|
27785
|
-
a[1] + b[1],
|
|
27786
|
-
a[2] + b[2]
|
|
27787
|
-
];
|
|
27788
|
-
}
|
|
27789
|
-
function sub(a, b) {
|
|
27790
|
-
return [
|
|
27791
|
-
a[0] - b[0],
|
|
27792
|
-
a[1] - b[1],
|
|
27793
|
-
a[2] - b[2]
|
|
27794
|
-
];
|
|
27795
|
-
}
|
|
27796
|
-
function scale(a, k) {
|
|
27797
|
-
return [
|
|
27798
|
-
a[0] * k,
|
|
27799
|
-
a[1] * k,
|
|
27800
|
-
a[2] * k
|
|
27801
|
-
];
|
|
27802
|
-
}
|
|
27803
|
-
function dot$1(a, b) {
|
|
27804
|
-
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
27805
|
-
}
|
|
27806
|
-
function cross$1(a, b) {
|
|
27807
|
-
return [
|
|
27808
|
-
a[1] * b[2] - a[2] * b[1],
|
|
27809
|
-
a[2] * b[0] - a[0] * b[2],
|
|
27810
|
-
a[0] * b[1] - a[1] * b[0]
|
|
27811
|
-
];
|
|
27812
|
-
}
|
|
27813
|
-
function norm(a) {
|
|
27814
|
-
return Math.hypot(a[0], a[1], a[2]);
|
|
27815
|
-
}
|
|
27816
|
-
function normalize(a) {
|
|
27817
|
-
const n = norm(a);
|
|
27818
|
-
return n === 0 ? [
|
|
27819
|
-
0,
|
|
27820
|
-
0,
|
|
27821
|
-
0
|
|
27822
|
-
] : [
|
|
27823
|
-
a[0] / n,
|
|
27824
|
-
a[1] / n,
|
|
27825
|
-
a[2] / n
|
|
27826
|
-
];
|
|
27827
|
-
}
|
|
27828
|
-
|
|
27829
27932
|
// src/lib/math/travel/path-sample.ts
|
|
27830
27933
|
function crossingWindow(path, index) {
|
|
27831
27934
|
const count = path.arcs[index].points.length;
|
|
@@ -27952,7 +28055,7 @@ function eclipticToScene(r) {
|
|
|
27952
28055
|
-r[1] * k
|
|
27953
28056
|
];
|
|
27954
28057
|
}
|
|
27955
|
-
function elementsToState(el, jd
|
|
28058
|
+
function elementsToState(el, jd) {
|
|
27956
28059
|
const { a, e, ma, n, epoch, q, tp } = el;
|
|
27957
28060
|
const t = elementsJd(el, jd);
|
|
27958
28061
|
const { om, w } = propagateOrbitAngles(el, jd);
|
|
@@ -27961,15 +28064,14 @@ function elementsToState(el, jd, muKm3S2) {
|
|
|
27961
28064
|
if (q == null || tp == null || !isFinite(q) || !isFinite(tp) || q <= 0) return null;
|
|
27962
28065
|
const solved = solveBarker(q, tp, t);
|
|
27963
28066
|
if (!solved) return null;
|
|
27964
|
-
const mu = muKm3S2 && muKm3S2 > 0 ? muKm3S2 : GM_SUN_KM3_S2;
|
|
27965
28067
|
const rKm = solved.r * AU_KM;
|
|
27966
28068
|
const pKm = 2 * q * AU_KM;
|
|
27967
|
-
return buildState(rKm, solved.nu, pKm, 1,
|
|
28069
|
+
return buildState(rKm, solved.nu, pKm, 1, GM_SUN_KM3_S2, w, el.i, om, equatorial);
|
|
27968
28070
|
}
|
|
27969
28071
|
if (!isFinite(e) || !isFinite(ma)) return null;
|
|
27970
28072
|
const aKm = a * AU_KM;
|
|
27971
28073
|
const nRadPerSec = n * DEG2RAD$1 / SEC_PER_DAY$1;
|
|
27972
|
-
const mu =
|
|
28074
|
+
const mu = nRadPerSec * nRadPerSec * Math.abs(aKm) * Math.abs(aKm) * Math.abs(aKm);
|
|
27973
28075
|
if (!isFinite(mu) || mu <= 0) return null;
|
|
27974
28076
|
const M = (ma + n * (t - epoch)) * DEG2RAD$1;
|
|
27975
28077
|
let nu;
|
|
@@ -28008,6 +28110,37 @@ function buildState(rKm, nu, pKm, e, mu, w, i, om, equatorial) {
|
|
|
28008
28110
|
};
|
|
28009
28111
|
}
|
|
28010
28112
|
|
|
28113
|
+
// src/lib/scene/objects/travel/stop-at-camera.ts
|
|
28114
|
+
function depthAt(positions, i, eye, ahead) {
|
|
28115
|
+
return (positions[i * 3] - eye.x) * ahead.x + (positions[i * 3 + 1] - eye.y) * ahead.y + (positions[i * 3 + 2] - eye.z) * ahead.z;
|
|
28116
|
+
}
|
|
28117
|
+
function collapse(positions, kept, gone, from, to, eye, ahead, clearance) {
|
|
28118
|
+
const keptDepth = depthAt(positions, kept, eye, ahead);
|
|
28119
|
+
const goneDepth = depthAt(positions, gone, eye, ahead);
|
|
28120
|
+
const share = Math.max(0, (keptDepth - clearance) / (keptDepth - goneDepth));
|
|
28121
|
+
const kx = positions[kept * 3];
|
|
28122
|
+
const ky = positions[kept * 3 + 1];
|
|
28123
|
+
const kz = positions[kept * 3 + 2];
|
|
28124
|
+
const x = kx + (positions[gone * 3] - kx) * share;
|
|
28125
|
+
const y = ky + (positions[gone * 3 + 1] - ky) * share;
|
|
28126
|
+
const z = kz + (positions[gone * 3 + 2] - kz) * share;
|
|
28127
|
+
for (let i = from; i < to; i++) {
|
|
28128
|
+
positions[i * 3] = x;
|
|
28129
|
+
positions[i * 3 + 1] = y;
|
|
28130
|
+
positions[i * 3 + 2] = z;
|
|
28131
|
+
}
|
|
28132
|
+
}
|
|
28133
|
+
function stopAtCamera(positions, count, eye, ahead, clearance) {
|
|
28134
|
+
if (depthAt(positions, 0, eye, ahead) > 0 && depthAt(positions, count - 1, eye, ahead) > 0) return;
|
|
28135
|
+
let first = 0;
|
|
28136
|
+
while (first < count && depthAt(positions, first, eye, ahead) <= 0) first++;
|
|
28137
|
+
if (first === count) return;
|
|
28138
|
+
let last = count - 1;
|
|
28139
|
+
while (depthAt(positions, last, eye, ahead) <= 0) last--;
|
|
28140
|
+
if (first > 0) collapse(positions, first, first - 1, 0, first, eye, ahead, clearance);
|
|
28141
|
+
if (last < count - 1) collapse(positions, last, last + 1, last + 1, count, eye, ahead, clearance);
|
|
28142
|
+
}
|
|
28143
|
+
|
|
28011
28144
|
// src/lib/travel/arc-colors.ts
|
|
28012
28145
|
var ARC_COLORS = {
|
|
28013
28146
|
cruise: "#7fdbff",
|
|
@@ -28161,6 +28294,7 @@ var SEVERITY_ORDER = [
|
|
|
28161
28294
|
var HAZARD_MARKER_SIZE = .03;
|
|
28162
28295
|
var HAZARD_CHIP_GAP_PX = 16;
|
|
28163
28296
|
var FADE_FRACTION = .2;
|
|
28297
|
+
var CAMERA_CLEARANCE = 1e-5;
|
|
28164
28298
|
function dotTexture(color) {
|
|
28165
28299
|
const size = 64;
|
|
28166
28300
|
const canvas = document.createElement("canvas");
|
|
@@ -28281,6 +28415,7 @@ var TravelPathOverlay = class {
|
|
|
28281
28415
|
layer = null;
|
|
28282
28416
|
offset = new Vector3();
|
|
28283
28417
|
projected = new Vector3();
|
|
28418
|
+
ahead = new Vector3();
|
|
28284
28419
|
rects = [];
|
|
28285
28420
|
order = [];
|
|
28286
28421
|
hovered = null;
|
|
@@ -28504,7 +28639,7 @@ var TravelPathOverlay = class {
|
|
|
28504
28639
|
}
|
|
28505
28640
|
addArcs(path, width, brightness, owner = null, patched = false, renderOrder = PATH_RENDER_ORDER) {
|
|
28506
28641
|
const parts = patched && path.frame === "planetary";
|
|
28507
|
-
const ends = (at) => parts && path.endOrbits.some((orbit) => orbit.at === at && orbit.approach.length > 1);
|
|
28642
|
+
const ends = (at) => parts && path.endOrbits.some((orbit) => orbit.at === at && (orbit.approach.length > 1 || this.crossing && orbit.anchorId !== path.centerId && systemOf(orbit.bodyId) !== orbit.bodyId));
|
|
28508
28643
|
path.arcs.forEach((arc, index) => {
|
|
28509
28644
|
const { from, to } = patched ? crossingWindow(path, index) : {
|
|
28510
28645
|
from: 0,
|
|
@@ -28608,8 +28743,11 @@ var TravelPathOverlay = class {
|
|
|
28608
28743
|
craft.local = eclipticToScene(at.r);
|
|
28609
28744
|
craft.anchorId = at.centerId === this.path.centerId ? null : at.centerId;
|
|
28610
28745
|
}
|
|
28611
|
-
reposition(centerScenePos, basis, bodyScenePos) {
|
|
28746
|
+
reposition(centerScenePos, basis, bodyScenePos, camera) {
|
|
28612
28747
|
if (this.center === null) return;
|
|
28748
|
+
const eye = camera.position;
|
|
28749
|
+
camera.getWorldDirection(this.ahead);
|
|
28750
|
+
const clearance = eye.length() * CAMERA_CLEARANCE;
|
|
28613
28751
|
const centre = [
|
|
28614
28752
|
centerScenePos[0] - basis[0],
|
|
28615
28753
|
centerScenePos[1] - basis[1],
|
|
@@ -28631,6 +28769,7 @@ var TravelPathOverlay = class {
|
|
|
28631
28769
|
arc.positions[i * 3 + 1] = arc.local[i * 3 + 1] + ay;
|
|
28632
28770
|
arc.positions[i * 3 + 2] = arc.local[i * 3 + 2] + az;
|
|
28633
28771
|
}
|
|
28772
|
+
stopAtCamera(arc.positions, arc.count, eye, this.ahead, clearance);
|
|
28634
28773
|
writeFatTrailVertices(arc.line.geometry, arc.positions, arc.alphas, arc.alphas, arc.count);
|
|
28635
28774
|
}
|
|
28636
28775
|
for (const marker of this.markers) {
|
|
@@ -29222,6 +29361,14 @@ function estimateVelocity(id, jd, pos) {
|
|
|
29222
29361
|
});
|
|
29223
29362
|
return dir ?? void 0;
|
|
29224
29363
|
}
|
|
29364
|
+
function turnBody(body, bo, jd) {
|
|
29365
|
+
const orientation = body.orientation;
|
|
29366
|
+
if (!orientation) return;
|
|
29367
|
+
if (bo.mesh) applyOrientation(bo.mesh, orientation, jd, body.nutPrec);
|
|
29368
|
+
if (bo.model) applyOrientation(bo.model, orientation, jd, body.nutPrec);
|
|
29369
|
+
if (bo.nomenclatureAnchor) applyOrientation(bo.nomenclatureAnchor, orientation, jd, body.nutPrec);
|
|
29370
|
+
for (const ring of bo.rings) applyOrientation(ring.mesh, orientation, jd, body.nutPrec);
|
|
29371
|
+
}
|
|
29225
29372
|
var Placer = class {
|
|
29226
29373
|
ctx;
|
|
29227
29374
|
bodyObjects;
|
|
@@ -29242,6 +29389,9 @@ var Placer = class {
|
|
|
29242
29389
|
this.diagnostics = diagnostics;
|
|
29243
29390
|
this.held = held;
|
|
29244
29391
|
}
|
|
29392
|
+
get jd() {
|
|
29393
|
+
return this.passJd;
|
|
29394
|
+
}
|
|
29245
29395
|
begin(jd, focusedId, frame) {
|
|
29246
29396
|
this.passJd = jd;
|
|
29247
29397
|
this.focusedId = focusedId;
|
|
@@ -29360,11 +29510,8 @@ var Placer = class {
|
|
|
29360
29510
|
const oc = bo.trail.userData.orbitCenter;
|
|
29361
29511
|
if (oc) oc.set(body.orbitCenter[0], body.orbitCenter[1], body.orbitCenter[2]);
|
|
29362
29512
|
}
|
|
29363
|
-
if (body.orientation
|
|
29364
|
-
|
|
29365
|
-
if (bo.model) applyOrientation(bo.model, body.orientation, jd, body.nutPrec);
|
|
29366
|
-
if (bo.nomenclatureAnchor) applyOrientation(bo.nomenclatureAnchor, body.orientation, jd, body.nutPrec);
|
|
29367
|
-
} else if (isModelBearing(body)) {
|
|
29513
|
+
if (body.orientation) turnBody(body, bo, jd);
|
|
29514
|
+
else if (isModelBearing(body)) {
|
|
29368
29515
|
const track = body.attitudeTrack;
|
|
29369
29516
|
if (!body.pointingOverride && track && track.orientationAt(jd, attitudeQuat)) {
|
|
29370
29517
|
if (bo.mesh) bo.mesh.quaternion.copy(attitudeQuat);
|
|
@@ -29387,7 +29534,6 @@ var Placer = class {
|
|
|
29387
29534
|
}
|
|
29388
29535
|
}
|
|
29389
29536
|
}
|
|
29390
|
-
if (body.orientation) for (const ring of bo.rings) applyOrientation(ring.mesh, body.orientation, jd, body.nutPrec);
|
|
29391
29537
|
return pos;
|
|
29392
29538
|
}
|
|
29393
29539
|
settleProbe(body, bo, jd) {
|
|
@@ -30739,6 +30885,7 @@ var _corner = new Vector3();
|
|
|
30739
30885
|
function surfaceClearanceKm(body) {
|
|
30740
30886
|
return SURFACE_CLEARANCE_KM[body.data.objectType] ?? effectiveRadiusKm(body.data) * .25;
|
|
30741
30887
|
}
|
|
30888
|
+
var POINT_MIN_DISTANCE = kmToScene(.01);
|
|
30742
30889
|
function minCameraDistance(body, minRadiusKm) {
|
|
30743
30890
|
return kmToScene((minRadiusKm ?? effectiveRadiusKm(body.data)) + surfaceClearanceKm(body));
|
|
30744
30891
|
}
|
|
@@ -31305,7 +31452,7 @@ var FocusController = class {
|
|
|
31305
31452
|
prepareFocusTarget(focus, [...pos], camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
31306
31453
|
}
|
|
31307
31454
|
focusOnPoint(position, distance) {
|
|
31308
|
-
const { focus, camera, callbacks } = this.deps;
|
|
31455
|
+
const { focus, camera, controls, callbacks } = this.deps;
|
|
31309
31456
|
const camWorld = this.cameraTruePos();
|
|
31310
31457
|
let camPos;
|
|
31311
31458
|
if (distance !== void 0) {
|
|
@@ -31317,6 +31464,7 @@ var FocusController = class {
|
|
|
31317
31464
|
];
|
|
31318
31465
|
}
|
|
31319
31466
|
this.focusedBody = void 0;
|
|
31467
|
+
controls.minDistance = POINT_MIN_DISTANCE;
|
|
31320
31468
|
this.promotion.emitUserPromotedCount();
|
|
31321
31469
|
prepareFocusTarget(focus, [...position], camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
31322
31470
|
const spherical = cartesianToSpherical(camPos ?? camWorld, position, void 0);
|
|
@@ -31565,6 +31713,10 @@ var SIDES = 72;
|
|
|
31565
31713
|
var LABEL_ROOM_PX = 40;
|
|
31566
31714
|
var LABEL_WIDTH_PX = 128;
|
|
31567
31715
|
var SIDE_MIN = .001;
|
|
31716
|
+
var BALL_ELEVATION_RAD = 45 * Math.PI / 180;
|
|
31717
|
+
var BALL_SLACK = 3;
|
|
31718
|
+
var BALL_ROOM_MIN = .25;
|
|
31719
|
+
var RING_SHARE = 1 / 4;
|
|
31568
31720
|
var NORTH = [
|
|
31569
31721
|
0,
|
|
31570
31722
|
1,
|
|
@@ -31588,11 +31740,12 @@ function openArea(insets, width, height) {
|
|
|
31588
31740
|
left: share(insets.left + LABEL_ROOM_PX, width)
|
|
31589
31741
|
};
|
|
31590
31742
|
}
|
|
31591
|
-
function tripVantage(target, origin, camera, view) {
|
|
31743
|
+
function tripVantage(target, origin, camera, view, ring = 0) {
|
|
31592
31744
|
const line = sub(origin, target);
|
|
31593
31745
|
const length = norm(line);
|
|
31594
31746
|
const narrow = narrowHalfFov(view.fovDeg, view.aspect);
|
|
31595
31747
|
if (!(length > 0) || !(narrow > 0)) return null;
|
|
31748
|
+
if (ring >= RING_SHARE * length) return ballVantage(target, target, length + ring, null, camera, view);
|
|
31596
31749
|
const offset = sub(camera, target);
|
|
31597
31750
|
if (shows(offset, line, length, view, narrow)) return {
|
|
31598
31751
|
offset: [
|
|
@@ -31634,6 +31787,61 @@ function tripVantage(target, origin, camera, view) {
|
|
|
31634
31787
|
kept: false
|
|
31635
31788
|
};
|
|
31636
31789
|
}
|
|
31790
|
+
function ballVantage(target, center, radius, normal, camera, view) {
|
|
31791
|
+
if (!(radius > 0)) return null;
|
|
31792
|
+
const pole = normal ?? NORTH;
|
|
31793
|
+
const ball = sub(center, target);
|
|
31794
|
+
const offset = sub(camera, target);
|
|
31795
|
+
const distance = norm(offset);
|
|
31796
|
+
if (distance > 0) {
|
|
31797
|
+
const least = ballDistance(ball, radius, scale(offset, 1 / distance), view);
|
|
31798
|
+
if (least !== null && distance >= least && distance <= BALL_SLACK * least) return {
|
|
31799
|
+
offset: [
|
|
31800
|
+
offset[0],
|
|
31801
|
+
offset[1],
|
|
31802
|
+
offset[2]
|
|
31803
|
+
],
|
|
31804
|
+
kept: true
|
|
31805
|
+
};
|
|
31806
|
+
}
|
|
31807
|
+
const first = sideOf(offset, pole) ?? sideOf(EAST, pole) ?? sideOf(NORTH, pole);
|
|
31808
|
+
if (!first) return null;
|
|
31809
|
+
const second = cross$1(pole, first);
|
|
31810
|
+
let best = null;
|
|
31811
|
+
for (let i = 0; i < SIDES; i++) {
|
|
31812
|
+
const turn = 2 * Math.PI * i / SIDES;
|
|
31813
|
+
const side = add(scale(first, Math.cos(turn)), scale(second, Math.sin(turn)));
|
|
31814
|
+
const direction = add(scale(side, Math.cos(BALL_ELEVATION_RAD)), scale(pole, Math.sin(BALL_ELEVATION_RAD)));
|
|
31815
|
+
const away = ballDistance(ball, radius, direction, view);
|
|
31816
|
+
if (away !== null && (!best || away < best.distance)) best = {
|
|
31817
|
+
direction,
|
|
31818
|
+
distance: away
|
|
31819
|
+
};
|
|
31820
|
+
}
|
|
31821
|
+
if (!best) return null;
|
|
31822
|
+
const [x, y, z] = scale(best.direction, best.distance);
|
|
31823
|
+
return {
|
|
31824
|
+
offset: [
|
|
31825
|
+
x,
|
|
31826
|
+
y,
|
|
31827
|
+
z
|
|
31828
|
+
],
|
|
31829
|
+
kept: false
|
|
31830
|
+
};
|
|
31831
|
+
}
|
|
31832
|
+
function ballDistance(center, radius, back, view) {
|
|
31833
|
+
const right = cross$1(view.up, back);
|
|
31834
|
+
const width = norm(right);
|
|
31835
|
+
if (!(width > SIDE_MIN)) return null;
|
|
31836
|
+
const across = scale(right, 1 / width);
|
|
31837
|
+
const up = cross$1(back, across);
|
|
31838
|
+
const tanY = Math.tan(view.fovDeg * Math.PI / 360);
|
|
31839
|
+
const tanX = tanY * view.aspect;
|
|
31840
|
+
const room = (share, tan) => Math.max(share, BALL_ROOM_MIN) * tan;
|
|
31841
|
+
const x = dot$1(center, across);
|
|
31842
|
+
const y = dot$1(center, up);
|
|
31843
|
+
return Math.max((x + radius) / room(view.open.right, tanX), (radius - x) / room(view.open.left, tanX), (y + radius) / room(view.open.top, tanY), (radius - y) / room(view.open.bottom, tanY)) + dot$1(center, back) + radius;
|
|
31844
|
+
}
|
|
31637
31845
|
function distanceAt(length, tanAngle) {
|
|
31638
31846
|
return length * (Math.cos(VANTAGE_RAD) + Math.sin(VANTAGE_RAD) / tanAngle);
|
|
31639
31847
|
}
|
|
@@ -32145,6 +32353,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32145
32353
|
atRest = false;
|
|
32146
32354
|
focusLoads = 0;
|
|
32147
32355
|
unsubscribeBodiesAdded = null;
|
|
32356
|
+
unsubscribeNutation = null;
|
|
32148
32357
|
_trailView = {
|
|
32149
32358
|
camX: 0,
|
|
32150
32359
|
camY: 0,
|
|
@@ -32261,7 +32470,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32261
32470
|
this.controls = new OrbitControls(this.camera, canvas);
|
|
32262
32471
|
this.controls.enableDamping = true;
|
|
32263
32472
|
this.orbitDampingFactor = orbitDamping(sceneSettings().resolvedReducedMotion);
|
|
32264
|
-
this.controls.minDistance = focusBody ? minCameraDistance(focusBody) :
|
|
32473
|
+
this.controls.minDistance = focusBody ? minCameraDistance(focusBody) : POINT_MIN_DISTANCE;
|
|
32265
32474
|
Object.defineProperty(this.controls, "maxDistance", { get: () => this.maxCameraDistance() });
|
|
32266
32475
|
this.controls.target.set(0, 0, 0);
|
|
32267
32476
|
this.controls.update();
|
|
@@ -32272,6 +32481,13 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32272
32481
|
for (const type of SceneRenderer.INPUT_EVENTS) canvas.addEventListener(type, this.invalidate, { passive: true });
|
|
32273
32482
|
window.addEventListener("keydown", this.invalidate);
|
|
32274
32483
|
this.unsubscribeBodiesAdded = ctx.bodies.onBodiesAdded(this.invalidate);
|
|
32484
|
+
this.unsubscribeNutation = onSystemsGlobalSettled(() => {
|
|
32485
|
+
for (const body of joinAwaitedNutation(ctx)) {
|
|
32486
|
+
const bo = this.bodyObjects.get(body.data.id);
|
|
32487
|
+
if (bo) turnBody(body, bo, this.sceneJd);
|
|
32488
|
+
}
|
|
32489
|
+
this.invalidate();
|
|
32490
|
+
});
|
|
32275
32491
|
this.cameraUp = new CameraUpController(this.camera, this.controls, ctx, (id) => this.isLanded(id));
|
|
32276
32492
|
this.systemData = new SystemDataLoader(this.scene, ctx, this.renderer, this.textureLoader, this.bodyObjects, clock, () => this.focusController.reapplyInitialViewIfPending());
|
|
32277
32493
|
this.focusController = new FocusController({
|
|
@@ -32341,6 +32557,12 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32341
32557
|
}
|
|
32342
32558
|
setTravelPath(plan, options = [], hazards = [], steps = []) {
|
|
32343
32559
|
this.travelPath.set(plan, options, hazards, steps);
|
|
32560
|
+
const centerId = this.travelPath.centerId;
|
|
32561
|
+
this.ctx.bodies.pinTripCentre(centerId);
|
|
32562
|
+
if (centerId !== null && !this.ctx.getBody(centerId)) {
|
|
32563
|
+
const at = jdToDate(this.sceneJd);
|
|
32564
|
+
this.ctx.ensureBody(centerId, at).catch((e) => console.warn(`[map] no centre ${centerId} for the trip:`, e));
|
|
32565
|
+
}
|
|
32344
32566
|
this.refreshTravelPath();
|
|
32345
32567
|
}
|
|
32346
32568
|
setTravelHover(id) {
|
|
@@ -32387,7 +32609,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32387
32609
|
}
|
|
32388
32610
|
this.travelPath.setVisible(true);
|
|
32389
32611
|
this.travelPath.setClock(this.craftJd);
|
|
32390
|
-
this.travelPath.reposition(center, this.focus.focusTruePos, (id) => this.where(id));
|
|
32612
|
+
this.travelPath.reposition(center, this.focus.focusTruePos, (id) => this.where(id), this.camera);
|
|
32391
32613
|
this.travelPath.updateCameraOffset(this.camera.position);
|
|
32392
32614
|
this.reserveTravelLabelSpace();
|
|
32393
32615
|
}
|
|
@@ -32554,13 +32776,10 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32554
32776
|
this.focusController.panCameraToBody(body, distance);
|
|
32555
32777
|
return this.focus.focusDurationMs;
|
|
32556
32778
|
}
|
|
32557
|
-
|
|
32558
|
-
const origin = this.where(originId);
|
|
32559
|
-
const target = this.where(targetId);
|
|
32560
|
-
if (!origin || !target) return null;
|
|
32779
|
+
tripCamera(insets) {
|
|
32561
32780
|
const { clientWidth, clientHeight } = this.renderer.domElement;
|
|
32562
32781
|
const { up } = this.camera;
|
|
32563
|
-
return
|
|
32782
|
+
return {
|
|
32564
32783
|
fovDeg: this.camera.fov,
|
|
32565
32784
|
aspect: this.camera.aspect,
|
|
32566
32785
|
up: [
|
|
@@ -32569,7 +32788,22 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32569
32788
|
up.z
|
|
32570
32789
|
],
|
|
32571
32790
|
open: openArea(insets, clientWidth || 1, clientHeight || 1)
|
|
32572
|
-
}
|
|
32791
|
+
};
|
|
32792
|
+
}
|
|
32793
|
+
tripVantage(originId, targetId, insets, ringKm = 0) {
|
|
32794
|
+
const origin = this.where(originId);
|
|
32795
|
+
const target = this.where(targetId);
|
|
32796
|
+
if (!origin || !target) return null;
|
|
32797
|
+
const camera = this.focusController.cameraTruePos();
|
|
32798
|
+
return tripVantage(target, origin, camera, this.tripCamera(insets), kmToScene(ringKm));
|
|
32799
|
+
}
|
|
32800
|
+
ballVantage(targetId, centerId, radiusKm, normal, insets) {
|
|
32801
|
+
const target = this.where(targetId);
|
|
32802
|
+
const center = this.where(centerId);
|
|
32803
|
+
if (!target || !center) return null;
|
|
32804
|
+
const camera = this.focusController.cameraTruePos();
|
|
32805
|
+
const radius = kmToScene(radiusKm);
|
|
32806
|
+
return ballVantage(target, center, radius, normal, camera, this.tripCamera(insets));
|
|
32573
32807
|
}
|
|
32574
32808
|
viewFrom(id, offset) {
|
|
32575
32809
|
const body = this.ctx.getBody(id);
|
|
@@ -32787,9 +33021,26 @@ var SceneRenderer = class SceneRenderer {
|
|
|
32787
33021
|
fovDeg: this.camera.fov,
|
|
32788
33022
|
dtMs,
|
|
32789
33023
|
outside: 1 - this.starfield.skyboxShare(),
|
|
32790
|
-
focused: this.focusController.current
|
|
33024
|
+
focused: this.focusController.current ?? this.tripEndInView()
|
|
32791
33025
|
});
|
|
32792
33026
|
}
|
|
33027
|
+
tripEndInView() {
|
|
33028
|
+
if (!this.travelFocus) return void 0;
|
|
33029
|
+
const at = this.focus.focusTruePos;
|
|
33030
|
+
let nearest;
|
|
33031
|
+
let nearestDistance = Infinity;
|
|
33032
|
+
for (const id of this.travelPath.endIds) {
|
|
33033
|
+
const body = this.ctx.getBody(id);
|
|
33034
|
+
const place = body && this.whereBody(body);
|
|
33035
|
+
if (!place) continue;
|
|
33036
|
+
const distance = f64dist(at, place);
|
|
33037
|
+
if (distance < nearestDistance) {
|
|
33038
|
+
nearest = body;
|
|
33039
|
+
nearestDistance = distance;
|
|
33040
|
+
}
|
|
33041
|
+
}
|
|
33042
|
+
return nearest;
|
|
33043
|
+
}
|
|
32793
33044
|
updateSystemShare(dtMs) {
|
|
32794
33045
|
const visibility = this.ctx.visibility;
|
|
32795
33046
|
if (!this.viewOpen) {
|
|
@@ -33797,11 +34048,12 @@ var SceneRenderer = class SceneRenderer {
|
|
|
33797
34048
|
for (const type of SceneRenderer.INPUT_EVENTS) this.canvas.removeEventListener(type, this.invalidate);
|
|
33798
34049
|
window.removeEventListener("keydown", this.invalidate);
|
|
33799
34050
|
this.unsubscribeBodiesAdded?.();
|
|
34051
|
+
this.unsubscribeNutation?.();
|
|
33800
34052
|
this.controls.dispose();
|
|
33801
34053
|
this.haloDebug.dispose();
|
|
33802
34054
|
this.travelPath.dispose();
|
|
33803
34055
|
this.extensions.dispose();
|
|
33804
|
-
for (const bo of this.bodyObjects.values())
|
|
34056
|
+
for (const bo of this.bodyObjects.values()) unloadBodyModel(bo);
|
|
33805
34057
|
for (const bo of this.bodyObjects.values()) {
|
|
33806
34058
|
disposeTileOverlay(bo);
|
|
33807
34059
|
disposeReliefTiles(bo);
|
|
@@ -35965,7 +36217,9 @@ var MapObjectCollection = class {
|
|
|
35965
36217
|
};
|
|
35966
36218
|
|
|
35967
36219
|
// src/lib/scene/extensions/measure.ts
|
|
36220
|
+
var turnsYet = (anchor, ctx) => isSurface(anchor) && ctx.bodies.nutationAwaited.has(anchor.body);
|
|
35968
36221
|
function anchorOffsetKm(a, b, ctx, jd) {
|
|
36222
|
+
if (turnsYet(a, ctx) || turnsYet(b, ctx)) return null;
|
|
35969
36223
|
const from = resolveAnchor(a, ctx, jd);
|
|
35970
36224
|
const to = resolveAnchor(b, ctx, jd);
|
|
35971
36225
|
if (!from || !to) return null;
|
|
@@ -36005,6 +36259,7 @@ function subsolarPoint$1(id, ctx, jd) {
|
|
|
36005
36259
|
const sun = ctx.getBody(SUN_ID);
|
|
36006
36260
|
if (!body || !sun || body === sun) return null;
|
|
36007
36261
|
if (!body.orientation && !ctx.bodies.orientationRead.has(id)) return null;
|
|
36262
|
+
if (ctx.bodies.nutationAwaited.has(id)) return null;
|
|
36008
36263
|
const centre = bodyCentre(body, ctx, jd);
|
|
36009
36264
|
const sunCentre = bodyCentre(sun, ctx, jd);
|
|
36010
36265
|
return centre && sunCentre && subsolarAt(body, centre, sunCentre, jd);
|
|
@@ -36967,9 +37222,16 @@ var SpaceMap = class {
|
|
|
36967
37222
|
snapToBodyFacing(id, towardId, elevationDeg, distance) {
|
|
36968
37223
|
this.renderer?.snapToBodyFacing(id, towardId, elevationDeg, distance);
|
|
36969
37224
|
}
|
|
36970
|
-
tripView(originId, targetId, insets) {
|
|
37225
|
+
tripView(originId, targetId, insets, ringKm = 0) {
|
|
37226
|
+
return this.vantageView(this.renderer?.tripVantage(originId, targetId, insets, ringKm));
|
|
37227
|
+
}
|
|
37228
|
+
tripBallView(targetId, ball, insets) {
|
|
37229
|
+
const normal = this.ctx.exoplanets.planeNormal(targetId);
|
|
37230
|
+
const { centerId, radiusKm } = ball;
|
|
37231
|
+
return this.vantageView(this.renderer?.ballVantage(targetId, centerId, radiusKm, normal, insets));
|
|
37232
|
+
}
|
|
37233
|
+
vantageView(vantage) {
|
|
36971
37234
|
const renderer = this.renderer;
|
|
36972
|
-
const vantage = renderer?.tripVantage(originId, targetId, insets);
|
|
36973
37235
|
if (!renderer || !vantage || vantage.kept) return null;
|
|
36974
37236
|
return (body) => renderer.viewFrom(body.data.id, vantage.offset) ?? {};
|
|
36975
37237
|
}
|