spacemap 0.1.17 → 0.1.19
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 +19 -20
- package/dist/spacemap.iife.js +223 -152
- package/dist/spacemap.js +223 -152
- package/index.d.ts +45 -15
- package/index.js +2673 -192
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*! spacemap SDK — https://spacemap.co — Mozilla Public License 2.0. Bundles MIT and ISC libraries: THIRD_PARTY_NOTICES.txt in the spacemap npm package. */
|
|
2
|
-
import { ACESFilmicToneMapping, AdditiveBlending, AmbientLight, ArrowHelper, BackSide, Box3, BufferAttribute, BufferGeometry, CanvasTexture, ClampToEdgeWrapping, Color, CubeTexture, CustomBlending, DataArrayTexture, DataTexture, DataUtils, DepthTexture, DirectionalLight, DoubleSide, Euler, Float32BufferAttribute, FloatType, FrontSide, Group, HalfFloatType, InstancedBufferAttribute, InstancedMesh, Line, LineBasicMaterial, LineLoop, LinearFilter, LinearMipmapLinearFilter, LinearSRGBColorSpace, MathUtils, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, NoBlending, NoColorSpace, NormalBlending, OneFactor, OneMinusSrcAlphaFactor, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, RGBAFormat, RGFormat, RawShaderMaterial, Raycaster, RedFormat, RepeatWrapping, RingGeometry, SRGBColorSpace, Scene, ShaderChunk, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapeUtils, SphereGeometry, Sprite, SpriteMaterial, TOUCH, Texture, TextureLoader, Uint16BufferAttribute, UnsignedByteType, Vector2, Vector3, Vector4, WebGLRenderTarget, WebGLRenderer } from "three";
|
|
2
|
+
import { ACESFilmicToneMapping, AddEquation, AdditiveBlending, AmbientLight, ArrowHelper, BackSide, Box3, BufferAttribute, BufferGeometry, CanvasTexture, ClampToEdgeWrapping, Color, CubeTexture, CustomBlending, DataArrayTexture, DataTexture, DataUtils, DepthTexture, DirectionalLight, DoubleSide, Euler, Float32BufferAttribute, FloatType, FrontSide, Group, HalfFloatType, InstancedBufferAttribute, InstancedMesh, Line, LineBasicMaterial, LineLoop, LinearFilter, LinearMipmapLinearFilter, LinearSRGBColorSpace, MathUtils, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, NoBlending, NoColorSpace, NormalBlending, OneFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, RGBAFormat, RGFormat, RawShaderMaterial, Raycaster, RedFormat, RepeatWrapping, RingGeometry, SRGBColorSpace, Scene, ShaderChunk, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapeUtils, SphereGeometry, Sprite, SpriteMaterial, TOUCH, Texture, TextureLoader, Uint16BufferAttribute, UnsignedByteType, Vector2, Vector3, Vector4, WebGLRenderTarget, WebGLRenderer } from "three";
|
|
3
3
|
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
|
4
4
|
import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer.js";
|
|
5
5
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -22,6 +22,7 @@ var DEFAULT_HOST = {
|
|
|
22
22
|
imagesUrl: "https://images.spacemap.co",
|
|
23
23
|
liveUrl: "https://live.spacemap.co",
|
|
24
24
|
tilesUrl: "",
|
|
25
|
+
extrasolarUrl: "",
|
|
25
26
|
locale: () => "en",
|
|
26
27
|
messages: {
|
|
27
28
|
body_note_no_model: () => "no model available",
|
|
@@ -31,6 +32,7 @@ var DEFAULT_HOST = {
|
|
|
31
32
|
scene_canvas_label: () => "Interactive 3D map of the Solar System",
|
|
32
33
|
attribution_orbits: () => "Orbits",
|
|
33
34
|
attribution_imagery: () => "Imagery",
|
|
35
|
+
attribution_stars: () => "Stars",
|
|
34
36
|
credits_see_all: () => "See full credits",
|
|
35
37
|
layer_surface: () => "Surface",
|
|
36
38
|
layer_clouds: () => "Clouds",
|
|
@@ -69,6 +71,7 @@ function configureHost(overrides) {
|
|
|
69
71
|
current$1.imagesUrl = current$1.imagesUrl.replace(/\/$/, "");
|
|
70
72
|
current$1.liveUrl = current$1.liveUrl.replace(/\/$/, "");
|
|
71
73
|
current$1.tilesUrl = current$1.tilesUrl.replace(/\/$/, "");
|
|
74
|
+
current$1.extrasolarUrl = current$1.extrasolarUrl.replace(/\/$/, "");
|
|
72
75
|
}
|
|
73
76
|
function host() {
|
|
74
77
|
return current$1;
|
|
@@ -3003,10 +3006,10 @@ function buildVersionedUrl(base, path, cls) {
|
|
|
3003
3006
|
|
|
3004
3007
|
//#endregion
|
|
3005
3008
|
//#region src/lib/fetch/position/format.ts
|
|
3006
|
-
var MAGIC$
|
|
3009
|
+
var MAGIC$3 = 1346456915;
|
|
3007
3010
|
var COMMON_HEADER_SIZE = 24;
|
|
3008
3011
|
var EXTENSION_SIZE = 8;
|
|
3009
|
-
var HEADER_SIZE$
|
|
3012
|
+
var HEADER_SIZE$3 = 24 + 8;
|
|
3010
3013
|
var labelsUrl = (lang) => `${dataBase()}/v1/labels/${lang}.gz`;
|
|
3011
3014
|
function zoomSegment(zoom) {
|
|
3012
3015
|
return zoom === null ? "" : `${zoom}/`;
|
|
@@ -3073,6 +3076,9 @@ function buildObjectId(idType, value) {
|
|
|
3073
3076
|
|
|
3074
3077
|
//#endregion
|
|
3075
3078
|
//#region src/lib/types/objects.ts
|
|
3079
|
+
function isExtrasolar(body) {
|
|
3080
|
+
return body.data.objectType === ObjectType.EXTRASOLAR;
|
|
3081
|
+
}
|
|
3076
3082
|
function isSurfaceFeature(body) {
|
|
3077
3083
|
return body.data.objectType === ObjectType.SURFACE_FEATURE;
|
|
3078
3084
|
}
|
|
@@ -3094,6 +3100,7 @@ var ObjectType = /* @__PURE__ */ function(ObjectType) {
|
|
|
3094
3100
|
ObjectType[ObjectType["DEBRIS"] = 14] = "DEBRIS";
|
|
3095
3101
|
ObjectType[ObjectType["UNDOCUMENTED"] = 15] = "UNDOCUMENTED";
|
|
3096
3102
|
ObjectType[ObjectType["SURFACE_FEATURE"] = 16] = "SURFACE_FEATURE";
|
|
3103
|
+
ObjectType[ObjectType["EXTRASOLAR"] = 17] = "EXTRASOLAR";
|
|
3097
3104
|
return ObjectType;
|
|
3098
3105
|
}({});
|
|
3099
3106
|
var ZONE_A_RANGE = {
|
|
@@ -3177,7 +3184,8 @@ var NOMINAL_RADIUS_KM_BY_SOURCE = {
|
|
|
3177
3184
|
var NOMINAL_RADIUS_KM_BY_TYPE = {
|
|
3178
3185
|
[ObjectType.SPACECRAFT]: .005,
|
|
3179
3186
|
[ObjectType.DEBRIS]: .005,
|
|
3180
|
-
[ObjectType.SURFACE_FEATURE]: .001
|
|
3187
|
+
[ObjectType.SURFACE_FEATURE]: .001,
|
|
3188
|
+
[ObjectType.EXTRASOLAR]: 695700
|
|
3181
3189
|
};
|
|
3182
3190
|
var NOMINAL_RADIUS_KM_DEFAULT = .1;
|
|
3183
3191
|
function effectiveRadiusKm(data) {
|
|
@@ -3292,6 +3300,13 @@ var CreditsStore = class {
|
|
|
3292
3300
|
set skybox(value) {
|
|
3293
3301
|
set(this.#skybox, value, true);
|
|
3294
3302
|
}
|
|
3303
|
+
#stars = state([]);
|
|
3304
|
+
get stars() {
|
|
3305
|
+
return get(this.#stars);
|
|
3306
|
+
}
|
|
3307
|
+
set stars(value) {
|
|
3308
|
+
set(this.#stars, value);
|
|
3309
|
+
}
|
|
3295
3310
|
#orbitSources = state(proxy(/* @__PURE__ */ new Set()));
|
|
3296
3311
|
get orbitSources() {
|
|
3297
3312
|
return get(this.#orbitSources);
|
|
@@ -3342,6 +3357,9 @@ var CreditsStore = class {
|
|
|
3342
3357
|
imageryOf(layer) {
|
|
3343
3358
|
return [...this.imagery.values()].filter((c) => c.layer === layer);
|
|
3344
3359
|
}
|
|
3360
|
+
setStars(credits) {
|
|
3361
|
+
if (this.stars.length === 0) this.stars = credits;
|
|
3362
|
+
}
|
|
3345
3363
|
registerOrientation(credit) {
|
|
3346
3364
|
if (this.orientation.has(credit.bodyId)) return;
|
|
3347
3365
|
this.orientation.set(credit.bodyId, credit);
|
|
@@ -3446,7 +3464,7 @@ function rowKey(cols, i) {
|
|
|
3446
3464
|
return objectKey(cols.idType, cols.id[i]);
|
|
3447
3465
|
}
|
|
3448
3466
|
function parseSharedColumns(buffer, rowCount) {
|
|
3449
|
-
let offset = HEADER_SIZE$
|
|
3467
|
+
let offset = HEADER_SIZE$3;
|
|
3450
3468
|
const id = new Int32Array(buffer, offset, rowCount);
|
|
3451
3469
|
offset += align8(rowCount * 4);
|
|
3452
3470
|
const objectType = new Uint8Array(buffer, offset, rowCount);
|
|
@@ -5315,6 +5333,12 @@ function equatorialToScene(xEq, yEq, zEq, out) {
|
|
|
5315
5333
|
target.z = -(yEq * COS_OBL$1 + zEq * SIN_OBL$1);
|
|
5316
5334
|
return target;
|
|
5317
5335
|
}
|
|
5336
|
+
function sceneToEquatorial(x, y, z, out) {
|
|
5337
|
+
out.x = x;
|
|
5338
|
+
out.y = -y * SIN_OBL$1 - z * COS_OBL$1;
|
|
5339
|
+
out.z = y * COS_OBL$1 - z * SIN_OBL$1;
|
|
5340
|
+
return out;
|
|
5341
|
+
}
|
|
5318
5342
|
|
|
5319
5343
|
//#endregion
|
|
5320
5344
|
//#region src/lib/time/jd.ts
|
|
@@ -6385,7 +6409,7 @@ function resolveBodyColor(data) {
|
|
|
6385
6409
|
const planetId = `naif-${data.id.replace("naif-", "")}99`;
|
|
6386
6410
|
if (BODY_COLORS[planetId]) return BODY_COLORS[planetId];
|
|
6387
6411
|
}
|
|
6388
|
-
if (objectType === ObjectType.STAR) return TYPE_COLOR_STAR;
|
|
6412
|
+
if (objectType === ObjectType.STAR || objectType === ObjectType.EXTRASOLAR) return TYPE_COLOR_STAR;
|
|
6389
6413
|
if (objectType === ObjectType.PLANET || objectType === ObjectType.DWARF_PLANET) return TYPE_COLOR_PLANET;
|
|
6390
6414
|
if (objectType === ObjectType.MOON) return TYPE_COLOR_MOON;
|
|
6391
6415
|
if (isAsteroid(objectType)) return TYPE_COLOR_ASTEROID;
|
|
@@ -6724,12 +6748,14 @@ function adoptRow(held, fresh) {
|
|
|
6724
6748
|
held.orbitCenterId = fresh.orbitCenterId ?? held.orbitCenterId;
|
|
6725
6749
|
held.trailAnchorId = fresh.trailAnchorId;
|
|
6726
6750
|
}
|
|
6751
|
+
var EXTRASOLAR_KEPT = 64;
|
|
6727
6752
|
var BodyIndex = class {
|
|
6728
6753
|
bodiesById = /* @__PURE__ */ new Map();
|
|
6729
6754
|
majorBodies = [];
|
|
6730
6755
|
asteroidBodiesByZone = /* @__PURE__ */ new Map();
|
|
6731
6756
|
spacecraftByParent = /* @__PURE__ */ new Map();
|
|
6732
6757
|
focusFeature = null;
|
|
6758
|
+
extrasolar = /* @__PURE__ */ new Map();
|
|
6733
6759
|
dirtyAsteroidZones = /* @__PURE__ */ new Set();
|
|
6734
6760
|
dirtySpacecraftGroups = /* @__PURE__ */ new Set();
|
|
6735
6761
|
minorStreaming = false;
|
|
@@ -6764,6 +6790,8 @@ var BodyIndex = class {
|
|
|
6764
6790
|
const major = this.bodiesById.get(id);
|
|
6765
6791
|
if (major) return major;
|
|
6766
6792
|
if (this.focusFeature?.data.id === id) return this.focusFeature;
|
|
6793
|
+
const star = this.extrasolar.get(id);
|
|
6794
|
+
if (star) return star;
|
|
6767
6795
|
if (zone !== void 0) return this.spacecraftByParent.get(zone)?.get(id) ?? this.asteroidBodiesByZone.get(zone)?.get(id);
|
|
6768
6796
|
for (const byId of this.spacecraftByParent.values()) {
|
|
6769
6797
|
const hit = byId.get(id);
|
|
@@ -6774,6 +6802,13 @@ var BodyIndex = class {
|
|
|
6774
6802
|
if (hit) return hit;
|
|
6775
6803
|
}
|
|
6776
6804
|
}
|
|
6805
|
+
addExtrasolar(body, ids = []) {
|
|
6806
|
+
for (const id of [body.data.id, ...ids]) {
|
|
6807
|
+
this.extrasolar.delete(id);
|
|
6808
|
+
this.extrasolar.set(id, body);
|
|
6809
|
+
}
|
|
6810
|
+
while (this.extrasolar.size > EXTRASOLAR_KEPT) this.extrasolar.delete(this.extrasolar.keys().next().value);
|
|
6811
|
+
}
|
|
6777
6812
|
addBodies(bodies) {
|
|
6778
6813
|
let added = false;
|
|
6779
6814
|
for (const b of bodies) {
|
|
@@ -7210,6 +7245,7 @@ var MAP_LAYERS = [
|
|
|
7210
7245
|
"spacecraft",
|
|
7211
7246
|
"satellites",
|
|
7212
7247
|
"debris",
|
|
7248
|
+
"starfield",
|
|
7213
7249
|
"orbits",
|
|
7214
7250
|
"labels",
|
|
7215
7251
|
"halos",
|
|
@@ -7358,6 +7394,117 @@ function computeVisibilityFromRatio(ratio, thresholds, focusedMultiplier, isFocu
|
|
|
7358
7394
|
return VISIBILITY.HIDE;
|
|
7359
7395
|
}
|
|
7360
7396
|
|
|
7397
|
+
//#endregion
|
|
7398
|
+
//#region src/lib/scene/visibility/switch-fade.ts
|
|
7399
|
+
var SYSTEM_SWITCH_FADE_MS = 300;
|
|
7400
|
+
var SwitchFade = class {
|
|
7401
|
+
progress;
|
|
7402
|
+
target;
|
|
7403
|
+
started;
|
|
7404
|
+
constructor(on, durationMs = 300) {
|
|
7405
|
+
this.durationMs = durationMs;
|
|
7406
|
+
this.started = on !== void 0;
|
|
7407
|
+
this.progress = this.target = on ? 1 : 0;
|
|
7408
|
+
}
|
|
7409
|
+
step(on, dtMs) {
|
|
7410
|
+
this.target = on ? 1 : 0;
|
|
7411
|
+
if (!this.started) {
|
|
7412
|
+
this.started = true;
|
|
7413
|
+
this.progress = this.target;
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7416
|
+
const delta = dtMs / this.durationMs;
|
|
7417
|
+
this.progress = this.target === 1 ? Math.min(this.progress + delta, 1) : Math.max(this.progress - delta, 0);
|
|
7418
|
+
}
|
|
7419
|
+
get share() {
|
|
7420
|
+
const p = this.progress;
|
|
7421
|
+
return p * p * (3 - 2 * p);
|
|
7422
|
+
}
|
|
7423
|
+
get fading() {
|
|
7424
|
+
return this.progress !== this.target;
|
|
7425
|
+
}
|
|
7426
|
+
};
|
|
7427
|
+
|
|
7428
|
+
//#endregion
|
|
7429
|
+
//#region src/lib/scene/visibility/zone-fade.ts
|
|
7430
|
+
var ZoneFades = class {
|
|
7431
|
+
main = new SwitchFade(true);
|
|
7432
|
+
systems = [];
|
|
7433
|
+
stepped = false;
|
|
7434
|
+
step(activeSystemId, dtMs) {
|
|
7435
|
+
if (!this.stepped) {
|
|
7436
|
+
this.stepped = true;
|
|
7437
|
+
this.main = new SwitchFade(activeSystemId === null);
|
|
7438
|
+
if (activeSystemId !== null) this.systems.push({
|
|
7439
|
+
id: activeSystemId,
|
|
7440
|
+
fade: new SwitchFade(true)
|
|
7441
|
+
});
|
|
7442
|
+
return;
|
|
7443
|
+
}
|
|
7444
|
+
this.main.step(activeSystemId === null, dtMs);
|
|
7445
|
+
if (activeSystemId !== null && !this.systems.some((s) => s.id === activeSystemId)) this.systems.push({
|
|
7446
|
+
id: activeSystemId,
|
|
7447
|
+
fade: new SwitchFade(false)
|
|
7448
|
+
});
|
|
7449
|
+
for (let i = this.systems.length - 1; i >= 0; i--) {
|
|
7450
|
+
const { id, fade } = this.systems[i];
|
|
7451
|
+
fade.step(id === activeSystemId, dtMs);
|
|
7452
|
+
if (fade.share === 0 && !fade.fading) this.systems.splice(i, 1);
|
|
7453
|
+
}
|
|
7454
|
+
}
|
|
7455
|
+
get mainShare() {
|
|
7456
|
+
return this.main.share;
|
|
7457
|
+
}
|
|
7458
|
+
get fading() {
|
|
7459
|
+
return this.main.fading || this.systems.some((s) => s.fade.fading);
|
|
7460
|
+
}
|
|
7461
|
+
};
|
|
7462
|
+
|
|
7463
|
+
//#endregion
|
|
7464
|
+
//#region src/lib/scene/visibility/threshold-fade.ts
|
|
7465
|
+
var VisibilityThresholdFades = class {
|
|
7466
|
+
labels = /* @__PURE__ */ new Map();
|
|
7467
|
+
orbits = /* @__PURE__ */ new Map();
|
|
7468
|
+
frame = 0;
|
|
7469
|
+
dtMs = 0;
|
|
7470
|
+
beginFrame(dtMs) {
|
|
7471
|
+
this.frame++;
|
|
7472
|
+
this.dtMs = dtMs;
|
|
7473
|
+
this.prune(this.labels);
|
|
7474
|
+
this.prune(this.orbits);
|
|
7475
|
+
}
|
|
7476
|
+
labelShare(id, shown) {
|
|
7477
|
+
return this.share(this.labels, id, shown);
|
|
7478
|
+
}
|
|
7479
|
+
orbitShare(id, shown) {
|
|
7480
|
+
return this.share(this.orbits, id, shown);
|
|
7481
|
+
}
|
|
7482
|
+
get fading() {
|
|
7483
|
+
return this.anyFading(this.labels) || this.anyFading(this.orbits);
|
|
7484
|
+
}
|
|
7485
|
+
share(entries, id, shown) {
|
|
7486
|
+
let entry = entries.get(id);
|
|
7487
|
+
if (!entry) {
|
|
7488
|
+
entry = {
|
|
7489
|
+
fade: new SwitchFade(shown, 100),
|
|
7490
|
+
frame: this.frame
|
|
7491
|
+
};
|
|
7492
|
+
entries.set(id, entry);
|
|
7493
|
+
} else if (entry.frame !== this.frame) {
|
|
7494
|
+
entry.frame = this.frame;
|
|
7495
|
+
entry.fade.step(shown, this.dtMs);
|
|
7496
|
+
}
|
|
7497
|
+
return entry.fade.share;
|
|
7498
|
+
}
|
|
7499
|
+
anyFading(entries) {
|
|
7500
|
+
for (const entry of entries.values()) if (entry.frame === this.frame && entry.fade.fading) return true;
|
|
7501
|
+
return false;
|
|
7502
|
+
}
|
|
7503
|
+
prune(entries) {
|
|
7504
|
+
for (const [id, entry] of entries) if (entry.frame < this.frame - 1) entries.delete(id);
|
|
7505
|
+
}
|
|
7506
|
+
};
|
|
7507
|
+
|
|
7361
7508
|
//#endregion
|
|
7362
7509
|
//#region src/lib/scene/visibility/controller.svelte.ts
|
|
7363
7510
|
var ALL_LAYERS = new LayerSet();
|
|
@@ -7383,6 +7530,10 @@ var VisibilityController = class {
|
|
|
7383
7530
|
}
|
|
7384
7531
|
activeSystemId = null;
|
|
7385
7532
|
isZoomedIn = false;
|
|
7533
|
+
zones = new ZoneFades();
|
|
7534
|
+
thresholds = new VisibilityThresholdFades();
|
|
7535
|
+
systemShare = 1;
|
|
7536
|
+
starShare = 0;
|
|
7386
7537
|
focusedBodyIdPlain = SUN_ID;
|
|
7387
7538
|
focusedSystemIdPlain = null;
|
|
7388
7539
|
travelSystemActive = false;
|
|
@@ -7429,6 +7580,19 @@ var VisibilityController = class {
|
|
|
7429
7580
|
this.recomputeFullMoons();
|
|
7430
7581
|
}
|
|
7431
7582
|
}
|
|
7583
|
+
stepZones(dtMs) {
|
|
7584
|
+
this.zones.step(this.activeSystemId, dtMs);
|
|
7585
|
+
this.thresholds.beginFrame(dtMs);
|
|
7586
|
+
}
|
|
7587
|
+
labelThresholdShare(id, shown) {
|
|
7588
|
+
return this.thresholds.labelShare(id, shown);
|
|
7589
|
+
}
|
|
7590
|
+
orbitThresholdShare(id, shown) {
|
|
7591
|
+
return this.thresholds.orbitShare(id, shown);
|
|
7592
|
+
}
|
|
7593
|
+
get fading() {
|
|
7594
|
+
return this.zones.fading || this.thresholds.fading;
|
|
7595
|
+
}
|
|
7432
7596
|
refreshProbeFocusedSystem() {
|
|
7433
7597
|
const ps = this.getProbeStore();
|
|
7434
7598
|
if (!ps) return;
|
|
@@ -7442,6 +7606,7 @@ var VisibilityController = class {
|
|
|
7442
7606
|
this.lastRecomputeDist = -1;
|
|
7443
7607
|
}
|
|
7444
7608
|
resolveSystemId(body) {
|
|
7609
|
+
if (isExtrasolar(body)) return null;
|
|
7445
7610
|
const isSystemBarycenter = body.data.objectType === ObjectType.BARYCENTER && isTopLevelParent(body.data.parentId) && !isTopLevelParent(body.data.id);
|
|
7446
7611
|
const isTopLevel = body.data.objectType === ObjectType.STAR || !isSystemBarycenter && isTopLevelParent(body.data.parentId);
|
|
7447
7612
|
if (isSystemBarycenter) return body.data.id;
|
|
@@ -7474,6 +7639,7 @@ var VisibilityController = class {
|
|
|
7474
7639
|
this.recomputeFullMoons();
|
|
7475
7640
|
}
|
|
7476
7641
|
getMoonVisibility(moon) {
|
|
7642
|
+
if (this.systemShare === 0) return VISIBILITY.HIDE;
|
|
7477
7643
|
if (this.getLayers().hidesBody(moon.data.objectType, moon.data.parentId)) return VISIBILITY.HIDE;
|
|
7478
7644
|
const cached = this.moonVisibilityCache.get(moon.data.id);
|
|
7479
7645
|
if (cached !== void 0) return cached;
|
|
@@ -7498,6 +7664,7 @@ var VisibilityController = class {
|
|
|
7498
7664
|
return this.pinned.has(id);
|
|
7499
7665
|
}
|
|
7500
7666
|
getPlanetVisibility(body, camDistThreeJS) {
|
|
7667
|
+
if (this.systemShare === 0 && body.data.objectType !== ObjectType.STAR) return VISIBILITY.HIDE;
|
|
7501
7668
|
const pinned = this.pinned.has(body.data.id);
|
|
7502
7669
|
const vis = this.ratioVisibility(body, camDistThreeJS, pinned);
|
|
7503
7670
|
return pinned && vis !== VISIBILITY.CLOSE ? VISIBILITY.FULL : vis;
|
|
@@ -7556,15 +7723,24 @@ var VisibilityController = class {
|
|
|
7556
7723
|
return sysNaif !== null && this.isInFocusedSystem(`naif-${sysNaif}`);
|
|
7557
7724
|
}
|
|
7558
7725
|
hasFullRendering(body) {
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
const
|
|
7563
|
-
if (
|
|
7564
|
-
const
|
|
7565
|
-
|
|
7726
|
+
return this.fullRenderingShare(body) > 0;
|
|
7727
|
+
}
|
|
7728
|
+
fullRenderingShare(body) {
|
|
7729
|
+
const main = this.zones.mainShare;
|
|
7730
|
+
if (main === 1 || this.pinned.has(body.data.id)) return 1;
|
|
7731
|
+
const home = isTopLevelParent(body.data.parentId) ? body.data.id : body.data.parentId;
|
|
7732
|
+
const probeSystem = this.probeSystemId(body);
|
|
7733
|
+
let share = main;
|
|
7734
|
+
for (const { id, fade } of this.zones.systems) if (this.bodies.isInSystem(home, id) || probeSystem !== null && this.bodies.isInSystem(probeSystem, id)) share += fade.share;
|
|
7735
|
+
return Math.min(share, 1);
|
|
7736
|
+
}
|
|
7737
|
+
probeSystemId(body) {
|
|
7738
|
+
if (body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) return null;
|
|
7739
|
+
const sysNaif = this.getProbeStore()?.containingSystemAt(body.data.id, this.currentJd);
|
|
7740
|
+
return sysNaif === null || sysNaif === void 0 ? null : `naif-${sysNaif}`;
|
|
7566
7741
|
}
|
|
7567
7742
|
isMoonGroupVisible(parentId) {
|
|
7743
|
+
if (this.systemShare === 0) return false;
|
|
7568
7744
|
if (!this.getLayers().isVisible("moons")) return false;
|
|
7569
7745
|
if (!this.isInFocusedSystem(parentId)) return false;
|
|
7570
7746
|
const maxA = this.bodies.maxMoonA(parentId);
|
|
@@ -7572,19 +7748,25 @@ var VisibilityController = class {
|
|
|
7572
7748
|
return this.cameraDistThreeJS / 10 / maxA <= this.scaledPlanetary[VISIBILITY.FAR];
|
|
7573
7749
|
}
|
|
7574
7750
|
isSpacecraftGroupVisible(groupParentId) {
|
|
7575
|
-
|
|
7751
|
+
return this.spacecraftGroupShare(groupParentId) > 0;
|
|
7752
|
+
}
|
|
7753
|
+
spacecraftGroupShare(groupParentId) {
|
|
7754
|
+
if (this.systemShare === 0) return 0;
|
|
7755
|
+
if (this.getLayers().hidesSpacecraftGroup(groupParentId)) return 0;
|
|
7576
7756
|
const parent = this.bodies.bodiesById.get(groupParentId);
|
|
7577
|
-
if (groupParentId !== "naif-0" && !parent?.position) return
|
|
7578
|
-
|
|
7579
|
-
if (
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7757
|
+
if (groupParentId !== "naif-0" && !parent?.position) return 0;
|
|
7758
|
+
if (isTopLevelParent(groupParentId)) return this.zones.mainShare;
|
|
7759
|
+
if (parent?.data.objectType === ObjectType.STAR) return this.zones.mainShare;
|
|
7760
|
+
for (const { id, fade } of this.zones.systems) if (this.bodies.isInSystem(groupParentId, id)) return fade.share;
|
|
7761
|
+
return 0;
|
|
7762
|
+
}
|
|
7763
|
+
asteroidGroupShare(zone) {
|
|
7764
|
+
return this.isAsteroidGroupVisible(zone) ? this.zones.mainShare : 0;
|
|
7584
7765
|
}
|
|
7585
7766
|
isAsteroidGroupVisible(zone) {
|
|
7767
|
+
if (this.systemShare === 0) return false;
|
|
7586
7768
|
if (this.getLayers().hidesZone(zone)) return false;
|
|
7587
|
-
if (this.
|
|
7769
|
+
if (this.zones.mainShare === 0) return false;
|
|
7588
7770
|
if (!this.bodies.bodiesById.get("naif-10")?.position) return false;
|
|
7589
7771
|
const filter = this.getSmallBodyFilter();
|
|
7590
7772
|
if (filter && zone.startsWith("small_bodies/")) {
|
|
@@ -7745,7 +7927,7 @@ function parseChebyshevPayload$1(buffer, startJd, endJd) {
|
|
|
7745
7927
|
const float64Coeffs = (view.getUint8(28) & 1) !== 0;
|
|
7746
7928
|
const coeffBytes = float64Coeffs ? 8 : 4;
|
|
7747
7929
|
const bodies = [];
|
|
7748
|
-
let offset = HEADER_SIZE$
|
|
7930
|
+
let offset = HEADER_SIZE$3;
|
|
7749
7931
|
for (let b = 0; b < bodyCount; b++) {
|
|
7750
7932
|
const naifId = view.getInt32(offset, true);
|
|
7751
7933
|
const parentId = view.getInt32(offset + 4, true);
|
|
@@ -7905,7 +8087,7 @@ function parseProbesPayload(buffer, startJd, endJd, float64Coeffs) {
|
|
|
7905
8087
|
const subchunkS = subchunkDays * SECONDS_PER_DAY$2;
|
|
7906
8088
|
const chunkStartEt = tdbJdToEt(startJd);
|
|
7907
8089
|
const probes = [];
|
|
7908
|
-
let offset = HEADER_SIZE$
|
|
8090
|
+
let offset = HEADER_SIZE$3;
|
|
7909
8091
|
for (let p = 0; p < probeCount; p++) {
|
|
7910
8092
|
const objIdValue = view.getInt32(offset, true);
|
|
7911
8093
|
const idTypeOrdinal = view.getUint8(offset + 4);
|
|
@@ -8129,7 +8311,7 @@ var SEC_PER_DAY = 86400;
|
|
|
8129
8311
|
var AU_PER_DAY_TO_KM_PER_SEC = AU_KM / SEC_PER_DAY;
|
|
8130
8312
|
var SMALL_E = 1e-9;
|
|
8131
8313
|
var SMALL_SIN_I = 1e-9;
|
|
8132
|
-
function clamp$
|
|
8314
|
+
function clamp$4(x, lo, hi) {
|
|
8133
8315
|
return x < lo ? lo : x > hi ? hi : x;
|
|
8134
8316
|
}
|
|
8135
8317
|
function stateVectorToElements(r, v, muAuDay2, jd) {
|
|
@@ -8143,7 +8325,7 @@ function stateVectorToElements(r, v, muAuDay2, jd) {
|
|
|
8143
8325
|
const hz = rx * vy - ry * vx;
|
|
8144
8326
|
const hNorm = Math.hypot(hx, hy, hz);
|
|
8145
8327
|
if (hNorm === 0) return null;
|
|
8146
|
-
const iRad = Math.acos(clamp$
|
|
8328
|
+
const iRad = Math.acos(clamp$4(hz / hNorm, -1, 1));
|
|
8147
8329
|
const sinI = Math.sin(iRad);
|
|
8148
8330
|
const evX = (vy * hz - vz * hy) / muAuDay2 - rx / rNorm;
|
|
8149
8331
|
const evY = (vz * hx - vx * hz) / muAuDay2 - ry / rNorm;
|
|
@@ -8158,7 +8340,7 @@ function stateVectorToElements(r, v, muAuDay2, jd) {
|
|
|
8158
8340
|
let omRad;
|
|
8159
8341
|
if (sinI < SMALL_SIN_I || nNorm === 0) omRad = 0;
|
|
8160
8342
|
else {
|
|
8161
|
-
omRad = Math.acos(clamp$
|
|
8343
|
+
omRad = Math.acos(clamp$4(nx / nNorm, -1, 1));
|
|
8162
8344
|
if (ny < 0) omRad = 2 * Math.PI - omRad;
|
|
8163
8345
|
}
|
|
8164
8346
|
let wRad;
|
|
@@ -8169,7 +8351,7 @@ function stateVectorToElements(r, v, muAuDay2, jd) {
|
|
|
8169
8351
|
if (wRad < 0) wRad += 2 * Math.PI;
|
|
8170
8352
|
} else {
|
|
8171
8353
|
const cosW = (nx * evX + ny * evY) / (nNorm * e);
|
|
8172
|
-
wRad = Math.acos(clamp$
|
|
8354
|
+
wRad = Math.acos(clamp$4(cosW, -1, 1));
|
|
8173
8355
|
if (evZ < 0) wRad = 2 * Math.PI - wRad;
|
|
8174
8356
|
}
|
|
8175
8357
|
let nuRad;
|
|
@@ -8180,11 +8362,11 @@ function stateVectorToElements(r, v, muAuDay2, jd) {
|
|
|
8180
8362
|
if (nuRad < 0) nuRad += 2 * Math.PI;
|
|
8181
8363
|
} else if (e < SMALL_E) {
|
|
8182
8364
|
const cosU = (nx * rx + ny * ry) / (nNorm * rNorm);
|
|
8183
|
-
nuRad = Math.acos(clamp$
|
|
8365
|
+
nuRad = Math.acos(clamp$4(cosU, -1, 1));
|
|
8184
8366
|
if (rz < 0) nuRad = 2 * Math.PI - nuRad;
|
|
8185
8367
|
} else {
|
|
8186
8368
|
const cosNu = (evX * rx + evY * ry + evZ * rz) / (e * rNorm);
|
|
8187
|
-
nuRad = Math.acos(clamp$
|
|
8369
|
+
nuRad = Math.acos(clamp$4(cosNu, -1, 1));
|
|
8188
8370
|
if (rDotV < 0) nuRad = 2 * Math.PI - nuRad;
|
|
8189
8371
|
}
|
|
8190
8372
|
let maRad;
|
|
@@ -9998,6 +10180,46 @@ async function dataVersionChanged() {
|
|
|
9998
10180
|
}
|
|
9999
10181
|
}
|
|
10000
10182
|
|
|
10183
|
+
//#endregion
|
|
10184
|
+
//#region src/lib/fetch/extrasolar/ids.ts
|
|
10185
|
+
var CELL_LEVEL = 10;
|
|
10186
|
+
var CELL_COUNT = 12 * 4 ** 10;
|
|
10187
|
+
var FIRST_TABLE = 1;
|
|
10188
|
+
var LAST_TABLE = 30;
|
|
10189
|
+
var CELL_SPAN = 2 ** 24;
|
|
10190
|
+
var TABLE_SPAN = 32;
|
|
10191
|
+
var SERIAL_SPAN = 2 ** 29;
|
|
10192
|
+
var ID_TEXT = /^[1-9][0-9]{0,15}$/;
|
|
10193
|
+
var EXTRASOLAR_PREFIX = "x-";
|
|
10194
|
+
function decodeId(id) {
|
|
10195
|
+
return {
|
|
10196
|
+
serial: Math.floor(id / SERIAL_SPAN),
|
|
10197
|
+
table: Math.floor(id / CELL_SPAN) % TABLE_SPAN,
|
|
10198
|
+
cell: id % CELL_SPAN
|
|
10199
|
+
};
|
|
10200
|
+
}
|
|
10201
|
+
function cellOf(id) {
|
|
10202
|
+
return id % CELL_SPAN;
|
|
10203
|
+
}
|
|
10204
|
+
function tableOf(id) {
|
|
10205
|
+
return Math.floor(id / CELL_SPAN) % TABLE_SPAN;
|
|
10206
|
+
}
|
|
10207
|
+
function parseExtrasolarId(text, tables) {
|
|
10208
|
+
if (!ID_TEXT.test(text)) return null;
|
|
10209
|
+
const id = Number(text);
|
|
10210
|
+
if (id > Number.MAX_SAFE_INTEGER) return null;
|
|
10211
|
+
const { table, cell } = decodeId(id);
|
|
10212
|
+
if (cell >= CELL_COUNT) return null;
|
|
10213
|
+
if (tables ? !tables.has(table) : table < 1 || table > 30) return null;
|
|
10214
|
+
return id;
|
|
10215
|
+
}
|
|
10216
|
+
function isExtrasolarObjectId(objectId) {
|
|
10217
|
+
return objectId.startsWith("x-");
|
|
10218
|
+
}
|
|
10219
|
+
function extrasolarObjectId(id) {
|
|
10220
|
+
return `${"x-"}${id}`;
|
|
10221
|
+
}
|
|
10222
|
+
|
|
10001
10223
|
//#endregion
|
|
10002
10224
|
//#region src/lib/fetch/gz.ts
|
|
10003
10225
|
function gunzip(res) {
|
|
@@ -10100,6 +10322,10 @@ function isViewable(entry) {
|
|
|
10100
10322
|
return textureAllowed(entry.distribution);
|
|
10101
10323
|
}
|
|
10102
10324
|
function fetchObjectDetail(fileId, fetchLocalized = true, lang = getLocale(), background = false) {
|
|
10325
|
+
if (isExtrasolarObjectId(fileId)) return Promise.resolve({
|
|
10326
|
+
global: null,
|
|
10327
|
+
localized: null
|
|
10328
|
+
});
|
|
10103
10329
|
return fetchBundlePair(OBJECT_BUNDLES, fileId, lang, fetchLocalized, background);
|
|
10104
10330
|
}
|
|
10105
10331
|
|
|
@@ -10135,6 +10361,585 @@ function planRideMarkers(rides, focusedId, sceneName) {
|
|
|
10135
10361
|
};
|
|
10136
10362
|
}
|
|
10137
10363
|
|
|
10364
|
+
//#endregion
|
|
10365
|
+
//#region src/lib/fetch/extrasolar/container.ts
|
|
10366
|
+
var MAGIC$2 = "SMXS";
|
|
10367
|
+
var FORMAT_VERSION = 1;
|
|
10368
|
+
var KIND_NODE = 3;
|
|
10369
|
+
var HEADER_SIZE$2 = 32;
|
|
10370
|
+
var TOKEN_AT = 8;
|
|
10371
|
+
var TOKEN_SIZE = 16;
|
|
10372
|
+
var HAS_VALIDITY = 1;
|
|
10373
|
+
var NODE_INFO_SIZE = 80;
|
|
10374
|
+
var TYPE_CODES = {
|
|
10375
|
+
1: "bool",
|
|
10376
|
+
2: "int8",
|
|
10377
|
+
3: "int16",
|
|
10378
|
+
4: "int32",
|
|
10379
|
+
5: "uint8",
|
|
10380
|
+
6: "uint16",
|
|
10381
|
+
7: "uint32",
|
|
10382
|
+
8: "float32",
|
|
10383
|
+
9: "float64",
|
|
10384
|
+
10: "id",
|
|
10385
|
+
11: "string",
|
|
10386
|
+
12: "dict",
|
|
10387
|
+
13: "cross_ids",
|
|
10388
|
+
14: "bits"
|
|
10389
|
+
};
|
|
10390
|
+
var ContainerFormatError = class extends Error {
|
|
10391
|
+
constructor(message) {
|
|
10392
|
+
super(message);
|
|
10393
|
+
this.name = "ContainerFormatError";
|
|
10394
|
+
}
|
|
10395
|
+
};
|
|
10396
|
+
var decoder = new TextDecoder();
|
|
10397
|
+
function bitAt(bytes, i) {
|
|
10398
|
+
return (bytes[i >> 3] >> (i & 7) & 1) === 1;
|
|
10399
|
+
}
|
|
10400
|
+
var StringColumn = class {
|
|
10401
|
+
constructor(offsets, bytes, validity) {
|
|
10402
|
+
this.offsets = offsets;
|
|
10403
|
+
this.bytes = bytes;
|
|
10404
|
+
this.validity = validity;
|
|
10405
|
+
}
|
|
10406
|
+
get length() {
|
|
10407
|
+
return this.offsets.length - 1;
|
|
10408
|
+
}
|
|
10409
|
+
get(i) {
|
|
10410
|
+
if (this.validity && !bitAt(this.validity, i)) return null;
|
|
10411
|
+
return decoder.decode(this.bytes.subarray(this.offsets[i], this.offsets[i + 1]));
|
|
10412
|
+
}
|
|
10413
|
+
};
|
|
10414
|
+
var DictColumn = class {
|
|
10415
|
+
constructor(codes, values, validity) {
|
|
10416
|
+
this.codes = codes;
|
|
10417
|
+
this.values = values;
|
|
10418
|
+
this.validity = validity;
|
|
10419
|
+
}
|
|
10420
|
+
get length() {
|
|
10421
|
+
return this.codes.length;
|
|
10422
|
+
}
|
|
10423
|
+
get(i) {
|
|
10424
|
+
if (this.validity && !bitAt(this.validity, i)) return null;
|
|
10425
|
+
return this.values[this.codes[i]];
|
|
10426
|
+
}
|
|
10427
|
+
};
|
|
10428
|
+
var CrossIdsColumn = class {
|
|
10429
|
+
constructor(list, codes, ids, pairs, validity) {
|
|
10430
|
+
this.list = list;
|
|
10431
|
+
this.codes = codes;
|
|
10432
|
+
this.ids = ids;
|
|
10433
|
+
this.pairs = pairs;
|
|
10434
|
+
this.validity = validity;
|
|
10435
|
+
}
|
|
10436
|
+
get length() {
|
|
10437
|
+
return this.list.length - 1;
|
|
10438
|
+
}
|
|
10439
|
+
get(i) {
|
|
10440
|
+
if (this.validity && !bitAt(this.validity, i)) return null;
|
|
10441
|
+
const out = [];
|
|
10442
|
+
for (let j = this.list[i]; j < this.list[i + 1]; j++) {
|
|
10443
|
+
const code = this.codes[j];
|
|
10444
|
+
out.push({
|
|
10445
|
+
catalogue: this.pairs.get(2 * code) ?? "",
|
|
10446
|
+
release: this.pairs.get(2 * code + 1) ?? "",
|
|
10447
|
+
catalogueId: this.ids.get(j) ?? ""
|
|
10448
|
+
});
|
|
10449
|
+
}
|
|
10450
|
+
return out;
|
|
10451
|
+
}
|
|
10452
|
+
};
|
|
10453
|
+
var BitColumn = class {
|
|
10454
|
+
constructor(bytes, length) {
|
|
10455
|
+
this.bytes = bytes;
|
|
10456
|
+
this.length = length;
|
|
10457
|
+
}
|
|
10458
|
+
get(i) {
|
|
10459
|
+
return bitAt(this.bytes, i);
|
|
10460
|
+
}
|
|
10461
|
+
};
|
|
10462
|
+
var Container = class {
|
|
10463
|
+
kind;
|
|
10464
|
+
token;
|
|
10465
|
+
rows;
|
|
10466
|
+
info;
|
|
10467
|
+
columns = /* @__PURE__ */ new Map();
|
|
10468
|
+
constructor(buffer) {
|
|
10469
|
+
this.buffer = buffer;
|
|
10470
|
+
if (buffer.byteLength < HEADER_SIZE$2) throw new ContainerFormatError("payload too short");
|
|
10471
|
+
const view = new DataView(buffer);
|
|
10472
|
+
if (decoder.decode(new Uint8Array(buffer, 0, 4)) !== "SMXS") throw new ContainerFormatError("not an extrasolar file");
|
|
10473
|
+
const version = view.getUint16(4, true);
|
|
10474
|
+
if (version !== 1) throw new ContainerFormatError(`format version ${version}, this code reads ${1}`);
|
|
10475
|
+
this.kind = view.getUint8(6);
|
|
10476
|
+
this.token = decoder.decode(new Uint8Array(buffer, TOKEN_AT, TOKEN_SIZE));
|
|
10477
|
+
this.rows = view.getUint32(24, true);
|
|
10478
|
+
const count = view.getUint16(28, true);
|
|
10479
|
+
const infoSize = view.getUint16(30, true);
|
|
10480
|
+
this.info = new DataView(buffer, HEADER_SIZE$2, infoSize);
|
|
10481
|
+
let at = HEADER_SIZE$2 + infoSize;
|
|
10482
|
+
for (let c = 0; c < count; c++) {
|
|
10483
|
+
const code = view.getUint8(at);
|
|
10484
|
+
const flags = view.getUint8(at + 1);
|
|
10485
|
+
const buffers = view.getUint8(at + 2);
|
|
10486
|
+
const nameSize = view.getUint8(at + 3);
|
|
10487
|
+
at += 4;
|
|
10488
|
+
const name = decoder.decode(new Uint8Array(buffer, at, nameSize));
|
|
10489
|
+
at += nameSize;
|
|
10490
|
+
const places = [];
|
|
10491
|
+
for (let b = 0; b < buffers; b++) {
|
|
10492
|
+
const offset = view.getUint32(at, true);
|
|
10493
|
+
const length = view.getUint32(at + 4, true);
|
|
10494
|
+
if (offset % 8 !== 0 || offset + length > buffer.byteLength) throw new ContainerFormatError(`${name}: buffer ${b} out of place`);
|
|
10495
|
+
places.push({
|
|
10496
|
+
offset,
|
|
10497
|
+
length
|
|
10498
|
+
});
|
|
10499
|
+
at += 8;
|
|
10500
|
+
}
|
|
10501
|
+
const type = TYPE_CODES[code];
|
|
10502
|
+
if (!type) throw new ContainerFormatError(`${name}: unknown type ${code}`);
|
|
10503
|
+
const first = (flags & HAS_VALIDITY) !== 0 ? places.shift() : void 0;
|
|
10504
|
+
this.columns.set(name, {
|
|
10505
|
+
type,
|
|
10506
|
+
validity: first ? new Uint8Array(buffer, first.offset, first.length) : null,
|
|
10507
|
+
places
|
|
10508
|
+
});
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
names() {
|
|
10512
|
+
return [...this.columns.keys()];
|
|
10513
|
+
}
|
|
10514
|
+
has(name) {
|
|
10515
|
+
return this.columns.has(name);
|
|
10516
|
+
}
|
|
10517
|
+
type(name) {
|
|
10518
|
+
return this.entry(name).type;
|
|
10519
|
+
}
|
|
10520
|
+
isValid(name, row) {
|
|
10521
|
+
const { validity } = this.entry(name);
|
|
10522
|
+
return validity === null || bitAt(validity, row);
|
|
10523
|
+
}
|
|
10524
|
+
bool(name) {
|
|
10525
|
+
return this.numbers(name, "bool", Uint8Array);
|
|
10526
|
+
}
|
|
10527
|
+
int8(name) {
|
|
10528
|
+
return this.numbers(name, "int8", Int8Array);
|
|
10529
|
+
}
|
|
10530
|
+
int16(name) {
|
|
10531
|
+
return this.numbers(name, "int16", Int16Array);
|
|
10532
|
+
}
|
|
10533
|
+
int32(name) {
|
|
10534
|
+
return this.numbers(name, "int32", Int32Array);
|
|
10535
|
+
}
|
|
10536
|
+
uint8(name) {
|
|
10537
|
+
return this.numbers(name, "uint8", Uint8Array);
|
|
10538
|
+
}
|
|
10539
|
+
uint16(name) {
|
|
10540
|
+
return this.numbers(name, "uint16", Uint16Array);
|
|
10541
|
+
}
|
|
10542
|
+
uint32(name) {
|
|
10543
|
+
return this.numbers(name, "uint32", Uint32Array);
|
|
10544
|
+
}
|
|
10545
|
+
float32(name) {
|
|
10546
|
+
return this.numbers(name, "float32", Float32Array);
|
|
10547
|
+
}
|
|
10548
|
+
float64(name) {
|
|
10549
|
+
return this.numbers(name, "float64", Float64Array);
|
|
10550
|
+
}
|
|
10551
|
+
ids(name) {
|
|
10552
|
+
return this.numbers(name, "id", Float64Array);
|
|
10553
|
+
}
|
|
10554
|
+
strings(name) {
|
|
10555
|
+
const { validity, places } = this.typed(name, "string");
|
|
10556
|
+
return this.stringList(places[0], places[1], validity);
|
|
10557
|
+
}
|
|
10558
|
+
dict(name) {
|
|
10559
|
+
const { validity, places } = this.typed(name, "dict");
|
|
10560
|
+
const values = this.stringList(places[1], places[2], null);
|
|
10561
|
+
const strings = [];
|
|
10562
|
+
for (let k = 0; k < values.length; k++) strings.push(values.get(k) ?? "");
|
|
10563
|
+
return new DictColumn(this.view(places[0], Uint16Array), strings, validity);
|
|
10564
|
+
}
|
|
10565
|
+
crossIds(name) {
|
|
10566
|
+
const { validity, places } = this.typed(name, "cross_ids");
|
|
10567
|
+
return new CrossIdsColumn(this.view(places[0], Uint32Array), this.view(places[1], Uint16Array), this.stringList(places[2], places[3], null), this.stringList(places[4], places[5], null), validity);
|
|
10568
|
+
}
|
|
10569
|
+
bits(name) {
|
|
10570
|
+
const { places } = this.typed(name, "bits");
|
|
10571
|
+
return new BitColumn(this.view(places[0], Uint8Array), this.rows);
|
|
10572
|
+
}
|
|
10573
|
+
entry(name) {
|
|
10574
|
+
const entry = this.columns.get(name);
|
|
10575
|
+
if (!entry) throw new ContainerFormatError(`no column ${name}`);
|
|
10576
|
+
return entry;
|
|
10577
|
+
}
|
|
10578
|
+
typed(name, type) {
|
|
10579
|
+
const entry = this.entry(name);
|
|
10580
|
+
if (entry.type !== type) throw new ContainerFormatError(`${name} is ${entry.type}, not ${type}`);
|
|
10581
|
+
return entry;
|
|
10582
|
+
}
|
|
10583
|
+
numbers(name, type, Kind) {
|
|
10584
|
+
return this.view(this.typed(name, type).places[0], Kind);
|
|
10585
|
+
}
|
|
10586
|
+
view(place, Kind) {
|
|
10587
|
+
return new Kind(this.buffer, place.offset, place.length / Kind.BYTES_PER_ELEMENT);
|
|
10588
|
+
}
|
|
10589
|
+
stringList(offsets, bytes, validity) {
|
|
10590
|
+
return new StringColumn(this.view(offsets, Uint32Array), new Uint8Array(this.buffer, bytes.offset, bytes.length), validity);
|
|
10591
|
+
}
|
|
10592
|
+
};
|
|
10593
|
+
function nodeInfo(container) {
|
|
10594
|
+
const info = container.info;
|
|
10595
|
+
if (container.kind !== 3 || info.byteLength !== NODE_INFO_SIZE) throw new ContainerFormatError("not a render node");
|
|
10596
|
+
const childRange = new Float32Array(8);
|
|
10597
|
+
for (let k = 0; k < 8; k++) childRange[k] = info.getFloat32(40 + 4 * k, true);
|
|
10598
|
+
return {
|
|
10599
|
+
centre: [
|
|
10600
|
+
info.getFloat64(0, true),
|
|
10601
|
+
info.getFloat64(8, true),
|
|
10602
|
+
info.getFloat64(16, true)
|
|
10603
|
+
],
|
|
10604
|
+
half: info.getFloat64(24, true),
|
|
10605
|
+
minRange: info.getFloat32(32, true),
|
|
10606
|
+
maxRange: info.getFloat32(36, true),
|
|
10607
|
+
childRange,
|
|
10608
|
+
depth: info.getUint8(72),
|
|
10609
|
+
childMask: info.getUint8(73)
|
|
10610
|
+
};
|
|
10611
|
+
}
|
|
10612
|
+
|
|
10613
|
+
//#endregion
|
|
10614
|
+
//#region src/lib/fetch/extrasolar/files.ts
|
|
10615
|
+
var EXTRASOLAR_FORMAT = 1;
|
|
10616
|
+
var ExtrasolarStaleError = class extends Error {
|
|
10617
|
+
constructor(path, found, expected) {
|
|
10618
|
+
super(`${path}: token ${found}, expected ${expected}`);
|
|
10619
|
+
this.name = "ExtrasolarStaleError";
|
|
10620
|
+
}
|
|
10621
|
+
};
|
|
10622
|
+
function extrasolarBase() {
|
|
10623
|
+
return host().extrasolarUrl;
|
|
10624
|
+
}
|
|
10625
|
+
function extrasolarAllowed(entry) {
|
|
10626
|
+
return textureAllowed(entry.distribution);
|
|
10627
|
+
}
|
|
10628
|
+
function extrasolarPath(pattern, fields) {
|
|
10629
|
+
return pattern.replace(/\{(\w+)\}/g, (whole, key) => {
|
|
10630
|
+
const value = fields[key];
|
|
10631
|
+
if (value === void 0) throw new Error(`extrasolarPath: no ${key} for ${pattern}`);
|
|
10632
|
+
return String(value);
|
|
10633
|
+
});
|
|
10634
|
+
}
|
|
10635
|
+
function extrasolarUrl(path, token) {
|
|
10636
|
+
return `${extrasolarBase()}/v1/x/${path}?v=${token}`;
|
|
10637
|
+
}
|
|
10638
|
+
var metadataBase = null;
|
|
10639
|
+
var metadataPending = null;
|
|
10640
|
+
function fetchExtrasolarMetadata() {
|
|
10641
|
+
const base = extrasolarBase();
|
|
10642
|
+
if (metadataPending && metadataBase === base) return metadataPending;
|
|
10643
|
+
metadataBase = base;
|
|
10644
|
+
if (base === "") {
|
|
10645
|
+
metadataPending = Promise.resolve(null);
|
|
10646
|
+
return metadataPending;
|
|
10647
|
+
}
|
|
10648
|
+
const p = (async () => {
|
|
10649
|
+
const res = await fetchWithTimeout(`${base}/v1/x/metadata.json`, { cache: "no-cache" });
|
|
10650
|
+
if (!res.ok) throw new Error(`extrasolar metadata.json returned ${res.status}`);
|
|
10651
|
+
const meta = await res.json();
|
|
10652
|
+
if (meta.format !== 1) throw new Error(`extrasolar format ${meta.format}, this code reads ${1}`);
|
|
10653
|
+
return meta;
|
|
10654
|
+
})();
|
|
10655
|
+
metadataPending = p;
|
|
10656
|
+
p.catch(() => {
|
|
10657
|
+
if (metadataPending === p) metadataPending = null;
|
|
10658
|
+
});
|
|
10659
|
+
return p;
|
|
10660
|
+
}
|
|
10661
|
+
var staleListeners = /* @__PURE__ */ new Set();
|
|
10662
|
+
var staleReported = false;
|
|
10663
|
+
function onExtrasolarStale(listener) {
|
|
10664
|
+
staleListeners.add(listener);
|
|
10665
|
+
return () => staleListeners.delete(listener);
|
|
10666
|
+
}
|
|
10667
|
+
function reportStale() {
|
|
10668
|
+
if (staleReported) return;
|
|
10669
|
+
staleReported = true;
|
|
10670
|
+
for (const listener of staleListeners) listener();
|
|
10671
|
+
}
|
|
10672
|
+
async function fetchExtrasolarFile(path, token, options = {}) {
|
|
10673
|
+
const url = extrasolarUrl(path, token);
|
|
10674
|
+
const res = await fetchWithTimeout(url, {
|
|
10675
|
+
signal: options.signal,
|
|
10676
|
+
priority: options.priority
|
|
10677
|
+
});
|
|
10678
|
+
if (!res.ok) throw new Error(`${url} returned ${res.status}`);
|
|
10679
|
+
const container = new Container(await gunzip(res).arrayBuffer());
|
|
10680
|
+
if (container.token !== token) {
|
|
10681
|
+
reportStale();
|
|
10682
|
+
throw new ExtrasolarStaleError(path, container.token, token);
|
|
10683
|
+
}
|
|
10684
|
+
return container;
|
|
10685
|
+
}
|
|
10686
|
+
|
|
10687
|
+
//#endregion
|
|
10688
|
+
//#region src/lib/fetch/extrasolar/split.ts
|
|
10689
|
+
var ROOTS = 12;
|
|
10690
|
+
var SplitTree = class SplitTree {
|
|
10691
|
+
constructor(first, level) {
|
|
10692
|
+
this.first = first;
|
|
10693
|
+
this.level = level;
|
|
10694
|
+
}
|
|
10695
|
+
static fromBits(bits) {
|
|
10696
|
+
const first = [];
|
|
10697
|
+
const level = [];
|
|
10698
|
+
let at = 0;
|
|
10699
|
+
const visit = (depth, pixel) => {
|
|
10700
|
+
if (at >= bits.length) throw new ContainerFormatError("split table too short");
|
|
10701
|
+
if (!bits.get(at++)) {
|
|
10702
|
+
first.push(pixel * 4 ** (10 - depth));
|
|
10703
|
+
level.push(depth);
|
|
10704
|
+
return;
|
|
10705
|
+
}
|
|
10706
|
+
if (depth === 10) throw new ContainerFormatError("a cell cannot split");
|
|
10707
|
+
for (let k = 0; k < 4; k++) visit(depth + 1, 4 * pixel + k);
|
|
10708
|
+
};
|
|
10709
|
+
for (let root = 0; root < ROOTS; root++) visit(0, root);
|
|
10710
|
+
if (at !== bits.length) throw new ContainerFormatError(`split table has ${bits.length} bits, the tree ${at}`);
|
|
10711
|
+
return new SplitTree(Uint32Array.from(first), Uint8Array.from(level));
|
|
10712
|
+
}
|
|
10713
|
+
get leaves() {
|
|
10714
|
+
return this.first.length;
|
|
10715
|
+
}
|
|
10716
|
+
tileOf(cell) {
|
|
10717
|
+
const first = this.first;
|
|
10718
|
+
let lo = 0;
|
|
10719
|
+
let hi = first.length - 1;
|
|
10720
|
+
while (lo < hi) {
|
|
10721
|
+
const mid = lo + hi + 1 >> 1;
|
|
10722
|
+
if (first[mid] <= cell) lo = mid;
|
|
10723
|
+
else hi = mid - 1;
|
|
10724
|
+
}
|
|
10725
|
+
const level = this.level[lo];
|
|
10726
|
+
return {
|
|
10727
|
+
level,
|
|
10728
|
+
pixel: first[lo] / 4 ** (10 - level)
|
|
10729
|
+
};
|
|
10730
|
+
}
|
|
10731
|
+
};
|
|
10732
|
+
function findId(ids, id) {
|
|
10733
|
+
let lo = 0;
|
|
10734
|
+
let hi = ids.length - 1;
|
|
10735
|
+
while (lo <= hi) {
|
|
10736
|
+
const mid = lo + hi >>> 1;
|
|
10737
|
+
const at = ids[mid];
|
|
10738
|
+
if (at === id) return mid;
|
|
10739
|
+
if (at < id) lo = mid + 1;
|
|
10740
|
+
else hi = mid - 1;
|
|
10741
|
+
}
|
|
10742
|
+
return -1;
|
|
10743
|
+
}
|
|
10744
|
+
|
|
10745
|
+
//#endregion
|
|
10746
|
+
//#region src/lib/fetch/extrasolar/lookup.ts
|
|
10747
|
+
var METADATA_SLUG = "metadata";
|
|
10748
|
+
var TableRow = class {
|
|
10749
|
+
constructor(slug, table, container, row) {
|
|
10750
|
+
this.slug = slug;
|
|
10751
|
+
this.table = table;
|
|
10752
|
+
this.container = container;
|
|
10753
|
+
this.row = row;
|
|
10754
|
+
}
|
|
10755
|
+
has(name) {
|
|
10756
|
+
return this.container.has(name);
|
|
10757
|
+
}
|
|
10758
|
+
number(name) {
|
|
10759
|
+
const c = this.container;
|
|
10760
|
+
if (!c.has(name) || !c.isValid(name, this.row)) return null;
|
|
10761
|
+
let value;
|
|
10762
|
+
switch (c.type(name)) {
|
|
10763
|
+
case "float32":
|
|
10764
|
+
value = c.float32(name)[this.row];
|
|
10765
|
+
break;
|
|
10766
|
+
case "float64":
|
|
10767
|
+
value = c.float64(name)[this.row];
|
|
10768
|
+
break;
|
|
10769
|
+
case "int8":
|
|
10770
|
+
value = c.int8(name)[this.row];
|
|
10771
|
+
break;
|
|
10772
|
+
case "int16":
|
|
10773
|
+
value = c.int16(name)[this.row];
|
|
10774
|
+
break;
|
|
10775
|
+
case "int32":
|
|
10776
|
+
value = c.int32(name)[this.row];
|
|
10777
|
+
break;
|
|
10778
|
+
case "uint8":
|
|
10779
|
+
value = c.uint8(name)[this.row];
|
|
10780
|
+
break;
|
|
10781
|
+
case "uint16":
|
|
10782
|
+
value = c.uint16(name)[this.row];
|
|
10783
|
+
break;
|
|
10784
|
+
case "uint32":
|
|
10785
|
+
value = c.uint32(name)[this.row];
|
|
10786
|
+
break;
|
|
10787
|
+
case "id":
|
|
10788
|
+
value = c.ids(name)[this.row];
|
|
10789
|
+
break;
|
|
10790
|
+
default: throw new Error(`${this.slug}.${name} is not a number column`);
|
|
10791
|
+
}
|
|
10792
|
+
return Number.isNaN(value) ? null : value;
|
|
10793
|
+
}
|
|
10794
|
+
text(name) {
|
|
10795
|
+
const c = this.container;
|
|
10796
|
+
if (!c.has(name)) return null;
|
|
10797
|
+
const type = c.type(name);
|
|
10798
|
+
if (type === "dict") return c.dict(name).get(this.row);
|
|
10799
|
+
if (type === "string") return c.strings(name).get(this.row);
|
|
10800
|
+
throw new Error(`${this.slug}.${name} is not a text column`);
|
|
10801
|
+
}
|
|
10802
|
+
flag(name) {
|
|
10803
|
+
const c = this.container;
|
|
10804
|
+
if (!c.has(name) || !c.isValid(name, this.row)) return null;
|
|
10805
|
+
return c.bool(name)[this.row] === 1;
|
|
10806
|
+
}
|
|
10807
|
+
};
|
|
10808
|
+
var DEFAULT_LIMITS = {
|
|
10809
|
+
tiles: 32,
|
|
10810
|
+
results: 64
|
|
10811
|
+
};
|
|
10812
|
+
var MIN_PARALLAX_SNR = 5;
|
|
10813
|
+
function parallaxDistancePc(row) {
|
|
10814
|
+
const parallax = row.number("parallax");
|
|
10815
|
+
const error = row.number("parallax_error");
|
|
10816
|
+
if (parallax === null || error === null || !(parallax / error > MIN_PARALLAX_SNR)) return null;
|
|
10817
|
+
return 1e3 / parallax;
|
|
10818
|
+
}
|
|
10819
|
+
function homeRow(record) {
|
|
10820
|
+
return record.home && record.rows.get(record.home) || null;
|
|
10821
|
+
}
|
|
10822
|
+
function hasDistance(record) {
|
|
10823
|
+
const home = homeRow(record);
|
|
10824
|
+
return home !== null && parallaxDistancePc(home) !== null;
|
|
10825
|
+
}
|
|
10826
|
+
function readMetadataRow(c, row) {
|
|
10827
|
+
const alias = c.isValid("alias_target", row) ? c.ids("alias_target")[row] : 0;
|
|
10828
|
+
return {
|
|
10829
|
+
id: c.ids("id")[row],
|
|
10830
|
+
name: c.strings("name").get(row),
|
|
10831
|
+
nameSource: c.dict("name_source").get(row),
|
|
10832
|
+
class: c.dict("class").get(row),
|
|
10833
|
+
classSource: c.dict("class_source").get(row),
|
|
10834
|
+
crossIds: c.crossIds("cross_ids").get(row) ?? [],
|
|
10835
|
+
mask: c.uint32("mask")[row],
|
|
10836
|
+
objectFile: c.strings("object_file").get(row),
|
|
10837
|
+
aliasTarget: alias > 0 ? alias : null
|
|
10838
|
+
};
|
|
10839
|
+
}
|
|
10840
|
+
var ExtrasolarLookup = class {
|
|
10841
|
+
splits = /* @__PURE__ */ new Map();
|
|
10842
|
+
tiles;
|
|
10843
|
+
results;
|
|
10844
|
+
constructor(source, limits = DEFAULT_LIMITS) {
|
|
10845
|
+
this.source = source;
|
|
10846
|
+
this.tiles = new LruPromiseCache(limits.tiles);
|
|
10847
|
+
this.results = new LruPromiseCache(limits.results);
|
|
10848
|
+
}
|
|
10849
|
+
async live(id, options = {}) {
|
|
10850
|
+
const first = await this.lookup(id, options);
|
|
10851
|
+
return first.kind === "alias" ? this.lookup(first.target, options) : first;
|
|
10852
|
+
}
|
|
10853
|
+
lookup(id, options = {}) {
|
|
10854
|
+
const data = options.data ?? true;
|
|
10855
|
+
return this.results.getOrCompute(`${id}:${data}`, () => this.resolve(String(id), data));
|
|
10856
|
+
}
|
|
10857
|
+
async resolve(text, data) {
|
|
10858
|
+
const meta = await this.source.metadata();
|
|
10859
|
+
if (!meta) return {
|
|
10860
|
+
kind: "missing",
|
|
10861
|
+
reason: "unconfigured"
|
|
10862
|
+
};
|
|
10863
|
+
const numbered = /* @__PURE__ */ new Map();
|
|
10864
|
+
for (const [slug, table] of Object.entries(meta.tables)) if (table.number !== null) numbered.set(table.number, {
|
|
10865
|
+
slug,
|
|
10866
|
+
table
|
|
10867
|
+
});
|
|
10868
|
+
const id = parseExtrasolarId(text, new Set(numbered.keys()));
|
|
10869
|
+
if (id === null) return {
|
|
10870
|
+
kind: "missing",
|
|
10871
|
+
reason: "id"
|
|
10872
|
+
};
|
|
10873
|
+
const metaTable = meta.tables[METADATA_SLUG];
|
|
10874
|
+
if (!metaTable || !this.source.allowed(metaTable)) return {
|
|
10875
|
+
kind: "missing",
|
|
10876
|
+
reason: "barred"
|
|
10877
|
+
};
|
|
10878
|
+
const cell = cellOf(id);
|
|
10879
|
+
const home = numbered.get(tableOf(id));
|
|
10880
|
+
const homeShown = data && this.source.allowed(home.table);
|
|
10881
|
+
const [metaTile, homeTile] = await Promise.all([this.tile(METADATA_SLUG, metaTable, cell), homeShown ? this.tile(home.slug, home.table, cell) : null]);
|
|
10882
|
+
const metaRow = findId(metaTile.ids("id"), id);
|
|
10883
|
+
if (metaRow < 0) return {
|
|
10884
|
+
kind: "missing",
|
|
10885
|
+
reason: "no-row"
|
|
10886
|
+
};
|
|
10887
|
+
const metadata = readMetadataRow(metaTile, metaRow);
|
|
10888
|
+
if (metadata.aliasTarget !== null) return {
|
|
10889
|
+
kind: "alias",
|
|
10890
|
+
id,
|
|
10891
|
+
target: metadata.aliasTarget
|
|
10892
|
+
};
|
|
10893
|
+
const rows = /* @__PURE__ */ new Map();
|
|
10894
|
+
const adopt = (entry, tile) => {
|
|
10895
|
+
const row = findId(tile.ids("id"), id);
|
|
10896
|
+
if (row >= 0) rows.set(entry.slug, new TableRow(entry.slug, entry.table, tile, row));
|
|
10897
|
+
};
|
|
10898
|
+
if (homeTile) adopt(home, homeTile);
|
|
10899
|
+
if (data) {
|
|
10900
|
+
const others = [];
|
|
10901
|
+
for (let n = 1; n <= 30; n++) {
|
|
10902
|
+
const entry = numbered.get(n);
|
|
10903
|
+
if (!(metadata.mask >>> n & 1) || !entry || entry === home) continue;
|
|
10904
|
+
if (this.source.allowed(entry.table)) others.push(entry);
|
|
10905
|
+
}
|
|
10906
|
+
const tiles = await Promise.all(others.map((entry) => this.tile(entry.slug, entry.table, cell)));
|
|
10907
|
+
others.forEach((entry, i) => adopt(entry, tiles[i]));
|
|
10908
|
+
}
|
|
10909
|
+
return {
|
|
10910
|
+
kind: "live",
|
|
10911
|
+
record: {
|
|
10912
|
+
id,
|
|
10913
|
+
metadata,
|
|
10914
|
+
rows,
|
|
10915
|
+
home: home.slug,
|
|
10916
|
+
credits: meta.credits
|
|
10917
|
+
}
|
|
10918
|
+
};
|
|
10919
|
+
}
|
|
10920
|
+
split(slug, table) {
|
|
10921
|
+
const key = `${slug}?v=${table.token}`;
|
|
10922
|
+
const held = this.splits.get(key);
|
|
10923
|
+
if (held) return held;
|
|
10924
|
+
const p = this.source.file(table.split, table.token).then((c) => SplitTree.fromBits(c.bits("split")));
|
|
10925
|
+
this.splits.set(key, p);
|
|
10926
|
+
p.catch(() => {
|
|
10927
|
+
if (this.splits.get(key) === p) this.splits.delete(key);
|
|
10928
|
+
});
|
|
10929
|
+
return p;
|
|
10930
|
+
}
|
|
10931
|
+
async tile(slug, table, cell) {
|
|
10932
|
+
const split = await this.split(slug, table);
|
|
10933
|
+
const path = extrasolarPath(table.tile, split.tileOf(cell));
|
|
10934
|
+
return this.tiles.getOrCompute(`${path}?v=${table.token}`, () => this.source.file(path, table.token));
|
|
10935
|
+
}
|
|
10936
|
+
};
|
|
10937
|
+
var extrasolarLookup = new ExtrasolarLookup({
|
|
10938
|
+
metadata: fetchExtrasolarMetadata,
|
|
10939
|
+
file: (path, token) => fetchExtrasolarFile(path, token),
|
|
10940
|
+
allowed: extrasolarAllowed
|
|
10941
|
+
});
|
|
10942
|
+
|
|
10138
10943
|
//#endregion
|
|
10139
10944
|
//#region src/lib/fetch/coverage.ts
|
|
10140
10945
|
var EDGE_INSET_JD = 1 / 1440;
|
|
@@ -10147,6 +10952,10 @@ async function earthZoneEndJd() {
|
|
|
10147
10952
|
return zoom && isDateSegmented(zoom) ? unixMsToJD(lastCoveredMs(zoom)) : Infinity;
|
|
10148
10953
|
}
|
|
10149
10954
|
async function coverageOf(id) {
|
|
10955
|
+
if (isExtrasolarObjectId(id)) {
|
|
10956
|
+
const result = await extrasolarLookup.live(id.slice("x-".length));
|
|
10957
|
+
return result.kind === "live" && hasDistance(result.record) ? [[-Infinity, Infinity]] : [];
|
|
10958
|
+
}
|
|
10150
10959
|
const detail = await fetchObjectDetail(id, false);
|
|
10151
10960
|
const windows = detail.global?.coverage?.windows ?? [];
|
|
10152
10961
|
const tle = detail.global?.orbit?.scale === "planet";
|
|
@@ -10378,6 +11187,30 @@ function applySouthTowardParent(obj, bodyPos, parentPos) {
|
|
|
10378
11187
|
function applyUpVector(obj, dir) {
|
|
10379
11188
|
obj.quaternion.setFromUnitVectors(LOCAL_NORTH, dir);
|
|
10380
11189
|
}
|
|
11190
|
+
function landedHeadingAt(headings, jd) {
|
|
11191
|
+
let deg;
|
|
11192
|
+
for (const h of headings ?? []) if (h.jd <= jd) deg = h.deg;
|
|
11193
|
+
return deg;
|
|
11194
|
+
}
|
|
11195
|
+
var LOCAL_FRONT = new Vector3(0, 0, 1);
|
|
11196
|
+
var _up = new Vector3();
|
|
11197
|
+
var _front = new Vector3();
|
|
11198
|
+
var _north = new Vector3();
|
|
11199
|
+
var _east = new Vector3();
|
|
11200
|
+
var _target = new Vector3();
|
|
11201
|
+
var _cross = new Vector3();
|
|
11202
|
+
var _turn = new Quaternion();
|
|
11203
|
+
function turnToHeading(obj, north, headingDeg) {
|
|
11204
|
+
_up.copy(LOCAL_NORTH).applyQuaternion(obj.quaternion);
|
|
11205
|
+
_north.copy(north).addScaledVector(_up, -north.dot(_up)).normalize();
|
|
11206
|
+
_east.crossVectors(_north, _up);
|
|
11207
|
+
const h = headingDeg * DEG2RAD$5;
|
|
11208
|
+
_target.copy(_north).multiplyScalar(Math.cos(h)).addScaledVector(_east, Math.sin(h));
|
|
11209
|
+
_front.copy(LOCAL_FRONT).applyQuaternion(obj.quaternion);
|
|
11210
|
+
_front.addScaledVector(_up, -_front.dot(_up)).normalize();
|
|
11211
|
+
const angle = Math.atan2(_cross.crossVectors(_front, _target).dot(_up), _front.dot(_target));
|
|
11212
|
+
obj.quaternion.premultiply(_turn.setFromAxisAngle(_up, angle));
|
|
11213
|
+
}
|
|
10381
11214
|
var AXIS_VECTORS = {
|
|
10382
11215
|
"+x": new Vector3(1, 0, 0),
|
|
10383
11216
|
"-x": new Vector3(-1, 0, 0),
|
|
@@ -11405,7 +12238,7 @@ function isLowEndDevice() {
|
|
|
11405
12238
|
return typeof mem === "number" && mem > 0 && mem <= LOW_MEMORY_GIB;
|
|
11406
12239
|
}
|
|
11407
12240
|
var coarsePointer;
|
|
11408
|
-
function isCoarsePointer() {
|
|
12241
|
+
function isCoarsePointer$1() {
|
|
11409
12242
|
if (coarsePointer === void 0) coarsePointer = typeof window !== "undefined" && !!window.matchMedia?.("(pointer: coarse)").matches;
|
|
11410
12243
|
return coarsePointer;
|
|
11411
12244
|
}
|
|
@@ -11526,7 +12359,7 @@ var ATMOSPHERE_QUALITY_PRESETS = {
|
|
|
11526
12359
|
}
|
|
11527
12360
|
};
|
|
11528
12361
|
function heuristicAtmosphereTier() {
|
|
11529
|
-
if (isCoarsePointer()) return isLowEndDevice() ? "low" : "medium";
|
|
12362
|
+
if (isCoarsePointer$1()) return isLowEndDevice() ? "low" : "medium";
|
|
11530
12363
|
return isLowEndDevice() ? "high" : "ultra";
|
|
11531
12364
|
}
|
|
11532
12365
|
function resolveAtmosphereTier(tier) {
|
|
@@ -12893,7 +13726,7 @@ function attachCanvasForwarders(el, canvas) {
|
|
|
12893
13726
|
|
|
12894
13727
|
//#endregion
|
|
12895
13728
|
//#region src/lib/scene/relief/worker.ts?worker&inline
|
|
12896
|
-
var jsContent$1 = "function e(e,t,n,r){return`${e&&`${e}/`}${t}/${n}/${r}.webp`}function t(e,t,n,r,i=1){let a=t.length,o=n.length,s=new Float32Array(a*o*3),c=new Float32Array(a*o*3),l=new Float32Array(a*o*2),u=Float64Array.from(n,Math.sin),d=Float64Array.from(n,Math.cos);for(let f=0;f<a;f++){let a=t[f],p=Math.sin(a),m=Math.cos(a);for(let t=0;t<o;t++){let h=f*o+t,g=-d[t]*p,_=m,v=u[t]*p,y=e+(r?r[h]*i:0),b=h*3;s[b]=y*g,s[b+1]=y*_,s[b+2]=y*v,c[b]=g,c[b+1]=_,c[b+2]=v;let x=h*2;l[x]=n[t]/(2*Math.PI),l[x+1]=1-a/Math.PI}}let f=new Uint32Array((a-1)*(o-1)*6),p=0;for(let e=0;e<a-1;e++){let n=t[e]<=0,r=t[e+1]>=Math.PI;for(let t=0;t<o-1;t++){let i=e*o+t+1,a=e*o+t,s=(e+1)*o+t,c=(e+1)*o+t+1;n||(f[p++]=i,f[p++]=a,f[p++]=c),r||(f[p++]=a,f[p++]=s,f[p++]=c)}}return{positions:s,normals:c,uvs:l,indices:f.subarray(0,p)}}const n=[``,`north`,`south`],r=Math.SQRT2-1;function i(e,t){return e===0?2<<t:1<<t-1}function a(e,t){return e===0?1<<t:1<<t-1}function o(e,t,n,i){if(e===0){let e=Math.atan2(i,-t)/(2*Math.PI);return{x:e<0?e+1:e,y:Math.acos(Math.min(Math.max(n,-1),1))/Math.PI}}let a=e===1?1:-1,o=.5/(r*(1+a*n));return{x:.5+t*o,y:.5+a*i*o}}function s(e,t,n,r){let
|
|
13729
|
+
var jsContent$1 = "function e(e,t,n,r){return`${e&&`${e}/`}${t}/${n}/${r}.webp`}function t(e,t,n,r,i=1){let a=t.length,o=n.length,s=new Float32Array(a*o*3),c=new Float32Array(a*o*3),l=new Float32Array(a*o*2),u=Float64Array.from(n,Math.sin),d=Float64Array.from(n,Math.cos);for(let f=0;f<a;f++){let a=t[f],p=Math.sin(a),m=Math.cos(a);for(let t=0;t<o;t++){let h=f*o+t,g=-d[t]*p,_=m,v=u[t]*p,y=e+(r?r[h]*i:0),b=h*3;s[b]=y*g,s[b+1]=y*_,s[b+2]=y*v,c[b]=g,c[b+1]=_,c[b+2]=v;let x=h*2;l[x]=n[t]/(2*Math.PI),l[x+1]=1-a/Math.PI}}let f=new Uint32Array((a-1)*(o-1)*6),p=0;for(let e=0;e<a-1;e++){let n=t[e]<=0,r=t[e+1]>=Math.PI;for(let t=0;t<o-1;t++){let i=e*o+t+1,a=e*o+t,s=(e+1)*o+t,c=(e+1)*o+t+1;n||(f[p++]=i,f[p++]=a,f[p++]=c),r||(f[p++]=a,f[p++]=s,f[p++]=c)}}return{positions:s,normals:c,uvs:l,indices:f.subarray(0,p)}}const n=[``,`north`,`south`],r=Math.SQRT2-1;function i(e,t){return e===0?2<<t:1<<t-1}function a(e,t){return e===0?1<<t:1<<t-1}function o(e,t,n,i){if(e===0){let e=Math.atan2(i,-t)/(2*Math.PI);return{x:e<0?e+1:e,y:Math.acos(Math.min(Math.max(n,-1),1))/Math.PI}}let a=e===1?1:-1,o=.5/(r*(1+a*n));return{x:.5+t*o,y:.5+a*i*o}}function s(e){return e.cap===`north`?1:e.cap===`south`?2:0}function c(e,t,n,r,i){let a=e.max_level-n;return a>=0&&s(e)===t&&r>=e.x[0]>>a&&r<=e.x[1]>>a&&i>=e.y[0]>>a&&i<=e.y[1]>>a}function l(e,t,n,r,i){return e?.find(e=>c(e,t,n,r,i))}const u=Math.sin(60*Math.PI/180),d=2**18,f=2**17;function p(e,t,n,r){return((e*32+t)*d+n)*f+r}function m(e){let t=e%f,n=Math.floor(e/f)%d,r=Math.floor(e/(f*d));return{chart:Math.floor(r/32),level:r%32,x:n,y:t}}const h={chart:0,level:0,x0:0,x1:0,y0:0,y1:0,fx:0,fy:0};function g(e,t){return e<1||Math.abs(t)<=u?0:t>0?1:2}function _(e,t,n,r,s){let c=g(e,n),l=i(c,e)*s,u=a(c,e)*s,d=o(c,t,n,r),f=d.x*l-.5,p=d.y*u-.5,m=Math.floor(f),_=Math.floor(p),v=e=>Math.min(Math.max(e,0),l-1),y=e=>Math.min(Math.max(e,0),u-1);return h.chart=c,h.level=e,h.fx=f-m,h.fy=p-_,h.x0=c===0?(m+l)%l:v(m),h.x1=c===0?(m+1)%l:v(m+1),h.y0=y(_),h.y1=y(_+1),h}function v(e,t,n){let r=Float64Array.from(t.at,Math.sin),i=Float64Array.from(t.at,Math.cos),a=t.at.length;for(let o=0;o<e.at.length;o++){let s=Math.sin(e.at[o]),c=Math.cos(e.at[o]);for(let l=0;l<a;l++){let u=Math.min(e.level[o],t.level[l]);n(o*a+l,u,-i[l]*s,c,r[l]*s)}}}function y(e,t,n,r=1/0,i=()=>!0){let a=new Set([p(0,0,0,0),p(0,0,1,0)]);return v(e,t,(e,t,o,s,c)=>{for(let e=t;;e--){let t=_(e,o,s,c,n),l=Math.floor(t.x0/n),u=Math.floor(t.x1/n),d=Math.floor(t.y0/n),f=Math.floor(t.y1/n);if(!(e>r&&!(i(t.chart,e,l,d)&&i(t.chart,e,u,d)&&i(t.chart,e,l,f)&&i(t.chart,e,u,f)))){a.add(p(t.chart,e,l,d)),u!==l&&a.add(p(t.chart,e,u,d)),f!==d&&(a.add(p(t.chart,e,l,f)),u!==l&&a.add(p(t.chart,e,u,f)));return}}}),a}function b(e,t,n,r,i,a){for(let o=t;o>=0;o--){let t=_(o,n,r,i,a),s=(n,r)=>{let i=e(p(t.chart,o,Math.floor(n/a),Math.floor(r/a)));return i&&i[r%a*a+n%a]},c=s(t.x0,t.y0),l=s(t.x1,t.y0),u=s(t.x0,t.y1),d=s(t.x1,t.y1);if(c===void 0||l===void 0||u===void 0||d===void 0)continue;let f=c+(l-c)*t.fx;return f+(u+(d-u)*t.fx-f)*t.fy}}function x(e,t,n,r,i,a){let o=new Float32Array(e.at.length*t.at.length),s=!1;return v(e,t,(e,t,c,l,u)=>{let d=b(n,t,c,l,u,r);d===void 0?s=!0:o[e]=a+i*d/65535}),s?null:o}function S(e,t){let n=new Uint8Array(t*t*2);for(let r=0;r<t;r++){let i=(t-1-r)*t*4,a=r*t*2;for(let r=0;r<t;r++,i+=4,a+=2)n[a]=e[i],n[a+1]=e[i+1]}return n}function C(e,t){let n=new Float32Array(t*t);for(let r=0;r<t;r++){let i=(t-1-r)*t*4,a=(r+1)*t;for(let o=r*t;o<a;o++,i+=4)n[o]=(e[i]<<8|e[i+1])+e[i+2]/256}return n}function w(e,t,n){let r=n?new Float32Array(t*t):new Uint16Array(t*t);for(let t=0,i=0;t<r.length;t++,i+=4){let a=e[i]<<8|e[i+1];r[t]=n?a+e[i+2]/256:a}return r}const T=self,E=new Map;let D;async function O(e,t){let n=await fetch(e,{signal:t});if(!n.ok)throw Error(`tile ${n.status}`);let r=await createImageBitmap(await n.blob(),{premultiplyAlpha:`none`,colorSpaceConversion:`none`}),i=r.width;D?.width!==i&&(D=new OffscreenCanvas(i,i));let a=D.getContext(`2d`,{willReadFrequently:!0});if(!a)throw Error(`no 2d canvas in this worker`);return a.globalCompositeOperation=`copy`,a.drawImage(r,0,0),r.close(),{rgba:a.getImageData(0,0,i,i).data,size:i}}async function k(e,t){let{rgba:n,size:r}=await O(e.url,t),i=e.fine?C(n,r):S(n,r);T.postMessage({type:`tile`,id:e.id,size:r,heights:i},[i.buffer])}const A=new Map;let j=``;async function M(r,i){let{rows:a,columns:o,tileSize:s,maxLevel:c,patches:u}=r,d=[r.version,...u.map(e=>e.version)],f=`${r.root}?${d.join()}`;f!==j&&A.clear(),j=f;let p=(e,t,n,r)=>l(u,e,t,n,r),h=y(a,o,s,c,(e,t,n,r)=>p(e,t,n,r)!==void 0),g=[...h].filter(e=>!A.has(e));await Promise.all(Array.from({length:6},async()=>{for(let t=g.pop();t!==void 0;t=g.pop()){let{chart:a,level:o,x:s,y:l}=m(t),u=o>c,d=u?p(a,o,s,l)?.version:r.version,f=`${r.root}${e(n[a],o,s,l)}?v=${d}`;try{let{rgba:e,size:n}=await O(f,i);A.set(t,w(e,n,u))}catch{if(i.aborted)return}}})),i.throwIfAborted();let _=x(a,o,e=>A.get(e),s,r.scaleKm,r.biasKm);for(let e of[...A.keys()])h.has(e)||A.delete(e);if(!_)throw Error(`no height tiles for the grid`);let v=t(r.radius,a.at,o.at,_,r.unitsPerKm),b=v.indices.slice();T.postMessage({type:`grid`,id:r.id,...v,indices:b,riseKm:_},[v.positions.buffer,v.normals.buffer,v.uvs.buffer,b.buffer,_.buffer])}async function N(e){let t=new AbortController;E.set(e.id,t);try{e.type===`tile`?await k(e,t.signal):await M(e,t.signal)}catch{T.postMessage({type:`failed`,id:e.id})}finally{E.delete(e.id)}}self.onmessage=e=>{let t=e.data;t.type===`abort`?E.get(t.id)?.abort():N(t)};";
|
|
12897
13730
|
var blob$1 = typeof self !== "undefined" && self.Blob && new Blob(["URL.revokeObjectURL(import.meta.url);", jsContent$1], { type: "text/javascript;charset=utf-8" });
|
|
12898
13731
|
function WorkerWrapper$1(options) {
|
|
12899
13732
|
let objURL;
|
|
@@ -13088,34 +13921,40 @@ function placeWindow(chart, level, x, y, previous) {
|
|
|
13088
13921
|
ty0
|
|
13089
13922
|
};
|
|
13090
13923
|
}
|
|
13091
|
-
function
|
|
13924
|
+
function patchChart(patch) {
|
|
13925
|
+
return patch.cap === "north" ? 1 : patch.cap === "south" ? 2 : 0;
|
|
13926
|
+
}
|
|
13927
|
+
function inRange(patch, chart, level, x, y) {
|
|
13092
13928
|
const up = patch.max_level - level;
|
|
13093
|
-
return up >= 0 && x >= patch.x[0] >> up && x <= patch.x[1] >> up && y >= patch.y[0] >> up && y <= patch.y[1] >> up;
|
|
13929
|
+
return up >= 0 && patchChart(patch) === chart && x >= patch.x[0] >> up && x <= patch.x[1] >> up && y >= patch.y[0] >> up && y <= patch.y[1] >> up;
|
|
13094
13930
|
}
|
|
13095
|
-
function patchOfTile(patches, level, x, y) {
|
|
13096
|
-
return patches?.find((patch) => inRange(patch, level, x, y));
|
|
13931
|
+
function patchOfTile(patches, chart, level, x, y) {
|
|
13932
|
+
return patches?.find((patch) => inRange(patch, chart, level, x, y));
|
|
13097
13933
|
}
|
|
13098
|
-
function
|
|
13099
|
-
const over = pyramid.patches?.filter((patch) => level > pyramid.max_level - (patch.map_levels ?? 0) && inRange(patch, level, x, y));
|
|
13934
|
+
function tileVersion(pyramid, chart, level, x, y) {
|
|
13935
|
+
const over = pyramid.patches?.filter((patch) => level > pyramid.max_level - (patch.map_levels ?? 0) && inRange(patch, chart, level, x, y));
|
|
13100
13936
|
return over?.length ? over.map((patch) => patch.version).join(".") : pyramid.version;
|
|
13101
13937
|
}
|
|
13102
13938
|
var REACH_TILES = 4 / 2 - .5;
|
|
13103
|
-
function deepestLevel(maxLevel, patches, x, y) {
|
|
13939
|
+
function deepestLevel(maxLevel, patches, chart, x, y) {
|
|
13104
13940
|
let deepest = maxLevel;
|
|
13105
13941
|
if (!patches) return deepest;
|
|
13106
|
-
for (const patch of patches)
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
|
|
13942
|
+
for (const patch of patches) {
|
|
13943
|
+
if (patchChart(patch) !== chart) continue;
|
|
13944
|
+
for (let level = patch.max_level; level > deepest; level--) {
|
|
13945
|
+
const up = patch.max_level - level;
|
|
13946
|
+
const columns = chartColumns(chart, level);
|
|
13947
|
+
const x0 = patch.x[0] >> up;
|
|
13948
|
+
const y0 = patch.y[0] >> up;
|
|
13949
|
+
const halfWidth = ((patch.x[1] >> up) + 1 - x0) / 2;
|
|
13950
|
+
const halfHeight = ((patch.y[1] >> up) + 1 - y0) / 2;
|
|
13951
|
+
let east = x * columns - (x0 + halfWidth);
|
|
13952
|
+
if (chart === 0) east -= columns * Math.round(east / columns);
|
|
13953
|
+
const south = y * chartRows(chart, level) - (y0 + halfHeight);
|
|
13954
|
+
if (Math.abs(east) - halfWidth <= REACH_TILES && Math.abs(south) - halfHeight <= REACH_TILES) {
|
|
13955
|
+
deepest = level;
|
|
13956
|
+
break;
|
|
13957
|
+
}
|
|
13119
13958
|
}
|
|
13120
13959
|
}
|
|
13121
13960
|
return deepest;
|
|
@@ -13123,7 +13962,7 @@ function deepestLevel(maxLevel, patches, x, y) {
|
|
|
13123
13962
|
function windowHome(placement, x, y) {
|
|
13124
13963
|
const columns = chartColumns(placement.chart, placement.level);
|
|
13125
13964
|
let east = x * columns - placement.tx0;
|
|
13126
|
-
east -= columns * Math.round(east / columns);
|
|
13965
|
+
if (placement.chart === 0) east -= columns * Math.round(east / columns);
|
|
13127
13966
|
return {
|
|
13128
13967
|
x: east,
|
|
13129
13968
|
y: y * chartRows(placement.chart, placement.level) - placement.ty0
|
|
@@ -13141,6 +13980,7 @@ function slotTile(placement, sx, sy) {
|
|
|
13141
13980
|
|
|
13142
13981
|
//#endregion
|
|
13143
13982
|
//#region src/lib/scene/render-tier.ts
|
|
13983
|
+
var MB = 1024 * 1024;
|
|
13144
13984
|
var RENDER_TIER_PRESETS = {
|
|
13145
13985
|
low: {
|
|
13146
13986
|
maxPixelRatio: 1.25,
|
|
@@ -13154,7 +13994,10 @@ var RENDER_TIER_PRESETS = {
|
|
|
13154
13994
|
reliefWindows: 2,
|
|
13155
13995
|
reliefShadowSteps: 12,
|
|
13156
13996
|
terrainBandCells: 16,
|
|
13157
|
-
maxSkyboxFace: 2048
|
|
13997
|
+
maxSkyboxFace: 2048,
|
|
13998
|
+
starPoints: 12e5,
|
|
13999
|
+
starBytes: 80 * MB,
|
|
14000
|
+
starFetches: 3
|
|
13158
14001
|
},
|
|
13159
14002
|
medium: {
|
|
13160
14003
|
maxPixelRatio: 1.5,
|
|
@@ -13168,7 +14011,10 @@ var RENDER_TIER_PRESETS = {
|
|
|
13168
14011
|
reliefWindows: 3,
|
|
13169
14012
|
reliefShadowSteps: 16,
|
|
13170
14013
|
terrainBandCells: 24,
|
|
13171
|
-
maxSkyboxFace: 2048
|
|
14014
|
+
maxSkyboxFace: 2048,
|
|
14015
|
+
starPoints: 16e5,
|
|
14016
|
+
starBytes: 160 * MB,
|
|
14017
|
+
starFetches: 4
|
|
13172
14018
|
},
|
|
13173
14019
|
high: {
|
|
13174
14020
|
maxPixelRatio: 2,
|
|
@@ -13182,7 +14028,10 @@ var RENDER_TIER_PRESETS = {
|
|
|
13182
14028
|
reliefWindows: 4,
|
|
13183
14029
|
reliefShadowSteps: 32,
|
|
13184
14030
|
terrainBandCells: 32,
|
|
13185
|
-
maxSkyboxFace: Infinity
|
|
14031
|
+
maxSkyboxFace: Infinity,
|
|
14032
|
+
starPoints: 25e5,
|
|
14033
|
+
starBytes: 400 * MB,
|
|
14034
|
+
starFetches: 6
|
|
13186
14035
|
}
|
|
13187
14036
|
};
|
|
13188
14037
|
function maxTextureTier() {
|
|
@@ -13204,7 +14053,7 @@ function resolveRenderTier() {
|
|
|
13204
14053
|
const measured = sceneSettings().atmosphereCalibration?.tier;
|
|
13205
14054
|
if (measured === "low" || measured === "medium") return measured;
|
|
13206
14055
|
if (measured === "high" || measured === "ultra") return "high";
|
|
13207
|
-
if (isCoarsePointer()) return isLowEndDevice() ? "low" : "medium";
|
|
14056
|
+
if (isCoarsePointer$1()) return isLowEndDevice() ? "low" : "medium";
|
|
13208
14057
|
return isLowEndDevice() ? "medium" : "high";
|
|
13209
14058
|
}
|
|
13210
14059
|
function currentRenderTier() {
|
|
@@ -13214,6 +14063,15 @@ function renderPixelRatio() {
|
|
|
13214
14063
|
if (typeof window === "undefined") return 1;
|
|
13215
14064
|
return Math.min(window.devicePixelRatio, currentRenderTier().maxPixelRatio);
|
|
13216
14065
|
}
|
|
14066
|
+
function starBudget() {
|
|
14067
|
+
const tier = currentRenderTier();
|
|
14068
|
+
const bytes = isLowEndDevice() ? Math.min(tier.starBytes, 64 * MB) : tier.starBytes;
|
|
14069
|
+
return {
|
|
14070
|
+
points: tier.starPoints,
|
|
14071
|
+
bytes,
|
|
14072
|
+
fetches: tier.starFetches
|
|
14073
|
+
};
|
|
14074
|
+
}
|
|
13217
14075
|
|
|
13218
14076
|
//#endregion
|
|
13219
14077
|
//#region src/lib/scene/objects/surface/tile-chain.ts
|
|
@@ -13247,9 +14105,9 @@ void tileCap( vec3 dir, vec3 ddx, vec3 ddy, out float side, out mat3x2 cap ) {
|
|
|
13247
14105
|
);
|
|
13248
14106
|
}
|
|
13249
14107
|
|
|
13250
|
-
// The point at + off on the map
|
|
13251
|
-
//
|
|
13252
|
-
// and a tile index of a patch takes them all
|
|
14108
|
+
// The point at + off on the map and on the cap of its own hemisphere, which
|
|
14109
|
+
// side gives, each counted from at. A position on the sphere has seven
|
|
14110
|
+
// digits, and a tile index of a patch takes them all. The offset keeps its own.
|
|
13253
14111
|
void tileChartsFrom( vec3 at, vec3 off, out mat3x2 map, out mat3x2 cap, out float side ) {
|
|
13254
14112
|
vec3 pos = at + off;
|
|
13255
14113
|
vec3 ox = dFdx( off );
|
|
@@ -13271,12 +14129,19 @@ void tileChartsFrom( vec3 at, vec3 off, out mat3x2 map, out mat3x2 cap, out floa
|
|
|
13271
14129
|
( pos.z * oy.x - pos.x * oy.z ) * east, ( pos.y * ( pos.x * oy.x + pos.z * oy.z ) / flat1 - flat1 * oy.y ) * south
|
|
13272
14130
|
);
|
|
13273
14131
|
|
|
13274
|
-
// A cap goes no deeper than the pyramid, so the direction itself will do.
|
|
13275
14132
|
float range = length( pos );
|
|
13276
14133
|
vec3 dir = pos / range;
|
|
13277
14134
|
vec3 ddx = ( ox - dir * dot( dir, ox ) ) / range;
|
|
13278
14135
|
vec3 ddy = ( oy - dir * dot( dir, oy ) ) / range;
|
|
13279
14136
|
tileCap( dir, ddx, ddy, side, cap );
|
|
14137
|
+
|
|
14138
|
+
// The point on the cap, counted from at.
|
|
14139
|
+
float reach = length( at );
|
|
14140
|
+
float foldAt = max( reach + side * at.y, 1e-6 * reach );
|
|
14141
|
+
float grow = ( 2.0 * dot( at, off ) + dot( off, off ) ) / ( range + reach );
|
|
14142
|
+
vec2 planeAt = vec2( at.x, side * at.z );
|
|
14143
|
+
vec2 shift = vec2( off.x, side * off.z ) * foldAt - planeAt * ( grow + side * off.y );
|
|
14144
|
+
cap[ 0 ] = shift / ( ( range + side * pos.y ) * foldAt ) * ( 0.5 / ${CAP_EDGE.toFixed(8)} );
|
|
13280
14145
|
}
|
|
13281
14146
|
|
|
13282
14147
|
// Long and short half-axes of the footprint whose sides are fx and fy. The
|
|
@@ -13319,7 +14184,7 @@ float tileWindowWeight( mat3x2 map, mat3x2 cap, float side, vec4 window, vec4 gr
|
|
|
13319
14184
|
|
|
13320
14185
|
float rows = ${4 .toFixed(1)};
|
|
13321
14186
|
// Offset from the window's north-west corner, in tiles.
|
|
13322
|
-
vec2 rel =
|
|
14187
|
+
vec2 rel = home + chart[ 0 ] * grid.xy;
|
|
13323
14188
|
if ( onMap ) {
|
|
13324
14189
|
// Half a pixel in from a pole of the map: past it the filter would
|
|
13325
14190
|
// read the far row. The map's longitude wraps.
|
|
@@ -13421,8 +14286,8 @@ function release(store, win) {
|
|
|
13421
14286
|
store.free.push(win.layer);
|
|
13422
14287
|
}
|
|
13423
14288
|
function patchOf(tiles, win, tile) {
|
|
13424
|
-
if (win.
|
|
13425
|
-
return patchOfTile(tiles.patches, win.level, tile.x, tile.y);
|
|
14289
|
+
if (win.level <= tiles.max_level) return void 0;
|
|
14290
|
+
return patchOfTile(tiles.patches, win.chart, win.level, tile.x, tile.y);
|
|
13426
14291
|
}
|
|
13427
14292
|
function exists(tiles, win, slot) {
|
|
13428
14293
|
if (win.level <= tiles.max_level) return true;
|
|
@@ -13478,7 +14343,7 @@ function pump(chain, store, tiles, win, x, y, renderer, invalidate) {
|
|
|
13478
14343
|
});
|
|
13479
14344
|
chain.inflight++;
|
|
13480
14345
|
const folder = CHART_FOLDER[win.chart];
|
|
13481
|
-
const version = patchOf(tiles, win, tile)?.version ?? (win.chart
|
|
14346
|
+
const version = patchOf(tiles, win, tile)?.version ?? tileVersion(tiles, win.chart, win.level, tile.x, tile.y);
|
|
13482
14347
|
const pyramid = {
|
|
13483
14348
|
id: tiles.id,
|
|
13484
14349
|
version
|
|
@@ -13533,9 +14398,9 @@ function pointUnderCamera(mesh, camera) {
|
|
|
13533
14398
|
return over.copy(camera.position).applyMatrix4(inverse.copy(mesh.matrixWorld).invert()).normalize();
|
|
13534
14399
|
}
|
|
13535
14400
|
function finestLevelAt$1(chain, tiles, over) {
|
|
13536
|
-
|
|
13537
|
-
const point = chartPoint(
|
|
13538
|
-
return deepestLevel(tiles.max_level, tiles.patches, point.x, point.y);
|
|
14401
|
+
const chart = cameraChart(over.y, chain?.chart);
|
|
14402
|
+
const point = chartPoint(chart, over.x, over.y, over.z);
|
|
14403
|
+
return deepestLevel(tiles.max_level, tiles.patches, chart, point.x, point.y);
|
|
13539
14404
|
}
|
|
13540
14405
|
function chainLength(tiles, limit) {
|
|
13541
14406
|
let finest = tiles.max_level;
|
|
@@ -13557,7 +14422,11 @@ function updateChain(chain, tiles, count, level, over, radius, frame, renderer,
|
|
|
13557
14422
|
anchor.set(Math.fround(over.x * radius), Math.fround(over.y * radius), Math.fround(over.z * radius));
|
|
13558
14423
|
chain.uniforms.anchor.value.copy(anchor);
|
|
13559
14424
|
const range = anchor.length();
|
|
13560
|
-
const
|
|
14425
|
+
const homes = [
|
|
14426
|
+
0,
|
|
14427
|
+
1,
|
|
14428
|
+
2
|
|
14429
|
+
].map((chart) => chartPoint(chart, anchor.x / range, anchor.y / range, anchor.z / range));
|
|
13561
14430
|
const windowOf = (link) => store.windows.find((win) => win.chart === link.chart && win.level === link.level);
|
|
13562
14431
|
store.windows = store.windows.filter((win) => {
|
|
13563
14432
|
win.rank = links.findIndex((link) => link.chart === win.chart && link.level === win.level);
|
|
@@ -13598,10 +14467,9 @@ function updateChain(chain, tiles, count, level, over, radius, frame, renderer,
|
|
|
13598
14467
|
continue;
|
|
13599
14468
|
}
|
|
13600
14469
|
chain.uniforms.grid.value[i].set(chartColumns(win.chart, win.level), chartRows(win.chart, win.level), win.placement.tx0, win.placement.ty0);
|
|
13601
|
-
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
}
|
|
14470
|
+
const home = homes[win.chart];
|
|
14471
|
+
const from = windowHome(win.placement, home.x, home.y);
|
|
14472
|
+
chain.uniforms.home.value[i].set(from.x, from.y);
|
|
13605
14473
|
link.set(win.chart, win.layer, win.level, 1);
|
|
13606
14474
|
let loaded = 0;
|
|
13607
14475
|
for (let slot = 0; slot < SLOTS; slot++) if (isLoaded(store, win.layer, slot)) loaded |= 1 << slot;
|
|
@@ -13716,10 +14584,10 @@ void reliefSight( int i, mat3x2 map, mat3x2 cap, float side, vec2 mapStep, vec2
|
|
|
13716
14584
|
vec4 grid = uReliefGrid[ i ];
|
|
13717
14585
|
bool onMap = window.x < 0.5;
|
|
13718
14586
|
float rows = ${4 .toFixed(1)};
|
|
13719
|
-
// In tiles from the window's north-west corner
|
|
13720
|
-
//
|
|
13721
|
-
// nearest the window.
|
|
13722
|
-
vec2 rel =
|
|
14587
|
+
// In tiles from the window's north-west corner, counted from the point
|
|
14588
|
+
// under the camera. On the map the fragment is then on the side of the
|
|
14589
|
+
// globe nearest the window.
|
|
14590
|
+
vec2 rel = uReliefHome[ i ] + ( onMap ? map[ 0 ] : cap[ 0 ] ) * grid.xy;
|
|
13723
14591
|
vec2 rate = ( onMap ? mapStep : capStep ) * grid.xy;
|
|
13724
14592
|
|
|
13725
14593
|
// Half a pixel in from the window's sides: past it the filter reads the
|
|
@@ -15770,6 +16638,7 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
15770
16638
|
try {
|
|
15771
16639
|
const detail = await fetchObjectDetail(bo.body.data.id, false);
|
|
15772
16640
|
bo.body.pointing = detail.global?.pointing;
|
|
16641
|
+
bo.body.landedHeadings = landedHeadings(detail.global?.events?.items);
|
|
15773
16642
|
const attitudeManifest = detail.global?.attitude;
|
|
15774
16643
|
if (attitudeManifest && (bo.modelLoadEpoch ?? 0) === epoch) {
|
|
15775
16644
|
const probeId = bo.body.data.id.replace(/^probe-/, "");
|
|
@@ -15809,8 +16678,9 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
15809
16678
|
bo.model = root;
|
|
15810
16679
|
bo.modelName = slug;
|
|
15811
16680
|
setHaloLoading(bo, false);
|
|
15812
|
-
|
|
15813
|
-
|
|
16681
|
+
const spanM = meta.exports[tier]?.scale_meters;
|
|
16682
|
+
if (spanM) {
|
|
16683
|
+
const spanKm = spanM / 2e3;
|
|
15814
16684
|
bo.body.data.radiusKm = spanKm * (meta.body_span_ratio ?? 1);
|
|
15815
16685
|
bo.radiusScene = kmToScene(effectiveRadiusKm(bo.body.data));
|
|
15816
16686
|
bo.modelUnitOverride = meta.body_span_ratio ? kmToScene(spanKm) : void 0;
|
|
@@ -15923,6 +16793,7 @@ function unloadBodyModel(bo) {
|
|
|
15923
16793
|
bo.model = null;
|
|
15924
16794
|
bo.modelName = void 0;
|
|
15925
16795
|
bo.body.pointing = void 0;
|
|
16796
|
+
bo.body.landedHeadings = void 0;
|
|
15926
16797
|
bo.body.attitudeTrack = void 0;
|
|
15927
16798
|
bo.body.modelBaseFrame = void 0;
|
|
15928
16799
|
if (bo.mesh && restoreSphere) bo.mesh.visible = true;
|
|
@@ -15942,6 +16813,17 @@ function setHaloLoading(bo, loading) {
|
|
|
15942
16813
|
bo.loadingShown = void 0;
|
|
15943
16814
|
}
|
|
15944
16815
|
}
|
|
16816
|
+
function landedHeadings(events) {
|
|
16817
|
+
const out = [];
|
|
16818
|
+
for (const e of events ?? []) {
|
|
16819
|
+
const deg = e.type === "landing" ? e.site?.heading_deg : void 0;
|
|
16820
|
+
if (deg !== void 0) out.push({
|
|
16821
|
+
jd: e.jd,
|
|
16822
|
+
deg
|
|
16823
|
+
});
|
|
16824
|
+
}
|
|
16825
|
+
return out.length ? out : void 0;
|
|
16826
|
+
}
|
|
15945
16827
|
function withBaseFrame(scene, q) {
|
|
15946
16828
|
if (!q) return scene;
|
|
15947
16829
|
const wrapper = new Group();
|
|
@@ -17318,7 +18200,7 @@ function setLabelName(bo, name, variant, isLarge) {
|
|
|
17318
18200
|
}
|
|
17319
18201
|
function getLabelVariant(body) {
|
|
17320
18202
|
const t = body.data.objectType;
|
|
17321
|
-
if (isMajorBody(t) || t === ObjectType.STAR || isAsteroid(t) || t === ObjectType.COMET || t === ObjectType.BARYCENTER || t === ObjectType.LAGRANGE_POINT) return "major";
|
|
18203
|
+
if (isMajorBody(t) || t === ObjectType.STAR || t === ObjectType.EXTRASOLAR || isAsteroid(t) || t === ObjectType.COMET || t === ObjectType.BARYCENTER || t === ObjectType.LAGRANGE_POINT) return "major";
|
|
17322
18204
|
if (t === ObjectType.SPACECRAFT) return "spacecraft";
|
|
17323
18205
|
if (t === ObjectType.DEBRIS) return "debris";
|
|
17324
18206
|
return "none";
|
|
@@ -18277,6 +19159,231 @@ async function loadSkybox(scene, renderer, ctx, disposed = () => false) {
|
|
|
18277
19159
|
}
|
|
18278
19160
|
}
|
|
18279
19161
|
|
|
19162
|
+
//#endregion
|
|
19163
|
+
//#region src/lib/scene/starfield/tree.ts
|
|
19164
|
+
var PC_AU = 648e3 / Math.PI;
|
|
19165
|
+
var PC_SCENE = PC_AU * 10;
|
|
19166
|
+
function childCentre(centre, half, k) {
|
|
19167
|
+
const q = half / 2;
|
|
19168
|
+
return [
|
|
19169
|
+
centre[0] + (k & 1 ? q : -q),
|
|
19170
|
+
centre[1] + (k & 2 ? q : -q),
|
|
19171
|
+
centre[2] + (k & 4 ? q : -q)
|
|
19172
|
+
];
|
|
19173
|
+
}
|
|
19174
|
+
function boxDistance(p, centre, half) {
|
|
19175
|
+
const dx = Math.max(Math.abs(p[0] - centre[0]) - half, 0);
|
|
19176
|
+
const dy = Math.max(Math.abs(p[1] - centre[1]) - half, 0);
|
|
19177
|
+
const dz = Math.max(Math.abs(p[2] - centre[2]) - half, 0);
|
|
19178
|
+
return Math.hypot(dx, dy, dz);
|
|
19179
|
+
}
|
|
19180
|
+
function boxMagnitude(distance, range, rangeLimitMag) {
|
|
19181
|
+
if (distance <= 0) return -Infinity;
|
|
19182
|
+
if (range <= 0) return Infinity;
|
|
19183
|
+
return rangeLimitMag + 5 * Math.log10(distance / range);
|
|
19184
|
+
}
|
|
19185
|
+
function sceneToTree(x, y, z) {
|
|
19186
|
+
const eq = sceneToEquatorial(x, y, z, {
|
|
19187
|
+
x: 0,
|
|
19188
|
+
y: 0,
|
|
19189
|
+
z: 0
|
|
19190
|
+
});
|
|
19191
|
+
return [
|
|
19192
|
+
eq.x / PC_SCENE,
|
|
19193
|
+
eq.y / PC_SCENE,
|
|
19194
|
+
eq.z / PC_SCENE
|
|
19195
|
+
];
|
|
19196
|
+
}
|
|
19197
|
+
function treeToScene(x, y, z, out) {
|
|
19198
|
+
return equatorialToScene(x * PC_SCENE, y * PC_SCENE, z * PC_SCENE, out);
|
|
19199
|
+
}
|
|
19200
|
+
function icrsToTree(raDeg, decDeg, distancePc) {
|
|
19201
|
+
const ra = raDeg * Math.PI / 180;
|
|
19202
|
+
const dec = decDeg * Math.PI / 180;
|
|
19203
|
+
const c = Math.cos(dec) * distancePc;
|
|
19204
|
+
return [
|
|
19205
|
+
c * Math.cos(ra),
|
|
19206
|
+
c * Math.sin(ra),
|
|
19207
|
+
Math.sin(dec) * distancePc
|
|
19208
|
+
];
|
|
19209
|
+
}
|
|
19210
|
+
function treeMatrixElements(focus, out) {
|
|
19211
|
+
const x = treeToScene(1, 0, 0, {
|
|
19212
|
+
x: 0,
|
|
19213
|
+
y: 0,
|
|
19214
|
+
z: 0
|
|
19215
|
+
});
|
|
19216
|
+
const y = treeToScene(0, 1, 0, {
|
|
19217
|
+
x: 0,
|
|
19218
|
+
y: 0,
|
|
19219
|
+
z: 0
|
|
19220
|
+
});
|
|
19221
|
+
const z = treeToScene(0, 0, 1, {
|
|
19222
|
+
x: 0,
|
|
19223
|
+
y: 0,
|
|
19224
|
+
z: 0
|
|
19225
|
+
});
|
|
19226
|
+
out.length = 16;
|
|
19227
|
+
out[0] = x.x;
|
|
19228
|
+
out[1] = x.y;
|
|
19229
|
+
out[2] = x.z;
|
|
19230
|
+
out[3] = 0;
|
|
19231
|
+
out[4] = y.x;
|
|
19232
|
+
out[5] = y.y;
|
|
19233
|
+
out[6] = y.z;
|
|
19234
|
+
out[7] = 0;
|
|
19235
|
+
out[8] = z.x;
|
|
19236
|
+
out[9] = z.y;
|
|
19237
|
+
out[10] = z.z;
|
|
19238
|
+
out[11] = 0;
|
|
19239
|
+
out[12] = -focus[0];
|
|
19240
|
+
out[13] = -focus[1];
|
|
19241
|
+
out[14] = -focus[2];
|
|
19242
|
+
out[15] = 1;
|
|
19243
|
+
return out;
|
|
19244
|
+
}
|
|
19245
|
+
|
|
19246
|
+
//#endregion
|
|
19247
|
+
//#region src/lib/scene/starfield/record.ts
|
|
19248
|
+
var RENDER_EPOCH = 2016;
|
|
19249
|
+
var MAG_STEP = 1 / 1024;
|
|
19250
|
+
var G_MINUS_V = [
|
|
19251
|
+
-.02907,
|
|
19252
|
+
-.02385,
|
|
19253
|
+
-.2297,
|
|
19254
|
+
-.001768
|
|
19255
|
+
];
|
|
19256
|
+
var B_V_RANGE = [-.3, 2.4];
|
|
19257
|
+
var G_MINUS_HP = -.28;
|
|
19258
|
+
var BP_RP_FROM_V_I = [
|
|
19259
|
+
-.01612,
|
|
19260
|
+
1.274,
|
|
19261
|
+
-.08143
|
|
19262
|
+
];
|
|
19263
|
+
var V_I_RANGE = [-.5, 3.5];
|
|
19264
|
+
var MAS_PER_RAD = 180 / Math.PI * 36e5;
|
|
19265
|
+
function clamp$3(x, [lo, hi]) {
|
|
19266
|
+
return Math.min(Math.max(x, lo), hi);
|
|
19267
|
+
}
|
|
19268
|
+
function poly(x, coefficients) {
|
|
19269
|
+
let out = 0;
|
|
19270
|
+
for (let i = coefficients.length - 1; i >= 0; i--) out = out * x + coefficients[i];
|
|
19271
|
+
return out;
|
|
19272
|
+
}
|
|
19273
|
+
function propagate(raDeg, decDeg, pmra, pmdec, years) {
|
|
19274
|
+
const ra = raDeg * Math.PI / 180;
|
|
19275
|
+
const dec = decDeg * Math.PI / 180;
|
|
19276
|
+
const rate = years / MAS_PER_RAD;
|
|
19277
|
+
const [cr, sr, cd, sd] = [
|
|
19278
|
+
Math.cos(ra),
|
|
19279
|
+
Math.sin(ra),
|
|
19280
|
+
Math.cos(dec),
|
|
19281
|
+
Math.sin(dec)
|
|
19282
|
+
];
|
|
19283
|
+
const x = cd * cr + rate * (pmra * -sr + pmdec * -sd * cr);
|
|
19284
|
+
const y = cd * sr + rate * (pmra * cr + pmdec * -sd * sr);
|
|
19285
|
+
const z = sd + rate * (pmdec * cd);
|
|
19286
|
+
const n = Math.hypot(x, y, z);
|
|
19287
|
+
return [(Math.atan2(y / n, x / n) * 180 / Math.PI % 360 + 360) % 360, Math.asin(Math.min(Math.max(z / n, -1), 1)) * 180 / Math.PI];
|
|
19288
|
+
}
|
|
19289
|
+
function starPlace(row) {
|
|
19290
|
+
const distancePc = parallaxDistancePc(row);
|
|
19291
|
+
let ra = row.number("ra");
|
|
19292
|
+
let dec = row.number("dec");
|
|
19293
|
+
if (distancePc === null || ra === null || dec === null) return null;
|
|
19294
|
+
const years = RENDER_EPOCH - (row.number("epoch") ?? 2016);
|
|
19295
|
+
const pmra = row.number("pmra");
|
|
19296
|
+
if (years !== 0 && pmra !== null) [ra, dec] = propagate(ra, dec, pmra, row.number("pmdec") ?? 0, years);
|
|
19297
|
+
const tree = icrsToTree(ra, dec, distancePc);
|
|
19298
|
+
const s = treeToScene(tree[0], tree[1], tree[2], {
|
|
19299
|
+
x: 0,
|
|
19300
|
+
y: 0,
|
|
19301
|
+
z: 0
|
|
19302
|
+
});
|
|
19303
|
+
return {
|
|
19304
|
+
ra,
|
|
19305
|
+
dec,
|
|
19306
|
+
distancePc,
|
|
19307
|
+
tree,
|
|
19308
|
+
scene: [
|
|
19309
|
+
s.x,
|
|
19310
|
+
s.y,
|
|
19311
|
+
s.z
|
|
19312
|
+
]
|
|
19313
|
+
};
|
|
19314
|
+
}
|
|
19315
|
+
function gEstimate(v, bv, hp) {
|
|
19316
|
+
if (v !== null && bv !== null) return v + poly(clamp$3(bv, B_V_RANGE), G_MINUS_V);
|
|
19317
|
+
return hp === null ? null : hp + G_MINUS_HP;
|
|
19318
|
+
}
|
|
19319
|
+
function apparentG(row) {
|
|
19320
|
+
return row.number("phot_g_mean_mag") ?? gEstimate(row.number("v_mag"), row.number("b_v"), row.number("hp_mag"));
|
|
19321
|
+
}
|
|
19322
|
+
function absoluteG(row) {
|
|
19323
|
+
const g = apparentG(row);
|
|
19324
|
+
const parallax = row.number("parallax");
|
|
19325
|
+
if (g === null || parallaxDistancePc(row) === null || parallax === null) return null;
|
|
19326
|
+
return Math.round((g + 5 * Math.log10(parallax) - 10) / MAG_STEP) * MAG_STEP;
|
|
19327
|
+
}
|
|
19328
|
+
function colourBpRp(row) {
|
|
19329
|
+
const bp = row.number("phot_bp_mean_mag");
|
|
19330
|
+
const rp = row.number("phot_rp_mean_mag");
|
|
19331
|
+
if (bp !== null && rp !== null) return bp - rp;
|
|
19332
|
+
const vi = row.number("v_i");
|
|
19333
|
+
return vi === null ? null : poly(clamp$3(vi, V_I_RANGE), BP_RP_FROM_V_I);
|
|
19334
|
+
}
|
|
19335
|
+
|
|
19336
|
+
//#endregion
|
|
19337
|
+
//#region src/lib/scene/focus/star-focus.ts
|
|
19338
|
+
function makeStarBody(record) {
|
|
19339
|
+
const home = homeRow(record);
|
|
19340
|
+
const place = home ? starPlace(home) : null;
|
|
19341
|
+
return {
|
|
19342
|
+
data: {
|
|
19343
|
+
id: extrasolarObjectId(record.id),
|
|
19344
|
+
name: record.metadata.name,
|
|
19345
|
+
objectType: ObjectType.EXTRASOLAR,
|
|
19346
|
+
parentId: "",
|
|
19347
|
+
radiusKm: NaN,
|
|
19348
|
+
hasLocalized: false,
|
|
19349
|
+
a: 0,
|
|
19350
|
+
e: 0,
|
|
19351
|
+
i: 0,
|
|
19352
|
+
om: 0,
|
|
19353
|
+
w: 0,
|
|
19354
|
+
ma: 0,
|
|
19355
|
+
n: 0,
|
|
19356
|
+
epoch: 0,
|
|
19357
|
+
validityStart: -Infinity,
|
|
19358
|
+
validityEnd: Infinity,
|
|
19359
|
+
orbitalSource: OrbitalSource.UNKNOWN
|
|
19360
|
+
},
|
|
19361
|
+
position: null,
|
|
19362
|
+
extrasolar: {
|
|
19363
|
+
id: record.id,
|
|
19364
|
+
record,
|
|
19365
|
+
scene: place?.scene ?? null,
|
|
19366
|
+
tree: place?.tree ?? null,
|
|
19367
|
+
absMag: home ? absoluteG(home) : null,
|
|
19368
|
+
colour: home ? colourBpRp(home) : null
|
|
19369
|
+
}
|
|
19370
|
+
};
|
|
19371
|
+
}
|
|
19372
|
+
|
|
19373
|
+
//#endregion
|
|
19374
|
+
//#region src/lib/scene/setup/extrasolar.ts
|
|
19375
|
+
async function ensureExtrasolarBody(ctx, objectId) {
|
|
19376
|
+
const text = objectId.slice("x-".length);
|
|
19377
|
+
const first = await extrasolarLookup.lookup(text);
|
|
19378
|
+
const result = first.kind === "alias" ? await extrasolarLookup.live(text) : first;
|
|
19379
|
+
if (result.kind === "live") {
|
|
19380
|
+
const held = ctx.getBody(extrasolarObjectId(result.record.id));
|
|
19381
|
+
const body = held?.extrasolar ? held : makeStarBody(result.record);
|
|
19382
|
+
ctx.bodies.addExtrasolar(body, first.kind === "alias" ? [objectId] : []);
|
|
19383
|
+
}
|
|
19384
|
+
return result;
|
|
19385
|
+
}
|
|
19386
|
+
|
|
18280
19387
|
//#endregion
|
|
18281
19388
|
//#region src/lib/scene/setup/scene-load.ts
|
|
18282
19389
|
var EAGER_ZOOM1_PARTS = 13;
|
|
@@ -18385,6 +19492,7 @@ async function loadScene(ctx, date, targetId) {
|
|
|
18385
19492
|
});
|
|
18386
19493
|
const probePromise = metadataPromise.then((metadata) => buildProbeStore(metadata, jd, ctx.layers));
|
|
18387
19494
|
const passengerPromise = passengerFor(targetId);
|
|
19495
|
+
const extrasolarTarget = targetId && isExtrasolarObjectId(targetId) ? ensureExtrasolarBody(ctx, targetId).catch((e) => console.warn(`scene-load: ${targetId} did not load:`, e)) : null;
|
|
18388
19496
|
const metadata = await metadataPromise;
|
|
18389
19497
|
loadProgress.reach("metadata");
|
|
18390
19498
|
ctx.chebStore = await chebPromise;
|
|
@@ -18410,7 +19518,8 @@ async function loadScene(ctx, date, targetId) {
|
|
|
18410
19518
|
addedSinceFlush = false;
|
|
18411
19519
|
ctx.bodies.flushMinor(added);
|
|
18412
19520
|
};
|
|
18413
|
-
if (
|
|
19521
|
+
if (extrasolarTarget) await extrasolarTarget;
|
|
19522
|
+
else if (targetId && !ctx.getBody(targetId)) {
|
|
18414
19523
|
const placeholders = await createPlaceholderBody(targetId, ctx);
|
|
18415
19524
|
if (routePlaceholders(ctx, placeholders, labels) > 0) noteAdded();
|
|
18416
19525
|
if (placeholders.length > 0) flush();
|
|
@@ -18418,7 +19527,7 @@ async function loadScene(ctx, date, targetId) {
|
|
|
18418
19527
|
ctx.bodies.majorBodies = major.filter((b) => b.data.objectType !== ObjectType.BARYCENTER && b.data.objectType !== ObjectType.LAGRANGE_POINT && b.data.orbitalSource !== OrbitalSource.SPICE_PROBE);
|
|
18419
19528
|
try {
|
|
18420
19529
|
ctx.refresher = new ZoneRefresher(ctx, metadata, loader, date);
|
|
18421
|
-
if (targetId) ctx.refresher.want(targetId);
|
|
19530
|
+
if (targetId && !extrasolarTarget) ctx.refresher.want(targetId);
|
|
18422
19531
|
} catch (e) {
|
|
18423
19532
|
console.error("scene-load: ZoneRefresher init failed; time-segmented zones stay empty:", e);
|
|
18424
19533
|
}
|
|
@@ -18587,12 +19696,17 @@ var ContextManager = class {
|
|
|
18587
19696
|
console.error("[scene-load]", e);
|
|
18588
19697
|
}
|
|
18589
19698
|
refreshTick(date) {
|
|
19699
|
+
if (this.visibility.systemShare === 0) return;
|
|
18590
19700
|
this.refresher?.tick(date);
|
|
18591
19701
|
}
|
|
18592
19702
|
async ready(date) {
|
|
18593
19703
|
await this.refresher?.ready(date);
|
|
18594
19704
|
}
|
|
18595
19705
|
async ensureBody(targetId, date) {
|
|
19706
|
+
if (isExtrasolarObjectId(targetId)) {
|
|
19707
|
+
await ensureExtrasolarBody(this, targetId);
|
|
19708
|
+
return;
|
|
19709
|
+
}
|
|
18596
19710
|
await this.refresher?.ensureBody(targetId, date);
|
|
18597
19711
|
}
|
|
18598
19712
|
applyGroupFilter(slug) {
|
|
@@ -18713,6 +19827,7 @@ var SimClock = class {
|
|
|
18713
19827
|
prevScale = 1;
|
|
18714
19828
|
stops = null;
|
|
18715
19829
|
settleTimer = null;
|
|
19830
|
+
arrivals = [];
|
|
18716
19831
|
constructor(initialJd, live = false) {
|
|
18717
19832
|
this.jd = initialJd;
|
|
18718
19833
|
this.settledJd = initialJd;
|
|
@@ -18769,6 +19884,19 @@ var SimClock = class {
|
|
|
18769
19884
|
this.setJD(jd);
|
|
18770
19885
|
this.jumps++;
|
|
18771
19886
|
this.settle();
|
|
19887
|
+
const jumps = this.jumps;
|
|
19888
|
+
return new Promise((resolve) => this.arrivals.push({
|
|
19889
|
+
jumps,
|
|
19890
|
+
resolve
|
|
19891
|
+
}));
|
|
19892
|
+
}
|
|
19893
|
+
placed(jumps) {
|
|
19894
|
+
if (this.arrivals.length === 0) return;
|
|
19895
|
+
this.arrivals = this.arrivals.filter((arrival) => {
|
|
19896
|
+
if (arrival.jumps > jumps) return true;
|
|
19897
|
+
arrival.resolve();
|
|
19898
|
+
return false;
|
|
19899
|
+
});
|
|
18772
19900
|
}
|
|
18773
19901
|
settle() {
|
|
18774
19902
|
if (this.settleTimer !== null) clearTimeout(this.settleTimer);
|
|
@@ -18809,7 +19937,7 @@ var SimClock = class {
|
|
|
18809
19937
|
return jdToDate(this.jd);
|
|
18810
19938
|
}
|
|
18811
19939
|
setDate(date) {
|
|
18812
|
-
this.jumpTo(dateToJD(date));
|
|
19940
|
+
return this.jumpTo(dateToJD(date));
|
|
18813
19941
|
}
|
|
18814
19942
|
};
|
|
18815
19943
|
|
|
@@ -19290,6 +20418,9 @@ function installOutputDither(pass) {
|
|
|
19290
20418
|
//#endregion
|
|
19291
20419
|
//#region src/lib/scene/setup/three-boot.ts
|
|
19292
20420
|
var CAMERA_FAR_DEFAULT = 1e5;
|
|
20421
|
+
var CAMERA_MAX_DISTANCE = 1e5 * PC_SCENE;
|
|
20422
|
+
var SOLAR_MAX_DISTANCE = 31620.5 * 10;
|
|
20423
|
+
var CAMERA_FAR_REVERSED = 1e6 * PC_SCENE;
|
|
19293
20424
|
function probeClipControl() {
|
|
19294
20425
|
try {
|
|
19295
20426
|
const gl = document.createElement("canvas").getContext("webgl2");
|
|
@@ -19343,7 +20474,9 @@ function bootThree(canvas, labelContainer, ctx) {
|
|
|
19343
20474
|
shadowLight.shadow.bias = -1e-4;
|
|
19344
20475
|
scene.add(shadowLight);
|
|
19345
20476
|
scene.add(shadowLight.target);
|
|
19346
|
-
const
|
|
20477
|
+
const aspect = canvas.clientWidth / canvas.clientHeight;
|
|
20478
|
+
const far = renderer.capabilities.reversedDepthBuffer ? CAMERA_FAR_REVERSED : CAMERA_FAR_DEFAULT;
|
|
20479
|
+
const camera = new PerspectiveCamera(60, aspect, kmToScene(.001), far);
|
|
19347
20480
|
const composerTarget = new WebGLRenderTarget(canvas.clientWidth, canvas.clientHeight, {
|
|
19348
20481
|
type: HalfFloatType,
|
|
19349
20482
|
resolveDepthBuffer: false,
|
|
@@ -19375,38 +20508,1006 @@ function bootThree(canvas, labelContainer, ctx) {
|
|
|
19375
20508
|
};
|
|
19376
20509
|
}
|
|
19377
20510
|
|
|
20511
|
+
//#endregion
|
|
20512
|
+
//#region src/lib/scene/starfield/band.ts
|
|
20513
|
+
var STARS_IN_AU = 2e3;
|
|
20514
|
+
var STARS_OUT_AU = 1800;
|
|
20515
|
+
function starsTakeSky(distanceAu, taken) {
|
|
20516
|
+
if (distanceAu > 2e3) return true;
|
|
20517
|
+
if (distanceAu < 1800) return false;
|
|
20518
|
+
return taken;
|
|
20519
|
+
}
|
|
20520
|
+
|
|
20521
|
+
//#endregion
|
|
20522
|
+
//#region src/lib/scene/starfield/colour.ts
|
|
20523
|
+
var BP_RP_TEFF = [
|
|
20524
|
+
[-.12, 10700],
|
|
20525
|
+
[-.087, 10400],
|
|
20526
|
+
[-.037, 9700],
|
|
20527
|
+
[.005, 9300],
|
|
20528
|
+
[.068, 8800],
|
|
20529
|
+
[.11, 8600],
|
|
20530
|
+
[.166, 8250],
|
|
20531
|
+
[.194, 8100],
|
|
20532
|
+
[.222, 7910],
|
|
20533
|
+
[.263, 7760],
|
|
20534
|
+
[.32, 7590],
|
|
20535
|
+
[.327, 7400],
|
|
20536
|
+
[.377, 7220],
|
|
20537
|
+
[.434, 7020],
|
|
20538
|
+
[.49, 6820],
|
|
20539
|
+
[.518, 6750],
|
|
20540
|
+
[.546, 6670],
|
|
20541
|
+
[.587, 6550],
|
|
20542
|
+
[.64, 6350],
|
|
20543
|
+
[.67, 6280],
|
|
20544
|
+
[.694, 6180],
|
|
20545
|
+
[.719, 6050],
|
|
20546
|
+
[.767, 5990],
|
|
20547
|
+
[.784, 5930],
|
|
20548
|
+
[.803, 5860],
|
|
20549
|
+
[.823, 5770],
|
|
20550
|
+
[.832, 5720],
|
|
20551
|
+
[.841, 5680],
|
|
20552
|
+
[.85, 5660],
|
|
20553
|
+
[.869, 5600],
|
|
20554
|
+
[.88, 5550],
|
|
20555
|
+
[.9, 5480],
|
|
20556
|
+
[.95, 5380],
|
|
20557
|
+
[.983, 5270],
|
|
20558
|
+
[1.01, 5170],
|
|
20559
|
+
[1.1, 5100],
|
|
20560
|
+
[1.21, 4830],
|
|
20561
|
+
[1.34, 4600],
|
|
20562
|
+
[1.43, 4440],
|
|
20563
|
+
[1.53, 4300],
|
|
20564
|
+
[1.7, 4100],
|
|
20565
|
+
[1.73, 3990],
|
|
20566
|
+
[1.79, 3930],
|
|
20567
|
+
[1.84, 3850],
|
|
20568
|
+
[1.97, 3770],
|
|
20569
|
+
[2.09, 3660],
|
|
20570
|
+
[2.13, 3620],
|
|
20571
|
+
[2.23, 3560],
|
|
20572
|
+
[2.39, 3470],
|
|
20573
|
+
[2.5, 3430],
|
|
20574
|
+
[2.78, 3270],
|
|
20575
|
+
[2.94, 3210],
|
|
20576
|
+
[3.16, 3110],
|
|
20577
|
+
[3.35, 3060],
|
|
20578
|
+
[3.71, 2930],
|
|
20579
|
+
[4.16, 2810],
|
|
20580
|
+
[4.5, 2740],
|
|
20581
|
+
[4.65, 2680],
|
|
20582
|
+
[4.72, 2630],
|
|
20583
|
+
[4.86, 2570],
|
|
20584
|
+
[5.1, 2420]
|
|
20585
|
+
];
|
|
20586
|
+
function teffFromBpRp(bpRp) {
|
|
20587
|
+
const table = BP_RP_TEFF;
|
|
20588
|
+
if (bpRp <= table[0][0]) return table[0][1];
|
|
20589
|
+
const last = table[table.length - 1];
|
|
20590
|
+
if (bpRp >= last[0]) return last[1];
|
|
20591
|
+
let lo = 0;
|
|
20592
|
+
let hi = table.length - 1;
|
|
20593
|
+
while (hi - lo > 1) {
|
|
20594
|
+
const mid = lo + hi >> 1;
|
|
20595
|
+
if (table[mid][0] <= bpRp) lo = mid;
|
|
20596
|
+
else hi = mid;
|
|
20597
|
+
}
|
|
20598
|
+
const [c0, t0] = table[lo];
|
|
20599
|
+
const [c1, t1] = table[hi];
|
|
20600
|
+
return t0 + (bpRp - c0) / (c1 - c0) * (t1 - t0);
|
|
20601
|
+
}
|
|
20602
|
+
function planckianRgb(teff, out) {
|
|
20603
|
+
const t = Math.min(Math.max(teff, 1667), 25e3);
|
|
20604
|
+
const t1 = 1e3 / t;
|
|
20605
|
+
const t2 = t1 * t1;
|
|
20606
|
+
const t3 = t2 * t1;
|
|
20607
|
+
const x = t <= 4e3 ? -.2661239 * t3 - .2343589 * t2 + .8776956 * t1 + .17991 : -3.0258469 * t3 + 2.1070379 * t2 + .2226347 * t1 + .24039;
|
|
20608
|
+
const x2 = x * x;
|
|
20609
|
+
const x3 = x2 * x;
|
|
20610
|
+
let y;
|
|
20611
|
+
if (t <= 2222) y = -1.1063814 * x3 - 1.3481102 * x2 + 2.18555832 * x - .20219683;
|
|
20612
|
+
else if (t <= 4e3) y = -.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - .16748867;
|
|
20613
|
+
else y = 3.081758 * x3 - 5.8733867 * x2 + 3.75112997 * x - .37001483;
|
|
20614
|
+
const X = x / y;
|
|
20615
|
+
const Z = (1 - x - y) / y;
|
|
20616
|
+
const r = Math.max(3.2404542 * X - 1.5371385 - .4985314 * Z, 0);
|
|
20617
|
+
const g = Math.max(-.969266 * X + 1.8760108 + .041556 * Z, 0);
|
|
20618
|
+
const b = Math.max(.0556434 * X - .2040259 + 1.0572252 * Z, 0);
|
|
20619
|
+
const peak = Math.max(r, g, b);
|
|
20620
|
+
out[0] = r / peak;
|
|
20621
|
+
out[1] = g / peak;
|
|
20622
|
+
out[2] = b / peak;
|
|
20623
|
+
return out;
|
|
20624
|
+
}
|
|
20625
|
+
var LUT_LO = BP_RP_TEFF[0][0];
|
|
20626
|
+
var LUT_HI = BP_RP_TEFF[BP_RP_TEFF.length - 1][0];
|
|
20627
|
+
var LUT_SIZE = 512;
|
|
20628
|
+
var lut = null;
|
|
20629
|
+
function buildLut() {
|
|
20630
|
+
const bytes = new Uint8Array(LUT_SIZE * 3);
|
|
20631
|
+
const rgb = [
|
|
20632
|
+
0,
|
|
20633
|
+
0,
|
|
20634
|
+
0
|
|
20635
|
+
];
|
|
20636
|
+
for (let i = 0; i < LUT_SIZE; i++) {
|
|
20637
|
+
planckianRgb(teffFromBpRp(LUT_LO + (LUT_HI - LUT_LO) * i / (LUT_SIZE - 1)), rgb);
|
|
20638
|
+
bytes[3 * i] = Math.round(rgb[0] * 255);
|
|
20639
|
+
bytes[3 * i + 1] = Math.round(rgb[1] * 255);
|
|
20640
|
+
bytes[3 * i + 2] = Math.round(rgb[2] * 255);
|
|
20641
|
+
}
|
|
20642
|
+
return bytes;
|
|
20643
|
+
}
|
|
20644
|
+
function starColours(bpRp, out) {
|
|
20645
|
+
const table = lut ??= buildLut();
|
|
20646
|
+
const scale = (LUT_SIZE - 1) / (LUT_HI - LUT_LO);
|
|
20647
|
+
for (let i = 0; i < bpRp.length; i++) {
|
|
20648
|
+
const c = bpRp[i];
|
|
20649
|
+
if (Number.isNaN(c)) {
|
|
20650
|
+
out[3 * i] = 255;
|
|
20651
|
+
out[3 * i + 1] = 255;
|
|
20652
|
+
out[3 * i + 2] = 255;
|
|
20653
|
+
continue;
|
|
20654
|
+
}
|
|
20655
|
+
const at = 3 * Math.round(Math.min(Math.max((c - LUT_LO) * scale, 0), LUT_SIZE - 1));
|
|
20656
|
+
out[3 * i] = table[at];
|
|
20657
|
+
out[3 * i + 1] = table[at + 1];
|
|
20658
|
+
out[3 * i + 2] = table[at + 2];
|
|
20659
|
+
}
|
|
20660
|
+
return out;
|
|
20661
|
+
}
|
|
20662
|
+
|
|
20663
|
+
//#endregion
|
|
20664
|
+
//#region src/lib/scene/starfield/material.ts
|
|
20665
|
+
function starUniforms() {
|
|
20666
|
+
return {
|
|
20667
|
+
uLimit: { value: 7 },
|
|
20668
|
+
uPixelRatio: { value: 1 },
|
|
20669
|
+
uGain: { value: 1 }
|
|
20670
|
+
};
|
|
20671
|
+
}
|
|
20672
|
+
var FADE_MAG = 1;
|
|
20673
|
+
var CONTRAST = .75;
|
|
20674
|
+
var CORE_AT_LIMIT = .06;
|
|
20675
|
+
var DOT_PX = 2.5;
|
|
20676
|
+
var MAX_DOT_PX = 14;
|
|
20677
|
+
var starVertexShader = (pick = false) => `
|
|
20678
|
+
uniform float uLimit;
|
|
20679
|
+
uniform float uPixelRatio;
|
|
20680
|
+
uniform float uGain;
|
|
20681
|
+
uniform float uFade;
|
|
20682
|
+
uniform float uFloor;
|
|
20683
|
+
uniform int uHidden;
|
|
20684
|
+
attribute float mag;
|
|
20685
|
+
attribute vec3 starColour;
|
|
20686
|
+
varying vec3 vColour;
|
|
20687
|
+
${pick ? "uniform highp uint uPickBase;\nflat varying highp uint vPick;" : ""}
|
|
20688
|
+
|
|
20689
|
+
void main() {
|
|
20690
|
+
${pick ? "vPick = uPickBase + uint(gl_VertexID);" : ""}
|
|
20691
|
+
vec4 mv = modelViewMatrix * vec4(position, 1.0);
|
|
20692
|
+
float pc = length(mv.xyz) / ${PC_SCENE.toFixed(4)};
|
|
20693
|
+
float m = mag + 5.0 * log2(max(pc, 1e-9)) * 0.30103 - 5.0;
|
|
20694
|
+
float above = max(uLimit - m, uFloor);
|
|
20695
|
+
if (above <= 0.0 || mv.z >= 0.0 || gl_VertexID == uHidden) {
|
|
20696
|
+
// Too faint, behind the camera, or drawn on its own: put it off screen.
|
|
20697
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
20698
|
+
gl_PointSize = 0.0;
|
|
20699
|
+
vColour = vec3(0.0);
|
|
20700
|
+
return;
|
|
20701
|
+
}
|
|
20702
|
+
float fade = smoothstep(0.0, ${FADE_MAG.toFixed(1)}, above) * uFade;
|
|
20703
|
+
// Energy relative to a star at the limit. Past a full core, the dot grows.
|
|
20704
|
+
float energy = ${CORE_AT_LIMIT.toFixed(3)} * exp2(above * ${(.4 * CONTRAST * Math.log2(10)).toFixed(5)});
|
|
20705
|
+
float core = min(energy, 1.0);
|
|
20706
|
+
float size = ${DOT_PX.toFixed(2)} * sqrt(max(energy, 1.0));
|
|
20707
|
+
vColour = starColour * core * fade * uGain;
|
|
20708
|
+
gl_Position = projectionMatrix * mv;
|
|
20709
|
+
// Past the far plane a star stays on it, behind everything else.
|
|
20710
|
+
${isReversedDepth() ? "gl_Position.z = max(gl_Position.z, 0.0);" : "gl_Position.z = min(gl_Position.z, gl_Position.w);"}
|
|
20711
|
+
gl_PointSize = min(size, ${MAX_DOT_PX.toFixed(1)}) * uPixelRatio;
|
|
20712
|
+
}
|
|
20713
|
+
`;
|
|
20714
|
+
var FRAGMENT = `
|
|
20715
|
+
varying vec3 vColour;
|
|
20716
|
+
|
|
20717
|
+
void main() {
|
|
20718
|
+
vec2 c = gl_PointCoord * 2.0 - 1.0;
|
|
20719
|
+
float r2 = dot(c, c);
|
|
20720
|
+
if (r2 > 1.0) discard;
|
|
20721
|
+
gl_FragColor = vec4(vColour * exp(-5.0 * r2), 1.0);
|
|
20722
|
+
}
|
|
20723
|
+
`;
|
|
20724
|
+
var PICK_MIN_LIGHT = 1 / 255;
|
|
20725
|
+
var pickFragment = () => `
|
|
20726
|
+
varying vec3 vColour;
|
|
20727
|
+
flat varying highp uint vPick;
|
|
20728
|
+
|
|
20729
|
+
void main() {
|
|
20730
|
+
vec2 c = gl_PointCoord * 2.0 - 1.0;
|
|
20731
|
+
float r2 = dot(c, c);
|
|
20732
|
+
if (r2 > 1.0) discard;
|
|
20733
|
+
float light = max(vColour.r, max(vColour.g, vColour.b)) * exp(-5.0 * r2);
|
|
20734
|
+
if (light < ${PICK_MIN_LIGHT.toFixed(6)}) discard;
|
|
20735
|
+
gl_FragDepth = ${isReversedDepth() ? "light / (1.0 + light)" : "1.0 / (1.0 + light)"};
|
|
20736
|
+
gl_FragColor = vec4(
|
|
20737
|
+
float(vPick & 255u),
|
|
20738
|
+
float((vPick >> 8) & 255u),
|
|
20739
|
+
float((vPick >> 16) & 255u),
|
|
20740
|
+
float(vPick >> 24)
|
|
20741
|
+
) / 255.0;
|
|
20742
|
+
}
|
|
20743
|
+
`;
|
|
20744
|
+
var NO_FLOOR = -1e9;
|
|
20745
|
+
function makeStarMaterial(shared, floor = NO_FLOOR) {
|
|
20746
|
+
return new ShaderMaterial({
|
|
20747
|
+
uniforms: {
|
|
20748
|
+
...shared,
|
|
20749
|
+
uFade: { value: 0 },
|
|
20750
|
+
uFloor: { value: floor },
|
|
20751
|
+
uHidden: { value: -1 }
|
|
20752
|
+
},
|
|
20753
|
+
vertexShader: starVertexShader(),
|
|
20754
|
+
fragmentShader: FRAGMENT,
|
|
20755
|
+
blending: CustomBlending,
|
|
20756
|
+
blendEquation: AddEquation,
|
|
20757
|
+
blendSrc: OneMinusDstColorFactor,
|
|
20758
|
+
blendDst: OneFactor,
|
|
20759
|
+
transparent: true,
|
|
20760
|
+
depthTest: true,
|
|
20761
|
+
depthWrite: false
|
|
20762
|
+
});
|
|
20763
|
+
}
|
|
20764
|
+
function makeStarPickMaterial(draw, base) {
|
|
20765
|
+
return new ShaderMaterial({
|
|
20766
|
+
uniforms: {
|
|
20767
|
+
...draw.uniforms,
|
|
20768
|
+
uPixelRatio: { value: 1 },
|
|
20769
|
+
uPickBase: { value: base }
|
|
20770
|
+
},
|
|
20771
|
+
vertexShader: starVertexShader(true),
|
|
20772
|
+
fragmentShader: pickFragment(),
|
|
20773
|
+
blending: NoBlending,
|
|
20774
|
+
depthTest: true,
|
|
20775
|
+
depthWrite: true
|
|
20776
|
+
});
|
|
20777
|
+
}
|
|
20778
|
+
|
|
20779
|
+
//#endregion
|
|
20780
|
+
//#region src/lib/scene/starfield/starfield.ts
|
|
20781
|
+
var TREE = "mw";
|
|
20782
|
+
var ROOT = "r";
|
|
20783
|
+
var METADATA = "metadata.json";
|
|
20784
|
+
var BASE_LIMIT_MAG = 7.5;
|
|
20785
|
+
var EXPOSURE_PC = 1;
|
|
20786
|
+
var MUST_LOAD_MAG = 1;
|
|
20787
|
+
var ABORT_MARGIN_MAG = 1;
|
|
20788
|
+
var LIMIT_EASE_MS = 300;
|
|
20789
|
+
var NODE_FADE_MS = 400;
|
|
20790
|
+
var SELECT_EVERY_MS = 100;
|
|
20791
|
+
var ADDS_PER_FRAME = 4;
|
|
20792
|
+
var BOOT_GATE_MS = 1e4;
|
|
20793
|
+
var FOCUS_FLOOR_MAG = 3;
|
|
20794
|
+
var NO_MAGNITUDE = 99;
|
|
20795
|
+
var STAR_RENDER_ORDER = -1;
|
|
20796
|
+
var NODE_PICK_PREFIX = "star:";
|
|
20797
|
+
var FOCUS_PICK_GROUP = "star-focus";
|
|
20798
|
+
var _view = new Vector3();
|
|
20799
|
+
var StarfieldSystem = class {
|
|
20800
|
+
group = new Group();
|
|
20801
|
+
uniforms = starUniforms();
|
|
20802
|
+
tree = null;
|
|
20803
|
+
credits = [];
|
|
20804
|
+
root = null;
|
|
20805
|
+
nodes = /* @__PURE__ */ new Map();
|
|
20806
|
+
drawn = /* @__PURE__ */ new Set();
|
|
20807
|
+
retries = new RetryBackoff(() => this.markDirty());
|
|
20808
|
+
idLoads = /* @__PURE__ */ new Map();
|
|
20809
|
+
loading = 0;
|
|
20810
|
+
cachedBytes = 0;
|
|
20811
|
+
drawable;
|
|
20812
|
+
metadataLoading = false;
|
|
20813
|
+
bootGate = null;
|
|
20814
|
+
stale = false;
|
|
20815
|
+
disposed = false;
|
|
20816
|
+
dirty = true;
|
|
20817
|
+
lastSelectMs = -Infinity;
|
|
20818
|
+
lastFrameMs = 0;
|
|
20819
|
+
targetLimit = BASE_LIMIT_MAG;
|
|
20820
|
+
allowedLimit = BASE_LIMIT_MAG;
|
|
20821
|
+
creditsShown = false;
|
|
20822
|
+
active = false;
|
|
20823
|
+
ready = false;
|
|
20824
|
+
skyTaken = false;
|
|
20825
|
+
sky = new SwitchFade();
|
|
20826
|
+
matrix = [];
|
|
20827
|
+
focusStar = null;
|
|
20828
|
+
focusPoints = null;
|
|
20829
|
+
focusPick = null;
|
|
20830
|
+
constructor(ctx, scene, budget, onChange, pickRegistry) {
|
|
20831
|
+
this.ctx = ctx;
|
|
20832
|
+
this.budget = budget;
|
|
20833
|
+
this.onChange = onChange;
|
|
20834
|
+
this.pickRegistry = pickRegistry;
|
|
20835
|
+
this.group.name = "starfield";
|
|
20836
|
+
this.group.matrixAutoUpdate = false;
|
|
20837
|
+
this.group.visible = false;
|
|
20838
|
+
scene.add(this.group);
|
|
20839
|
+
const open = extrasolarBase() !== "" && !ctx.layers.skipped.has("starfield");
|
|
20840
|
+
this.drawable = open ? "unknown" : "no";
|
|
20841
|
+
}
|
|
20842
|
+
setBudget(budget) {
|
|
20843
|
+
this.budget = budget;
|
|
20844
|
+
this.markDirty();
|
|
20845
|
+
}
|
|
20846
|
+
setFocusStar(star) {
|
|
20847
|
+
const next = star?.tree ? star : null;
|
|
20848
|
+
if (next?.id === this.focusStar?.id) return;
|
|
20849
|
+
this.focusStar = next;
|
|
20850
|
+
if (this.focusPoints) {
|
|
20851
|
+
this.focusPoints.removeFromParent();
|
|
20852
|
+
this.focusPoints.geometry.dispose();
|
|
20853
|
+
this.focusPoints.material.dispose();
|
|
20854
|
+
this.focusPoints = null;
|
|
20855
|
+
}
|
|
20856
|
+
this.dropPick(this.focusPick, FOCUS_PICK_GROUP);
|
|
20857
|
+
this.focusPick = null;
|
|
20858
|
+
if (next) this.focusPoints = this.focusPoint(next);
|
|
20859
|
+
for (const node of this.drawn) if (node.points) this.applyHidden(node);
|
|
20860
|
+
this.onChange();
|
|
20861
|
+
}
|
|
20862
|
+
treeEntry() {
|
|
20863
|
+
return this.tree;
|
|
20864
|
+
}
|
|
20865
|
+
drawnNodes() {
|
|
20866
|
+
const out = [];
|
|
20867
|
+
for (const node of this.drawn) {
|
|
20868
|
+
const view = this.view(node);
|
|
20869
|
+
if (view) out.push(view);
|
|
20870
|
+
}
|
|
20871
|
+
return out;
|
|
20872
|
+
}
|
|
20873
|
+
node(name) {
|
|
20874
|
+
const node = this.nodes.get(name);
|
|
20875
|
+
return node ? this.view(node) : void 0;
|
|
20876
|
+
}
|
|
20877
|
+
pickDraws() {
|
|
20878
|
+
if (!this.group.visible) return [];
|
|
20879
|
+
const out = [];
|
|
20880
|
+
for (const node of this.drawn) {
|
|
20881
|
+
const points = node.points;
|
|
20882
|
+
if (!points?.visible || !node.data) continue;
|
|
20883
|
+
node.pick ??= {
|
|
20884
|
+
base: this.pickRegistry.allocateRows(NODE_PICK_PREFIX + node.name, node.data.container.rows),
|
|
20885
|
+
material: null
|
|
20886
|
+
};
|
|
20887
|
+
out.push({
|
|
20888
|
+
points,
|
|
20889
|
+
material: this.pickMaterial(node.pick, points)
|
|
20890
|
+
});
|
|
20891
|
+
}
|
|
20892
|
+
const focus = this.focusPoints;
|
|
20893
|
+
if (focus) {
|
|
20894
|
+
this.focusPick ??= {
|
|
20895
|
+
base: this.pickRegistry.allocateRows(FOCUS_PICK_GROUP, 1),
|
|
20896
|
+
material: null
|
|
20897
|
+
};
|
|
20898
|
+
out.push({
|
|
20899
|
+
points: focus,
|
|
20900
|
+
material: this.pickMaterial(this.focusPick, focus)
|
|
20901
|
+
});
|
|
20902
|
+
}
|
|
20903
|
+
return out;
|
|
20904
|
+
}
|
|
20905
|
+
pickedStar(pickId) {
|
|
20906
|
+
const at = this.pickRegistry.locate(pickId);
|
|
20907
|
+
if (!at) return null;
|
|
20908
|
+
if (at.groupId === FOCUS_PICK_GROUP) return this.focusStar ? { id: this.focusStar.id } : null;
|
|
20909
|
+
if (!at.groupId.startsWith(NODE_PICK_PREFIX)) return null;
|
|
20910
|
+
return {
|
|
20911
|
+
node: at.groupId.slice(5),
|
|
20912
|
+
row: at.row
|
|
20913
|
+
};
|
|
20914
|
+
}
|
|
20915
|
+
async starId(star) {
|
|
20916
|
+
if ("id" in star) return star.id;
|
|
20917
|
+
const id = (this.nodes.get(star.node)?.ids?.column ?? await this.idsOf(star.node))[star.row];
|
|
20918
|
+
if (!id) throw new Error(`[starfield] row ${star.row} of node ${star.node} has no ID`);
|
|
20919
|
+
return id;
|
|
20920
|
+
}
|
|
20921
|
+
idsOf(name) {
|
|
20922
|
+
let load = this.idLoads.get(name);
|
|
20923
|
+
if (!load) {
|
|
20924
|
+
load = this.loadIds(name).finally(() => this.idLoads.delete(name));
|
|
20925
|
+
this.idLoads.set(name, load);
|
|
20926
|
+
}
|
|
20927
|
+
return load;
|
|
20928
|
+
}
|
|
20929
|
+
async loadIds(name) {
|
|
20930
|
+
const tree = this.tree;
|
|
20931
|
+
if (!tree) throw new Error("[starfield] no tree to read IDs from");
|
|
20932
|
+
const container = await fetchExtrasolarFile(extrasolarPath(tree.ids, { name }), tree.token, { priority: "high" });
|
|
20933
|
+
const node = this.nodes.get(name);
|
|
20934
|
+
const rows = node?.data?.container.rows;
|
|
20935
|
+
if (rows !== void 0 && container.rows !== rows) throw new Error(`[starfield] ID file of node ${name} has ${container.rows} rows, not ${rows}`);
|
|
20936
|
+
const column = container.ids("id");
|
|
20937
|
+
if (node?.data && !node.ids) {
|
|
20938
|
+
node.ids = {
|
|
20939
|
+
column,
|
|
20940
|
+
bytes: container.buffer.byteLength
|
|
20941
|
+
};
|
|
20942
|
+
this.cachedBytes += container.buffer.byteLength;
|
|
20943
|
+
}
|
|
20944
|
+
return column;
|
|
20945
|
+
}
|
|
20946
|
+
pickMaterial(range, points) {
|
|
20947
|
+
range.material ??= makeStarPickMaterial(points.material, range.base);
|
|
20948
|
+
return range.material;
|
|
20949
|
+
}
|
|
20950
|
+
dropPick(range, groupId) {
|
|
20951
|
+
if (!range) return;
|
|
20952
|
+
range.material?.dispose();
|
|
20953
|
+
this.pickRegistry.release(groupId);
|
|
20954
|
+
}
|
|
20955
|
+
rowPosition(name, row) {
|
|
20956
|
+
const data = this.nodes.get(name)?.data;
|
|
20957
|
+
if (!data || row < 0 || row >= data.container.rows) return null;
|
|
20958
|
+
const at = data.geometry.getAttribute("position").array;
|
|
20959
|
+
const [cx, cy, cz] = data.info.centre;
|
|
20960
|
+
const p = treeToScene(cx + at[3 * row], cy + at[3 * row + 1], cz + at[3 * row + 2], {
|
|
20961
|
+
x: 0,
|
|
20962
|
+
y: 0,
|
|
20963
|
+
z: 0
|
|
20964
|
+
});
|
|
20965
|
+
return [
|
|
20966
|
+
p.x,
|
|
20967
|
+
p.y,
|
|
20968
|
+
p.z
|
|
20969
|
+
];
|
|
20970
|
+
}
|
|
20971
|
+
stats() {
|
|
20972
|
+
let drawnPoints = 0;
|
|
20973
|
+
for (const node of this.drawn) drawnPoints += node.data?.container.rows ?? 0;
|
|
20974
|
+
let cachedNodes = 0;
|
|
20975
|
+
for (const node of this.nodes.values()) if (node.data) cachedNodes++;
|
|
20976
|
+
return {
|
|
20977
|
+
drawnNodes: this.drawn.size,
|
|
20978
|
+
drawnPoints,
|
|
20979
|
+
cachedNodes,
|
|
20980
|
+
cachedBytes: this.cachedBytes,
|
|
20981
|
+
loading: this.loading,
|
|
20982
|
+
limit: this.uniforms.uLimit.value,
|
|
20983
|
+
targetLimit: this.targetLimit,
|
|
20984
|
+
active: this.active,
|
|
20985
|
+
share: this.sky.share
|
|
20986
|
+
};
|
|
20987
|
+
}
|
|
20988
|
+
share() {
|
|
20989
|
+
return this.sky.share;
|
|
20990
|
+
}
|
|
20991
|
+
availability() {
|
|
20992
|
+
return this.drawable;
|
|
20993
|
+
}
|
|
20994
|
+
busy() {
|
|
20995
|
+
if (this.loading > 0) return true;
|
|
20996
|
+
if (this.sky.fading) return true;
|
|
20997
|
+
if (this.drawn.size === 0) return false;
|
|
20998
|
+
if (Math.abs(this.uniforms.uLimit.value - this.allowedLimit) > .01) return true;
|
|
20999
|
+
for (const node of this.drawn) {
|
|
21000
|
+
if (node.fade !== (node.wanted ? 1 : 0)) return true;
|
|
21001
|
+
if (node.wanted && node.data && !node.points) return true;
|
|
21002
|
+
}
|
|
21003
|
+
return false;
|
|
21004
|
+
}
|
|
21005
|
+
update(camera, focus, pixelRatio, gain, nowMs) {
|
|
21006
|
+
const dtMs = this.lastFrameMs ? Math.min(nowMs - this.lastFrameMs, 250) : 0;
|
|
21007
|
+
this.lastFrameMs = nowMs;
|
|
21008
|
+
const wx = camera.position.x + focus[0];
|
|
21009
|
+
const wy = camera.position.y + focus[1];
|
|
21010
|
+
const wz = camera.position.z + focus[2];
|
|
21011
|
+
const au = Math.hypot(wx, wy, wz) / 10;
|
|
21012
|
+
if (!this.active && au > 500) this.active = true;
|
|
21013
|
+
else if (this.active && au < 400) this.stopLoading();
|
|
21014
|
+
if (this.active) this.start();
|
|
21015
|
+
const shown = this.ctx.layers.isVisible("starfield");
|
|
21016
|
+
this.skyTaken = starsTakeSky(au, this.skyTaken);
|
|
21017
|
+
this.sky.step(shown && this.ready && this.root !== null && this.skyTaken, dtMs);
|
|
21018
|
+
const skyShare = this.sky.share;
|
|
21019
|
+
this.group.visible = shown && this.root !== null && skyShare > 0;
|
|
21020
|
+
if (!this.root) return;
|
|
21021
|
+
this.group.matrix.fromArray(treeMatrixElements(focus, this.matrix));
|
|
21022
|
+
this.group.matrixWorldNeedsUpdate = true;
|
|
21023
|
+
const cam = sceneToTree(wx, wy, wz);
|
|
21024
|
+
const fromSunPc = Math.hypot(wx, wy, wz) / PC_SCENE;
|
|
21025
|
+
const target = BASE_LIMIT_MAG + 5 * Math.log10(Math.max(1, fromSunPc / EXPOSURE_PC));
|
|
21026
|
+
if (target !== this.targetLimit) this.dirty = true;
|
|
21027
|
+
this.targetLimit = target;
|
|
21028
|
+
const due = this.dirty || nowMs - this.lastSelectMs >= SELECT_EVERY_MS;
|
|
21029
|
+
if (this.active && !this.stale && due) this.select(cam, nowMs);
|
|
21030
|
+
const limit = this.uniforms.uLimit;
|
|
21031
|
+
const ease = dtMs > 0 ? 1 - Math.exp(-dtMs / LIMIT_EASE_MS) : 1;
|
|
21032
|
+
limit.value += (this.allowedLimit - limit.value) * ease;
|
|
21033
|
+
this.uniforms.uPixelRatio.value = pixelRatio;
|
|
21034
|
+
this.uniforms.uGain.value = gain * skyShare;
|
|
21035
|
+
this.applyFades(dtMs, camera, focus);
|
|
21036
|
+
if (this.group.visible && !this.creditsShown && this.drawn.size > 0) {
|
|
21037
|
+
this.creditsShown = true;
|
|
21038
|
+
this.ctx.credits.setStars(this.credits);
|
|
21039
|
+
}
|
|
21040
|
+
}
|
|
21041
|
+
dispose() {
|
|
21042
|
+
this.disposed = true;
|
|
21043
|
+
for (const node of this.nodes.values()) {
|
|
21044
|
+
node.abort?.abort();
|
|
21045
|
+
this.undraw(node);
|
|
21046
|
+
this.dropPick(node.pick, NODE_PICK_PREFIX + node.name);
|
|
21047
|
+
node.data?.geometry.dispose();
|
|
21048
|
+
}
|
|
21049
|
+
this.nodes.clear();
|
|
21050
|
+
this.setFocusStar(null);
|
|
21051
|
+
this.group.removeFromParent();
|
|
21052
|
+
}
|
|
21053
|
+
stopLoading() {
|
|
21054
|
+
this.active = false;
|
|
21055
|
+
this.ready = false;
|
|
21056
|
+
for (const node of this.nodes.values()) {
|
|
21057
|
+
node.wanted = false;
|
|
21058
|
+
node.abort?.abort();
|
|
21059
|
+
}
|
|
21060
|
+
this.markDirty();
|
|
21061
|
+
}
|
|
21062
|
+
start() {
|
|
21063
|
+
if (this.drawable !== "unknown" || this.metadataLoading) return;
|
|
21064
|
+
if (this.retries.waiting(METADATA)) return;
|
|
21065
|
+
this.metadataLoading = true;
|
|
21066
|
+
this.bootGate ??= Promise.race([eagerMinorsDone, new Promise((resolve) => setTimeout(resolve, BOOT_GATE_MS))]);
|
|
21067
|
+
(async () => {
|
|
21068
|
+
await this.bootGate;
|
|
21069
|
+
const meta = await fetchExtrasolarMetadata();
|
|
21070
|
+
const tree = meta?.trees[TREE];
|
|
21071
|
+
if (this.disposed) return;
|
|
21072
|
+
this.retries.clear(METADATA);
|
|
21073
|
+
if (!meta || !tree || !extrasolarAllowed(tree)) {
|
|
21074
|
+
this.noStars();
|
|
21075
|
+
return;
|
|
21076
|
+
}
|
|
21077
|
+
this.tree = tree;
|
|
21078
|
+
this.credits = meta.credits;
|
|
21079
|
+
this.root = this.record(ROOT, 0, tree.centre, tree.half_size, Infinity, null);
|
|
21080
|
+
this.drawable = "yes";
|
|
21081
|
+
this.markDirty();
|
|
21082
|
+
})().catch((err) => {
|
|
21083
|
+
if (this.disposed) return;
|
|
21084
|
+
console.warn("[starfield] metadata.json did not load:", err);
|
|
21085
|
+
this.retries.fail(METADATA);
|
|
21086
|
+
}).finally(() => {
|
|
21087
|
+
this.metadataLoading = false;
|
|
21088
|
+
});
|
|
21089
|
+
}
|
|
21090
|
+
noStars() {
|
|
21091
|
+
this.drawable = "no";
|
|
21092
|
+
this.onChange();
|
|
21093
|
+
}
|
|
21094
|
+
record(name, depth, centre, half, range, parent) {
|
|
21095
|
+
const node = {
|
|
21096
|
+
name,
|
|
21097
|
+
depth,
|
|
21098
|
+
centre,
|
|
21099
|
+
half,
|
|
21100
|
+
range,
|
|
21101
|
+
parent,
|
|
21102
|
+
children: [],
|
|
21103
|
+
state: "idle",
|
|
21104
|
+
abort: null,
|
|
21105
|
+
data: null,
|
|
21106
|
+
points: null,
|
|
21107
|
+
magnitude: Infinity,
|
|
21108
|
+
wanted: false,
|
|
21109
|
+
lastWantedMs: -Infinity,
|
|
21110
|
+
fade: 0,
|
|
21111
|
+
hidden: null,
|
|
21112
|
+
pick: null,
|
|
21113
|
+
ids: null
|
|
21114
|
+
};
|
|
21115
|
+
this.nodes.set(name, node);
|
|
21116
|
+
return node;
|
|
21117
|
+
}
|
|
21118
|
+
markDirty() {
|
|
21119
|
+
this.dirty = true;
|
|
21120
|
+
this.onChange();
|
|
21121
|
+
}
|
|
21122
|
+
select(cam, nowMs) {
|
|
21123
|
+
const tree = this.tree;
|
|
21124
|
+
const root = this.root;
|
|
21125
|
+
if (!tree || !root) return;
|
|
21126
|
+
this.dirty = false;
|
|
21127
|
+
this.lastSelectMs = nowMs;
|
|
21128
|
+
for (const node of this.nodes.values()) node.wanted = false;
|
|
21129
|
+
const magnitude = (node) => node === root ? -Infinity : boxMagnitude(boxDistance(cam, node.centre, node.half), node.range, tree.range_limit_mag);
|
|
21130
|
+
const heap = new NodeHeap();
|
|
21131
|
+
root.magnitude = magnitude(root);
|
|
21132
|
+
heap.push(root);
|
|
21133
|
+
let points = 0;
|
|
21134
|
+
let allowed = this.targetLimit;
|
|
21135
|
+
const wanted = [];
|
|
21136
|
+
for (let node = heap.pop(); node; node = heap.pop()) {
|
|
21137
|
+
if (node.magnitude > this.targetLimit) break;
|
|
21138
|
+
const rows = node.data?.container.rows ?? tree.node_rows;
|
|
21139
|
+
if (points + rows > this.budget.points && node.magnitude > MUST_LOAD_MAG) {
|
|
21140
|
+
allowed = node.magnitude;
|
|
21141
|
+
break;
|
|
21142
|
+
}
|
|
21143
|
+
points += rows;
|
|
21144
|
+
node.wanted = true;
|
|
21145
|
+
node.lastWantedMs = nowMs;
|
|
21146
|
+
wanted.push(node);
|
|
21147
|
+
for (const child of node.children) {
|
|
21148
|
+
child.magnitude = magnitude(child);
|
|
21149
|
+
heap.push(child);
|
|
21150
|
+
}
|
|
21151
|
+
}
|
|
21152
|
+
this.allowedLimit = allowed;
|
|
21153
|
+
if (wanted.every((node) => node.data || this.retries.waiting(node.name))) this.ready = true;
|
|
21154
|
+
for (const node of wanted) {
|
|
21155
|
+
if (this.loading >= this.budget.fetches) break;
|
|
21156
|
+
if (node.state === "idle" && !this.retries.waiting(node.name)) this.load(node);
|
|
21157
|
+
}
|
|
21158
|
+
for (const node of this.nodes.values()) {
|
|
21159
|
+
if (node.state !== "loading" || node.wanted) continue;
|
|
21160
|
+
if (node.magnitude > allowed + ABORT_MARGIN_MAG || !node.parent?.wanted) node.abort?.abort();
|
|
21161
|
+
}
|
|
21162
|
+
for (const node of wanted) if (node.data) this.drawn.add(node);
|
|
21163
|
+
this.evict();
|
|
21164
|
+
}
|
|
21165
|
+
load(node) {
|
|
21166
|
+
const tree = this.tree;
|
|
21167
|
+
if (!tree) return;
|
|
21168
|
+
node.state = "loading";
|
|
21169
|
+
this.loading++;
|
|
21170
|
+
const abort = new AbortController();
|
|
21171
|
+
node.abort = abort;
|
|
21172
|
+
fetchExtrasolarFile(extrasolarPath(tree.node, { name: node.name }), tree.token, {
|
|
21173
|
+
signal: abort.signal,
|
|
21174
|
+
priority: "low"
|
|
21175
|
+
}).then((container) => {
|
|
21176
|
+
if (this.disposed) return;
|
|
21177
|
+
this.retries.clear(node.name);
|
|
21178
|
+
this.adopt(node, container);
|
|
21179
|
+
}).catch((err) => {
|
|
21180
|
+
node.state = "idle";
|
|
21181
|
+
if (this.disposed || abort.signal.aborted) return;
|
|
21182
|
+
if (err instanceof ExtrasolarStaleError) {
|
|
21183
|
+
this.stale = true;
|
|
21184
|
+
return;
|
|
21185
|
+
}
|
|
21186
|
+
this.retries.fail(node.name);
|
|
21187
|
+
console.warn(`[starfield] node ${node.name} did not load:`, err);
|
|
21188
|
+
}).finally(() => {
|
|
21189
|
+
this.loading--;
|
|
21190
|
+
node.abort = null;
|
|
21191
|
+
this.markDirty();
|
|
21192
|
+
});
|
|
21193
|
+
}
|
|
21194
|
+
adopt(node, container) {
|
|
21195
|
+
const info = nodeInfo(container);
|
|
21196
|
+
const rows = container.rows;
|
|
21197
|
+
const x = container.float32("x");
|
|
21198
|
+
const y = container.float32("y");
|
|
21199
|
+
const z = container.float32("z");
|
|
21200
|
+
const position = new Float32Array(3 * rows);
|
|
21201
|
+
for (let i = 0; i < rows; i++) {
|
|
21202
|
+
position[3 * i] = x[i];
|
|
21203
|
+
position[3 * i + 1] = y[i];
|
|
21204
|
+
position[3 * i + 2] = z[i];
|
|
21205
|
+
}
|
|
21206
|
+
const colour = starColours(container.float32("colour"), new Uint8Array(3 * rows));
|
|
21207
|
+
const geometry = new BufferGeometry();
|
|
21208
|
+
geometry.setAttribute("position", new BufferAttribute(position, 3));
|
|
21209
|
+
geometry.setAttribute("mag", new BufferAttribute(container.float32("mag"), 1));
|
|
21210
|
+
geometry.setAttribute("starColour", new BufferAttribute(colour, 3, true));
|
|
21211
|
+
const bytes = container.buffer.byteLength + position.byteLength + colour.byteLength;
|
|
21212
|
+
node.data = {
|
|
21213
|
+
container,
|
|
21214
|
+
info,
|
|
21215
|
+
geometry,
|
|
21216
|
+
bytes
|
|
21217
|
+
};
|
|
21218
|
+
node.state = "loaded";
|
|
21219
|
+
node.centre = info.centre;
|
|
21220
|
+
node.half = info.half;
|
|
21221
|
+
node.range = info.maxRange;
|
|
21222
|
+
this.cachedBytes += bytes;
|
|
21223
|
+
node.children = [];
|
|
21224
|
+
for (let k = 0; k < 8; k++) {
|
|
21225
|
+
if (!(info.childMask >> k & 1)) continue;
|
|
21226
|
+
node.children.push(this.record(node.name + k, node.depth + 1, childCentre(info.centre, info.half, k), info.half / 2, info.childRange[k], node));
|
|
21227
|
+
}
|
|
21228
|
+
}
|
|
21229
|
+
evict() {
|
|
21230
|
+
if (this.cachedBytes <= this.budget.bytes) return;
|
|
21231
|
+
const candidates = [...this.nodes.values()].filter((node) => node.data && !node.wanted && !node.points && node.children.every((child) => child.state === "idle" && !child.data));
|
|
21232
|
+
candidates.sort((a, b) => a.lastWantedMs - b.lastWantedMs || b.depth - a.depth);
|
|
21233
|
+
for (const node of candidates) {
|
|
21234
|
+
if (this.cachedBytes <= this.budget.bytes) break;
|
|
21235
|
+
if (node.children.some((child) => child.data)) continue;
|
|
21236
|
+
this.release(node);
|
|
21237
|
+
}
|
|
21238
|
+
}
|
|
21239
|
+
release(node) {
|
|
21240
|
+
const data = node.data;
|
|
21241
|
+
if (!data) return;
|
|
21242
|
+
data.geometry.dispose();
|
|
21243
|
+
this.cachedBytes -= data.bytes;
|
|
21244
|
+
node.data = null;
|
|
21245
|
+
node.state = "idle";
|
|
21246
|
+
node.hidden = null;
|
|
21247
|
+
this.dropPick(node.pick, NODE_PICK_PREFIX + node.name);
|
|
21248
|
+
node.pick = null;
|
|
21249
|
+
if (node.ids) this.cachedBytes -= node.ids.bytes;
|
|
21250
|
+
node.ids = null;
|
|
21251
|
+
for (const child of node.children) this.forget(child);
|
|
21252
|
+
node.children = [];
|
|
21253
|
+
}
|
|
21254
|
+
forget(node) {
|
|
21255
|
+
for (const child of node.children) this.forget(child);
|
|
21256
|
+
this.nodes.delete(node.name);
|
|
21257
|
+
}
|
|
21258
|
+
applyFades(dtMs, camera, focus) {
|
|
21259
|
+
const step = dtMs / NODE_FADE_MS;
|
|
21260
|
+
let adds = 0;
|
|
21261
|
+
const view = new ViewCone(camera);
|
|
21262
|
+
for (const node of [...this.drawn]) {
|
|
21263
|
+
const target = node.wanted ? 1 : 0;
|
|
21264
|
+
if (!node.points) {
|
|
21265
|
+
if (!node.wanted || !node.data || adds >= ADDS_PER_FRAME) {
|
|
21266
|
+
if (!node.wanted) this.drawn.delete(node);
|
|
21267
|
+
continue;
|
|
21268
|
+
}
|
|
21269
|
+
adds++;
|
|
21270
|
+
this.draw(node);
|
|
21271
|
+
}
|
|
21272
|
+
if (target > node.fade) node.fade = Math.min(node.fade + step, 1);
|
|
21273
|
+
else if (target < node.fade) node.fade = Math.max(node.fade - step, 0);
|
|
21274
|
+
if (node.fade === 0 && !node.wanted) {
|
|
21275
|
+
this.undraw(node);
|
|
21276
|
+
this.drawn.delete(node);
|
|
21277
|
+
continue;
|
|
21278
|
+
}
|
|
21279
|
+
const points = node.points;
|
|
21280
|
+
points.material.uniforms.uFade.value = node.fade;
|
|
21281
|
+
points.visible = view.sees(node, focus);
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
draw(node) {
|
|
21285
|
+
const data = node.data;
|
|
21286
|
+
const points = new Points(data.geometry, makeStarMaterial(this.uniforms));
|
|
21287
|
+
points.position.set(...data.info.centre);
|
|
21288
|
+
points.updateMatrix();
|
|
21289
|
+
points.matrixAutoUpdate = false;
|
|
21290
|
+
points.frustumCulled = false;
|
|
21291
|
+
points.renderOrder = STAR_RENDER_ORDER;
|
|
21292
|
+
points.userData.starNode = node.name;
|
|
21293
|
+
node.points = points;
|
|
21294
|
+
node.fade = 0;
|
|
21295
|
+
this.applyHidden(node);
|
|
21296
|
+
this.group.add(points);
|
|
21297
|
+
}
|
|
21298
|
+
focusPoint(star) {
|
|
21299
|
+
const geometry = new BufferGeometry();
|
|
21300
|
+
geometry.setAttribute("position", new BufferAttribute(new Float32Array(3), 3));
|
|
21301
|
+
const mag = star.absMag ?? NO_MAGNITUDE;
|
|
21302
|
+
geometry.setAttribute("mag", new BufferAttribute(new Float32Array([mag]), 1));
|
|
21303
|
+
const colour = starColours(new Float32Array([star.colour ?? NaN]), new Uint8Array(3));
|
|
21304
|
+
geometry.setAttribute("starColour", new BufferAttribute(colour, 3, true));
|
|
21305
|
+
const material = makeStarMaterial(this.uniforms, FOCUS_FLOOR_MAG);
|
|
21306
|
+
material.uniforms.uFade.value = 1;
|
|
21307
|
+
const points = new Points(geometry, material);
|
|
21308
|
+
points.position.set(...star.tree);
|
|
21309
|
+
points.updateMatrix();
|
|
21310
|
+
points.matrixAutoUpdate = false;
|
|
21311
|
+
points.frustumCulled = false;
|
|
21312
|
+
points.renderOrder = STAR_RENDER_ORDER;
|
|
21313
|
+
points.userData.focusStar = star.id;
|
|
21314
|
+
this.group.add(points);
|
|
21315
|
+
return points;
|
|
21316
|
+
}
|
|
21317
|
+
applyHidden(node) {
|
|
21318
|
+
const star = this.focusStar;
|
|
21319
|
+
let row = -1;
|
|
21320
|
+
if (star && node.data) {
|
|
21321
|
+
if (node.hidden?.id !== star.id) node.hidden = {
|
|
21322
|
+
id: star.id,
|
|
21323
|
+
row: findStarRow(node.data, star)
|
|
21324
|
+
};
|
|
21325
|
+
row = node.hidden.row;
|
|
21326
|
+
}
|
|
21327
|
+
node.points.material.uniforms.uHidden.value = row;
|
|
21328
|
+
}
|
|
21329
|
+
undraw(node) {
|
|
21330
|
+
const points = node.points;
|
|
21331
|
+
if (!points) return;
|
|
21332
|
+
points.removeFromParent();
|
|
21333
|
+
points.material.dispose();
|
|
21334
|
+
if (node.pick) {
|
|
21335
|
+
node.pick.material?.dispose();
|
|
21336
|
+
node.pick.material = null;
|
|
21337
|
+
}
|
|
21338
|
+
node.data?.geometry.dispose();
|
|
21339
|
+
node.points = null;
|
|
21340
|
+
node.fade = 0;
|
|
21341
|
+
}
|
|
21342
|
+
view(node) {
|
|
21343
|
+
const data = node.data;
|
|
21344
|
+
if (!data || !node.points) return void 0;
|
|
21345
|
+
return {
|
|
21346
|
+
name: node.name,
|
|
21347
|
+
rows: data.container.rows,
|
|
21348
|
+
points: node.points,
|
|
21349
|
+
container: data.container,
|
|
21350
|
+
info: data.info
|
|
21351
|
+
};
|
|
21352
|
+
}
|
|
21353
|
+
};
|
|
21354
|
+
var ROW_STEPS = 2 ** -22;
|
|
21355
|
+
var ROW_FLOOR_PC = 1e-9;
|
|
21356
|
+
var ROW_MAG = .01;
|
|
21357
|
+
function findStarRow(data, star) {
|
|
21358
|
+
const tree = star.tree;
|
|
21359
|
+
if (!tree) return -1;
|
|
21360
|
+
const { centre, half } = data.info;
|
|
21361
|
+
const ox = tree[0] - centre[0];
|
|
21362
|
+
const oy = tree[1] - centre[1];
|
|
21363
|
+
const oz = tree[2] - centre[2];
|
|
21364
|
+
if (Math.max(Math.abs(ox), Math.abs(oy), Math.abs(oz)) > half * 1.000001) return -1;
|
|
21365
|
+
const tx = Math.abs(ox) * ROW_STEPS + ROW_FLOOR_PC;
|
|
21366
|
+
const ty = Math.abs(oy) * ROW_STEPS + ROW_FLOOR_PC;
|
|
21367
|
+
const tz = Math.abs(oz) * ROW_STEPS + ROW_FLOOR_PC;
|
|
21368
|
+
const c = data.container;
|
|
21369
|
+
const x = c.float32("x");
|
|
21370
|
+
const y = c.float32("y");
|
|
21371
|
+
const z = c.float32("z");
|
|
21372
|
+
const mag = c.float32("mag");
|
|
21373
|
+
let best = -1;
|
|
21374
|
+
let bestDistance = Infinity;
|
|
21375
|
+
for (let i = 0; i < c.rows; i++) {
|
|
21376
|
+
const dx = Math.abs(x[i] - ox);
|
|
21377
|
+
const dy = Math.abs(y[i] - oy);
|
|
21378
|
+
const dz = Math.abs(z[i] - oz);
|
|
21379
|
+
if (dx > tx || dy > ty || dz > tz) continue;
|
|
21380
|
+
if (star.absMag !== null && Math.abs(mag[i] - star.absMag) > ROW_MAG) continue;
|
|
21381
|
+
const distance = dx + dy + dz;
|
|
21382
|
+
if (distance < bestDistance) {
|
|
21383
|
+
best = i;
|
|
21384
|
+
bestDistance = distance;
|
|
21385
|
+
}
|
|
21386
|
+
}
|
|
21387
|
+
return best;
|
|
21388
|
+
}
|
|
21389
|
+
var ViewCone = class {
|
|
21390
|
+
tanY;
|
|
21391
|
+
tanX;
|
|
21392
|
+
inverse;
|
|
21393
|
+
constructor(camera) {
|
|
21394
|
+
this.tanY = Math.tan(camera.fov / 2 * Math.PI / 180) / camera.zoom;
|
|
21395
|
+
this.tanX = this.tanY * camera.aspect;
|
|
21396
|
+
this.inverse = camera.matrixWorldInverse;
|
|
21397
|
+
}
|
|
21398
|
+
sees(node, focus) {
|
|
21399
|
+
const [cx, cy, cz] = node.centre;
|
|
21400
|
+
const at = treeToScene(cx, cy, cz, _view);
|
|
21401
|
+
_view.set(at.x - focus[0], at.y - focus[1], at.z - focus[2]).applyMatrix4(this.inverse);
|
|
21402
|
+
const r = node.half * Math.sqrt(3) * PC_SCENE;
|
|
21403
|
+
const depth = -_view.z;
|
|
21404
|
+
if (depth < -r) return false;
|
|
21405
|
+
const sideX = (Math.abs(_view.x) - depth * this.tanX) / Math.hypot(1, this.tanX);
|
|
21406
|
+
const sideY = (Math.abs(_view.y) - depth * this.tanY) / Math.hypot(1, this.tanY);
|
|
21407
|
+
return sideX <= r && sideY <= r;
|
|
21408
|
+
}
|
|
21409
|
+
};
|
|
21410
|
+
var NodeHeap = class {
|
|
21411
|
+
items = [];
|
|
21412
|
+
push(node) {
|
|
21413
|
+
const items = this.items;
|
|
21414
|
+
items.push(node);
|
|
21415
|
+
let i = items.length - 1;
|
|
21416
|
+
while (i > 0) {
|
|
21417
|
+
const up = i - 1 >> 1;
|
|
21418
|
+
if (items[up].magnitude <= items[i].magnitude) break;
|
|
21419
|
+
[items[up], items[i]] = [items[i], items[up]];
|
|
21420
|
+
i = up;
|
|
21421
|
+
}
|
|
21422
|
+
}
|
|
21423
|
+
pop() {
|
|
21424
|
+
const items = this.items;
|
|
21425
|
+
const top = items[0];
|
|
21426
|
+
const last = items.pop();
|
|
21427
|
+
if (items.length > 0 && last) {
|
|
21428
|
+
items[0] = last;
|
|
21429
|
+
let i = 0;
|
|
21430
|
+
for (;;) {
|
|
21431
|
+
const l = 2 * i + 1;
|
|
21432
|
+
const r = l + 1;
|
|
21433
|
+
let min = i;
|
|
21434
|
+
if (l < items.length && items[l].magnitude < items[min].magnitude) min = l;
|
|
21435
|
+
if (r < items.length && items[r].magnitude < items[min].magnitude) min = r;
|
|
21436
|
+
if (min === i) break;
|
|
21437
|
+
[items[min], items[i]] = [items[i], items[min]];
|
|
21438
|
+
i = min;
|
|
21439
|
+
}
|
|
21440
|
+
}
|
|
21441
|
+
return top;
|
|
21442
|
+
}
|
|
21443
|
+
};
|
|
21444
|
+
|
|
21445
|
+
//#endregion
|
|
21446
|
+
//#region src/lib/scene/visibility/system-fade.ts
|
|
21447
|
+
var SYSTEM_RADIUS_AU = 30.07;
|
|
21448
|
+
var SYSTEM_OUT_PX = 4.3;
|
|
21449
|
+
var SYSTEM_IN_PX = 4.75;
|
|
21450
|
+
function systemDiameterPx(distanceScene, viewportPx, fovDeg) {
|
|
21451
|
+
const pxPerRad = viewportPx / 2 / Math.tan(fovDeg * Math.PI / 360);
|
|
21452
|
+
return 2 * SYSTEM_RADIUS_AU * 10 / distanceScene * pxPerRad;
|
|
21453
|
+
}
|
|
21454
|
+
function systemShown(distanceScene, viewportPx, fovDeg, shown) {
|
|
21455
|
+
const px = systemDiameterPx(distanceScene, viewportPx, fovDeg);
|
|
21456
|
+
if (px < 4.3) return false;
|
|
21457
|
+
if (px > 4.75) return true;
|
|
21458
|
+
return shown;
|
|
21459
|
+
}
|
|
21460
|
+
|
|
19378
21461
|
//#endregion
|
|
19379
21462
|
//#region src/lib/scene/interaction/picking.ts
|
|
19380
|
-
function
|
|
19381
|
-
|
|
21463
|
+
function isCoarsePointer(pointerType) {
|
|
21464
|
+
return pointerType === "touch" || pointerType === "pen";
|
|
21465
|
+
}
|
|
21466
|
+
function pickRadiusPx(pointerType) {
|
|
21467
|
+
return isCoarsePointer(pointerType) ? 48 : 24;
|
|
21468
|
+
}
|
|
21469
|
+
function pickMoonDot(pick) {
|
|
21470
|
+
const { ctx } = pick;
|
|
21471
|
+
return pickDot(ctx.bodies.majorBodies.filter((body) => body.data.objectType === ObjectType.MOON && ctx.visibility.isMoonGroupVisible(body.data.parentId)), pick);
|
|
21472
|
+
}
|
|
21473
|
+
function pickPlanetDot(pick) {
|
|
21474
|
+
const { ctx, camera, focusTruePos } = pick;
|
|
21475
|
+
const [fx, fy, fz] = focusTruePos;
|
|
21476
|
+
const cam = camera.position;
|
|
21477
|
+
return pickDot(ctx.bodies.majorBodies.filter((body) => {
|
|
21478
|
+
const t = body.data.objectType;
|
|
21479
|
+
if (t !== ObjectType.STAR && t !== ObjectType.PLANET && t !== ObjectType.DWARF_PLANET) return false;
|
|
21480
|
+
const at = body.position;
|
|
21481
|
+
if (!at) return false;
|
|
21482
|
+
const dist = Math.hypot(at[0] - fx - cam.x, at[1] - fy - cam.y, at[2] - fz - cam.z);
|
|
21483
|
+
return ctx.visibility.getPlanetVisibility(body, dist) !== VISIBILITY.HIDE;
|
|
21484
|
+
}), pick);
|
|
21485
|
+
}
|
|
21486
|
+
function pickDot(bodies, pick) {
|
|
21487
|
+
const { pointer, camera, ctx, canvasWidth, canvasHeight, jd, isVisible } = pick;
|
|
19382
21488
|
const px = (pointer.x + 1) * .5 * canvasWidth;
|
|
19383
21489
|
const py = (1 - pointer.y) * .5 * canvasHeight;
|
|
19384
|
-
const v = tmpV3;
|
|
19385
|
-
const [fx, fy, fz] = focusTruePos;
|
|
21490
|
+
const v = pick.tmpV3;
|
|
21491
|
+
const [fx, fy, fz] = pick.focusTruePos;
|
|
19386
21492
|
const cam = camera.position;
|
|
19387
21493
|
let bestBody;
|
|
19388
|
-
let bestScreenDist =
|
|
21494
|
+
let bestScreenDist = pickRadiusPx(pick.pointerType);
|
|
19389
21495
|
let bestWorldDist = Infinity;
|
|
19390
|
-
const
|
|
21496
|
+
for (const body of bodies) {
|
|
19391
21497
|
const at = ctx.place(body, jd);
|
|
19392
|
-
if (!at)
|
|
21498
|
+
if (!at) continue;
|
|
19393
21499
|
v.set(at[0] - fx, at[1] - fy, at[2] - fz);
|
|
19394
21500
|
const worldDist = Math.hypot(v.x - cam.x, v.y - cam.y, v.z - cam.z);
|
|
19395
21501
|
v.project(camera);
|
|
19396
|
-
if (!ndcZVisible(v.z))
|
|
21502
|
+
if (!ndcZVisible(v.z)) continue;
|
|
19397
21503
|
const sx = (v.x + 1) * .5 * canvasWidth;
|
|
19398
21504
|
const sy = (1 - v.y) * .5 * canvasHeight;
|
|
19399
21505
|
const screenDist = Math.hypot(sx - px, sy - py);
|
|
19400
|
-
if (!(screenDist < bestScreenDist || screenDist === bestScreenDist && worldDist < bestWorldDist))
|
|
19401
|
-
if (isVisible && !isVisible(v.x, v.y, worldDist))
|
|
21506
|
+
if (!(screenDist < bestScreenDist || screenDist === bestScreenDist && worldDist < bestWorldDist)) continue;
|
|
21507
|
+
if (isVisible && !isVisible(v.x, v.y, worldDist)) continue;
|
|
19402
21508
|
bestScreenDist = screenDist;
|
|
19403
21509
|
bestWorldDist = worldDist;
|
|
19404
21510
|
bestBody = body;
|
|
19405
|
-
};
|
|
19406
|
-
for (const body of ctx.bodies.majorBodies) {
|
|
19407
|
-
if (body.data.objectType !== ObjectType.MOON) continue;
|
|
19408
|
-
if (!ctx.visibility.isMoonGroupVisible(body.data.parentId)) continue;
|
|
19409
|
-
testBody(body);
|
|
19410
21511
|
}
|
|
19411
21512
|
if (!bestBody) return null;
|
|
19412
21513
|
return {
|
|
@@ -19418,7 +21519,12 @@ function pickMoonDot(pointer, camera, ctx, focusTruePos, canvasWidth, canvasHeig
|
|
|
19418
21519
|
|
|
19419
21520
|
//#endregion
|
|
19420
21521
|
//#region src/lib/scene/interaction/pointer.ts
|
|
19421
|
-
var
|
|
21522
|
+
var CLICK_DRAG_PX = 3;
|
|
21523
|
+
var TOUCH_SLOP_PX = 10;
|
|
21524
|
+
function movedTooFar(e, from) {
|
|
21525
|
+
const limit = isCoarsePointer(e.pointerType) ? TOUCH_SLOP_PX : CLICK_DRAG_PX;
|
|
21526
|
+
return Math.hypot(e.clientX - from.x, e.clientY - from.y) > limit;
|
|
21527
|
+
}
|
|
19422
21528
|
function inHiddenRoot(obj) {
|
|
19423
21529
|
let top = obj;
|
|
19424
21530
|
while (top.parent?.parent) top = top.parent;
|
|
@@ -19426,11 +21532,13 @@ function inHiddenRoot(obj) {
|
|
|
19426
21532
|
}
|
|
19427
21533
|
var PointerInteraction = class {
|
|
19428
21534
|
downPos = new Vector2();
|
|
21535
|
+
down = /* @__PURE__ */ new Set();
|
|
21536
|
+
gesture = false;
|
|
19429
21537
|
pointer = new Vector2();
|
|
19430
21538
|
_tmpPointer = new Vector2();
|
|
19431
21539
|
raycaster = new Raycaster();
|
|
19432
21540
|
_tmpV3 = new Vector3();
|
|
19433
|
-
constructor(canvas, camera, ctx, clock, focus, clickables, meshToBody, onPick, modelPick, gpuPick, getClouds, pickRegistry) {
|
|
21541
|
+
constructor(canvas, camera, ctx, clock, focus, clickables, meshToBody, onPick, modelPick, gpuPick, getClouds, pickRegistry, getStars, onStarPick) {
|
|
19434
21542
|
this.canvas = canvas;
|
|
19435
21543
|
this.camera = camera;
|
|
19436
21544
|
this.ctx = ctx;
|
|
@@ -19443,22 +21551,39 @@ var PointerInteraction = class {
|
|
|
19443
21551
|
this.gpuPick = gpuPick;
|
|
19444
21552
|
this.getClouds = getClouds;
|
|
19445
21553
|
this.pickRegistry = pickRegistry;
|
|
21554
|
+
this.getStars = getStars;
|
|
21555
|
+
this.onStarPick = onStarPick;
|
|
19446
21556
|
}
|
|
19447
21557
|
attach() {
|
|
19448
21558
|
this.canvas.addEventListener("pointerdown", this.onDown);
|
|
21559
|
+
this.canvas.addEventListener("pointermove", this.onMove);
|
|
19449
21560
|
this.canvas.addEventListener("pointerup", this.onUp);
|
|
21561
|
+
this.canvas.addEventListener("pointercancel", this.onCancel);
|
|
19450
21562
|
}
|
|
19451
21563
|
detach() {
|
|
19452
21564
|
this.canvas.removeEventListener("pointerdown", this.onDown);
|
|
21565
|
+
this.canvas.removeEventListener("pointermove", this.onMove);
|
|
19453
21566
|
this.canvas.removeEventListener("pointerup", this.onUp);
|
|
21567
|
+
this.canvas.removeEventListener("pointercancel", this.onCancel);
|
|
19454
21568
|
}
|
|
19455
21569
|
onDown = (e) => {
|
|
19456
|
-
|
|
21570
|
+
if (e.isPrimary) {
|
|
21571
|
+
this.down.clear();
|
|
21572
|
+
this.gesture = false;
|
|
21573
|
+
this.downPos.set(e.clientX, e.clientY);
|
|
21574
|
+
} else this.gesture = true;
|
|
21575
|
+
this.down.add(e.pointerId);
|
|
21576
|
+
};
|
|
21577
|
+
onMove = (e) => {
|
|
21578
|
+
if (this.gesture || !this.down.has(e.pointerId)) return;
|
|
21579
|
+
if (movedTooFar(e, this.downPos)) this.gesture = true;
|
|
21580
|
+
};
|
|
21581
|
+
onCancel = (e) => {
|
|
21582
|
+
this.down.delete(e.pointerId);
|
|
21583
|
+
this.gesture = true;
|
|
19457
21584
|
};
|
|
19458
21585
|
onUp = (e) => {
|
|
19459
|
-
|
|
19460
|
-
const dy = e.clientY - this.downPos.y;
|
|
19461
|
-
if (dx * dx + dy * dy > CLICK_DRAG_PX2) return;
|
|
21586
|
+
if (!this.down.delete(e.pointerId) || this.gesture || movedTooFar(e, this.downPos)) return;
|
|
19462
21587
|
const rect = this.canvas.getBoundingClientRect();
|
|
19463
21588
|
this.pointer.set((e.clientX - rect.left) / rect.width * 2 - 1, -((e.clientY - rect.top) / rect.height) * 2 + 1);
|
|
19464
21589
|
const modelBody = this.modelPick(this.pointer.x, this.pointer.y);
|
|
@@ -19468,11 +21593,41 @@ var PointerInteraction = class {
|
|
|
19468
21593
|
}
|
|
19469
21594
|
this.raycaster.setFromCamera(this.pointer, this.camera);
|
|
19470
21595
|
const meshBody = this.resolveMeshHit(this.raycaster.intersectObjects([...this.clickables, ...attachedModelRoots]))?.body;
|
|
19471
|
-
const
|
|
21596
|
+
const dots = {
|
|
21597
|
+
pointer: this.pointer,
|
|
21598
|
+
camera: this.camera,
|
|
21599
|
+
ctx: this.ctx,
|
|
21600
|
+
focusTruePos: this.focus.focusTruePos,
|
|
21601
|
+
canvasWidth: this.canvas.clientWidth,
|
|
21602
|
+
canvasHeight: this.canvas.clientHeight,
|
|
21603
|
+
tmpV3: this._tmpV3,
|
|
21604
|
+
jd: this.clock.jd,
|
|
21605
|
+
pointerType: e.pointerType,
|
|
21606
|
+
isVisible: this.isDotVisible
|
|
21607
|
+
};
|
|
21608
|
+
const moonHit = pickMoonDot(dots);
|
|
19472
21609
|
const gpuHit = this.gpuPickCloud(e, rect);
|
|
19473
21610
|
const bestBody = this.nearer(moonHit, gpuHit)?.body ?? meshBody;
|
|
19474
|
-
if (bestBody)
|
|
21611
|
+
if (bestBody) {
|
|
21612
|
+
this.onPick(bestBody);
|
|
21613
|
+
return;
|
|
21614
|
+
}
|
|
21615
|
+
this.pickStar(e, rect, dots);
|
|
19475
21616
|
};
|
|
21617
|
+
pickStar(e, rect, dots) {
|
|
21618
|
+
const stars = this.getStars();
|
|
21619
|
+
if (stars.length === 0) return;
|
|
21620
|
+
const planet = pickPlanetDot(dots);
|
|
21621
|
+
if (planet) {
|
|
21622
|
+
this.onPick(planet.body);
|
|
21623
|
+
return;
|
|
21624
|
+
}
|
|
21625
|
+
const candidates = this.gpuPick.pickDraws(stars, this.camera, e.clientX, e.clientY, rect, pickRadiusPx(e.pointerType));
|
|
21626
|
+
for (const c of candidates) {
|
|
21627
|
+
if (!this.isDotVisible(c.ndcX, c.ndcY, Infinity)) continue;
|
|
21628
|
+
if (this.onStarPick(c.pickId)) return;
|
|
21629
|
+
}
|
|
21630
|
+
}
|
|
19476
21631
|
nearer(a, b) {
|
|
19477
21632
|
if (!a) return b;
|
|
19478
21633
|
if (!b) return a;
|
|
@@ -19481,9 +21636,8 @@ var PointerInteraction = class {
|
|
|
19481
21636
|
return a;
|
|
19482
21637
|
}
|
|
19483
21638
|
gpuPickCloud(e, rect) {
|
|
19484
|
-
const radius = e.pointerType === "touch" || e.pointerType === "pen" ? 48 : 24;
|
|
19485
21639
|
const jd = this.clock.jd;
|
|
19486
|
-
const candidates = this.gpuPick.pick(this.getClouds(), this.camera, e.clientX, e.clientY, rect,
|
|
21640
|
+
const candidates = this.gpuPick.pick(this.getClouds(), this.camera, e.clientX, e.clientY, rect, pickRadiusPx(e.pointerType));
|
|
19487
21641
|
for (const c of candidates) {
|
|
19488
21642
|
const bodyId = this.pickRegistry.resolve(c.pickId);
|
|
19489
21643
|
if (!bodyId) continue;
|
|
@@ -19566,6 +21720,7 @@ var GpuPickPass = class {
|
|
|
19566
21720
|
buffer = new Uint8Array(0);
|
|
19567
21721
|
prevClear = new Color();
|
|
19568
21722
|
proxies = [];
|
|
21723
|
+
idleGeometry = new BufferGeometry();
|
|
19569
21724
|
constructor(renderer, camera) {
|
|
19570
21725
|
this.renderer = renderer;
|
|
19571
21726
|
this.target = new WebGLRenderTarget(1, 1, { depthBuffer: true });
|
|
@@ -19588,19 +21743,33 @@ var GpuPickPass = class {
|
|
|
19588
21743
|
this.target.setSize(px, px);
|
|
19589
21744
|
this.buffer = new Uint8Array(px * px * 4);
|
|
19590
21745
|
}
|
|
19591
|
-
proxyAt(index, src) {
|
|
21746
|
+
proxyAt(index, src, material) {
|
|
19592
21747
|
let p = this.proxies[index];
|
|
19593
21748
|
if (!p) {
|
|
19594
|
-
p = new Points(src.geometry,
|
|
21749
|
+
p = new Points(src.geometry, material);
|
|
19595
21750
|
p.frustumCulled = false;
|
|
19596
21751
|
p.matrixAutoUpdate = false;
|
|
19597
21752
|
p.matrixWorldAutoUpdate = false;
|
|
19598
21753
|
this.proxies[index] = p;
|
|
19599
|
-
} else
|
|
21754
|
+
} else {
|
|
21755
|
+
p.geometry = src.geometry;
|
|
21756
|
+
p.material = material;
|
|
21757
|
+
}
|
|
19600
21758
|
p.matrixWorld.copy(src.matrixWorld);
|
|
19601
21759
|
return p;
|
|
19602
21760
|
}
|
|
19603
21761
|
pick(clouds, camera, clientX, clientY, rect, radiusCss) {
|
|
21762
|
+
const draws = [];
|
|
21763
|
+
for (const points of clouds) {
|
|
21764
|
+
if (!points.geometry.getAttribute("pickColor")) continue;
|
|
21765
|
+
draws.push({
|
|
21766
|
+
points,
|
|
21767
|
+
material: this.material
|
|
21768
|
+
});
|
|
21769
|
+
}
|
|
21770
|
+
return this.pickDraws(draws, camera, clientX, clientY, rect, radiusCss);
|
|
21771
|
+
}
|
|
21772
|
+
pickDraws(draws, camera, clientX, clientY, rect, radiusCss) {
|
|
19604
21773
|
const r = Math.max(1, Math.round(radiusCss));
|
|
19605
21774
|
const boxPx = r * 2 + 1;
|
|
19606
21775
|
this.ensureSize(boxPx);
|
|
@@ -19608,13 +21777,11 @@ var GpuPickPass = class {
|
|
|
19608
21777
|
const cy = clientY - rect.top;
|
|
19609
21778
|
const offsetX = cx - r;
|
|
19610
21779
|
const offsetY = cy - r;
|
|
19611
|
-
this.scene.clear();
|
|
19612
21780
|
let n = 0;
|
|
19613
|
-
for (const
|
|
19614
|
-
if (!
|
|
19615
|
-
if (
|
|
19616
|
-
|
|
19617
|
-
this.scene.add(this.proxyAt(n++, pts));
|
|
21781
|
+
for (const { points, material } of draws) {
|
|
21782
|
+
if (!points.visible) continue;
|
|
21783
|
+
if (points.geometry.drawRange.count === 0) continue;
|
|
21784
|
+
this.scene.add(this.proxyAt(n++, points, material));
|
|
19618
21785
|
}
|
|
19619
21786
|
if (n === 0) return [];
|
|
19620
21787
|
this.pickCamera.copy(camera);
|
|
@@ -19630,12 +21797,21 @@ var GpuPickPass = class {
|
|
|
19630
21797
|
this.renderer.clear(true, true, false);
|
|
19631
21798
|
this.renderer.render(this.scene, this.pickCamera);
|
|
19632
21799
|
this.renderer.readRenderTargetPixels(this.target, 0, 0, boxPx, boxPx, this.buffer);
|
|
21800
|
+
this.letGo(n);
|
|
19633
21801
|
this.renderer.setRenderTarget(prevTarget);
|
|
19634
21802
|
this.renderer.autoClear = prevAutoClear;
|
|
19635
21803
|
this.renderer.setClearColor(this.prevClear, prevClearAlpha);
|
|
19636
21804
|
this.pickCamera.clearViewOffset();
|
|
19637
21805
|
return this.decode(boxPx, r, rect, offsetX, offsetY);
|
|
19638
21806
|
}
|
|
21807
|
+
letGo(n) {
|
|
21808
|
+
this.scene.clear();
|
|
21809
|
+
for (let i = 0; i < n; i++) {
|
|
21810
|
+
this.proxies[i].geometry = this.idleGeometry;
|
|
21811
|
+
this.proxies[i].material = this.material;
|
|
21812
|
+
}
|
|
21813
|
+
this.renderer.render(this.scene, this.pickCamera);
|
|
21814
|
+
}
|
|
19639
21815
|
decode(boxPx, r, rect, offsetX, offsetY) {
|
|
19640
21816
|
const buf = this.buffer;
|
|
19641
21817
|
const nearest = /* @__PURE__ */ new Map();
|
|
@@ -19665,6 +21841,7 @@ var GpuPickPass = class {
|
|
|
19665
21841
|
dispose() {
|
|
19666
21842
|
this.target.dispose();
|
|
19667
21843
|
this.material.dispose();
|
|
21844
|
+
this.idleGeometry.dispose();
|
|
19668
21845
|
this.proxies.length = 0;
|
|
19669
21846
|
}
|
|
19670
21847
|
};
|
|
@@ -20078,7 +22255,7 @@ function buildMajorBodies(bodies, scene, clickables, meshToBody, bodyObjects, re
|
|
|
20078
22255
|
const id = body.data.id;
|
|
20079
22256
|
const isHalfPromoted = halfPromoteIds?.has(id) ?? false;
|
|
20080
22257
|
const t = body.data.objectType;
|
|
20081
|
-
const isVirtual = isHalfPromoted || t === ObjectType.BARYCENTER || t === ObjectType.LAGRANGE_POINT || t === ObjectType.COMET || isAsteroid(t) || body.data.orbitalSource === OrbitalSource.SPICE_PROBE;
|
|
22258
|
+
const isVirtual = isHalfPromoted || t === ObjectType.EXTRASOLAR || t === ObjectType.BARYCENTER || t === ObjectType.LAGRANGE_POINT || t === ObjectType.COMET || isAsteroid(t) || body.data.orbitalSource === OrbitalSource.SPICE_PROBE;
|
|
20082
22259
|
const color = resolveBodyColor(body.data);
|
|
20083
22260
|
const radius = kmToScene(effectiveRadiusKm(body.data));
|
|
20084
22261
|
const isStar = t === ObjectType.STAR;
|
|
@@ -20121,7 +22298,7 @@ function buildMajorBodies(bodies, scene, clickables, meshToBody, bodyObjects, re
|
|
|
20121
22298
|
}
|
|
20122
22299
|
}
|
|
20123
22300
|
const variant = getLabelVariant(body);
|
|
20124
|
-
const isLarge = isStar || t === ObjectType.PLANET;
|
|
22301
|
+
const isLarge = isStar || t === ObjectType.EXTRASOLAR || t === ObjectType.PLANET;
|
|
20125
22302
|
const isMinor = isHalfPromoted || MINOR_PROMOTED_IDS.has(id) || body.data.isMinor === true;
|
|
20126
22303
|
const label = createLabel(color, body.data.name ?? "", variant, host().bodyHref(id, body.data.name ?? ""), () => handleFocus(body), isLarge, onHoverChange ? (hovered) => onHoverChange(id, hovered) : void 0, isMinor);
|
|
20127
22304
|
if (label) {
|
|
@@ -21014,7 +23191,7 @@ function buildTrails(bodyObjects, scene, basisPos = [
|
|
|
21014
23191
|
for (const [, bo] of bodyObjects) {
|
|
21015
23192
|
if (bo.trail !== null) continue;
|
|
21016
23193
|
const { body } = bo;
|
|
21017
|
-
if (body.data.objectType === ObjectType.STAR) continue;
|
|
23194
|
+
if (body.data.objectType === ObjectType.STAR || isExtrasolar(body)) continue;
|
|
21018
23195
|
if (body.data.orbitalSource === OrbitalSource.SPICE_PROBE) {
|
|
21019
23196
|
if (bo.isMinor && !bo.mesh) continue;
|
|
21020
23197
|
} else if (!bo.mesh && !bo.focusUpgraded && isMeshUpgradable(body)) continue;
|
|
@@ -22130,10 +24307,18 @@ var PickRegistry = class {
|
|
|
22130
24307
|
#next = 1;
|
|
22131
24308
|
#groups = /* @__PURE__ */ new Map();
|
|
22132
24309
|
allocate(groupId, keys) {
|
|
24310
|
+
return this.add(groupId, keys.length, keys);
|
|
24311
|
+
}
|
|
24312
|
+
allocateRows(groupId, length) {
|
|
24313
|
+
return this.add(groupId, length, null);
|
|
24314
|
+
}
|
|
24315
|
+
add(groupId, length, keys) {
|
|
22133
24316
|
const base = this.#next;
|
|
22134
|
-
this.#next +=
|
|
24317
|
+
this.#next += length;
|
|
22135
24318
|
this.#groups.set(groupId, {
|
|
24319
|
+
groupId,
|
|
22136
24320
|
base,
|
|
24321
|
+
length,
|
|
22137
24322
|
keys
|
|
22138
24323
|
});
|
|
22139
24324
|
return base;
|
|
@@ -22142,8 +24327,20 @@ var PickRegistry = class {
|
|
|
22142
24327
|
this.#groups.delete(groupId);
|
|
22143
24328
|
}
|
|
22144
24329
|
resolve(pickId) {
|
|
22145
|
-
|
|
22146
|
-
|
|
24330
|
+
const group = this.find(pickId);
|
|
24331
|
+
if (!group?.keys) return null;
|
|
24332
|
+
return keyToId(group.keys[pickId - group.base]);
|
|
24333
|
+
}
|
|
24334
|
+
locate(pickId) {
|
|
24335
|
+
const group = this.find(pickId);
|
|
24336
|
+
if (!group || group.keys) return null;
|
|
24337
|
+
return {
|
|
24338
|
+
groupId: group.groupId,
|
|
24339
|
+
row: pickId - group.base
|
|
24340
|
+
};
|
|
24341
|
+
}
|
|
24342
|
+
find(pickId) {
|
|
24343
|
+
for (const group of this.#groups.values()) if (pickId >= group.base && pickId < group.base + group.length) return group;
|
|
22147
24344
|
return null;
|
|
22148
24345
|
}
|
|
22149
24346
|
clear() {
|
|
@@ -22243,6 +24440,8 @@ var PointCloudSystem = class {
|
|
|
22243
24440
|
brightMult: 1,
|
|
22244
24441
|
opacity: EMPHASIS_BASE_OPACITY
|
|
22245
24442
|
};
|
|
24443
|
+
fade = 1;
|
|
24444
|
+
moonFades = /* @__PURE__ */ new Map();
|
|
22246
24445
|
constructor(ctx, scene, bodyObjects, circleTexture, focus, mapLayer, onBasisRebuilt) {
|
|
22247
24446
|
this.ctx = ctx;
|
|
22248
24447
|
this.scene = scene;
|
|
@@ -22262,13 +24461,13 @@ var PointCloudSystem = class {
|
|
|
22262
24461
|
}
|
|
22263
24462
|
applyPointBudget() {
|
|
22264
24463
|
let total = 0;
|
|
22265
|
-
for (const kind of WORKER_CLOUD_KINDS) for (const pts of this.workerPoints[kind].values()) if (pts.visible) total += pts.userData.solvedCount ?? 0;
|
|
22266
|
-
|
|
22267
|
-
if (fraction === this.drawFraction) return;
|
|
22268
|
-
this.drawFraction = fraction;
|
|
24464
|
+
for (const kind of WORKER_CLOUD_KINDS) for (const pts of this.workerPoints[kind].values()) if (pts.visible && !pts.userData.leaving) total += pts.userData.solvedCount ?? 0;
|
|
24465
|
+
this.drawFraction = total > this.pointBudget ? this.pointBudget / total : 1;
|
|
22269
24466
|
for (const kind of WORKER_CLOUD_KINDS) for (const pts of this.workerPoints[kind].values()) {
|
|
24467
|
+
if (pts.userData.leaving || pts.userData.drawFraction === this.drawFraction) continue;
|
|
24468
|
+
pts.userData.drawFraction = this.drawFraction;
|
|
22270
24469
|
const n = pts.userData.solvedCount;
|
|
22271
|
-
if (n !== void 0) pts.geometry.setDrawRange(0, Math.ceil(n *
|
|
24470
|
+
if (n !== void 0) pts.geometry.setDrawRange(0, Math.ceil(n * this.drawFraction));
|
|
22272
24471
|
}
|
|
22273
24472
|
}
|
|
22274
24473
|
setEarthSatEmphasis(count) {
|
|
@@ -22291,7 +24490,49 @@ var PointCloudSystem = class {
|
|
|
22291
24490
|
const { size, dim, opacity } = this.earthSatEmphasis;
|
|
22292
24491
|
mat.size = size;
|
|
22293
24492
|
mat.color.setRGB(dim, dim, dim);
|
|
22294
|
-
|
|
24493
|
+
this.setOpacity(pts, opacity);
|
|
24494
|
+
}
|
|
24495
|
+
setFade(fade) {
|
|
24496
|
+
if (fade === this.fade) return;
|
|
24497
|
+
this.fade = fade;
|
|
24498
|
+
for (const kind of WORKER_CLOUD_KINDS) for (const pts of this.workerPoints[kind].values()) this.applyOpacity(pts);
|
|
24499
|
+
for (const pts of this.moonPoints.values()) this.applyOpacity(pts);
|
|
24500
|
+
}
|
|
24501
|
+
setOpacity(pts, opacity) {
|
|
24502
|
+
pts.userData.opacity = opacity;
|
|
24503
|
+
this.applyOpacity(pts);
|
|
24504
|
+
}
|
|
24505
|
+
applyOpacity(pts) {
|
|
24506
|
+
pts.material.opacity = pts.userData.opacity * this.fade * (pts.userData.share ?? 1);
|
|
24507
|
+
}
|
|
24508
|
+
updateVisibility(dtMs) {
|
|
24509
|
+
const vis = this.ctx.visibility;
|
|
24510
|
+
for (const [key, pts] of this.workerPoints.asteroid) this.setShare(pts, vis.asteroidGroupShare(parentIdFromSubkey(key)));
|
|
24511
|
+
for (const [key, pts] of this.workerPoints.spacecraft) this.setShare(pts, vis.spacecraftGroupShare(parentIdFromSubkey(key)));
|
|
24512
|
+
for (const [parentId, pts] of this.moonPoints) {
|
|
24513
|
+
let fade = this.moonFades.get(parentId);
|
|
24514
|
+
if (!fade) {
|
|
24515
|
+
fade = new SwitchFade(void 0, 100);
|
|
24516
|
+
this.moonFades.set(parentId, fade);
|
|
24517
|
+
}
|
|
24518
|
+
fade.step(vis.isMoonGroupVisible(parentId), dtMs);
|
|
24519
|
+
this.setShare(pts, fade.share);
|
|
24520
|
+
}
|
|
24521
|
+
}
|
|
24522
|
+
get fading() {
|
|
24523
|
+
for (const fade of this.moonFades.values()) if (fade.fading) return true;
|
|
24524
|
+
return false;
|
|
24525
|
+
}
|
|
24526
|
+
setShare(pts, share) {
|
|
24527
|
+
pts.visible = share > 0;
|
|
24528
|
+
const was = pts.userData.share;
|
|
24529
|
+
if (was === share) return;
|
|
24530
|
+
pts.userData.leaving = was !== void 0 && share < was;
|
|
24531
|
+
pts.userData.share = share;
|
|
24532
|
+
this.applyOpacity(pts);
|
|
24533
|
+
}
|
|
24534
|
+
fadeNewCloud(pts) {
|
|
24535
|
+
this.setOpacity(pts, pts.material.opacity);
|
|
22295
24536
|
}
|
|
22296
24537
|
setEmphasizedSmallBodyZone(zone, count) {
|
|
22297
24538
|
const prev = this.emphasizedSmallBodyZone;
|
|
@@ -22323,14 +24564,14 @@ var PointCloudSystem = class {
|
|
|
22323
24564
|
const { sizeMult, brightMult, opacity } = this.smallBodyEmphasis;
|
|
22324
24565
|
mat.size = baseSize * sizeMult;
|
|
22325
24566
|
mat.color.copy(baseColor).multiplyScalar(brightMult);
|
|
22326
|
-
|
|
24567
|
+
this.setOpacity(pts, opacity);
|
|
22327
24568
|
}
|
|
22328
24569
|
resetSmallBodyEmphasis(pts) {
|
|
22329
24570
|
if (pts.userData.smallBodyBaseSize === void 0) return;
|
|
22330
24571
|
const mat = pts.material;
|
|
22331
24572
|
mat.size = pts.userData.smallBodyBaseSize;
|
|
22332
24573
|
mat.color.copy(pts.userData.smallBodyBaseColor);
|
|
22333
|
-
|
|
24574
|
+
this.setOpacity(pts, EMPHASIS_BASE_OPACITY);
|
|
22334
24575
|
pts.userData.smallBodyBaseSize = void 0;
|
|
22335
24576
|
pts.userData.smallBodyBaseColor = void 0;
|
|
22336
24577
|
}
|
|
@@ -22345,8 +24586,10 @@ var PointCloudSystem = class {
|
|
|
22345
24586
|
for (const kind of WORKER_CLOUD_KINDS) for (const [key, p] of this.workerPoints[kind]) {
|
|
22346
24587
|
const bucketKey = parentIdFromSubkey(key);
|
|
22347
24588
|
p.userData.parentBodyId = this.cloudParentId(kind, bucketKey);
|
|
24589
|
+
this.fadeNewCloud(p);
|
|
22348
24590
|
this.emphasiseNewCloud(kind, bucketKey, p);
|
|
22349
24591
|
}
|
|
24592
|
+
for (const p of this.moonPoints.values()) this.fadeNewCloud(p);
|
|
22350
24593
|
this.assignMapLayer();
|
|
22351
24594
|
}
|
|
22352
24595
|
assignMapLayer() {
|
|
@@ -22504,6 +24747,7 @@ var PointCloudSystem = class {
|
|
|
22504
24747
|
0,
|
|
22505
24748
|
0
|
|
22506
24749
|
];
|
|
24750
|
+
this.fadeNewCloud(pts);
|
|
22507
24751
|
this.emphasiseNewCloud(kind, bucketKey, pts);
|
|
22508
24752
|
this.workerPoints[kind].set(key, pts);
|
|
22509
24753
|
this.pendingSceneAdds.push(pts);
|
|
@@ -22561,7 +24805,8 @@ var PointCloudSystem = class {
|
|
|
22561
24805
|
posAttr.needsUpdate = true;
|
|
22562
24806
|
this.bindPickIds(pts, pickIds);
|
|
22563
24807
|
pts.userData.solvedCount = count;
|
|
22564
|
-
pts.
|
|
24808
|
+
const fraction = pts.userData.drawFraction ?? this.drawFraction;
|
|
24809
|
+
pts.geometry.setDrawRange(0, Math.ceil(count * fraction));
|
|
22565
24810
|
this.lastSolvedJd.set(groupId, jd);
|
|
22566
24811
|
pts.userData.frontBasis = [
|
|
22567
24812
|
basisUsed[0],
|
|
@@ -22697,7 +24942,7 @@ var PointCloudSystem = class {
|
|
|
22697
24942
|
for (const [parentId, moons] of this.moonsByParent()) {
|
|
22698
24943
|
const pts = this.moonPoints.get(parentId);
|
|
22699
24944
|
if (!pts) continue;
|
|
22700
|
-
if (!this.ctx.visibility.isMoonGroupVisible(parentId)) continue;
|
|
24945
|
+
if (!pts.visible && !this.ctx.visibility.isMoonGroupVisible(parentId)) continue;
|
|
22701
24946
|
const posAttr = pts.geometry.getAttribute("position");
|
|
22702
24947
|
const arr = posAttr.array;
|
|
22703
24948
|
const n = Math.min(moons.length, arr.length / 3);
|
|
@@ -23002,9 +25247,16 @@ var _anchorOut = {
|
|
|
23002
25247
|
};
|
|
23003
25248
|
var _modelSphereCenter = new Vector3();
|
|
23004
25249
|
var _anchorProj = new Vector3();
|
|
23005
|
-
function
|
|
25250
|
+
function fadeLabel(label, share) {
|
|
25251
|
+
if (label.userData.systemShare === share) return;
|
|
25252
|
+
label.userData.systemShare = share;
|
|
25253
|
+
label.element.style.opacity = share < 1 ? String(share) : "";
|
|
25254
|
+
}
|
|
25255
|
+
function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBodyId, hideCappedMoonLabels, hoveredBodyIds, cullFrameCounter, renderer, tmpV3, forceCull, suppressHeliocentricTrails) {
|
|
23006
25256
|
const layers = ctx.layers;
|
|
23007
25257
|
const hideOrbits = !layers.isVisible("orbits");
|
|
25258
|
+
const systemShare = ctx.visibility.systemShare;
|
|
25259
|
+
const starShare = ctx.visibility.starShare;
|
|
23008
25260
|
const showNomenclature = layers.isVisible("nomenclature");
|
|
23009
25261
|
const fovRad = camera.fov * Math.PI / 180;
|
|
23010
25262
|
const screenW = renderer.domElement.clientWidth;
|
|
@@ -23136,10 +25388,18 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
23136
25388
|
let isClose;
|
|
23137
25389
|
const isFocused = body.data.id === focusedBodyId;
|
|
23138
25390
|
const isSystemRoot = focusedSystemId !== null && body.data.id === focusedSystemId;
|
|
23139
|
-
|
|
25391
|
+
let zoneShare = 1;
|
|
25392
|
+
let labelThresholdShare = 1;
|
|
25393
|
+
let orbitThresholdShare = 1;
|
|
25394
|
+
if (isExtrasolar(body)) {
|
|
25395
|
+
const visible = starShare > 0;
|
|
25396
|
+
group.visible = visible;
|
|
25397
|
+
showLabel = visible;
|
|
25398
|
+
isClose = false;
|
|
25399
|
+
} else if (bo.mesh === null && !isMajorBody(body.data.objectType)) {
|
|
23140
25400
|
const ot = body.data.objectType;
|
|
23141
25401
|
if (ot === ObjectType.BARYCENTER || ot === ObjectType.LAGRANGE_POINT) {
|
|
23142
|
-
let visible =
|
|
25402
|
+
let visible = systemShare > 0;
|
|
23143
25403
|
const primaryId = !isFocused ? barycenterPrimaryId(body.data.id) : void 0;
|
|
23144
25404
|
if (primaryId) {
|
|
23145
25405
|
const primary = bodyObjects.get(primaryId)?.body ?? ctx.getBody(primaryId);
|
|
@@ -23152,7 +25412,9 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
23152
25412
|
showLabel = visible;
|
|
23153
25413
|
isClose = false;
|
|
23154
25414
|
} else {
|
|
23155
|
-
const
|
|
25415
|
+
const vis = ctx.visibility.getPlanetVisibility(body, dist);
|
|
25416
|
+
zoneShare = ctx.visibility.fullRenderingShare(body);
|
|
25417
|
+
const visible = vis !== VISIBILITY.HIDE && zoneShare > 0;
|
|
23156
25418
|
group.visible = visible;
|
|
23157
25419
|
if (trail) trail.visible = visible;
|
|
23158
25420
|
showLabel = visible;
|
|
@@ -23161,18 +25423,32 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
23161
25423
|
} else {
|
|
23162
25424
|
const isMoon = body.data.objectType === ObjectType.MOON;
|
|
23163
25425
|
const vis = isMoon ? ctx.visibility.getMoonVisibility(body) : ctx.visibility.getPlanetVisibility(body, dist);
|
|
23164
|
-
|
|
25426
|
+
if (!isMoon) zoneShare = ctx.visibility.fullRenderingShare(body);
|
|
25427
|
+
const vf = isMoon ? moonVisFlags(vis, hideCappedMoonLabels, isFocused) : bodyVisFlags(vis, zoneShare > 0, isFocused || isSystemRoot);
|
|
23165
25428
|
group.visible = vf.groupVisible;
|
|
23166
25429
|
if (trail) trail.visible = vf.orbitVisible;
|
|
23167
25430
|
showLabel = vf.showLabel;
|
|
23168
25431
|
isClose = vf.isClose;
|
|
23169
25432
|
}
|
|
25433
|
+
if (focusedSystemId !== null && ctx.visibility.isBodyInSystem(body, focusedSystemId)) {
|
|
25434
|
+
labelThresholdShare = ctx.visibility.labelThresholdShare(body.data.id, showLabel);
|
|
25435
|
+
orbitThresholdShare = ctx.visibility.orbitThresholdShare(body.data.id, !!trail?.visible);
|
|
25436
|
+
if (labelThresholdShare > 0) {
|
|
25437
|
+
group.visible = true;
|
|
25438
|
+
showLabel = true;
|
|
25439
|
+
}
|
|
25440
|
+
if (trail && orbitThresholdShare > 0) trail.visible = true;
|
|
25441
|
+
}
|
|
23170
25442
|
if (!drawn) {
|
|
23171
25443
|
group.visible = false;
|
|
23172
25444
|
showLabel = false;
|
|
23173
25445
|
isClose = false;
|
|
23174
25446
|
}
|
|
23175
25447
|
if (trail && (!drawn || body.orbitCenter === null)) trail.visible = false;
|
|
25448
|
+
if (trail) {
|
|
25449
|
+
trail.userData.zoneShare = zoneShare;
|
|
25450
|
+
trail.userData.thresholdShare = orbitThresholdShare;
|
|
25451
|
+
}
|
|
23176
25452
|
let starIsPoint = false;
|
|
23177
25453
|
if (bo.starPoint && drawn) {
|
|
23178
25454
|
const t = bo.radiusScene / dist * projScale / STAR_POINT_HANDOFF_R;
|
|
@@ -23205,12 +25481,10 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
23205
25481
|
}
|
|
23206
25482
|
}
|
|
23207
25483
|
if (applyLabelDisplay(bo, showLabel, isClose, dist, projScale, focusedBodyId)) newlyShownLabel = true;
|
|
25484
|
+
if (bo.label) fadeLabel(bo.label, bo.starPoint ? 1 : isExtrasolar(body) ? starShare : systemShare * zoneShare * labelThresholdShare);
|
|
23208
25485
|
const nomScreenR = bo.radiusScene > 0 ? bo.radiusScene / dist * projScale : 0;
|
|
23209
25486
|
updateNomenclatureVisibility(bo, showNomenclature && body.data.id === nomFocusedBodyId, nomScreenR, camera, screenW, screenH);
|
|
23210
25487
|
}
|
|
23211
|
-
for (const [key, pts] of asteroidPoints) pts.visible = ctx.visibility.isAsteroidGroupVisible(parentIdFromSubkey(key));
|
|
23212
|
-
for (const [key, pts] of spacecraftPoints) pts.visible = ctx.visibility.isSpacecraftGroupVisible(parentIdFromSubkey(key));
|
|
23213
|
-
for (const [parentId, pts] of moonPoints) pts.visible = ctx.visibility.isMoonGroupVisible(parentId);
|
|
23214
25488
|
if (screenOccluders.length > 0) {
|
|
23215
25489
|
const focusedPrimaryId = focusedBodyId ? barycenterPrimaryId(focusedBodyId) : null;
|
|
23216
25490
|
for (const bo of bodyObjects.values()) {
|
|
@@ -23236,7 +25510,8 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
23236
25510
|
mat.uniforms.uCenterOffset.value.set(-camera.position.x, -camera.position.y, -camera.position.z);
|
|
23237
25511
|
const isFocused = bo.body.data.id === focusedBodyId;
|
|
23238
25512
|
const isHovered = hoveredBodyIds.has(bo.body.data.id);
|
|
23239
|
-
|
|
25513
|
+
const share = systemShare * (line.userData.zoneShare ?? 1) * (line.userData.thresholdShare ?? 1);
|
|
25514
|
+
mat.uniforms.uAlphaMultiplier.value = (isHovered ? 2 : isFocused ? 1.75 : 1) * share;
|
|
23240
25515
|
const isClosed = line.userData.isOpenCurve === false;
|
|
23241
25516
|
mat.uniforms.uAlphaMin.value = isFocused && isClosed ? .15 : 0;
|
|
23242
25517
|
mat.uniforms.uShowFull.value = isFocused ? 1 : 0;
|
|
@@ -24524,6 +26799,7 @@ var OutOfRangeNotifier = class {
|
|
|
24524
26799
|
//#region src/lib/scene/position/landed-probe.ts
|
|
24525
26800
|
var lastSeat = /* @__PURE__ */ new Map();
|
|
24526
26801
|
var _upScene = new Vector3();
|
|
26802
|
+
var _northScene = new Vector3();
|
|
24527
26803
|
function renderLandedProbe(d, probe, landed, jd, where, ctx, bodyObjects) {
|
|
24528
26804
|
const sample = landedPositionAt(landed, jd, landedOpenEnded(probe));
|
|
24529
26805
|
if (!sample) return null;
|
|
@@ -24553,18 +26829,24 @@ function renderLandedProbe(d, probe, landed, jd, where, ctx, bodyObjects) {
|
|
|
24553
26829
|
const quat = bodyQuaternion(landingBody.orientation, jd, landingBody.nutPrec);
|
|
24554
26830
|
const tmp = new Vector3(bx, by, bz).applyQuaternion(quat);
|
|
24555
26831
|
const up = eff ? _upScene.set(eff.normal[0], eff.normal[1], eff.normal[2]).applyQuaternion(quat) : null;
|
|
26832
|
+
const north = _northScene.set(-Math.sin(latR) * Math.cos(lngR), Math.cos(latR), Math.sin(latR) * Math.sin(lngR)).applyQuaternion(quat);
|
|
24556
26833
|
d.parentId = bodyKey;
|
|
24557
26834
|
return {
|
|
24558
26835
|
x: bodyWorldPos[0] + kmToScene(tmp.x),
|
|
24559
26836
|
y: bodyWorldPos[1] + kmToScene(tmp.y),
|
|
24560
26837
|
z: bodyWorldPos[2] + kmToScene(tmp.z),
|
|
24561
26838
|
parentPos: bodyWorldPos,
|
|
24562
|
-
up
|
|
26839
|
+
up,
|
|
26840
|
+
north
|
|
24563
26841
|
};
|
|
24564
26842
|
}
|
|
24565
26843
|
|
|
24566
26844
|
//#endregion
|
|
24567
26845
|
//#region src/lib/scene/position/placer.ts
|
|
26846
|
+
function fixedPlace(body) {
|
|
26847
|
+
const at = body.extrasolar?.scene;
|
|
26848
|
+
return at ? setPlaced(body, at[0], at[1], at[2]) : setUnplaced(body, "never");
|
|
26849
|
+
}
|
|
24568
26850
|
var newestPosScratch = [
|
|
24569
26851
|
0,
|
|
24570
26852
|
0,
|
|
@@ -24649,7 +26931,7 @@ var Placer = class {
|
|
|
24649
26931
|
return null;
|
|
24650
26932
|
}
|
|
24651
26933
|
this.settling.add(id);
|
|
24652
|
-
const pos = isSurfaceFeature(body) ? this.seatFeature(body, jd) : this.settle(body, jd);
|
|
26934
|
+
const pos = isSurfaceFeature(body) ? this.seatFeature(body, jd) : isExtrasolar(body) ? fixedPlace(body) : this.settle(body, jd);
|
|
24653
26935
|
this.settling.delete(id);
|
|
24654
26936
|
if (jd === this.passJd) this.settled.add(id);
|
|
24655
26937
|
return pos;
|
|
@@ -24810,8 +27092,12 @@ var Placer = class {
|
|
|
24810
27092
|
const up = landedRender.up;
|
|
24811
27093
|
if (bo?.mesh) if (up) applyUpVector(bo.mesh, up);
|
|
24812
27094
|
else applySouthTowardParent(bo.mesh, pos, landedRender.parentPos);
|
|
24813
|
-
if (bo?.model)
|
|
24814
|
-
|
|
27095
|
+
if (bo?.model) {
|
|
27096
|
+
if (up) applyUpVector(bo.model, up);
|
|
27097
|
+
else applySouthTowardParent(bo.model, pos, landedRender.parentPos);
|
|
27098
|
+
const heading = landedHeadingAt(body.landedHeadings, jd);
|
|
27099
|
+
if (heading !== void 0) turnToHeading(bo.model, landedRender.north, heading);
|
|
27100
|
+
}
|
|
24815
27101
|
return pos;
|
|
24816
27102
|
}
|
|
24817
27103
|
const primary = resolveProbePrimary(located.probe, jd, located.fitCenterNaifId, ctx.chebStore ?? null, (id) => this.at(id, jd) !== null);
|
|
@@ -25374,6 +27660,13 @@ function updateSunShadowLight(bodyObjects, focusTruePos, ctx, shadowLight, sunPo
|
|
|
25374
27660
|
shadowCam.updateProjectionMatrix();
|
|
25375
27661
|
}
|
|
25376
27662
|
|
|
27663
|
+
//#endregion
|
|
27664
|
+
//#region src/lib/scene/relief/grid-heights.ts
|
|
27665
|
+
var CAP_SIN = Math.sin(60 * Math.PI / 180);
|
|
27666
|
+
function gridChart(level, y) {
|
|
27667
|
+
return level < 1 || Math.abs(y) <= CAP_SIN ? 0 : y > 0 ? 1 : 2;
|
|
27668
|
+
}
|
|
27669
|
+
|
|
25377
27670
|
//#endregion
|
|
25378
27671
|
//#region src/lib/scene/lod/terrain-grid.ts
|
|
25379
27672
|
var COARSE_SEGS = 128;
|
|
@@ -25591,7 +27884,14 @@ function heightTilesOf(bo) {
|
|
|
25591
27884
|
return reliefTilesAvailable(tiles) && !tiledStates.get(bo)?.dead ? tiles : void 0;
|
|
25592
27885
|
}
|
|
25593
27886
|
function gridLevel(tiles, theta, phi, radius) {
|
|
25594
|
-
const
|
|
27887
|
+
const y = Math.cos(theta);
|
|
27888
|
+
const chart = gridChart(tiles.max_level, y);
|
|
27889
|
+
const point = chartPoint(chart, ...[
|
|
27890
|
+
-Math.cos(phi) * Math.sin(theta),
|
|
27891
|
+
y,
|
|
27892
|
+
Math.sin(phi) * Math.sin(theta)
|
|
27893
|
+
]);
|
|
27894
|
+
const deepest = deepestLevel(tiles.max_level, tiles.patches, chart, point.x, point.y);
|
|
25595
27895
|
if (deepest === tiles.max_level) return deepest;
|
|
25596
27896
|
const floatStep = 2 ** (Math.floor(Math.log2(radius)) - 23);
|
|
25597
27897
|
const cells = 2 * Math.PI * radius / (CELL_FLOAT_STEPS * floatStep);
|
|
@@ -25659,7 +27959,8 @@ function updateTiledWindow(bo, mesh, tiles, altitude, centerTheta, centerPhi, in
|
|
|
25659
27959
|
root: tileRoot(tiles),
|
|
25660
27960
|
version: tiles.version,
|
|
25661
27961
|
maxLevel: tiles.max_level,
|
|
25662
|
-
patches: (tiles.patches ?? []).map(({ max_level, x, y, version }) => ({
|
|
27962
|
+
patches: (tiles.patches ?? []).map(({ cap, max_level, x, y, version }) => ({
|
|
27963
|
+
cap,
|
|
25663
27964
|
max_level,
|
|
25664
27965
|
x,
|
|
25665
27966
|
y,
|
|
@@ -26230,6 +28531,7 @@ var PromotionRegistry = class {
|
|
|
26230
28531
|
autoPromoted = /* @__PURE__ */ new Set();
|
|
26231
28532
|
pinned = /* @__PURE__ */ new Set();
|
|
26232
28533
|
pinPromoted = /* @__PURE__ */ new Set();
|
|
28534
|
+
focusPromoted = /* @__PURE__ */ new Set();
|
|
26233
28535
|
groupTargets = null;
|
|
26234
28536
|
groupMode = "none";
|
|
26235
28537
|
smallBodyZone = null;
|
|
@@ -26314,10 +28616,15 @@ var PromotionRegistry = class {
|
|
|
26314
28616
|
if (!this.buildBodyInstance(body)) return;
|
|
26315
28617
|
this.finalizeBuilds();
|
|
26316
28618
|
}
|
|
28619
|
+
releaseFocusPromoted(focusedId) {
|
|
28620
|
+
if (this.focusPromoted.size === 0) return;
|
|
28621
|
+
const ids = [...this.focusPromoted].filter((id) => id !== focusedId);
|
|
28622
|
+
this.tearDownBodies(ids, this.focusPromoted);
|
|
28623
|
+
}
|
|
26317
28624
|
buildBodyInstance(body, halfPromoteIds, auto = false) {
|
|
26318
28625
|
const { bodyObjects, ctx, clock, scene, clickables, meshToBody, renderer } = this.deps;
|
|
26319
28626
|
if (bodyObjects.has(body.data.id)) return false;
|
|
26320
|
-
if (!body.orbitElements && !body.data.pageOnly && body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) {
|
|
28627
|
+
if (!body.orbitElements && !body.data.pageOnly && !isExtrasolar(body) && body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) {
|
|
26321
28628
|
body.orbitElements = body.data;
|
|
26322
28629
|
body.orbitCenterId = body.data.parentId;
|
|
26323
28630
|
}
|
|
@@ -26331,7 +28638,8 @@ var PromotionRegistry = class {
|
|
|
26331
28638
|
if (zone) ctx.bodies.dirtyAsteroidZones.add(zone);
|
|
26332
28639
|
}
|
|
26333
28640
|
const id = body.data.id;
|
|
26334
|
-
if (this.defaults.has(id)) {} else if (
|
|
28641
|
+
if (this.defaults.has(id)) {} else if (isExtrasolar(body)) this.focusPromoted.add(id);
|
|
28642
|
+
else if (auto) this.autoPromoted.add(id);
|
|
26335
28643
|
else {
|
|
26336
28644
|
this.userPromoted.add(id);
|
|
26337
28645
|
this.emitUserPromotedCount();
|
|
@@ -26524,6 +28832,7 @@ var PromotionRegistry = class {
|
|
|
26524
28832
|
var FocusController = class {
|
|
26525
28833
|
promotion;
|
|
26526
28834
|
readerFocusFilter = null;
|
|
28835
|
+
focusSets = 0;
|
|
26527
28836
|
focusedBody;
|
|
26528
28837
|
_tmpV3 = new Vector3();
|
|
26529
28838
|
pendingInitialView = null;
|
|
@@ -26551,6 +28860,9 @@ var FocusController = class {
|
|
|
26551
28860
|
get current() {
|
|
26552
28861
|
return this.focusedBody;
|
|
26553
28862
|
}
|
|
28863
|
+
get focusChanges() {
|
|
28864
|
+
return this.focusSets;
|
|
28865
|
+
}
|
|
26554
28866
|
place(body) {
|
|
26555
28867
|
return this.deps.ctx.place(body, this.deps.clock.jd);
|
|
26556
28868
|
}
|
|
@@ -26643,6 +28955,7 @@ var FocusController = class {
|
|
|
26643
28955
|
];
|
|
26644
28956
|
}
|
|
26645
28957
|
this.focusedBody = void 0;
|
|
28958
|
+
this.promotion.releaseFocusPromoted(void 0);
|
|
26646
28959
|
prepareFocusTarget(focus, [...position], camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
26647
28960
|
const spherical = cartesianToSpherical(camPos ?? camWorld, position, void 0);
|
|
26648
28961
|
callbacks.onCameraPosition?.(spherical.latitude, spherical.longitude, spherical.distance);
|
|
@@ -26661,6 +28974,8 @@ var FocusController = class {
|
|
|
26661
28974
|
}
|
|
26662
28975
|
setFocusTarget(body, camPos) {
|
|
26663
28976
|
const { ctx, bodyObjects, controls, callbacks, focus, camera, systemData, loadTexture } = this.deps;
|
|
28977
|
+
this.focusSets++;
|
|
28978
|
+
const drawsItself = !isSurfaceFeature(body) && !isExtrasolar(body);
|
|
26664
28979
|
if (!isSurfaceFeature(body)) this.promotion.ensureBodyObjects(body);
|
|
26665
28980
|
const prev = this.focusedBody;
|
|
26666
28981
|
const prevNomBodyId = nomenclatureBodyId(prev, bodyObjects);
|
|
@@ -26678,12 +28993,13 @@ var FocusController = class {
|
|
|
26678
28993
|
}
|
|
26679
28994
|
if (body.data.id !== prev?.data.id) this.pendingInitialView = null;
|
|
26680
28995
|
this.focusedBody = body;
|
|
28996
|
+
this.promotion.releaseFocusPromoted(body.data.id);
|
|
26681
28997
|
if (!isSurfaceFeature(body)) ctx.bodies.focusFeature = null;
|
|
26682
28998
|
controls.minDistance = minCameraDistance(body, modelMinRadiusKm(bodyObjects.get(body.data.id)));
|
|
26683
28999
|
const featureHost = isSurfaceFeature(body) ? ctx.getBody(body.featureAnchor.hostId) : void 0;
|
|
26684
29000
|
ctx.visibility.setFocused(featureHost ?? body);
|
|
26685
29001
|
callbacks.onFocusChange(body);
|
|
26686
|
-
if (
|
|
29002
|
+
if (drawsItself) loadTexture(body);
|
|
26687
29003
|
if (newNomBodyId && newNomBodyId !== body.data.id) {
|
|
26688
29004
|
const landingBody = ctx.getBody(newNomBodyId);
|
|
26689
29005
|
if (landingBody) loadTexture(landingBody);
|
|
@@ -26755,6 +29071,30 @@ var FocusController = class {
|
|
|
26755
29071
|
callbacks.onCameraPosition?.(settled.latitude, settled.longitude, settled.distance);
|
|
26756
29072
|
this.pendingInitialView = null;
|
|
26757
29073
|
}
|
|
29074
|
+
snapToBodyFrom(id, fromId, distance) {
|
|
29075
|
+
const { ctx, camera, controls, callbacks } = this.deps;
|
|
29076
|
+
const body = ctx.getBody(id);
|
|
29077
|
+
const from = ctx.getBody(fromId);
|
|
29078
|
+
const pos = body && this.place(body);
|
|
29079
|
+
const at = from && this.place(from);
|
|
29080
|
+
if (!body || !pos || !at) return;
|
|
29081
|
+
this.settleOnBodyInstant(body, pos);
|
|
29082
|
+
const d = f64dist(at, pos);
|
|
29083
|
+
const offset = [
|
|
29084
|
+
(at[0] - pos[0]) / d * distance,
|
|
29085
|
+
(at[1] - pos[1]) / d * distance,
|
|
29086
|
+
(at[2] - pos[2]) / d * distance
|
|
29087
|
+
];
|
|
29088
|
+
camera.position.set(offset[0], offset[1], offset[2]);
|
|
29089
|
+
controls.update();
|
|
29090
|
+
const settled = cartesianToSpherical(offset, [
|
|
29091
|
+
0,
|
|
29092
|
+
0,
|
|
29093
|
+
0
|
|
29094
|
+
]);
|
|
29095
|
+
callbacks.onCameraPosition?.(settled.latitude, settled.longitude, settled.distance);
|
|
29096
|
+
this.pendingInitialView = null;
|
|
29097
|
+
}
|
|
26758
29098
|
settleOnBodyInstant(body, pos) {
|
|
26759
29099
|
const { focus, pointClouds } = this.deps;
|
|
26760
29100
|
this.setFocusTarget(body);
|
|
@@ -26775,7 +29115,7 @@ var FocusController = class {
|
|
|
26775
29115
|
const body = this.focusedBody;
|
|
26776
29116
|
if (!body) return;
|
|
26777
29117
|
const { camera, controls, callbacks } = this.deps;
|
|
26778
|
-
const bodyFixed = !isModelBearing(body);
|
|
29118
|
+
const bodyFixed = !isModelBearing(body) && !isExtrasolar(body);
|
|
26779
29119
|
const quat = bodyFixed ? this.focusedBodyQuat(body) ?? [
|
|
26780
29120
|
0,
|
|
26781
29121
|
0,
|
|
@@ -27174,6 +29514,7 @@ var TIGHT_FAR_RELEASE = 2.3;
|
|
|
27174
29514
|
var SUN_PROXY_FAR_FRACTION = .9;
|
|
27175
29515
|
var _sunLiftDir = new Vector3();
|
|
27176
29516
|
var TRAIL_DRIFT_PX = .25;
|
|
29517
|
+
var SKY_GONE_SHARE = .9999;
|
|
27177
29518
|
var MODEL_SHADOW_DIST = 10;
|
|
27178
29519
|
var MODEL_SHADOW_EXTENT = 4;
|
|
27179
29520
|
var MODEL_SHADOW_NEAR = 4;
|
|
@@ -27204,6 +29545,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27204
29545
|
bodyObjects = /* @__PURE__ */ new Map();
|
|
27205
29546
|
circleTexture = makeCircleTexture();
|
|
27206
29547
|
pointClouds;
|
|
29548
|
+
starfield;
|
|
27207
29549
|
systemData;
|
|
27208
29550
|
positionDiagnostics = new PositionDiagnostics();
|
|
27209
29551
|
placer;
|
|
@@ -27224,6 +29566,8 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27224
29566
|
limitsArmed = false;
|
|
27225
29567
|
bodySelectEnabled = true;
|
|
27226
29568
|
featureSelectEnabled = true;
|
|
29569
|
+
starSelectEnabled = false;
|
|
29570
|
+
pickSerial = 0;
|
|
27227
29571
|
modelScene;
|
|
27228
29572
|
modelCamera;
|
|
27229
29573
|
modelLight;
|
|
@@ -27325,6 +29669,8 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27325
29669
|
fpsSamples = [];
|
|
27326
29670
|
fpsSampleHead = 0;
|
|
27327
29671
|
stashedSky = null;
|
|
29672
|
+
systemOn = true;
|
|
29673
|
+
systemFade = new SwitchFade();
|
|
27328
29674
|
namesShown = true;
|
|
27329
29675
|
halosShown = true;
|
|
27330
29676
|
textureLoader = new TextureLoader();
|
|
@@ -27413,11 +29759,12 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27413
29759
|
this.pointClouds = new PointCloudSystem(ctx, this.scene, this.bodyObjects, this.circleTexture, this.focus, SceneRenderer.MAP_LAYER, () => this.rebuildTrailBasis());
|
|
27414
29760
|
this.pointClouds.seedBasis(focusPos);
|
|
27415
29761
|
this.pointClouds.setPointBudget(this.tier.pointBudget);
|
|
29762
|
+
this.starfield = new StarfieldSystem(ctx, this.scene, starBudget(), this.invalidate, this.pointClouds.pickRegistry);
|
|
27416
29763
|
this.controls = new OrbitControls(this.camera, canvas);
|
|
27417
29764
|
this.controls.enableDamping = true;
|
|
27418
29765
|
this.orbitDampingFactor = orbitDamping(sceneSettings().resolvedReducedMotion);
|
|
27419
29766
|
this.controls.minDistance = focusBody ? minCameraDistance(focusBody) : kmToScene(.01);
|
|
27420
|
-
this.controls
|
|
29767
|
+
Object.defineProperty(this.controls, "maxDistance", { get: () => this.maxCameraDistance() });
|
|
27421
29768
|
this.controls.target.set(0, 0, 0);
|
|
27422
29769
|
this.controls.update();
|
|
27423
29770
|
this.controls.addEventListener("end", this.onControlsEnd);
|
|
@@ -27474,7 +29821,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27474
29821
|
this.systemData.syncToFocus();
|
|
27475
29822
|
this.focusController.readerFocusFilter = this.readerMayFocus;
|
|
27476
29823
|
this.gpuPick = new GpuPickPass(this.renderer, this.camera);
|
|
27477
|
-
this.pointerInteraction = new PointerInteraction(canvas, this.camera, ctx, clock, this.focus, this.clickables, this.meshToBody,
|
|
29824
|
+
this.pointerInteraction = new PointerInteraction(canvas, this.camera, ctx, clock, this.focus, this.clickables, this.meshToBody, this.pickBody, (ndcX, ndcY) => this.pickFocusedModel(ndcX, ndcY), this.gpuPick, () => [...this.pointClouds.asteroids().values(), ...this.pointClouds.spacecraft().values()], this.pointClouds.pickRegistry, () => this.starSelectEnabled && this.bodySelectEnabled ? this.starfield.pickDraws() : [], (pickId) => this.pickStar(pickId));
|
|
27478
29825
|
this.pointerInteraction.attach();
|
|
27479
29826
|
this.tick();
|
|
27480
29827
|
}
|
|
@@ -27733,6 +30080,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27733
30080
|
this.lastRenderedCamPos.copy(this.camera.position);
|
|
27734
30081
|
this.lastRenderedCamQuat.copy(this.camera.quaternion);
|
|
27735
30082
|
this.applyJdUpdate(true);
|
|
30083
|
+
if (!this.dateHeld() && !this.seekAwaitsData) this.clock.placed(this.seenJumps);
|
|
27736
30084
|
this.refreshTravelFocus();
|
|
27737
30085
|
this.callbacks.onFrame?.(this.sceneJd, renderedDtMs);
|
|
27738
30086
|
const controlsSettled = this.holdingCamera ? this.applyHeldCamera() : stepFocusAnimation(this.focus, this.camera, this.controls, () => this.repositionAll(), () => this.pointClouds.rebuildBasis());
|
|
@@ -27757,7 +30105,9 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27757
30105
|
if (this.systemData.hasPendingUnloads() && performance.now() - this.focus.focusStartTime >= this.focus.focusDurationMs) this.systemData.drainPendingUnloads();
|
|
27758
30106
|
const { distance } = this.getCameraState();
|
|
27759
30107
|
this.updateTravelCraftSystem();
|
|
30108
|
+
this.updateSystemShare(renderedDtMs);
|
|
27760
30109
|
this.ctx.visibility.updateCamera(distance, this.sceneJd);
|
|
30110
|
+
this.ctx.visibility.stepZones(renderedDtMs);
|
|
27761
30111
|
this.updateTightFar(distance);
|
|
27762
30112
|
this.systemData.syncToFocus();
|
|
27763
30113
|
this.refreshTravelPath();
|
|
@@ -27769,7 +30119,9 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27769
30119
|
viewportPx: this.viewportH,
|
|
27770
30120
|
ctx: this.ctx
|
|
27771
30121
|
});
|
|
27772
|
-
this.
|
|
30122
|
+
this.ctx.visibility.starShare = this.starfield.share();
|
|
30123
|
+
this.cullFrameCounter = updateBodyVisibility(this.bodyObjects, this.camera, this.ctx, this.focus.focusTruePos, this.focusController.current?.data.id, this.hideCappedMoonLabels, this.hoveredBodyIds, this.cullFrameCounter, this.renderer, this._tmpV3, !controlsSettled, this.tightFar);
|
|
30124
|
+
this.pointClouds.updateVisibility(renderedDtMs);
|
|
27773
30125
|
this.pointClouds.applyPointBudget();
|
|
27774
30126
|
refreshDeferredTrails(this.bodyObjects, this.focus, this.lastUpdatedJd, this.trailView());
|
|
27775
30127
|
updateRingShaders(this.bodyObjects, this.focus.focusTruePos, sceneSettings().realisticLighting, sceneSettings().overexposeRings, this.camera.position, this.pxPerRad());
|
|
@@ -27779,7 +30131,6 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27779
30131
|
occluders.length = 0;
|
|
27780
30132
|
const atmoState = updateAtmosphereShaders(this.bodyObjects, this.camera.position, sceneSettings().showAtmospheres, sceneSettings().realisticLighting, this.sunIntensityScale, currentAtmosphereConfig(), this.sceneJd);
|
|
27781
30133
|
recordAtmospherePerf(renderedDtMs, atmoState.shellProminent);
|
|
27782
|
-
this.scene.backgroundIntensity = atmoState.skyboxIntensity;
|
|
27783
30134
|
this.applyLayerChrome();
|
|
27784
30135
|
const sunBo = this.bodyObjects.get(SUN_ID);
|
|
27785
30136
|
if (sunBo) applyStarTint(sunBo.corona, sunBo.starPoint, atmoState.sunTint);
|
|
@@ -27801,6 +30152,11 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27801
30152
|
this.pointClouds.drainOnePendingSceneAdd();
|
|
27802
30153
|
this.updateDepthFar();
|
|
27803
30154
|
this.updateSunProxy(atmoState.sunRefraction);
|
|
30155
|
+
this.camera.updateMatrixWorld();
|
|
30156
|
+
const focusedStar = this.focusController.current;
|
|
30157
|
+
this.starfield.setFocusStar(focusedStar && isExtrasolar(focusedStar) ? focusedStar.extrasolar ?? null : null);
|
|
30158
|
+
this.starfield.update(this.camera, this.focus.focusTruePos, this.renderer.getPixelRatio(), atmoState.skyboxIntensity, nowMs);
|
|
30159
|
+
this.updateSky(atmoState.skyboxIntensity);
|
|
27804
30160
|
if (atmoState.insideShell) this.atmoDepthPass.run(this.renderer, this.scene, this.camera, this.bodyObjects, SceneRenderer.ATMO_DEPTH_LAYER);
|
|
27805
30161
|
this.syncSceneSamples(atmoState.insideShell);
|
|
27806
30162
|
this.composer.readBuffer.resolveDepthBuffer = this.overlayActive;
|
|
@@ -27826,6 +30182,8 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27826
30182
|
if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs) return true;
|
|
27827
30183
|
if (this.jumpJd !== null || this.seekAwaitsData) return true;
|
|
27828
30184
|
if (this.focusLoads > 0 || this.systemData.isLoading()) return true;
|
|
30185
|
+
if (this.systemFade.fading || this.ctx.visibility.fading) return true;
|
|
30186
|
+
if (this.pointClouds.fading) return true;
|
|
27829
30187
|
for (const bo of this.bodyObjects.values()) if (bo.group.visible && bodyLoading(bo)) return true;
|
|
27830
30188
|
return false;
|
|
27831
30189
|
}
|
|
@@ -27836,6 +30194,26 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27836
30194
|
target.samples = samples;
|
|
27837
30195
|
target.dispose();
|
|
27838
30196
|
}
|
|
30197
|
+
maxCameraDistance() {
|
|
30198
|
+
switch (this.starfield.availability()) {
|
|
30199
|
+
case "yes": return CAMERA_MAX_DISTANCE;
|
|
30200
|
+
case "no": return SOLAR_MAX_DISTANCE;
|
|
30201
|
+
case "unknown": return Math.max(SOLAR_MAX_DISTANCE, this.camera.position.distanceTo(this.controls.target));
|
|
30202
|
+
}
|
|
30203
|
+
}
|
|
30204
|
+
updateSystemShare(dtMs) {
|
|
30205
|
+
const [fx, fy, fz] = this.focus.focusTruePos;
|
|
30206
|
+
const cam = this.camera.position;
|
|
30207
|
+
const fromBarycentre = Math.hypot(fx + cam.x, fy + cam.y, fz + cam.z);
|
|
30208
|
+
this.systemOn = systemShown(fromBarycentre, this.viewportH, this.camera.fov, this.systemOn);
|
|
30209
|
+
this.systemFade.step(this.systemOn, dtMs);
|
|
30210
|
+
const share = this.systemFade.share;
|
|
30211
|
+
const visibility = this.ctx.visibility;
|
|
30212
|
+
const wasHidden = visibility.systemShare === 0;
|
|
30213
|
+
visibility.systemShare = share;
|
|
30214
|
+
this.pointClouds.setFade(share);
|
|
30215
|
+
if (wasHidden && share > 0) this.ctx.refreshTick(jdToDate(this.clock.jd));
|
|
30216
|
+
}
|
|
27839
30217
|
updateTightFar(distance) {
|
|
27840
30218
|
if (isReversedDepth()) return;
|
|
27841
30219
|
const focused = this.focusController.current;
|
|
@@ -27855,10 +30233,12 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27855
30233
|
}
|
|
27856
30234
|
updateDepthFar() {
|
|
27857
30235
|
if (isReversedDepth()) return;
|
|
27858
|
-
|
|
30236
|
+
const [fx, fy, fz] = this.focus.focusTruePos;
|
|
30237
|
+
const cam = this.camera.position;
|
|
30238
|
+
const span = Math.max(cam.length(), Math.hypot(fx + cam.x, fy + cam.y, fz + cam.z));
|
|
30239
|
+
let far = Math.max(CAMERA_FAR_DEFAULT, span * FAR_MARGIN);
|
|
27859
30240
|
const sysId = this.ctx.visibility.activeSystemId;
|
|
27860
30241
|
if (sysId) {
|
|
27861
|
-
const [fx, fy, fz] = this.focus.focusTruePos;
|
|
27862
30242
|
let maxDistSq = this.camera.position.lengthSq();
|
|
27863
30243
|
const consider = (pos) => {
|
|
27864
30244
|
const dx = pos[0] - fx;
|
|
@@ -27879,7 +30259,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
27879
30259
|
const p = bo.body.position;
|
|
27880
30260
|
if (p && this.ctx.visibility.hasFullRendering(bo.body)) consider(p);
|
|
27881
30261
|
}
|
|
27882
|
-
far = Math.min(
|
|
30262
|
+
far = Math.min(far, Math.max(FAR_MIN, Math.sqrt(maxDistSq) * FAR_MARGIN));
|
|
27883
30263
|
}
|
|
27884
30264
|
if (Math.abs(far - this.camera.far) > this.camera.far * FAR_UPDATE_EPS) {
|
|
27885
30265
|
this.camera.far = far;
|
|
@@ -28137,7 +30517,30 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28137
30517
|
setFeatureSelectEnabled(enabled) {
|
|
28138
30518
|
this.featureSelectEnabled = enabled;
|
|
28139
30519
|
}
|
|
30520
|
+
setStarSelectEnabled(enabled) {
|
|
30521
|
+
this.starSelectEnabled = enabled;
|
|
30522
|
+
}
|
|
28140
30523
|
readerMayFocus = (body) => this.bodySelectEnabled && (this.focusableBodies?.has(body.data.id) ?? true);
|
|
30524
|
+
pickBody = (body) => {
|
|
30525
|
+
this.pickSerial++;
|
|
30526
|
+
this.focusController.handleFocus(body);
|
|
30527
|
+
};
|
|
30528
|
+
pickStar(pickId) {
|
|
30529
|
+
const star = this.starfield.pickedStar(pickId);
|
|
30530
|
+
if (!star) return false;
|
|
30531
|
+
const serial = ++this.pickSerial;
|
|
30532
|
+
const focusChanges = this.focusController.focusChanges;
|
|
30533
|
+
const current = () => serial === this.pickSerial && focusChanges === this.focusController.focusChanges;
|
|
30534
|
+
(async () => {
|
|
30535
|
+
const objectId = extrasolarObjectId(await this.starfield.starId(star));
|
|
30536
|
+
if (!current()) return;
|
|
30537
|
+
await this.ctx.ensureBody(objectId, jdToDate(this.clock.jd));
|
|
30538
|
+
const body = this.ctx.getBody(objectId);
|
|
30539
|
+
if (!body) throw new Error(`no record for ${objectId}`);
|
|
30540
|
+
if (current()) this.pickBody(body);
|
|
30541
|
+
})().catch((err) => console.warn("[starfield] the picked star did not load:", err));
|
|
30542
|
+
return true;
|
|
30543
|
+
}
|
|
28141
30544
|
setCooperativeTouch(cooperative) {
|
|
28142
30545
|
this.controls.touches.ONE = cooperative ? NO_TOUCH_GESTURE : TOUCH.ROTATE;
|
|
28143
30546
|
this.canvas.style.touchAction = cooperative ? "pan-x pan-y" : "none";
|
|
@@ -28226,7 +30629,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28226
30629
|
}
|
|
28227
30630
|
shouldRender(nowMs) {
|
|
28228
30631
|
if (this.holdingCamera) return true;
|
|
28229
|
-
if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs || this.focusKey() !== this.lastRenderedFocusKey || this.ctx.bodies.minorStreaming || this.pointClouds.hasPendingSceneAdds() || this.systemData.hasPendingUnloads()) return true;
|
|
30632
|
+
if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs || this.focusKey() !== this.lastRenderedFocusKey || this.ctx.bodies.minorStreaming || this.pointClouds.hasPendingSceneAdds() || this.systemData.hasPendingUnloads() || this.systemFade.fading || this.ctx.visibility.fading || this.pointClouds.fading || this.starfield.busy()) return true;
|
|
28230
30633
|
if (this.covered) return false;
|
|
28231
30634
|
if (this.frameIndex - this.lastRenderedFrame >= SceneRenderer.IDLE_HEARTBEAT_FRAMES) return true;
|
|
28232
30635
|
if (this.cameraMovedFrom(this.lastRenderedCamPos, this.lastRenderedCamQuat) || this.frameIndex < this.wakeUntilFrame || this.haloDebug.active) return true;
|
|
@@ -28548,6 +30951,10 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28548
30951
|
this.disarmLimits();
|
|
28549
30952
|
this.focusController.snapToBody(id, latitude, longitude, zoom);
|
|
28550
30953
|
}
|
|
30954
|
+
snapToBodyFrom(id, fromId, distance) {
|
|
30955
|
+
this.disarmLimits();
|
|
30956
|
+
this.focusController.snapToBodyFrom(id, fromId, distance);
|
|
30957
|
+
}
|
|
28551
30958
|
setFocusTarget(body, camPos) {
|
|
28552
30959
|
this.disarmLimits();
|
|
28553
30960
|
this.applyJdUpdate();
|
|
@@ -28677,16 +31084,21 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28677
31084
|
setSkyDebugMarkersVisible(visible) {
|
|
28678
31085
|
this.skyDebugMarkers.setVisible(visible);
|
|
28679
31086
|
}
|
|
28680
|
-
|
|
28681
|
-
|
|
31087
|
+
updateSky(skyboxIntensity) {
|
|
31088
|
+
const share = this.starfield.share();
|
|
31089
|
+
if (this.ctx.layers.isVisible("stars") && share < SKY_GONE_SHARE) {
|
|
28682
31090
|
if (this.stashedSky) {
|
|
28683
31091
|
this.scene.background = this.stashedSky;
|
|
28684
31092
|
this.stashedSky = null;
|
|
28685
31093
|
}
|
|
28686
31094
|
} else if (this.scene.background) {
|
|
31095
|
+
if (this.stashedSky !== this.scene.background) disposeTexture(this.stashedSky);
|
|
28687
31096
|
this.stashedSky = this.scene.background;
|
|
28688
31097
|
this.scene.background = null;
|
|
28689
31098
|
}
|
|
31099
|
+
this.scene.backgroundIntensity = skyboxIntensity * (1 - share);
|
|
31100
|
+
}
|
|
31101
|
+
applyLayerChrome() {
|
|
28690
31102
|
const names = this.ctx.layers.isVisible("labels");
|
|
28691
31103
|
if (names !== this.namesShown) {
|
|
28692
31104
|
this.namesShown = names;
|
|
@@ -28753,6 +31165,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28753
31165
|
this.resize(this.canvas.clientWidth, this.canvas.clientHeight);
|
|
28754
31166
|
}
|
|
28755
31167
|
this.pointClouds.setPointBudget(this.tier.pointBudget);
|
|
31168
|
+
this.starfield.setBudget(starBudget());
|
|
28756
31169
|
}
|
|
28757
31170
|
resize(width, height) {
|
|
28758
31171
|
this.invalidate();
|
|
@@ -28790,9 +31203,11 @@ var SceneRenderer = class SceneRenderer {
|
|
|
28790
31203
|
disposeReliefTiles(bo);
|
|
28791
31204
|
}
|
|
28792
31205
|
this.pointClouds.dispose();
|
|
31206
|
+
this.starfield.dispose();
|
|
28793
31207
|
this.disposeScene(this.scene);
|
|
28794
31208
|
this.disposeScene(this.modelScene);
|
|
28795
31209
|
disposeTexture(this.scene.background);
|
|
31210
|
+
disposeTexture(this.stashedSky);
|
|
28796
31211
|
disposeTexture(this.scene.environment);
|
|
28797
31212
|
disposeTexture(this.modelScene.environment);
|
|
28798
31213
|
this.circleTexture.dispose();
|
|
@@ -31136,7 +33551,8 @@ var TYPES = {
|
|
|
31136
33551
|
[ObjectType.SPACECRAFT]: "spacecraft",
|
|
31137
33552
|
[ObjectType.DEBRIS]: "debris",
|
|
31138
33553
|
[ObjectType.UNDOCUMENTED]: "unknown",
|
|
31139
|
-
[ObjectType.SURFACE_FEATURE]: "surface-feature"
|
|
33554
|
+
[ObjectType.SURFACE_FEATURE]: "surface-feature",
|
|
33555
|
+
[ObjectType.EXTRASOLAR]: "star"
|
|
31140
33556
|
};
|
|
31141
33557
|
var ORBIT_CLASSES = {
|
|
31142
33558
|
[ObjectType.ASTEROID_INNER]: "inner",
|
|
@@ -31265,9 +33681,11 @@ var SpaceMap = class {
|
|
|
31265
33681
|
canvas = null;
|
|
31266
33682
|
labelLayer = null;
|
|
31267
33683
|
stopWatching = null;
|
|
33684
|
+
stopExtrasolarStale = null;
|
|
31268
33685
|
controls = null;
|
|
31269
33686
|
lostPanelTimer;
|
|
31270
33687
|
cooperative = null;
|
|
33688
|
+
starSelectEnabled = false;
|
|
31271
33689
|
limits = {};
|
|
31272
33690
|
initialFocusPending = true;
|
|
31273
33691
|
pendingFocusId = null;
|
|
@@ -31409,6 +33827,7 @@ var SpaceMap = class {
|
|
|
31409
33827
|
container.classList.add("sm-map");
|
|
31410
33828
|
mapsIn.set(container, (mapsIn.get(container) ?? 0) + 1);
|
|
31411
33829
|
this.controls = new ControlHost(this, container);
|
|
33830
|
+
this.stopExtrasolarStale = onExtrasolarStale(() => this.ctx.onDataStale?.());
|
|
31412
33831
|
const canvas = document.createElement("canvas");
|
|
31413
33832
|
canvas.className = "sm-map__canvas";
|
|
31414
33833
|
canvas.tabIndex = 0;
|
|
@@ -31472,6 +33891,8 @@ var SpaceMap = class {
|
|
|
31472
33891
|
this.controls = null;
|
|
31473
33892
|
this.stopWatching?.();
|
|
31474
33893
|
this.stopWatching = null;
|
|
33894
|
+
this.stopExtrasolarStale?.();
|
|
33895
|
+
this.stopExtrasolarStale = null;
|
|
31475
33896
|
clearTimeout(this.lostPanelTimer);
|
|
31476
33897
|
canvas.removeEventListener("keydown", this.onKeyDown);
|
|
31477
33898
|
canvas.removeEventListener("touchmove", this.onTouchMove);
|
|
@@ -31791,20 +34212,38 @@ var SpaceMap = class {
|
|
|
31791
34212
|
panTo(target) {
|
|
31792
34213
|
return this.toFeature(this.targetBody(target), target.feature, "pan");
|
|
31793
34214
|
}
|
|
31794
|
-
jumpTo(target) {
|
|
34215
|
+
async jumpTo(target) {
|
|
31795
34216
|
const body = this.targetBody(target);
|
|
34217
|
+
if (this.placedNow(body) && target.facing === void 0 || this.placedNow(target.facing)) this.navigation++;
|
|
34218
|
+
else {
|
|
34219
|
+
const loading = this.goTo(body, {
|
|
34220
|
+
clock: "keep",
|
|
34221
|
+
camera: "none"
|
|
34222
|
+
});
|
|
34223
|
+
const token = this.navigation;
|
|
34224
|
+
const arrival = await loading;
|
|
34225
|
+
if (arrival.outcome === "failed") throw arrival.error;
|
|
34226
|
+
if (arrival.outcome !== "placed" || token !== this.navigation) return;
|
|
34227
|
+
}
|
|
34228
|
+
this.snap(target, body);
|
|
34229
|
+
return this.settled(0);
|
|
34230
|
+
}
|
|
34231
|
+
placedNow(id) {
|
|
34232
|
+
const body = this.ctx.getBody(id);
|
|
34233
|
+
return !!body && !!this.renderer?.placeOf(body);
|
|
34234
|
+
}
|
|
34235
|
+
snap(target, body) {
|
|
31796
34236
|
if (target.facing !== void 0) {
|
|
31797
34237
|
this.snapToBodyFacing(body, target.facing, target.elevationDeg ?? 0, this.framing(target, body));
|
|
31798
|
-
return
|
|
34238
|
+
return;
|
|
31799
34239
|
}
|
|
31800
34240
|
if (target.feature) {
|
|
31801
34241
|
const { featureId, lat, lon, diameterM } = target.feature;
|
|
31802
34242
|
this.focusOnFeature(body, featureId, lat, lon, diameterM, null, "snap");
|
|
31803
|
-
return
|
|
34243
|
+
return;
|
|
31804
34244
|
}
|
|
31805
34245
|
const camera = this.getCamera();
|
|
31806
34246
|
this.snapToBody(body, target.lat ?? camera?.lat ?? 45, target.lon ?? camera?.lon ?? 0, this.framing(target, body));
|
|
31807
|
-
return this;
|
|
31808
34247
|
}
|
|
31809
34248
|
getCamera() {
|
|
31810
34249
|
const renderer = this.renderer;
|
|
@@ -31862,8 +34301,13 @@ var SpaceMap = class {
|
|
|
31862
34301
|
renderer.setZoomEnabled(this.scrollZoom.isEnabled());
|
|
31863
34302
|
renderer.setBodySelectEnabled(this.bodySelect.isEnabled());
|
|
31864
34303
|
renderer.setFeatureSelectEnabled(this.featureSelect.isEnabled());
|
|
34304
|
+
renderer.setStarSelectEnabled(this.starSelectEnabled);
|
|
31865
34305
|
renderer.setCooperativeTouch(cooperative);
|
|
31866
34306
|
}
|
|
34307
|
+
setStarSelectEnabled(enabled) {
|
|
34308
|
+
this.starSelectEnabled = enabled;
|
|
34309
|
+
this.renderer?.setStarSelectEnabled(enabled);
|
|
34310
|
+
}
|
|
31867
34311
|
onWheelCapture = (e) => {
|
|
31868
34312
|
if (!this.scrollZoom.isEnabled()) return;
|
|
31869
34313
|
if (this.cooperative && !this.cooperative.allowsWheel(e)) e.stopPropagation();
|
|
@@ -31904,6 +34348,9 @@ var SpaceMap = class {
|
|
|
31904
34348
|
snapToBody(id, latitude, longitude, zoom) {
|
|
31905
34349
|
this.renderer?.snapToBody(id, latitude, longitude, zoom);
|
|
31906
34350
|
}
|
|
34351
|
+
snapToBodyFrom(id, fromId, distance) {
|
|
34352
|
+
this.renderer?.snapToBodyFrom(id, fromId, distance);
|
|
34353
|
+
}
|
|
31907
34354
|
snapToBodyFacing(id, towardId, elevationDeg, distance) {
|
|
31908
34355
|
this.renderer?.snapToBodyFacing(id, towardId, elevationDeg, distance);
|
|
31909
34356
|
}
|
|
@@ -32251,7 +34698,7 @@ var MarkerDrawing = class {
|
|
|
32251
34698
|
const screen = this.visible ? viewport.project(this.at.lon, this.at.lat) : null;
|
|
32252
34699
|
this.element.style.display = screen ? "" : "none";
|
|
32253
34700
|
if (!screen) return;
|
|
32254
|
-
const shift = viewport.
|
|
34701
|
+
const shift = viewport.shiftInFrame(screen[0]);
|
|
32255
34702
|
this.element.style.transform = `translate(${screen[0] + shift}px, ${screen[1]}px) translate(${-this.align[0] * 100}%, ${-this.align[1] * 100}%)`;
|
|
32256
34703
|
}
|
|
32257
34704
|
setPosition(at) {
|
|
@@ -32355,14 +34802,15 @@ function stepOfLevel(level, tileSize) {
|
|
|
32355
34802
|
return 2 * Math.PI / (2 * tileSize * 2 ** level);
|
|
32356
34803
|
}
|
|
32357
34804
|
function finestLevelAt(pyramid, lon, lat) {
|
|
32358
|
-
const [across, fy] = chartFraction(0, lon, lat);
|
|
32359
|
-
const fx = across - Math.floor(across);
|
|
32360
34805
|
let finest = pyramid.max_level;
|
|
32361
34806
|
for (const patch of pyramid.patches ?? []) {
|
|
32362
34807
|
if (patch.max_level <= finest) continue;
|
|
32363
|
-
const
|
|
32364
|
-
const
|
|
32365
|
-
|
|
34808
|
+
const chart = patchChart(patch);
|
|
34809
|
+
const [across, fy] = chartFraction(chart, lon, lat);
|
|
34810
|
+
const fx = chart === 0 ? across - Math.floor(across) : across;
|
|
34811
|
+
const x = Math.floor(fx * chartColumns(chart, patch.max_level));
|
|
34812
|
+
const y = Math.floor(fy * chartRows(chart, patch.max_level));
|
|
34813
|
+
if (patchOfTile([patch], chart, patch.max_level, x, y)) finest = patch.max_level;
|
|
32366
34814
|
}
|
|
32367
34815
|
return finest;
|
|
32368
34816
|
}
|
|
@@ -32445,12 +34893,13 @@ function fitted(chart, level, tileSize, extent, margin) {
|
|
|
32445
34893
|
}
|
|
32446
34894
|
}
|
|
32447
34895
|
function reaches(patch, extent) {
|
|
32448
|
-
const
|
|
32449
|
-
const
|
|
34896
|
+
const chart = patchChart(patch);
|
|
34897
|
+
const columns = chartColumns(chart, patch.max_level);
|
|
34898
|
+
const rows = chartRows(chart, patch.max_level);
|
|
32450
34899
|
const south = (patch.y[1] + 1) / rows;
|
|
32451
34900
|
if (patch.y[0] / rows > extent.maxY || south < extent.minY) return false;
|
|
32452
34901
|
let west = patch.x[0] / columns;
|
|
32453
|
-
west -= Math.round(west - (extent.minX + extent.maxX) / 2);
|
|
34902
|
+
if (chart === 0) west -= Math.round(west - (extent.minX + extent.maxX) / 2);
|
|
32454
34903
|
const east = west + (patch.x[1] + 1 - patch.x[0]) / columns;
|
|
32455
34904
|
return west <= extent.maxX && east >= extent.minX;
|
|
32456
34905
|
}
|
|
@@ -32464,18 +34913,32 @@ function planWindows(pyramid, frame, firstLevel, moves = true) {
|
|
|
32464
34913
|
if (place) places.push(place);
|
|
32465
34914
|
}
|
|
32466
34915
|
const onMap = extentOn(0, frame, places, centre);
|
|
34916
|
+
const cap = centre[1] >= 0 ? 1 : 2;
|
|
34917
|
+
const onCap = extentOn(cap, frame, places, centre);
|
|
32467
34918
|
const wanted = levelForStep(frame.step, pyramid.tile_size);
|
|
32468
34919
|
const specs = [];
|
|
32469
34920
|
let finest = pyramid.max_level;
|
|
32470
|
-
|
|
34921
|
+
let finestOn = {
|
|
34922
|
+
chart: 0,
|
|
34923
|
+
extent: onMap
|
|
34924
|
+
};
|
|
34925
|
+
for (const patch of pyramid.patches ?? []) {
|
|
34926
|
+
const chart = patchChart(patch);
|
|
34927
|
+
const extent = chart === 0 ? onMap : chart === cap ? onCap : null;
|
|
34928
|
+
if (extent && patch.max_level > finest && reaches(patch, extent)) {
|
|
34929
|
+
finest = patch.max_level;
|
|
34930
|
+
finestOn = {
|
|
34931
|
+
chart,
|
|
34932
|
+
extent
|
|
34933
|
+
};
|
|
34934
|
+
}
|
|
34935
|
+
}
|
|
32471
34936
|
for (let level = Math.min(wanted, finest); level > pyramid.max_level; level--) {
|
|
32472
|
-
const spec = fitted(
|
|
34937
|
+
const spec = fitted(finestOn.chart, level, pyramid.tile_size, finestOn.extent, margin);
|
|
32473
34938
|
if (!spec) continue;
|
|
32474
34939
|
specs.push(spec);
|
|
32475
34940
|
break;
|
|
32476
34941
|
}
|
|
32477
|
-
const cap = centre[1] >= 0 ? 1 : 2;
|
|
32478
|
-
const onCap = extentOn(cap, frame, places, centre);
|
|
32479
34942
|
for (let level = Math.min(wanted, pyramid.max_level); level >= firstLevel; level--) {
|
|
32480
34943
|
const map = fitted(0, level, pyramid.tile_size, onMap, margin);
|
|
32481
34944
|
const polar = onCap && level >= 1 ? fitted(cap, level, pyramid.tile_size, onCap, margin) : void 0;
|
|
@@ -32497,7 +34960,7 @@ function windowTiles(spec, pyramid) {
|
|
|
32497
34960
|
for (let ty = Math.max(Math.floor(spec.y0 / size), 0); ty <= lastY; ty++) for (let tx = Math.floor(spec.x0 / size); tx <= lastX; tx++) {
|
|
32498
34961
|
if (spec.chart !== 0 && (tx < 0 || tx >= columns)) continue;
|
|
32499
34962
|
const x = (tx % columns + columns) % columns;
|
|
32500
|
-
const patch = spec.level > pyramid.max_level ? patchOfTile(pyramid.patches, spec.level, x, ty) : void 0;
|
|
34963
|
+
const patch = spec.level > pyramid.max_level ? patchOfTile(pyramid.patches, spec.chart, spec.level, x, ty) : void 0;
|
|
32501
34964
|
if (spec.level > pyramid.max_level && !patch) continue;
|
|
32502
34965
|
tiles.push({
|
|
32503
34966
|
cap: CHART_FOLDER[spec.chart],
|
|
@@ -33167,7 +35630,8 @@ var LayerTiles = class {
|
|
|
33167
35630
|
win.ctx = canvas.getContext("2d") ?? void 0;
|
|
33168
35631
|
} else win.data = new Uint8ClampedArray(spec.width * spec.height * 4);
|
|
33169
35632
|
for (const tile of windowTiles(spec, this.block)) {
|
|
33170
|
-
const
|
|
35633
|
+
const chart = CHART_FOLDER.indexOf(tile.cap);
|
|
35634
|
+
const version = tile.patch?.version ?? tileVersion(this.block, chart, tile.level, tile.x, tile.y);
|
|
33171
35635
|
const url = tileUrl({
|
|
33172
35636
|
id: this.block.id,
|
|
33173
35637
|
version
|
|
@@ -33239,7 +35703,10 @@ var Viewport = class {
|
|
|
33239
35703
|
}
|
|
33240
35704
|
unproject(px, py) {
|
|
33241
35705
|
const [x, y] = this.fromScreen(px, py);
|
|
33242
|
-
return this.projection.inverse(x, y);
|
|
35706
|
+
if (!this.repeatsHorizontally) return this.projection.inverse(x, y);
|
|
35707
|
+
const { minX, maxX } = this.projection.extent;
|
|
35708
|
+
const worldW = maxX - minX;
|
|
35709
|
+
return this.projection.inverse(((x - minX) % worldW + worldW) % worldW + minX, y);
|
|
33243
35710
|
}
|
|
33244
35711
|
get worldWidthPx() {
|
|
33245
35712
|
const { minX, maxX } = this.projection.extent;
|
|
@@ -33258,6 +35725,9 @@ var Viewport = class {
|
|
|
33258
35725
|
w
|
|
33259
35726
|
].filter((shift) => left + shift < this.width && left + shift + w > 0);
|
|
33260
35727
|
}
|
|
35728
|
+
shiftInFrame(x) {
|
|
35729
|
+
return this.repeatShifts.find((shift) => x + shift >= 0 && x + shift <= this.width) ?? 0;
|
|
35730
|
+
}
|
|
33261
35731
|
};
|
|
33262
35732
|
function repeats(projection, worldWidthPx, width) {
|
|
33263
35733
|
return projection.cyclic && projection.rectangular && worldWidthPx >= width - .5;
|
|
@@ -33694,7 +36164,9 @@ var FlatMap = class {
|
|
|
33694
36164
|
await this.adopt(bodyId);
|
|
33695
36165
|
}
|
|
33696
36166
|
project(lon, lat) {
|
|
33697
|
-
|
|
36167
|
+
const viewport = this.viewport();
|
|
36168
|
+
const screen = viewport.project(lon, lat);
|
|
36169
|
+
return screen ? [screen[0] + viewport.shiftInFrame(screen[0]), screen[1]] : null;
|
|
33698
36170
|
}
|
|
33699
36171
|
unproject(px, py) {
|
|
33700
36172
|
const found = this.viewport().unproject(px, py);
|
|
@@ -36600,9 +39072,11 @@ function attributionChips(ctx, labels = ORBIT_ARCHIVE_ORGANISATIONS) {
|
|
|
36600
39072
|
const bodyId = ctx.visibility.focusedBodyId;
|
|
36601
39073
|
const model = bodyId ? credits.model.get(bodyId) : void 0;
|
|
36602
39074
|
if (model) organisations.add(model.organisation);
|
|
39075
|
+
const stars = [...new Set(credits.stars.filter((c) => c.license).map((c) => c.organisation))];
|
|
36603
39076
|
return {
|
|
36604
39077
|
orbits,
|
|
36605
|
-
imagery: [...organisations].sort()
|
|
39078
|
+
imagery: [...organisations].sort(),
|
|
39079
|
+
stars
|
|
36606
39080
|
};
|
|
36607
39081
|
}
|
|
36608
39082
|
|
|
@@ -36638,12 +39112,14 @@ var AttributionControl = class {
|
|
|
36638
39112
|
const root = bar();
|
|
36639
39113
|
const orbits = group();
|
|
36640
39114
|
const imagery = group();
|
|
36641
|
-
|
|
39115
|
+
const stars = group();
|
|
39116
|
+
root.append(link(HOME_URL, "spacemap", "sm-attribution__home"), orbits, imagery, stars, link(CREDITS_URL, host().messages.credits_see_all()));
|
|
36642
39117
|
this.stop = effect_root(() => {
|
|
36643
39118
|
user_effect(() => {
|
|
36644
39119
|
const chips = attributionChips(map.ctx);
|
|
36645
39120
|
fill(orbits, host().messages.attribution_orbits(), chips.orbits);
|
|
36646
39121
|
fill(imagery, host().messages.attribution_imagery(), chips.imagery);
|
|
39122
|
+
fill(stars, host().messages.attribution_stars(), chips.stars);
|
|
36647
39123
|
});
|
|
36648
39124
|
});
|
|
36649
39125
|
return root;
|
|
@@ -37008,13 +39484,14 @@ async function fetchPanoramas(bodyId) {
|
|
|
37008
39484
|
//#endregion
|
|
37009
39485
|
//#region src/sdk/index.ts
|
|
37010
39486
|
async function createMap(options) {
|
|
37011
|
-
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
39487
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, extrasolarUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
37012
39488
|
const element = resolveContainer(container);
|
|
37013
39489
|
applyHost({
|
|
37014
39490
|
dataUrl,
|
|
37015
39491
|
imagesUrl,
|
|
37016
39492
|
liveUrl,
|
|
37017
39493
|
tilesUrl,
|
|
39494
|
+
extrasolarUrl,
|
|
37018
39495
|
locale,
|
|
37019
39496
|
messages,
|
|
37020
39497
|
includeNonCommercial
|
|
@@ -37035,13 +39512,14 @@ async function createMap(options) {
|
|
|
37035
39512
|
return map;
|
|
37036
39513
|
}
|
|
37037
39514
|
async function createFlatMap(options) {
|
|
37038
|
-
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
39515
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, extrasolarUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
37039
39516
|
const element = resolveContainer(container);
|
|
37040
39517
|
applyHost({
|
|
37041
39518
|
dataUrl,
|
|
37042
39519
|
imagesUrl,
|
|
37043
39520
|
liveUrl,
|
|
37044
39521
|
tilesUrl,
|
|
39522
|
+
extrasolarUrl,
|
|
37045
39523
|
locale,
|
|
37046
39524
|
messages,
|
|
37047
39525
|
includeNonCommercial
|
|
@@ -37056,13 +39534,14 @@ async function createFlatMap(options) {
|
|
|
37056
39534
|
return map;
|
|
37057
39535
|
}
|
|
37058
39536
|
async function createPanorama(options) {
|
|
37059
|
-
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
39537
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, extrasolarUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
37060
39538
|
const element = resolveContainer(container);
|
|
37061
39539
|
applyHost({
|
|
37062
39540
|
dataUrl,
|
|
37063
39541
|
imagesUrl,
|
|
37064
39542
|
liveUrl,
|
|
37065
39543
|
tilesUrl,
|
|
39544
|
+
extrasolarUrl,
|
|
37066
39545
|
locale,
|
|
37067
39546
|
messages,
|
|
37068
39547
|
includeNonCommercial
|
|
@@ -37077,13 +39556,14 @@ async function createPanorama(options) {
|
|
|
37077
39556
|
return view;
|
|
37078
39557
|
}
|
|
37079
39558
|
async function createSystemMap(options) {
|
|
37080
|
-
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
39559
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, extrasolarUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
37081
39560
|
const element = resolveContainer(container);
|
|
37082
39561
|
applyHost({
|
|
37083
39562
|
dataUrl,
|
|
37084
39563
|
imagesUrl,
|
|
37085
39564
|
liveUrl,
|
|
37086
39565
|
tilesUrl,
|
|
39566
|
+
extrasolarUrl,
|
|
37087
39567
|
locale,
|
|
37088
39568
|
messages,
|
|
37089
39569
|
includeNonCommercial
|
|
@@ -37111,6 +39591,7 @@ function applyHost(options) {
|
|
|
37111
39591
|
if (options.imagesUrl !== void 0) overrides.imagesUrl = options.imagesUrl;
|
|
37112
39592
|
if (options.liveUrl !== void 0) overrides.liveUrl = options.liveUrl;
|
|
37113
39593
|
if (options.tilesUrl !== void 0) overrides.tilesUrl = options.tilesUrl;
|
|
39594
|
+
if (options.extrasolarUrl !== void 0) overrides.extrasolarUrl = options.extrasolarUrl;
|
|
37114
39595
|
if (options.locale !== void 0) overrides.locale = () => options.locale;
|
|
37115
39596
|
if (options.messages !== void 0) overrides.messages = options.messages;
|
|
37116
39597
|
if (options.includeNonCommercial) overrides.textures = "non-commercial";
|