spacemap 0.1.22 → 0.1.25
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 +2 -1
- package/dist/spacemap.iife.js +327 -252
- package/dist/spacemap.js +326 -251
- package/index.d.ts +27 -6
- package/index.js +864 -449
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -3061,9 +3061,6 @@ function isExtrasolar(body) {
|
|
|
3061
3061
|
function isExoplanet(body) {
|
|
3062
3062
|
return body.data.objectType === ObjectType.EXOPLANET;
|
|
3063
3063
|
}
|
|
3064
|
-
function isSurfaceFeature(body) {
|
|
3065
|
-
return body.data.objectType === ObjectType.SURFACE_FEATURE;
|
|
3066
|
-
}
|
|
3067
3064
|
var ObjectType = /* @__PURE__ */ function(ObjectType) {
|
|
3068
3065
|
ObjectType[ObjectType["BARYCENTER"] = 0] = "BARYCENTER";
|
|
3069
3066
|
ObjectType[ObjectType["LAGRANGE_POINT"] = 1] = "LAGRANGE_POINT";
|
|
@@ -6837,7 +6834,6 @@ var BodyIndex = class {
|
|
|
6837
6834
|
majorBodies = [];
|
|
6838
6835
|
asteroidBodiesByZone = /* @__PURE__ */ new Map();
|
|
6839
6836
|
spacecraftByParent = /* @__PURE__ */ new Map();
|
|
6840
|
-
focusFeature = null;
|
|
6841
6837
|
extrasolar = /* @__PURE__ */ new Map();
|
|
6842
6838
|
pinnedExtrasolar = /* @__PURE__ */ new Set();
|
|
6843
6839
|
tripCentreId = null;
|
|
@@ -6884,7 +6880,6 @@ var BodyIndex = class {
|
|
|
6884
6880
|
getBody(id, zone) {
|
|
6885
6881
|
const major = this.bodiesById.get(id);
|
|
6886
6882
|
if (major) return major;
|
|
6887
|
-
if (this.focusFeature?.data.id === id) return this.focusFeature;
|
|
6888
6883
|
const star = this.extrasolar.get(id) ?? this.exoplanets.get(this.exoplanetAliases.get(id) ?? id);
|
|
6889
6884
|
if (star) return star;
|
|
6890
6885
|
if (zone !== void 0) return this.spacecraftByParent.get(zone)?.get(id) ?? this.asteroidBodiesByZone.get(zone)?.get(id);
|
|
@@ -7778,6 +7773,7 @@ var VisibilityController = class {
|
|
|
7778
7773
|
systemCentres = /* @__PURE__ */ new Map();
|
|
7779
7774
|
focusedBodyIdPlain = SUN_ID;
|
|
7780
7775
|
focusedSystemIdPlain = null;
|
|
7776
|
+
subjectIdPlain;
|
|
7781
7777
|
travelSystemActive = false;
|
|
7782
7778
|
cameraDistThreeJS = 0;
|
|
7783
7779
|
lastRecomputeDist = -1;
|
|
@@ -7881,11 +7877,15 @@ var VisibilityController = class {
|
|
|
7881
7877
|
this.focusedSystemIdPlain = sysId;
|
|
7882
7878
|
this.lastRecomputeDist = -1;
|
|
7883
7879
|
}
|
|
7880
|
+
inFocus(id) {
|
|
7881
|
+
return id === this.focusedBodyIdPlain || id === this.subjectIdPlain;
|
|
7882
|
+
}
|
|
7884
7883
|
clearTravelSystem() {
|
|
7885
7884
|
this.travelSystemActive = false;
|
|
7886
7885
|
}
|
|
7887
|
-
setFocused(body) {
|
|
7886
|
+
setFocused(body, subject) {
|
|
7888
7887
|
this.travelSystemActive = false;
|
|
7888
|
+
this.subjectIdPlain = subject?.data.id;
|
|
7889
7889
|
if (body.data.id === this.focusedBodyIdPlain) return;
|
|
7890
7890
|
this.focusedBodyId = body.data.id;
|
|
7891
7891
|
this.focusedBodyIdPlain = body.data.id;
|
|
@@ -7910,7 +7910,7 @@ var VisibilityController = class {
|
|
|
7910
7910
|
else if (isAsteroidMoon && !this.matchesSmallBodyClass(moon.data.parentId)) vis = VISIBILITY.HIDE;
|
|
7911
7911
|
else {
|
|
7912
7912
|
const ratio = this.cameraDistThreeJS / 10 / moon.data.a;
|
|
7913
|
-
const isFocused = moon.data.id
|
|
7913
|
+
const isFocused = this.inFocus(moon.data.id);
|
|
7914
7914
|
vis = computeVisibilityFromRatio(ratio, this.scaledPlanetary, 5, isFocused);
|
|
7915
7915
|
if (!isAsteroidMoon && vis === VISIBILITY.FULL && !this.fullMoonIds.has(moon.data.id) && !isFocused) vis = VISIBILITY.CAPPED;
|
|
7916
7916
|
}
|
|
@@ -7958,7 +7958,7 @@ var VisibilityController = class {
|
|
|
7958
7958
|
if (!this.isInFocusedSystem(body.data.parentId)) return VISIBILITY.HIDE;
|
|
7959
7959
|
const refA = body.data.a;
|
|
7960
7960
|
if (!refA || refA <= 0) return VISIBILITY.FULL;
|
|
7961
|
-
const isFocused = body.data.id
|
|
7961
|
+
const isFocused = this.inFocus(body.data.id);
|
|
7962
7962
|
return computeVisibilityFromRatio(camDistThreeJS / 10 / refA, this.scaledPlanetary, 5, isFocused);
|
|
7963
7963
|
}
|
|
7964
7964
|
let refA = body.extrasolar?.pairAxis ?? body.data.a;
|
|
@@ -7970,7 +7970,7 @@ var VisibilityController = class {
|
|
|
7970
7970
|
if (refA === 0 && body.data.e < .9) console.warn(`No semi-major axis available for body ${body.data.id} (${body.data.name}), falling back to FULL visibility`);
|
|
7971
7971
|
return VISIBILITY.FULL;
|
|
7972
7972
|
}
|
|
7973
|
-
const isFocused = body.data.id
|
|
7973
|
+
const isFocused = this.inFocus(body.data.id);
|
|
7974
7974
|
const noLine = isExtrasolar(body) && !body.orbitElements;
|
|
7975
7975
|
const tier = computeVisibilityFromRatio(camDistThreeJS / 10 / refA, this.scaledSystem, 50, isFocused || noLine);
|
|
7976
7976
|
return tier === VISIBILITY.CLOSE ? VISIBILITY.FULL : tier;
|
|
@@ -7978,7 +7978,7 @@ var VisibilityController = class {
|
|
|
7978
7978
|
getProbeVisibility(body, camDistThreeJS) {
|
|
7979
7979
|
const ps = this.getProbeStore();
|
|
7980
7980
|
if ((ps ? ps.containingSystemAt(body.data.id, this.currentJd) : null) === EARTH_SYSTEM_NAIF && this.getEarthSatGroupFilter()) return VISIBILITY.HIDE;
|
|
7981
|
-
const isFocused = body.data.id
|
|
7981
|
+
const isFocused = this.inFocus(body.data.id);
|
|
7982
7982
|
if (!(ps ? !ps.hasHeliocentricFit(body.data.id, this.currentJd) : false)) {
|
|
7983
7983
|
const sun = this.bodies.bodiesById.get(SUN_ID);
|
|
7984
7984
|
const helioDist = sun?.position && body.position ? f64dist(body.position, sun.position) / 10 : 0;
|
|
@@ -8043,7 +8043,7 @@ var VisibilityController = class {
|
|
|
8043
8043
|
bodyUnclearedShare(body) {
|
|
8044
8044
|
if (this.clearing.share === 0) return 1;
|
|
8045
8045
|
const { id, objectType, parentId, orbitalSource } = body.data;
|
|
8046
|
-
if (
|
|
8046
|
+
if (this.inFocus(id) || id === this.focusedSystemIdPlain || this.pinned.has(id) || this.tripEnds.has(id)) return 1;
|
|
8047
8047
|
const layer = bodyLayer(objectType, parentId);
|
|
8048
8048
|
if (layer === "spacecraft" || orbitalSource === OrbitalSource.SPICE_PROBE) {
|
|
8049
8049
|
const page = this.getProbePage();
|
|
@@ -16768,7 +16768,7 @@ function seedAccepted() {
|
|
|
16768
16768
|
for (let i = 0; i < _reservedActive; i++) copyRect(poolSlot(_accepted, i, makeRect), _reserved[i]);
|
|
16769
16769
|
return _reservedActive;
|
|
16770
16770
|
}
|
|
16771
|
-
function cullOverlappingLabels(bodyObjects, screenWidth, screenHeight, camera,
|
|
16771
|
+
function cullOverlappingLabels(bodyObjects, screenWidth, screenHeight, camera, ctx, hoveredBodyIds, screenOccluders, focusTruePos = [
|
|
16772
16772
|
0,
|
|
16773
16773
|
0,
|
|
16774
16774
|
0
|
|
@@ -16783,7 +16783,7 @@ function cullOverlappingLabels(bodyObjects, screenWidth, screenHeight, camera, f
|
|
|
16783
16783
|
if (span && span.offsetWidth > 0) bo.labelTextWidth = span.offsetWidth;
|
|
16784
16784
|
}
|
|
16785
16785
|
if (!labelScreenRect(bo, camera, focusTruePos, screenWidth, screenHeight, _projRect)) continue;
|
|
16786
|
-
const isFocused = body.data.id
|
|
16786
|
+
const isFocused = ctx.visibility.inFocus(body.data.id);
|
|
16787
16787
|
const isHovered = hoveredBodyIds.has(body.data.id);
|
|
16788
16788
|
const c = poolSlot(_candidates, _candidatesActive++, makeCandidate);
|
|
16789
16789
|
copyRect(c.rect, _projRect);
|
|
@@ -17623,19 +17623,60 @@ function isModelBearing(body) {
|
|
|
17623
17623
|
var modelLoader = new GLTFLoader();
|
|
17624
17624
|
modelLoader.setMeshoptDecoder(MeshoptDecoder);
|
|
17625
17625
|
var _loader = modelLoader;
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17626
|
+
function modelUrl(slug, tier) {
|
|
17627
|
+
return versionedUrl(`/v1/models/${slug}/${tier}.glb`, "models");
|
|
17628
|
+
}
|
|
17629
|
+
async function loadModelBundle(slug, tier) {
|
|
17630
|
+
const first = await loadTier(slug, tier);
|
|
17631
|
+
if (!first || textureAllowed(first.meta.exports[tier]?.distribution)) return first;
|
|
17632
|
+
disposeGltf(first.gltf.scene);
|
|
17633
|
+
const other = tier === "high" ? "low" : "high";
|
|
17634
|
+
if (!first.meta.tiers?.includes(other)) return null;
|
|
17635
|
+
if (!textureAllowed(first.meta.exports[other]?.distribution)) return null;
|
|
17636
|
+
const second = await loadTier(slug, other);
|
|
17637
|
+
if (!second || textureAllowed(second.meta.exports[other]?.distribution)) return second;
|
|
17638
|
+
disposeGltf(second.gltf.scene);
|
|
17639
|
+
return null;
|
|
17640
|
+
}
|
|
17641
|
+
async function loadTier(slug, tier) {
|
|
17642
|
+
const gltf = await _loader.loadAsync(modelUrl(slug, tier));
|
|
17643
|
+
const meta = (gltf.asset?.extras)?.spacemap;
|
|
17644
|
+
if (!meta?.exports?.high) {
|
|
17645
|
+
disposeGltf(gltf.scene);
|
|
17646
|
+
console.warn(`models/${slug}/${tier}.glb carries no payload`);
|
|
17647
|
+
return null;
|
|
17633
17648
|
}
|
|
17634
|
-
return
|
|
17649
|
+
return {
|
|
17650
|
+
gltf,
|
|
17651
|
+
meta,
|
|
17652
|
+
tier
|
|
17653
|
+
};
|
|
17654
|
+
}
|
|
17655
|
+
var _tiers;
|
|
17656
|
+
function fetchModelTiers() {
|
|
17657
|
+
if (!_tiers) {
|
|
17658
|
+
const p = fetch(versionedUrl("/v1/models/tiers.json", "models")).then((r) => {
|
|
17659
|
+
if (!r.ok) throw new Error(`models/tiers.json: ${r.status}`);
|
|
17660
|
+
return r.json();
|
|
17661
|
+
}).then((d) => d.bundles ?? {}).catch((e) => {
|
|
17662
|
+
console.warn("models/tiers.json did not load:", e);
|
|
17663
|
+
if (_tiers === p) _tiers = void 0;
|
|
17664
|
+
return {};
|
|
17665
|
+
});
|
|
17666
|
+
_tiers = p;
|
|
17667
|
+
}
|
|
17668
|
+
return _tiers;
|
|
17669
|
+
}
|
|
17670
|
+
async function bundleTiers(slug) {
|
|
17671
|
+
return (await fetchModelTiers())[slug];
|
|
17672
|
+
}
|
|
17673
|
+
function tierAllowed(tiers, tier) {
|
|
17674
|
+
const info = tiers[tier];
|
|
17675
|
+
return !!info && textureAllowed(info.distribution);
|
|
17635
17676
|
}
|
|
17636
|
-
function allowedTier(
|
|
17637
|
-
if (
|
|
17638
|
-
|
|
17677
|
+
function allowedTier(tiers) {
|
|
17678
|
+
if (!tiers) return "high";
|
|
17679
|
+
return ["high", "low"].find((tier) => tierAllowed(tiers, tier));
|
|
17639
17680
|
}
|
|
17640
17681
|
function shapeModelCredit(meta) {
|
|
17641
17682
|
return meta.credit ?? meta.exports.high.credit;
|
|
@@ -17700,13 +17741,13 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
17700
17741
|
if ((bo.modelLoadEpoch ?? 0) !== epoch) return;
|
|
17701
17742
|
if (bo.mesh) bo.mesh.visible = false;
|
|
17702
17743
|
setHaloLoading(bo, true);
|
|
17703
|
-
const
|
|
17704
|
-
const
|
|
17705
|
-
if (!
|
|
17744
|
+
const wanted = allowedTier(await bundleTiers(slug));
|
|
17745
|
+
const loaded = wanted && await loadModelBundle(slug, wanted);
|
|
17746
|
+
if (!loaded) {
|
|
17706
17747
|
bo.noPhysical = "model";
|
|
17707
17748
|
return;
|
|
17708
17749
|
}
|
|
17709
|
-
const gltf
|
|
17750
|
+
const { gltf, meta, tier } = loaded;
|
|
17710
17751
|
if ((bo.modelLoadEpoch ?? 0) !== epoch || !bo.mesh) {
|
|
17711
17752
|
disposeGltf(gltf.scene);
|
|
17712
17753
|
return;
|
|
@@ -17758,20 +17799,9 @@ async function loadNaturalBodyModel(bo, ctx) {
|
|
|
17758
17799
|
return;
|
|
17759
17800
|
}
|
|
17760
17801
|
if (detail.global?.orientation && !bo.body.orientation) applyBodyOrientation(bo.body, detail.global.orientation, detail.global.nut_prec, ctx);
|
|
17761
|
-
const
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
disposeGltf(gltf.scene);
|
|
17765
|
-
return;
|
|
17766
|
-
}
|
|
17767
|
-
let meta;
|
|
17768
|
-
try {
|
|
17769
|
-
meta = await metaPromise;
|
|
17770
|
-
} catch (e) {
|
|
17771
|
-
disposeGltf(gltf.scene);
|
|
17772
|
-
console.warn(`Failed to load shape-model metadata for ${slug}:`, e);
|
|
17773
|
-
return;
|
|
17774
|
-
}
|
|
17802
|
+
const loaded = await loadModelBundle(slug, "high");
|
|
17803
|
+
if (!loaded) return;
|
|
17804
|
+
const { gltf, meta } = loaded;
|
|
17775
17805
|
if ((bo.modelLoadEpoch ?? 0) !== epoch) {
|
|
17776
17806
|
disposeGltf(gltf.scene);
|
|
17777
17807
|
return;
|
|
@@ -18063,12 +18093,6 @@ function onLinkTap(el, handler) {
|
|
|
18063
18093
|
}
|
|
18064
18094
|
|
|
18065
18095
|
// src/lib/scene/objects/surface/nomenclature.ts
|
|
18066
|
-
function nomenclatureBodyId(focused, bodyObjects) {
|
|
18067
|
-
if (!focused) return void 0;
|
|
18068
|
-
if (isSurfaceFeature(focused)) return focused.featureAnchor.hostId;
|
|
18069
|
-
if (bodyObjects.get(focused.data.id)?.isLanded) return focused.data.parentId;
|
|
18070
|
-
return focused.data.id;
|
|
18071
|
-
}
|
|
18072
18096
|
var DEG2RAD$4 = Math.PI / 180;
|
|
18073
18097
|
var MIN_BODY_SCREEN_RADIUS_PX = 128;
|
|
18074
18098
|
var LABEL_DISC_FRACTION = .9;
|
|
@@ -22390,6 +22414,87 @@ var SimClock = class {
|
|
|
22390
22414
|
}
|
|
22391
22415
|
};
|
|
22392
22416
|
|
|
22417
|
+
// src/lib/scene/camera/two-finger.ts
|
|
22418
|
+
var TWO_FINGER_SLOP = 10;
|
|
22419
|
+
var TWO_FINGER_ALONE = 30;
|
|
22420
|
+
var TILT_MAX_SPREAD_SLOPE = Math.tan(MathUtils.degToRad(40));
|
|
22421
|
+
var TILT_MAX_SIDEWAYS = .5;
|
|
22422
|
+
var TILT_MIN_TOGETHER = .3;
|
|
22423
|
+
var TWIST_ENGAGE = MathUtils.degToRad(12);
|
|
22424
|
+
var TwoFingerGesture = class {
|
|
22425
|
+
mode = "map";
|
|
22426
|
+
a0 = {
|
|
22427
|
+
x: 0,
|
|
22428
|
+
y: 0
|
|
22429
|
+
};
|
|
22430
|
+
b0 = {
|
|
22431
|
+
x: 0,
|
|
22432
|
+
y: 0
|
|
22433
|
+
};
|
|
22434
|
+
lastMidY = 0;
|
|
22435
|
+
lastAngle = 0;
|
|
22436
|
+
twisted = 0;
|
|
22437
|
+
twisting = false;
|
|
22438
|
+
begin(a, b, canTilt) {
|
|
22439
|
+
this.mode = canTilt ? "undecided" : "map";
|
|
22440
|
+
this.a0.x = a.x;
|
|
22441
|
+
this.a0.y = a.y;
|
|
22442
|
+
this.b0.x = b.x;
|
|
22443
|
+
this.b0.y = b.y;
|
|
22444
|
+
this.lastMidY = (a.y + b.y) / 2;
|
|
22445
|
+
this.lastAngle = Math.atan2(b.y - a.y, b.x - a.x);
|
|
22446
|
+
this.twisted = 0;
|
|
22447
|
+
this.twisting = false;
|
|
22448
|
+
}
|
|
22449
|
+
move(a, b) {
|
|
22450
|
+
const angle = Math.atan2(b.y - a.y, b.x - a.x);
|
|
22451
|
+
const turn = MathUtils.euclideanModulo(angle - this.lastAngle + Math.PI, 2 * Math.PI) - Math.PI;
|
|
22452
|
+
this.lastAngle = angle;
|
|
22453
|
+
const midY = (a.y + b.y) / 2;
|
|
22454
|
+
let down = midY - this.lastMidY;
|
|
22455
|
+
this.lastMidY = midY;
|
|
22456
|
+
this.twisted += turn;
|
|
22457
|
+
if (this.mode === "undecided") {
|
|
22458
|
+
this.mode = this.decide(a, b);
|
|
22459
|
+
if (this.mode === "tilt") down = midY - (this.a0.y + this.b0.y) / 2;
|
|
22460
|
+
}
|
|
22461
|
+
switch (this.mode) {
|
|
22462
|
+
case "undecided": return {
|
|
22463
|
+
mode: "undecided",
|
|
22464
|
+
down: 0,
|
|
22465
|
+
twist: 0
|
|
22466
|
+
};
|
|
22467
|
+
case "tilt": return {
|
|
22468
|
+
mode: "tilt",
|
|
22469
|
+
down,
|
|
22470
|
+
twist: 0
|
|
22471
|
+
};
|
|
22472
|
+
case "map": {
|
|
22473
|
+
const engaged = this.twisting;
|
|
22474
|
+
this.twisting ||= Math.abs(this.twisted) > TWIST_ENGAGE;
|
|
22475
|
+
return {
|
|
22476
|
+
mode: "map",
|
|
22477
|
+
down: 0,
|
|
22478
|
+
twist: engaged ? turn : 0
|
|
22479
|
+
};
|
|
22480
|
+
}
|
|
22481
|
+
}
|
|
22482
|
+
}
|
|
22483
|
+
decide(a, b) {
|
|
22484
|
+
const ax = a.x - this.a0.x;
|
|
22485
|
+
const ay = a.y - this.a0.y;
|
|
22486
|
+
const bx = b.x - this.b0.x;
|
|
22487
|
+
const by = b.y - this.b0.y;
|
|
22488
|
+
const aTravel = Math.hypot(ax, ay);
|
|
22489
|
+
const bTravel = Math.hypot(bx, by);
|
|
22490
|
+
if (Math.max(aTravel, bTravel) < (aTravel > 0 && bTravel > 0 ? 10 : TWO_FINGER_ALONE)) return "undecided";
|
|
22491
|
+
const sideBySide = Math.abs(this.b0.y - this.a0.y) <= Math.abs(this.b0.x - this.a0.x) * TILT_MAX_SPREAD_SLOPE;
|
|
22492
|
+
const vertical = Math.abs(ax) <= Math.abs(ay) * TILT_MAX_SIDEWAYS && Math.abs(bx) <= Math.abs(by) * TILT_MAX_SIDEWAYS;
|
|
22493
|
+
const together = Math.sign(ay) === Math.sign(by) && Math.min(aTravel, bTravel) >= Math.max(aTravel, bTravel) * TILT_MIN_TOGETHER;
|
|
22494
|
+
return sideBySide && vertical && together ? "tilt" : "map";
|
|
22495
|
+
}
|
|
22496
|
+
};
|
|
22497
|
+
|
|
22393
22498
|
// src/lib/scene/camera/look-controls.ts
|
|
22394
22499
|
var MAX_TILT = MathUtils.degToRad(80);
|
|
22395
22500
|
var LOOK_NONE_HEIGHT = 1;
|
|
@@ -22411,14 +22516,15 @@ var _turnBack = new Quaternion();
|
|
|
22411
22516
|
var _was = new Quaternion();
|
|
22412
22517
|
var _pivotGround = new Vector3();
|
|
22413
22518
|
var _look = new Matrix4();
|
|
22519
|
+
var GLIDE_DONE = 1e-5;
|
|
22414
22520
|
var ANCHOR_PASSES = 3;
|
|
22415
22521
|
var SIN_MIN_ANCHOR_ELEVATION = Math.sin(MathUtils.degToRad(20));
|
|
22416
22522
|
var GROUND_PASSES = 3;
|
|
22523
|
+
var PIVOT_REFINE = 1e-7;
|
|
22417
22524
|
var orbitHandlers = OrbitControls.prototype;
|
|
22418
22525
|
var LookOrbitControls = class extends OrbitControls {
|
|
22419
22526
|
canvas;
|
|
22420
22527
|
surfaceRadius;
|
|
22421
|
-
standingHeight;
|
|
22422
22528
|
onTurnSettled;
|
|
22423
22529
|
heading = 0;
|
|
22424
22530
|
tilt = 0;
|
|
@@ -22426,6 +22532,7 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22426
22532
|
orbitPos = new Vector3();
|
|
22427
22533
|
orbitQuat = new Quaternion();
|
|
22428
22534
|
pivotRadius = 0;
|
|
22535
|
+
placedByName = false;
|
|
22429
22536
|
lastUp = new Vector3();
|
|
22430
22537
|
holdsNorth = false;
|
|
22431
22538
|
northTurning = false;
|
|
@@ -22436,16 +22543,22 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22436
22543
|
dragPointer = null;
|
|
22437
22544
|
dragX = 0;
|
|
22438
22545
|
dragY = 0;
|
|
22439
|
-
|
|
22546
|
+
twoFinger = new TwoFingerGesture();
|
|
22547
|
+
glideHeading = 0;
|
|
22548
|
+
glideTilt = 0;
|
|
22549
|
+
constructor(camera, canvas, surfaceRadius, onTurnSettled = () => {}) {
|
|
22440
22550
|
super(camera, canvas);
|
|
22441
22551
|
this.canvas = canvas;
|
|
22442
22552
|
this.surfaceRadius = surfaceRadius;
|
|
22443
|
-
this.standingHeight = standingHeight;
|
|
22444
22553
|
this.onTurnSettled = onTurnSettled;
|
|
22445
22554
|
this.mouseButtons.MIDDLE = null;
|
|
22446
|
-
this.addEventListener("start", () =>
|
|
22555
|
+
this.addEventListener("start", () => {
|
|
22556
|
+
this.pendingLook = null;
|
|
22557
|
+
this.placedByName = false;
|
|
22558
|
+
});
|
|
22447
22559
|
canvas.addEventListener("pointerdown", this.onLookDown);
|
|
22448
22560
|
canvas.addEventListener("mousedown", this.blockMiddle);
|
|
22561
|
+
canvas.addEventListener("pointerdown", this.onCatch);
|
|
22449
22562
|
}
|
|
22450
22563
|
get orbitPosition() {
|
|
22451
22564
|
return this.looking ? this.orbitPos : this.object.position;
|
|
@@ -22464,6 +22577,8 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22464
22577
|
const cam = this.object;
|
|
22465
22578
|
this.looking = false;
|
|
22466
22579
|
this.pendingLook = null;
|
|
22580
|
+
this.placedByName = false;
|
|
22581
|
+
this.stopGlide();
|
|
22467
22582
|
if (tilt === 0 && heading === 0) return;
|
|
22468
22583
|
if (!this.groundKnown()) {
|
|
22469
22584
|
this.pendingLook = {
|
|
@@ -22484,6 +22599,7 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22484
22599
|
this.heading = heading;
|
|
22485
22600
|
this.holdAngles(share);
|
|
22486
22601
|
this.looking = true;
|
|
22602
|
+
this.placedByName = true;
|
|
22487
22603
|
this.place();
|
|
22488
22604
|
}
|
|
22489
22605
|
toOrbitAxes(x, y) {
|
|
@@ -22501,6 +22617,12 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22501
22617
|
endLook() {
|
|
22502
22618
|
this.looking = false;
|
|
22503
22619
|
this.pendingLook = null;
|
|
22620
|
+
this.placedByName = false;
|
|
22621
|
+
this.stopGlide();
|
|
22622
|
+
}
|
|
22623
|
+
stopGlide() {
|
|
22624
|
+
this.glideHeading = 0;
|
|
22625
|
+
this.glideTilt = 0;
|
|
22504
22626
|
}
|
|
22505
22627
|
groundKnown() {
|
|
22506
22628
|
_n.subVectors(this.orbitPosition, this.target).normalize();
|
|
@@ -22530,22 +22652,28 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22530
22652
|
this.placedQuat.copy(cam.quaternion);
|
|
22531
22653
|
return;
|
|
22532
22654
|
}
|
|
22533
|
-
if (this.pivotRadius === 0) {
|
|
22534
|
-
const was = _from$1.subVectors(this.placedPos, this.target);
|
|
22535
|
-
const now = _hit.subVectors(cam.position, this.target);
|
|
22536
|
-
const stretch = now.length() / was.length();
|
|
22537
|
-
_turnBack.setFromUnitVectors(was.normalize(), now.normalize());
|
|
22538
|
-
this.orbitPos.sub(this.target).applyQuaternion(_turnBack).multiplyScalar(stretch).add(this.target);
|
|
22539
|
-
this.orbitQuat.premultiply(_turnBack);
|
|
22540
|
-
this.place();
|
|
22541
|
-
return;
|
|
22542
|
-
}
|
|
22543
22655
|
const holds = this.holdsNorth;
|
|
22544
22656
|
_n.subVectors(this.orbitPos, this.target).normalize();
|
|
22545
22657
|
this.lookToward(_pivotGround.copy(_n).multiplyScalar(this.pivotRadius));
|
|
22546
22658
|
this.holdsNorth = holds;
|
|
22547
22659
|
}
|
|
22548
22660
|
update(deltaTime = null) {
|
|
22661
|
+
const changed = this.step(deltaTime);
|
|
22662
|
+
return this.glide() || changed;
|
|
22663
|
+
}
|
|
22664
|
+
glide() {
|
|
22665
|
+
if (!this.glideHeading && !this.glideTilt) return false;
|
|
22666
|
+
const f = this.dampingFactor;
|
|
22667
|
+
this.turnNow(this.glideHeading * f, this.glideTilt * f);
|
|
22668
|
+
this.glideHeading *= 1 - f;
|
|
22669
|
+
this.glideTilt *= 1 - f;
|
|
22670
|
+
if (Math.abs(this.glideHeading) < GLIDE_DONE && Math.abs(this.glideTilt) < GLIDE_DONE) {
|
|
22671
|
+
this.stopGlide();
|
|
22672
|
+
this.onTurnSettled();
|
|
22673
|
+
}
|
|
22674
|
+
return true;
|
|
22675
|
+
}
|
|
22676
|
+
step(deltaTime) {
|
|
22549
22677
|
if (!this.looking) {
|
|
22550
22678
|
const changed = super.update(deltaTime);
|
|
22551
22679
|
if (this.pendingLook && this.groundKnown()) this.setLook(this.pendingLook);
|
|
@@ -22554,14 +22682,19 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22554
22682
|
const cam = this.object;
|
|
22555
22683
|
if (!cam.position.equals(this.placedPos) || !cam.quaternion.equals(this.placedQuat)) {
|
|
22556
22684
|
this.looking = false;
|
|
22685
|
+
this.stopGlide();
|
|
22557
22686
|
return super.update(deltaTime);
|
|
22558
22687
|
}
|
|
22688
|
+
if (!this.followGround()) return super.update(deltaTime);
|
|
22559
22689
|
cam.position.copy(this.orbitPos);
|
|
22560
22690
|
cam.quaternion.copy(this.orbitQuat);
|
|
22561
22691
|
_was.copy(this.orbitQuat);
|
|
22562
22692
|
const changed = super.update(deltaTime);
|
|
22563
22693
|
const moved = cam.position.distanceToSquared(this.orbitPos) > this.orbitPos.lengthSq() * 1e-20;
|
|
22564
|
-
if (moved)
|
|
22694
|
+
if (moved) {
|
|
22695
|
+
this.orbitMoved = true;
|
|
22696
|
+
this.placedByName = false;
|
|
22697
|
+
}
|
|
22565
22698
|
this.orbitPos.copy(cam.position);
|
|
22566
22699
|
this.orbitQuat.copy(cam.quaternion);
|
|
22567
22700
|
if (moved) this.samplePivot();
|
|
@@ -22586,6 +22719,7 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22586
22719
|
super.dispose();
|
|
22587
22720
|
this.canvas.removeEventListener("pointerdown", this.onLookDown);
|
|
22588
22721
|
this.canvas.removeEventListener("mousedown", this.blockMiddle);
|
|
22722
|
+
this.canvas.removeEventListener("pointerdown", this.onCatch);
|
|
22589
22723
|
this.endLookDrag();
|
|
22590
22724
|
}
|
|
22591
22725
|
_handleMouseDownRotate(event) {
|
|
@@ -22600,6 +22734,28 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22600
22734
|
_handleTouchMoveRotate(event) {
|
|
22601
22735
|
orbitHandlers._handleTouchMoveRotate.call(this, this.rotatedTouch(event));
|
|
22602
22736
|
}
|
|
22737
|
+
_handleTouchStartDollyPan(event) {
|
|
22738
|
+
orbitHandlers._handleTouchStartDollyPan.call(this, event);
|
|
22739
|
+
const [a, b] = this.fingers();
|
|
22740
|
+
this.twoFinger.begin(a, b, this.enableRotate && this.lookShare() > 0);
|
|
22741
|
+
}
|
|
22742
|
+
_handleTouchMoveDollyPan(event) {
|
|
22743
|
+
const [a, b] = this.fingers();
|
|
22744
|
+
const step = this.twoFinger.move(a, b);
|
|
22745
|
+
if (step.mode === "undecided") return;
|
|
22746
|
+
const rate = TURN_PER_HEIGHT / (this.canvas.clientHeight || 1);
|
|
22747
|
+
if (step.mode === "tilt") {
|
|
22748
|
+
if (this.enableRotate) this.turn(0, -step.down * rate);
|
|
22749
|
+
return;
|
|
22750
|
+
}
|
|
22751
|
+
orbitHandlers._handleTouchMoveDollyPan.call(this, event);
|
|
22752
|
+
if (step.twist !== 0 && this.enableRotate) this.turn(step.twist, 0);
|
|
22753
|
+
}
|
|
22754
|
+
fingers() {
|
|
22755
|
+
const touch = this;
|
|
22756
|
+
const [a, b] = touch._pointers;
|
|
22757
|
+
return [touch._pointerPositions[a], touch._pointerPositions[b]];
|
|
22758
|
+
}
|
|
22603
22759
|
_handleMouseWheel(event) {
|
|
22604
22760
|
const anchored = this.enableRotate && this.groundAt(event.clientX, event.clientY, _anchor) && -_ray.dot(_hit.copy(_anchor).normalize()) > SIN_MIN_ANCHOR_ELEVATION;
|
|
22605
22761
|
orbitHandlers._handleMouseWheel.call(this, event);
|
|
@@ -22664,6 +22820,7 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22664
22820
|
const level = offset.addScaledVector(n, -offset.dot(n));
|
|
22665
22821
|
this.heading = Math.atan2(_ray.crossVectors(down, level).dot(n), down.dot(level));
|
|
22666
22822
|
this.holdsNorth = false;
|
|
22823
|
+
this.placedByName = false;
|
|
22667
22824
|
this.looking = true;
|
|
22668
22825
|
if (!this.holdAngles(this.lookShare())) {
|
|
22669
22826
|
this.looking = false;
|
|
@@ -22712,7 +22869,15 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22712
22869
|
pageY: 2 * y - other.y
|
|
22713
22870
|
};
|
|
22714
22871
|
}
|
|
22715
|
-
turn(
|
|
22872
|
+
turn(heading, tilt) {
|
|
22873
|
+
if (!this.enableDamping) {
|
|
22874
|
+
this.turnNow(heading, tilt);
|
|
22875
|
+
return;
|
|
22876
|
+
}
|
|
22877
|
+
this.glideHeading += heading;
|
|
22878
|
+
this.glideTilt += tilt;
|
|
22879
|
+
}
|
|
22880
|
+
turnNow(heading, tilt) {
|
|
22716
22881
|
const cam = this.object;
|
|
22717
22882
|
const share = this.lookShare();
|
|
22718
22883
|
if (share === 0) return;
|
|
@@ -22726,9 +22891,8 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22726
22891
|
this.lastUp.copy(cam.up);
|
|
22727
22892
|
}
|
|
22728
22893
|
this.holdsNorth = false;
|
|
22729
|
-
|
|
22730
|
-
this.
|
|
22731
|
-
this.tilt = Math.max(0, this.tilt - dy * rate);
|
|
22894
|
+
this.heading += heading;
|
|
22895
|
+
this.tilt = Math.max(0, this.tilt + tilt);
|
|
22732
22896
|
this.holdAngles(share);
|
|
22733
22897
|
this.place();
|
|
22734
22898
|
}
|
|
@@ -22736,10 +22900,7 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22736
22900
|
_n.subVectors(this.orbitPosition, this.target);
|
|
22737
22901
|
const range = _n.length();
|
|
22738
22902
|
const ground = range > 0 ? this.surfaceRadius(_n.divideScalar(range)) : null;
|
|
22739
|
-
|
|
22740
|
-
if (ground > 0) return this.shareAt((range - ground) / ground);
|
|
22741
|
-
const height = this.standingHeight();
|
|
22742
|
-
return height === null ? 0 : this.shareAt(height);
|
|
22903
|
+
return ground ? this.shareAt((range - ground) / ground) : 0;
|
|
22743
22904
|
}
|
|
22744
22905
|
shareAt(height) {
|
|
22745
22906
|
const t = MathUtils.clamp((1 - height) / .5, 0, 1);
|
|
@@ -22751,6 +22912,22 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22751
22912
|
this.heading = MathUtils.clamp(this.heading, -Math.PI * share, Math.PI * share);
|
|
22752
22913
|
return share > 0;
|
|
22753
22914
|
}
|
|
22915
|
+
followGround() {
|
|
22916
|
+
_n.subVectors(this.orbitPos, this.target);
|
|
22917
|
+
const range = _n.length();
|
|
22918
|
+
const ground = range > 0 ? this.surfaceRadius(_n.divideScalar(range)) : null;
|
|
22919
|
+
if (ground === null) return true;
|
|
22920
|
+
const pivot = Math.min(ground, range);
|
|
22921
|
+
if (Math.abs(pivot - this.pivotRadius) <= ground * PIVOT_REFINE) return true;
|
|
22922
|
+
if (this.placedByName) {
|
|
22923
|
+
this.pivotRadius = pivot;
|
|
22924
|
+
return true;
|
|
22925
|
+
}
|
|
22926
|
+
const holds = this.holdsNorth;
|
|
22927
|
+
this.lookToward(_pivotGround.copy(_n).multiplyScalar(ground));
|
|
22928
|
+
this.holdsNorth = holds;
|
|
22929
|
+
return this.looking;
|
|
22930
|
+
}
|
|
22754
22931
|
samplePivot() {
|
|
22755
22932
|
_n.subVectors(this.orbitPos, this.target);
|
|
22756
22933
|
const range = _n.length();
|
|
@@ -22772,6 +22949,11 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22772
22949
|
this.placedPos.copy(cam.position);
|
|
22773
22950
|
this.placedQuat.copy(cam.quaternion);
|
|
22774
22951
|
}
|
|
22952
|
+
onCatch = () => {
|
|
22953
|
+
if (!this.glideHeading && !this.glideTilt) return;
|
|
22954
|
+
this.stopGlide();
|
|
22955
|
+
this.onTurnSettled();
|
|
22956
|
+
};
|
|
22775
22957
|
blockMiddle = (event) => {
|
|
22776
22958
|
if (event.button === 1) event.preventDefault();
|
|
22777
22959
|
};
|
|
@@ -22796,7 +22978,8 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22796
22978
|
const dy = event.clientY - this.dragY;
|
|
22797
22979
|
this.dragX = event.clientX;
|
|
22798
22980
|
this.dragY = event.clientY;
|
|
22799
|
-
|
|
22981
|
+
const rate = TURN_PER_HEIGHT / (this.canvas.clientHeight || 1);
|
|
22982
|
+
if (dx !== 0 || dy !== 0) this.turn(-dx * rate, -dy * rate);
|
|
22800
22983
|
};
|
|
22801
22984
|
onLookUp = (event) => {
|
|
22802
22985
|
if (event.pointerId !== this.dragPointer) return;
|
|
@@ -22815,35 +22998,42 @@ var LookOrbitControls = class extends OrbitControls {
|
|
|
22815
22998
|
// src/lib/scene/framing.ts
|
|
22816
22999
|
var DEFAULT_FOCUS_ID = EARTH_ID;
|
|
22817
23000
|
|
|
22818
|
-
// src/lib/scene/
|
|
22819
|
-
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
function
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
|
|
22831
|
-
|
|
22832
|
-
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
|
|
22846
|
-
|
|
23001
|
+
// src/lib/scene/position/feature-seat.ts
|
|
23002
|
+
var DEG2RAD$3 = Math.PI / 180;
|
|
23003
|
+
var _fixed = new Vector3();
|
|
23004
|
+
var _addend = new Vector3();
|
|
23005
|
+
function featureSeat(anchor, host, hostPos, hostBo, jd) {
|
|
23006
|
+
_addend.set(0, 0, 0);
|
|
23007
|
+
const idx = hostBo?.nomenclatureActiveIndex ?? -1;
|
|
23008
|
+
const label = idx >= 0 ? hostBo?.nomenclatureLabels?.[idx] ?? null : null;
|
|
23009
|
+
if (label && label.element.dataset.featureId === String(anchor.featureId)) {
|
|
23010
|
+
const modelAnchor = hostBo?.nomenclatureAnchor;
|
|
23011
|
+
if (modelAnchor) {
|
|
23012
|
+
_fixed.copy(label.position);
|
|
23013
|
+
_addend.copy(modelAnchor.position);
|
|
23014
|
+
} else {
|
|
23015
|
+
_fixed.copy(label.position);
|
|
23016
|
+
if (hostBo?.mesh) _fixed.multiply(hostBo.mesh.scale);
|
|
23017
|
+
}
|
|
23018
|
+
} else {
|
|
23019
|
+
const radiusKm = host.data.radiusKm;
|
|
23020
|
+
const latR = anchor.lat * DEG2RAD$3;
|
|
23021
|
+
const lngR = anchor.lon * DEG2RAD$3;
|
|
23022
|
+
const seat = host.orientation && Number.isFinite(radiusKm) && radiusKm > 0 ? renderedSeatAt(hostBo, host.data.id, radiusKm, latR, lngR) : null;
|
|
23023
|
+
if (seat) _fixed.set(seat.pointKm[0], seat.pointKm[1], seat.pointKm[2]);
|
|
23024
|
+
else {
|
|
23025
|
+
const r = Number.isFinite(radiusKm) && radiusKm > 0 ? radiusKm : 0;
|
|
23026
|
+
const [nx, ny, nz] = bodyFixedUnit(latR, lngR);
|
|
23027
|
+
_fixed.set(r * nx, r * ny, r * nz);
|
|
23028
|
+
}
|
|
23029
|
+
_fixed.multiplyScalar(kmToScene(1));
|
|
23030
|
+
}
|
|
23031
|
+
if (host.orientation) _fixed.applyQuaternion(bodyQuaternion(host.orientation, jd, host.nutPrec));
|
|
23032
|
+
return [
|
|
23033
|
+
hostPos[0] + _fixed.x + _addend.x,
|
|
23034
|
+
hostPos[1] + _fixed.y + _addend.y,
|
|
23035
|
+
hostPos[2] + _fixed.z + _addend.z
|
|
23036
|
+
];
|
|
22847
23037
|
}
|
|
22848
23038
|
|
|
22849
23039
|
// src/lib/scene/label/throttled-renderer.ts
|
|
@@ -23045,6 +23235,7 @@ function makeTrailMaterial(color, brightness = TRAIL_DIM) {
|
|
|
23045
23235
|
}
|
|
23046
23236
|
var TRIM_DEPTH = CAMERA_NEAR / 2;
|
|
23047
23237
|
var TRIM_SHARE = 1e-6;
|
|
23238
|
+
var MITER_LIMIT = 4;
|
|
23048
23239
|
function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
|
|
23049
23240
|
return new ShaderMaterial({
|
|
23050
23241
|
defines: dash ? { DASHED: "" } : {},
|
|
@@ -23070,9 +23261,11 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
|
|
|
23070
23261
|
uniform float uLineWidth;
|
|
23071
23262
|
uniform vec2 uResolution;
|
|
23072
23263
|
uniform vec2 uTrim;
|
|
23073
|
-
attribute
|
|
23264
|
+
attribute vec3 corner;
|
|
23265
|
+
attribute vec3 segPrev;
|
|
23074
23266
|
attribute vec3 segStart;
|
|
23075
23267
|
attribute vec3 segEnd;
|
|
23268
|
+
attribute vec3 segNext;
|
|
23076
23269
|
attribute vec2 alphaStart;
|
|
23077
23270
|
attribute vec2 alphaEnd;
|
|
23078
23271
|
varying float vAlpha;
|
|
@@ -23082,9 +23275,78 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
|
|
|
23082
23275
|
varying float vDistW;
|
|
23083
23276
|
varying float vW;
|
|
23084
23277
|
#endif
|
|
23278
|
+
vec3 toView(vec3 point) {
|
|
23279
|
+
return mat3(viewMatrix) * (point + uCenterOffset);
|
|
23280
|
+
}
|
|
23281
|
+
vec2 toPx(vec3 view) {
|
|
23282
|
+
vec4 clip = projectionMatrix * vec4(view, 1.0);
|
|
23283
|
+
return clip.xy / clip.w * uResolution * 0.5;
|
|
23284
|
+
}
|
|
23285
|
+
// The unit direction on screen from one point to another, with the
|
|
23286
|
+
// distance in pixels in z.
|
|
23287
|
+
vec3 heading(vec2 from, vec2 to) {
|
|
23288
|
+
vec2 d = to - from;
|
|
23289
|
+
// Scaled before normalising: an end cut just ahead of the camera can
|
|
23290
|
+
// sit 1e20 px off screen, and its square overflows a float.
|
|
23291
|
+
float span = max(abs(d.x), abs(d.y));
|
|
23292
|
+
// Seen end on: any direction will do.
|
|
23293
|
+
if (span < 1e-4) return vec3(1.0, 0.0, 0.0);
|
|
23294
|
+
vec2 u = d / span;
|
|
23295
|
+
return vec3(normalize(u), span * length(u));
|
|
23296
|
+
}
|
|
23297
|
+
// The screen headings into and out of the middle of three points. Both
|
|
23298
|
+
// segments at a turn call this with the same points, so they place
|
|
23299
|
+
// their shared corners alike.
|
|
23300
|
+
void turnAt(vec3 before, vec3 at, vec3 after, out vec3 din, out vec3 dout) {
|
|
23301
|
+
vec2 p = toPx(toView(at));
|
|
23302
|
+
din = heading(toPx(toView(before)), p);
|
|
23303
|
+
dout = heading(p, toPx(toView(after)));
|
|
23304
|
+
}
|
|
23305
|
+
// A mitred corner: its offset, and how far along the quad's own
|
|
23306
|
+
// segment it moves.
|
|
23307
|
+
vec3 mitredAt(vec2 offset, vec2 own) {
|
|
23308
|
+
return vec3(offset, dot(offset, own));
|
|
23309
|
+
}
|
|
23310
|
+
// This corner's offset in pixels from the point where the line turns
|
|
23311
|
+
// from din to dout, and in z how far it moves along the quad's own
|
|
23312
|
+
// segment, own. The quads either side compute it from the same three
|
|
23313
|
+
// points, so they meet exactly. A square end, the fallback, moves
|
|
23314
|
+
// nothing along: exactly nothing, since a dash can end right there.
|
|
23315
|
+
vec3 cornerAt(vec3 din, vec3 dout, vec2 own, bool joins) {
|
|
23316
|
+
float h = uLineWidth * 0.5;
|
|
23317
|
+
vec3 square = vec3(vec2(-own.y, own.x) * corner.x * h, 0.0);
|
|
23318
|
+
bool bevel = corner.z > 0.5;
|
|
23319
|
+
if (!joins || din.z == 0.0 || dout.z == 0.0) return bevel ? vec3(0.0) : square;
|
|
23320
|
+
float inner = din.x * dout.y - din.y * dout.x >= 0.0 ? 1.0 : -1.0;
|
|
23321
|
+
vec2 bisector = din.xy + dout.xy;
|
|
23322
|
+
// The cosine of half the turn.
|
|
23323
|
+
float cosHalf = length(bisector) * 0.5;
|
|
23324
|
+
float safeCos = max(cosHalf, 1e-6);
|
|
23325
|
+
vec2 mitre = vec2(-bisector.y, bisector.x) / (2.0 * safeCos) * (h / safeCos);
|
|
23326
|
+
// How far along each segment the inner corner moves. Past half the
|
|
23327
|
+
// shorter segment it would cross that segment's other corner, and
|
|
23328
|
+
// square ends overlap instead, as with no join.
|
|
23329
|
+
float reach = h * sqrt(max(1.0 - cosHalf * cosHalf, 0.0)) / safeCos;
|
|
23330
|
+
bool fits = cosHalf > 1e-3 && reach <= 0.5 * min(din.z, dout.z);
|
|
23331
|
+
bool mitred = fits && cosHalf * ${MITER_LIMIT.toFixed(1)} >= 1.0;
|
|
23332
|
+
if (bevel) {
|
|
23333
|
+
// Each segment's square outer corner and the inner corner they
|
|
23334
|
+
// share, at the turn's own distance along the line. Flat under a
|
|
23335
|
+
// mitre, which covers the turn by itself, and beside a segment
|
|
23336
|
+
// shorter than the line is wide, where it could reach into the
|
|
23337
|
+
// quad past that segment.
|
|
23338
|
+
if (mitred || (!fits && min(din.z, dout.z) < uLineWidth)) return vec3(0.0);
|
|
23339
|
+
if (corner.z < 1.5) return vec3(-inner * vec2(-din.y, din.x) * h, 0.0);
|
|
23340
|
+
if (corner.z < 2.5) return vec3(-inner * vec2(-dout.y, dout.x) * h, 0.0);
|
|
23341
|
+
return vec3(fits ? inner * mitre : vec2(0.0), 0.0);
|
|
23342
|
+
}
|
|
23343
|
+
if (!fits) return square;
|
|
23344
|
+
if (corner.x == inner) return mitredAt(inner * mitre, own);
|
|
23345
|
+
return mitred ? mitredAt(-inner * mitre, own) : square;
|
|
23346
|
+
}
|
|
23085
23347
|
void main() {
|
|
23086
|
-
vec3 a =
|
|
23087
|
-
vec3 b =
|
|
23348
|
+
vec3 a = toView(segStart);
|
|
23349
|
+
vec3 b = toView(segEnd);
|
|
23088
23350
|
float aheadA = -a.z;
|
|
23089
23351
|
float aheadB = -b.z;
|
|
23090
23352
|
float front = max(aheadA, aheadB);
|
|
@@ -23106,28 +23368,32 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
|
|
|
23106
23368
|
float cutB = aheadB < uTrim.x ? (cut - aheadB) / (aheadA - aheadB) : 0.0;
|
|
23107
23369
|
if (cutA > 0.0) a = vec3(mix(a.xy, b.xy, cutA), -cut);
|
|
23108
23370
|
if (cutB > 0.0) b = vec3(mix(b.xy, a.xy, cutB), -cut);
|
|
23109
|
-
vec4 clipA = projectionMatrix * vec4(a, 1.0);
|
|
23110
|
-
vec4 clipB = projectionMatrix * vec4(b, 1.0);
|
|
23111
|
-
vec2 ndcA = clipA.xy / clipA.w;
|
|
23112
|
-
vec2 ndcB = clipB.xy / clipB.w;
|
|
23113
|
-
vec2 dirPx = (ndcB - ndcA) * uResolution * 0.5;
|
|
23114
|
-
// Scaled before normalising: an end cut just ahead of the camera can
|
|
23115
|
-
// sit 1e20 px off screen, and its square overflows a float.
|
|
23116
|
-
float span = max(abs(dirPx.x), abs(dirPx.y));
|
|
23117
|
-
// A segment seen end on: any perpendicular will do.
|
|
23118
|
-
vec2 dirN = span < 1e-4 ? vec2(1.0, 0.0) : normalize(dirPx / span);
|
|
23119
|
-
vec2 offsetNDC = vec2(-dirN.y, dirN.x) * corner.x * uLineWidth / uResolution;
|
|
23120
23371
|
bool atEnd = corner.y > 0.5;
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23372
|
+
vec3 own = heading(toPx(a), toPx(b));
|
|
23373
|
+
// The turn this corner sits on, read in the line's direction: only
|
|
23374
|
+
// with all three of its points ahead of the camera, so that both
|
|
23375
|
+
// segments at the turn agree on it.
|
|
23376
|
+
bool joins = cutA == 0.0 && cutB == 0.0 && -toView(atEnd ? segNext : segPrev).z >= uTrim.x;
|
|
23377
|
+
vec3 din = own;
|
|
23378
|
+
vec3 dout = own;
|
|
23379
|
+
if (joins) {
|
|
23380
|
+
if (atEnd) turnAt(segStart, segEnd, segNext, din, dout);
|
|
23381
|
+
else turnAt(segPrev, segStart, segEnd, din, dout);
|
|
23382
|
+
own = atEnd ? din : dout;
|
|
23383
|
+
}
|
|
23384
|
+
vec3 offset = cornerAt(din, dout, own.xy, joins);
|
|
23385
|
+
vec4 clip = projectionMatrix * vec4(atEnd ? b : a, 1.0);
|
|
23386
|
+
gl_Position = vec4((clip.xy / clip.w + offset.xy * 2.0 / uResolution) * clip.w, clip.zw);
|
|
23124
23387
|
vec2 alphas = atEnd ? mix(alphaEnd, alphaStart, cutB) : mix(alphaStart, alphaEnd, cutA);
|
|
23125
23388
|
vAlpha = mix(alphas.x, alphas.y, uShowFull);
|
|
23126
23389
|
#ifdef DASHED
|
|
23127
23390
|
// Carried times w and divided back out per fragment, which undoes
|
|
23128
23391
|
// the perspective a varying is interpolated with: the dashes are
|
|
23129
|
-
// even on screen however much nearer one end is.
|
|
23392
|
+
// even on screen however much nearer one end is. A corner a mitre
|
|
23393
|
+
// moves along the segment counts on by as much, so dash ends stay
|
|
23394
|
+
// square to the line.
|
|
23130
23395
|
float dist = atEnd ? mix(distEnd, distStart, cutB) : mix(distStart, distEnd, cutA);
|
|
23396
|
+
dist += offset.z;
|
|
23131
23397
|
vDistW = dist * clip.w;
|
|
23132
23398
|
vW = clip.w;
|
|
23133
23399
|
#endif
|
|
@@ -25015,7 +25281,7 @@ function clampCameraOutsideBody(camera, body, focusTruePos, surface, clearanceKm
|
|
|
25015
25281
|
const dist = Math.hypot(dx, dy, dz);
|
|
25016
25282
|
if (surface?.seated) {
|
|
25017
25283
|
if (dist < 1e-9) {
|
|
25018
|
-
camera.position.set(cx, cy +
|
|
25284
|
+
camera.position.set(cx, cy + kmToScene(radiusKm + LANDED_KEEP_AWAY_KM), cz);
|
|
25019
25285
|
return;
|
|
25020
25286
|
}
|
|
25021
25287
|
const hh = Math.tan(camera.fov * Math.PI / 360) * camera.near;
|
|
@@ -25060,7 +25326,7 @@ function clampCameraOutsideBody(camera, body, focusTruePos, surface, clearanceKm
|
|
|
25060
25326
|
_dirBF.y,
|
|
25061
25327
|
_dirBF.z
|
|
25062
25328
|
]);
|
|
25063
|
-
const wall =
|
|
25329
|
+
const wall = kmToScene((sKm ?? radiusKm) + LANDED_KEEP_AWAY_KM);
|
|
25064
25330
|
if (wall - r > lift) lift = wall - r;
|
|
25065
25331
|
}
|
|
25066
25332
|
if (lift <= 0) return;
|
|
@@ -25284,7 +25550,6 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25284
25550
|
screenOccluders.length = occluderCount;
|
|
25285
25551
|
const focusedSystemId = ctx.visibility.focusedSystemId;
|
|
25286
25552
|
let newlyShownLabel = false;
|
|
25287
|
-
const nomFocusedBodyId = focusedBodyId ? nomenclatureBodyId(ctx.getBody(focusedBodyId), bodyObjects) : void 0;
|
|
25288
25553
|
for (const bo of bodyObjects.values()) {
|
|
25289
25554
|
const { body, group, trail } = bo;
|
|
25290
25555
|
const dist = bo.cachedDist;
|
|
@@ -25292,7 +25557,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25292
25557
|
const drawn = isDrawn(bo);
|
|
25293
25558
|
bo.root.visible = drawn;
|
|
25294
25559
|
let showLabel;
|
|
25295
|
-
const isFocused = body.data.id
|
|
25560
|
+
const isFocused = ctx.visibility.inFocus(body.data.id);
|
|
25296
25561
|
const isSystemRoot = focusedSystemId !== null && body.data.id === focusedSystemId;
|
|
25297
25562
|
const centreShare = ctx.visibility.systemCentres.get(body.data.id);
|
|
25298
25563
|
const isHeldStar = isFocused || ctx.visibility.isPinned(body.data.id) || centreShare === 1;
|
|
@@ -25401,7 +25666,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25401
25666
|
if (bo.label) fadeLabel(bo.label, labelShare);
|
|
25402
25667
|
if (bo.planetDot) bo.planetDot.material.opacity = labelShare;
|
|
25403
25668
|
const nomScreenR = bo.radiusScene > 0 ? bo.radiusScene / dist * projScale : 0;
|
|
25404
|
-
const isNomFocused = showNomenclature && body.data.id ===
|
|
25669
|
+
const isNomFocused = showNomenclature && body.data.id === focusedBodyId;
|
|
25405
25670
|
updateNomenclatureVisibility(bo, isNomFocused, nomScreenR, projScale, camera, screenW, screenH);
|
|
25406
25671
|
}
|
|
25407
25672
|
if (screenOccluders.length > 0) {
|
|
@@ -25413,7 +25678,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25413
25678
|
if (isScreenOccluded(_occlusionRect.x, _occlusionRect.y, bo.cachedDist, id, screenOccluders, id === focusedBodyId ? focusedPrimaryId : null)) bo.label.visible = false;
|
|
25414
25679
|
}
|
|
25415
25680
|
}
|
|
25416
|
-
const groundBo =
|
|
25681
|
+
const groundBo = focusedBodyId ? bodyObjects.get(focusedBodyId) : void 0;
|
|
25417
25682
|
const ground = groundBo?.body.position;
|
|
25418
25683
|
if (groundBo && ground) {
|
|
25419
25684
|
const relief = reliefRadiiKm(groundBo);
|
|
@@ -25424,17 +25689,17 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25424
25689
|
for (const bo of bodyObjects.values()) {
|
|
25425
25690
|
const at = bo.body.position;
|
|
25426
25691
|
const id = bo.body.data.id;
|
|
25427
|
-
if (!bo.label?.visible || !at || id
|
|
25692
|
+
if (!bo.label?.visible || !at || ctx.visibility.inFocus(id)) continue;
|
|
25428
25693
|
if (behindGround(at[0] - ground[0], at[1] - ground[1], at[2] - ground[2], cx, cy, cz, top)) bo.label.visible = false;
|
|
25429
25694
|
}
|
|
25430
25695
|
}
|
|
25431
25696
|
if (forceCull || newlyShownLabel || ++cullFrameCounter >= 3) {
|
|
25432
25697
|
cullFrameCounter = 0;
|
|
25433
|
-
cullOverlappingLabels(bodyObjects, screenW, screenH, camera,
|
|
25698
|
+
cullOverlappingLabels(bodyObjects, screenW, screenH, camera, ctx, hoveredBodyIds, screenOccluders, focusTruePos);
|
|
25434
25699
|
}
|
|
25435
25700
|
refreshVisibleBodyLabelRects(bodyObjects, screenW, screenH, camera, focusTruePos, sceneSettings().showConstellations);
|
|
25436
|
-
if (
|
|
25437
|
-
const focusedBo = bodyObjects.get(
|
|
25701
|
+
if (focusedBodyId) {
|
|
25702
|
+
const focusedBo = bodyObjects.get(focusedBodyId);
|
|
25438
25703
|
if (focusedBo) cullOverlappingNomenclatureLabels(focusedBo);
|
|
25439
25704
|
}
|
|
25440
25705
|
for (const bo of bodyObjects.values()) {
|
|
@@ -25442,7 +25707,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
25442
25707
|
if (!line?.visible) continue;
|
|
25443
25708
|
const mat = line.material;
|
|
25444
25709
|
mat.uniforms.uCenterOffset.value.set(-camera.position.x, -camera.position.y, -camera.position.z);
|
|
25445
|
-
const isFocused = bo.body.data.id
|
|
25710
|
+
const isFocused = ctx.visibility.inFocus(bo.body.data.id);
|
|
25446
25711
|
const isHovered = hoveredBodyIds.has(bo.body.data.id);
|
|
25447
25712
|
const share = ctx.visibility.systemShareOf(bo.body) * (line.userData.zoneShare ?? 1) * (line.userData.thresholdShare ?? 1) * (line.userData.haloShare ?? 1);
|
|
25448
25713
|
mat.uniforms.uAlphaMultiplier.value = (isHovered ? 2 : isFocused ? 1.75 : 1) * share;
|
|
@@ -29525,13 +29790,9 @@ var GALACTIC_NORTH_SCENE = (() => {
|
|
|
29525
29790
|
function galacticNorthVector(out) {
|
|
29526
29791
|
return (out ?? new Vector3()).copy(GALACTIC_NORTH_SCENE);
|
|
29527
29792
|
}
|
|
29528
|
-
function bodyNorthVector(body, jd, out
|
|
29793
|
+
function bodyNorthVector(body, jd, out) {
|
|
29529
29794
|
const target = out ?? new Vector3();
|
|
29530
29795
|
if (!body.orientation) {
|
|
29531
|
-
if (landed && body.orbitCenter && body.position) {
|
|
29532
|
-
target.set(body.position[0] - body.orbitCenter[0], body.position[1] - body.orbitCenter[1], body.position[2] - body.orbitCenter[2]);
|
|
29533
|
-
if (target.lengthSq() > 1e-20) return target.normalize();
|
|
29534
|
-
}
|
|
29535
29796
|
target.copy(SCENE_UP);
|
|
29536
29797
|
if (body.modelBaseFrame) target.applyQuaternion(body.modelBaseFrame).normalize();
|
|
29537
29798
|
return target;
|
|
@@ -29545,7 +29806,6 @@ var CameraUpController = class {
|
|
|
29545
29806
|
camera;
|
|
29546
29807
|
controls;
|
|
29547
29808
|
ctx;
|
|
29548
|
-
isLanded;
|
|
29549
29809
|
refId = null;
|
|
29550
29810
|
startVec = new Vector3(0, 1, 0);
|
|
29551
29811
|
targetVec = new Vector3(0, 1, 0);
|
|
@@ -29556,11 +29816,10 @@ var CameraUpController = class {
|
|
|
29556
29816
|
plane = null;
|
|
29557
29817
|
_quatA = new Quaternion();
|
|
29558
29818
|
_quatB = new Quaternion();
|
|
29559
|
-
constructor(camera, controls, ctx
|
|
29819
|
+
constructor(camera, controls, ctx) {
|
|
29560
29820
|
this.camera = camera;
|
|
29561
29821
|
this.controls = controls;
|
|
29562
29822
|
this.ctx = ctx;
|
|
29563
|
-
this.isLanded = isLanded;
|
|
29564
29823
|
}
|
|
29565
29824
|
setNorthReference(id) {
|
|
29566
29825
|
if (id === this.refId) return;
|
|
@@ -29579,10 +29838,8 @@ var CameraUpController = class {
|
|
|
29579
29838
|
else this.targetVec.copy(SCENE_UP);
|
|
29580
29839
|
} else {
|
|
29581
29840
|
const refBody = this.refId ? this.ctx.getBody(this.refId) : void 0;
|
|
29582
|
-
if (refBody)
|
|
29583
|
-
|
|
29584
|
-
bodyNorthVector(refBody, jd, this.targetVec, landed);
|
|
29585
|
-
} else this.targetVec.copy(SCENE_UP);
|
|
29841
|
+
if (refBody) bodyNorthVector(refBody, jd, this.targetVec);
|
|
29842
|
+
else this.targetVec.copy(SCENE_UP);
|
|
29586
29843
|
}
|
|
29587
29844
|
if (this.needsDurationCalc) {
|
|
29588
29845
|
this.needsDurationCalc = false;
|
|
@@ -30323,46 +30580,71 @@ function orbitalElementsToCurve(el, numPoints = 512) {
|
|
|
30323
30580
|
|
|
30324
30581
|
// src/lib/scene/objects/trail/geometry.ts
|
|
30325
30582
|
var TRAIL_RENDER_ORDER = 3;
|
|
30326
|
-
var
|
|
30583
|
+
var CORNERS = [
|
|
30327
30584
|
-1,
|
|
30328
30585
|
0,
|
|
30586
|
+
0,
|
|
30329
30587
|
1,
|
|
30330
30588
|
0,
|
|
30589
|
+
0,
|
|
30331
30590
|
-1,
|
|
30332
30591
|
1,
|
|
30592
|
+
0,
|
|
30333
30593
|
1,
|
|
30334
|
-
1
|
|
30594
|
+
1,
|
|
30595
|
+
0,
|
|
30596
|
+
0,
|
|
30597
|
+
0,
|
|
30598
|
+
1,
|
|
30599
|
+
0,
|
|
30600
|
+
0,
|
|
30601
|
+
2,
|
|
30602
|
+
0,
|
|
30603
|
+
0,
|
|
30604
|
+
3
|
|
30335
30605
|
];
|
|
30336
|
-
var
|
|
30606
|
+
var CORNER_INDICES = [
|
|
30337
30607
|
0,
|
|
30338
30608
|
1,
|
|
30339
30609
|
2,
|
|
30340
30610
|
2,
|
|
30341
30611
|
1,
|
|
30342
|
-
3
|
|
30612
|
+
3,
|
|
30613
|
+
4,
|
|
30614
|
+
5,
|
|
30615
|
+
6
|
|
30343
30616
|
];
|
|
30344
30617
|
function instanced(geometry, name) {
|
|
30345
30618
|
return geometry.getAttribute(name).data;
|
|
30346
30619
|
}
|
|
30347
|
-
function
|
|
30348
|
-
const buffer = new InstancedInterleavedBuffer(new Float32Array(
|
|
30349
|
-
geometry.setAttribute(
|
|
30350
|
-
geometry.setAttribute(names[1], new InterleavedBufferAttribute(buffer, stride, stride));
|
|
30620
|
+
function setPointAttributes(geometry, names, count, stride) {
|
|
30621
|
+
const buffer = new InstancedInterleavedBuffer(new Float32Array(count * stride), stride);
|
|
30622
|
+
names.forEach((name, i) => geometry.setAttribute(name, new InterleavedBufferAttribute(buffer, stride, i * stride)));
|
|
30351
30623
|
}
|
|
30352
30624
|
function makeFatTrailGeometry(capacity) {
|
|
30353
30625
|
const geometry = new InstancedBufferGeometry();
|
|
30354
|
-
geometry.setAttribute("corner", new Float32BufferAttribute(
|
|
30355
|
-
geometry.setIndex(
|
|
30356
|
-
|
|
30357
|
-
|
|
30626
|
+
geometry.setAttribute("corner", new Float32BufferAttribute(CORNERS, 3));
|
|
30627
|
+
geometry.setIndex(CORNER_INDICES);
|
|
30628
|
+
setPointAttributes(geometry, [
|
|
30629
|
+
"segPrev",
|
|
30630
|
+
"segStart",
|
|
30631
|
+
"segEnd",
|
|
30632
|
+
"segNext"
|
|
30633
|
+
], capacity + 2, 3);
|
|
30634
|
+
setPointAttributes(geometry, ["alphaStart", "alphaEnd"], capacity, 2);
|
|
30358
30635
|
geometry.instanceCount = 0;
|
|
30359
30636
|
return geometry;
|
|
30360
30637
|
}
|
|
30361
30638
|
function writeFatTrailVertices(geometry, posSrc, trailSrc, fullSrc, n) {
|
|
30362
30639
|
const points = instanced(geometry, "segStart");
|
|
30363
30640
|
const alphas = instanced(geometry, "alphaStart");
|
|
30364
|
-
const m = Math.min(n,
|
|
30365
|
-
points.array
|
|
30641
|
+
const m = Math.min(n, alphas.count);
|
|
30642
|
+
const arr = points.array;
|
|
30643
|
+
arr.set(posSrc.subarray(0, m * 3), 3);
|
|
30644
|
+
const last = (m - 1) * 3;
|
|
30645
|
+
const closed = m > 2 && posSrc[0] === posSrc[last] && posSrc[1] === posSrc[last + 1] && posSrc[2] === posSrc[last + 2];
|
|
30646
|
+
arr.copyWithin(0, closed ? last : 3, closed ? last + 3 : 6);
|
|
30647
|
+
arr.copyWithin((m + 1) * 3, closed ? 6 : m * 3, closed ? 9 : (m + 1) * 3);
|
|
30366
30648
|
const alphaArr = alphas.array;
|
|
30367
30649
|
for (let i = 0; i < m; i++) {
|
|
30368
30650
|
alphaArr[2 * i] = trailSrc[i];
|
|
@@ -30371,7 +30653,7 @@ function writeFatTrailVertices(geometry, posSrc, trailSrc, fullSrc, n) {
|
|
|
30371
30653
|
points.needsUpdate = true;
|
|
30372
30654
|
alphas.needsUpdate = true;
|
|
30373
30655
|
geometry.instanceCount = Math.max(0, m - 1);
|
|
30374
|
-
geometry.setDrawRange(0,
|
|
30656
|
+
geometry.setDrawRange(0, CORNER_INDICES.length);
|
|
30375
30657
|
}
|
|
30376
30658
|
function makeEmptyTrail() {
|
|
30377
30659
|
const geometry = new BufferGeometry();
|
|
@@ -30401,7 +30683,7 @@ function writeLineDistances(geometry, src, n) {
|
|
|
30401
30683
|
}
|
|
30402
30684
|
function buildFatLineFromThin(capacity, posArr, trailAlphas, fullAlphas, total, color, lineWidth, brightness, dash) {
|
|
30403
30685
|
const geometry = makeFatTrailGeometry(capacity);
|
|
30404
|
-
if (dash)
|
|
30686
|
+
if (dash) setPointAttributes(geometry, ["distStart", "distEnd"], capacity, 1);
|
|
30405
30687
|
writeFatTrailVertices(geometry, posArr, trailAlphas, fullAlphas, total);
|
|
30406
30688
|
const mesh = new Mesh(geometry, makeFatTrailMaterial(color, lineWidth, brightness, dash));
|
|
30407
30689
|
mesh.matrixAutoUpdate = false;
|
|
@@ -30768,7 +31050,7 @@ var SystemDataLoader = class {
|
|
|
30768
31050
|
};
|
|
30769
31051
|
|
|
30770
31052
|
// src/lib/scene/debug/skybox-adjust.ts
|
|
30771
|
-
var DEG2RAD$
|
|
31053
|
+
var DEG2RAD$2 = Math.PI / 180;
|
|
30772
31054
|
var SkyboxAdjuster = class {
|
|
30773
31055
|
scene;
|
|
30774
31056
|
adjust = {
|
|
@@ -30792,15 +31074,15 @@ var SkyboxAdjuster = class {
|
|
|
30792
31074
|
this.adjust.rxDeg = rxDeg;
|
|
30793
31075
|
this.adjust.ryDeg = ryDeg;
|
|
30794
31076
|
this.adjust.rzDeg = rzDeg;
|
|
30795
|
-
this._adjustQ.setFromAxisAngle(this._axisX, rxDeg * DEG2RAD$
|
|
31077
|
+
this._adjustQ.setFromAxisAngle(this._axisX, rxDeg * DEG2RAD$2).premultiply(this._tmpQ.setFromAxisAngle(this._axisY, ryDeg * DEG2RAD$2)).premultiply(this._tmpQ.setFromAxisAngle(this._axisZ, rzDeg * DEG2RAD$2));
|
|
30796
31078
|
this._q.copy(SKYBOX_BASE_ROTATION).multiply(this._adjustQ);
|
|
30797
31079
|
this.scene.backgroundRotation.setFromQuaternion(this._q);
|
|
30798
31080
|
}
|
|
30799
31081
|
};
|
|
30800
31082
|
|
|
30801
31083
|
// src/lib/scene/objects/sky/debug-markers.ts
|
|
30802
|
-
var DEG2RAD$
|
|
30803
|
-
var OBL_RAD = EARTH_OBLIQUITY_DEG * DEG2RAD$
|
|
31084
|
+
var DEG2RAD$1 = Math.PI / 180;
|
|
31085
|
+
var OBL_RAD = EARTH_OBLIQUITY_DEG * DEG2RAD$1;
|
|
30804
31086
|
var COS_OBL = Math.cos(OBL_RAD);
|
|
30805
31087
|
var SIN_OBL = Math.sin(OBL_RAD);
|
|
30806
31088
|
var SKY_MARKER_RADIUS = 5e3;
|
|
@@ -30916,8 +31198,8 @@ var REFERENCE_CIRCLES = [
|
|
|
30916
31198
|
}
|
|
30917
31199
|
];
|
|
30918
31200
|
function eqToScene(raDeg, decDeg, out) {
|
|
30919
|
-
const ra = raDeg * DEG2RAD$
|
|
30920
|
-
const dec = decDeg * DEG2RAD$
|
|
31201
|
+
const ra = raDeg * DEG2RAD$1;
|
|
31202
|
+
const dec = decDeg * DEG2RAD$1;
|
|
30921
31203
|
const cd = Math.cos(dec);
|
|
30922
31204
|
const xEq = cd * Math.cos(ra);
|
|
30923
31205
|
const yEq = cd * Math.sin(ra);
|
|
@@ -32600,16 +32882,16 @@ function between(points, jds, jd, from = 0, to = points.length) {
|
|
|
32600
32882
|
}
|
|
32601
32883
|
|
|
32602
32884
|
// src/lib/math/travel/state.ts
|
|
32603
|
-
var DEG2RAD
|
|
32604
|
-
var COS_EPS = Math.cos(EARTH_OBLIQUITY_DEG * DEG2RAD
|
|
32605
|
-
var SIN_EPS = Math.sin(EARTH_OBLIQUITY_DEG * DEG2RAD
|
|
32885
|
+
var DEG2RAD = Math.PI / 180;
|
|
32886
|
+
var COS_EPS = Math.cos(EARTH_OBLIQUITY_DEG * DEG2RAD);
|
|
32887
|
+
var SIN_EPS = Math.sin(EARTH_OBLIQUITY_DEG * DEG2RAD);
|
|
32606
32888
|
function planeRotation(w, i, om, equatorial) {
|
|
32607
|
-
const cosW = Math.cos(w * DEG2RAD
|
|
32608
|
-
const sinW = Math.sin(w * DEG2RAD
|
|
32609
|
-
const cosI = Math.cos(i * DEG2RAD
|
|
32610
|
-
const sinI = Math.sin(i * DEG2RAD
|
|
32611
|
-
const cosOm = Math.cos(om * DEG2RAD
|
|
32612
|
-
const sinOm = Math.sin(om * DEG2RAD
|
|
32889
|
+
const cosW = Math.cos(w * DEG2RAD);
|
|
32890
|
+
const sinW = Math.sin(w * DEG2RAD);
|
|
32891
|
+
const cosI = Math.cos(i * DEG2RAD);
|
|
32892
|
+
const sinI = Math.sin(i * DEG2RAD);
|
|
32893
|
+
const cosOm = Math.cos(om * DEG2RAD);
|
|
32894
|
+
const sinOm = Math.sin(om * DEG2RAD);
|
|
32613
32895
|
return {
|
|
32614
32896
|
m11: cosOm * cosW - sinOm * sinW * cosI,
|
|
32615
32897
|
m12: -cosOm * sinW - sinOm * cosW * cosI,
|
|
@@ -32659,10 +32941,10 @@ function elementsToState(el, jd) {
|
|
|
32659
32941
|
}
|
|
32660
32942
|
if (!isFinite(e) || !isFinite(ma)) return null;
|
|
32661
32943
|
const aKm = a * AU_KM;
|
|
32662
|
-
const nRadPerSec = n * DEG2RAD
|
|
32944
|
+
const nRadPerSec = n * DEG2RAD / SEC_PER_DAY$1;
|
|
32663
32945
|
const mu = nRadPerSec * nRadPerSec * Math.abs(aKm) * Math.abs(aKm) * Math.abs(aKm);
|
|
32664
32946
|
if (!isFinite(mu) || mu <= 0) return null;
|
|
32665
|
-
const M = (ma + n * (t - epoch)) * DEG2RAD
|
|
32947
|
+
const M = (ma + n * (t - epoch)) * DEG2RAD;
|
|
32666
32948
|
let nu;
|
|
32667
32949
|
let rKm;
|
|
32668
32950
|
if (e < 1 || a > 0) {
|
|
@@ -33658,65 +33940,6 @@ function addVec(out, a, b) {
|
|
|
33658
33940
|
out[2] = a[2] + b[2];
|
|
33659
33941
|
}
|
|
33660
33942
|
|
|
33661
|
-
// src/lib/scene/position/feature-seat.ts
|
|
33662
|
-
var DEG2RAD = Math.PI / 180;
|
|
33663
|
-
var SEAT_GLIDE_TAU_MS = 120;
|
|
33664
|
-
var SEAT_GLIDE_SNAP_SQ = kmToScene(.001) ** 2;
|
|
33665
|
-
var glideState = /* @__PURE__ */ new WeakMap();
|
|
33666
|
-
var _fixed = new Vector3();
|
|
33667
|
-
var _addend = new Vector3();
|
|
33668
|
-
function glideVec(current, target, k) {
|
|
33669
|
-
if (current.distanceToSquared(target) <= SEAT_GLIDE_SNAP_SQ) current.copy(target);
|
|
33670
|
-
else current.lerp(target, k);
|
|
33671
|
-
}
|
|
33672
|
-
function seatFeatureBody(fb, host, hostPos, hostBo, jd) {
|
|
33673
|
-
const anchor = fb.featureAnchor;
|
|
33674
|
-
setOrbitCenter(fb, hostPos);
|
|
33675
|
-
_addend.set(0, 0, 0);
|
|
33676
|
-
const idx = hostBo?.nomenclatureActiveIndex ?? -1;
|
|
33677
|
-
const label = idx >= 0 ? hostBo?.nomenclatureLabels?.[idx] ?? null : null;
|
|
33678
|
-
if (label && label.element.dataset.featureId === String(anchor.featureId)) {
|
|
33679
|
-
const modelAnchor = hostBo?.nomenclatureAnchor;
|
|
33680
|
-
if (modelAnchor) {
|
|
33681
|
-
_fixed.copy(label.position);
|
|
33682
|
-
_addend.copy(modelAnchor.position);
|
|
33683
|
-
} else {
|
|
33684
|
-
_fixed.copy(label.position);
|
|
33685
|
-
if (hostBo?.mesh) _fixed.multiply(hostBo.mesh.scale);
|
|
33686
|
-
}
|
|
33687
|
-
} else {
|
|
33688
|
-
const radiusKm = host.data.radiusKm;
|
|
33689
|
-
const latR = anchor.lat * DEG2RAD;
|
|
33690
|
-
const lngR = anchor.lon * DEG2RAD;
|
|
33691
|
-
const seat = host.orientation && Number.isFinite(radiusKm) && radiusKm > 0 ? renderedSeatAt(hostBo, host.data.id, radiusKm, latR, lngR) : null;
|
|
33692
|
-
if (seat) _fixed.set(seat.pointKm[0], seat.pointKm[1], seat.pointKm[2]);
|
|
33693
|
-
else {
|
|
33694
|
-
const r = Number.isFinite(radiusKm) && radiusKm > 0 ? radiusKm : 0;
|
|
33695
|
-
const [nx, ny, nz] = bodyFixedUnit(latR, lngR);
|
|
33696
|
-
_fixed.set(r * nx, r * ny, r * nz);
|
|
33697
|
-
}
|
|
33698
|
-
_fixed.multiplyScalar(kmToScene(1));
|
|
33699
|
-
}
|
|
33700
|
-
const now = performance.now();
|
|
33701
|
-
let st = glideState.get(fb);
|
|
33702
|
-
if (!st) {
|
|
33703
|
-
st = {
|
|
33704
|
-
fixed: _fixed.clone(),
|
|
33705
|
-
addend: _addend.clone(),
|
|
33706
|
-
t: now
|
|
33707
|
-
};
|
|
33708
|
-
glideState.set(fb, st);
|
|
33709
|
-
} else {
|
|
33710
|
-
const k = 1 - Math.exp(-(now - st.t) / SEAT_GLIDE_TAU_MS);
|
|
33711
|
-
st.t = now;
|
|
33712
|
-
glideVec(st.fixed, _fixed, k);
|
|
33713
|
-
glideVec(st.addend, _addend, k);
|
|
33714
|
-
}
|
|
33715
|
-
_fixed.copy(st.fixed);
|
|
33716
|
-
if (host.orientation) _fixed.applyQuaternion(bodyQuaternion(host.orientation, jd, host.nutPrec));
|
|
33717
|
-
return setPlaced(fb, hostPos[0] + _fixed.x + st.addend.x, hostPos[1] + _fixed.y + st.addend.y, hostPos[2] + _fixed.z + st.addend.z);
|
|
33718
|
-
}
|
|
33719
|
-
|
|
33720
33943
|
// src/lib/math/orbit/lagrange.ts
|
|
33721
33944
|
var EARTH_SUN_MASS_RATIO = 30034e-10;
|
|
33722
33945
|
var ALONG_MIN = .45;
|
|
@@ -34051,7 +34274,7 @@ var Placer = class {
|
|
|
34051
34274
|
return null;
|
|
34052
34275
|
}
|
|
34053
34276
|
this.settling.add(id);
|
|
34054
|
-
const pos =
|
|
34277
|
+
const pos = isExtrasolar(body) && !body.data.parentId ? fixedPlace(body) : this.settle(body, jd);
|
|
34055
34278
|
this.settling.delete(id);
|
|
34056
34279
|
if (jd === this.passJd) this.settled.set(id, body);
|
|
34057
34280
|
return pos;
|
|
@@ -34060,13 +34283,6 @@ var Placer = class {
|
|
|
34060
34283
|
const why = (this.ctx.getBody(aboveId) ?? this.bodyObjects.get(aboveId)?.body)?.unplaced;
|
|
34061
34284
|
return why === "loading" || why === "never" ? why : "parent";
|
|
34062
34285
|
}
|
|
34063
|
-
seatFeature(body, jd) {
|
|
34064
|
-
const hostId = body.featureAnchor.hostId;
|
|
34065
|
-
const host = this.ctx.getBody(hostId);
|
|
34066
|
-
const hostPos = host && this.place(host, jd);
|
|
34067
|
-
if (!host || !hostPos) return setUnplaced(body, this.under(hostId));
|
|
34068
|
-
return seatFeatureBody(body, host, hostPos, this.bodyObjects.get(hostId), jd);
|
|
34069
|
-
}
|
|
34070
34286
|
rideAt(jd) {
|
|
34071
34287
|
if (jd !== this.rideJd || this.focusedId !== this.rideFocus) {
|
|
34072
34288
|
this.rideJd = jd;
|
|
@@ -35250,14 +35466,13 @@ function updateSphereLOD(bodyObjects, camera, renderer, ctx, focusedId, invalida
|
|
|
35250
35466
|
const fovRad = camera.fov * Math.PI / 180;
|
|
35251
35467
|
const projScale = renderer.domElement.clientHeight / (2 * Math.tan(fovRad / 2));
|
|
35252
35468
|
const activeSystem = ctx.visibility.activeSystemId;
|
|
35253
|
-
const terrainId = focusedId;
|
|
35254
35469
|
for (const bo of bodyObjects.values()) {
|
|
35255
35470
|
if (!bo.mesh || !bo.radiusScene || !bo.group.visible) continue;
|
|
35256
35471
|
if (bo.cachedDist <= 0) continue;
|
|
35257
35472
|
const isStar = bo.body.data.objectType === ObjectType.STAR;
|
|
35258
35473
|
const id = bo.body.data.id;
|
|
35259
|
-
if (id ===
|
|
35260
|
-
if (updateTerrainWindow(bo, camera, id ===
|
|
35474
|
+
if (id === focusedId || bo.terrainWindow) {
|
|
35475
|
+
if (updateTerrainWindow(bo, camera, id === focusedId, invalidate)) continue;
|
|
35261
35476
|
}
|
|
35262
35477
|
const desired = desiredSphereSegments(bo.radiusScene / bo.cachedDist * projScale, isStar, activeSystem ? id === activeSystem || ctx.visibility.isInActiveSystem(bo.body.data.parentId) : id === focusedId, bo.currentSegments ?? 64);
|
|
35263
35478
|
if (desired === bo.currentSegments) continue;
|
|
@@ -35642,8 +35857,11 @@ var PromotionRegistry = class {
|
|
|
35642
35857
|
this.finalizeBuilds();
|
|
35643
35858
|
}
|
|
35644
35859
|
syncSystemBodies(bodies) {
|
|
35645
|
-
const
|
|
35646
|
-
|
|
35860
|
+
for (const id of this.systemPromoted) {
|
|
35861
|
+
if (!this.deps.inFocus(id)) continue;
|
|
35862
|
+
this.systemPromoted.delete(id);
|
|
35863
|
+
this.userPromoted.add(id);
|
|
35864
|
+
}
|
|
35647
35865
|
const wanted = new Set(bodies.map((b) => b.data.id));
|
|
35648
35866
|
const dropped = [...this.systemPromoted].filter((id) => !wanted.has(id) && !this.pinned.has(id));
|
|
35649
35867
|
this.tearDownBodies(dropped, this.systemPromoted);
|
|
@@ -35755,11 +35973,10 @@ var PromotionRegistry = class {
|
|
|
35755
35973
|
}
|
|
35756
35974
|
teardownAutoPromoted(forceAll) {
|
|
35757
35975
|
if (this.autoPromoted.size === 0) return;
|
|
35758
|
-
const focusedId = this.deps.getFocusedId();
|
|
35759
35976
|
const jd = this.deps.clock.jd;
|
|
35760
35977
|
const toRemove = [];
|
|
35761
35978
|
for (const id of this.autoPromoted) {
|
|
35762
|
-
if (id
|
|
35979
|
+
if (this.deps.inFocus(id)) continue;
|
|
35763
35980
|
if (forceAll || this.groupTargets && !this.groupTargets.has(id)) {
|
|
35764
35981
|
toRemove.push(id);
|
|
35765
35982
|
continue;
|
|
@@ -35772,21 +35989,21 @@ var PromotionRegistry = class {
|
|
|
35772
35989
|
}
|
|
35773
35990
|
setPinned(ids) {
|
|
35774
35991
|
this.pinned = new Set(ids);
|
|
35775
|
-
const focusedId = this.deps.getFocusedId();
|
|
35776
35992
|
const dropped = [...this.pinPromoted].filter((id) => !this.pinned.has(id));
|
|
35777
|
-
|
|
35778
|
-
|
|
35779
|
-
this.
|
|
35780
|
-
this.
|
|
35993
|
+
const kept = dropped.filter(this.deps.inFocus);
|
|
35994
|
+
for (const id of kept) {
|
|
35995
|
+
this.pinPromoted.delete(id);
|
|
35996
|
+
this.userPromoted.add(id);
|
|
35781
35997
|
}
|
|
35782
|
-
|
|
35998
|
+
if (kept.length > 0) this.emitUserPromotedCount();
|
|
35999
|
+
this.tearDownBodies(dropped.filter((id) => !this.deps.inFocus(id)), this.pinPromoted);
|
|
35783
36000
|
this.buildPinned();
|
|
35784
36001
|
}
|
|
35785
36002
|
buildPinned() {
|
|
35786
36003
|
let built = false;
|
|
35787
36004
|
for (const id of this.pinned) {
|
|
35788
36005
|
const body = this.deps.ctx.getBody(id);
|
|
35789
|
-
if (!body ||
|
|
36006
|
+
if (!body || body.data.objectType === ObjectType.MOON) continue;
|
|
35790
36007
|
if (!this.buildBodyInstance(body)) continue;
|
|
35791
36008
|
built = true;
|
|
35792
36009
|
if (this.userPromoted.delete(id)) this.pinPromoted.add(id);
|
|
@@ -35796,8 +36013,7 @@ var PromotionRegistry = class {
|
|
|
35796
36013
|
this.finalizeBuilds();
|
|
35797
36014
|
}
|
|
35798
36015
|
clearUserPromoted() {
|
|
35799
|
-
const
|
|
35800
|
-
const toRemove = [...this.userPromoted].filter((id) => id !== focusedId);
|
|
36016
|
+
const toRemove = [...this.userPromoted].filter((id) => !this.deps.inFocus(id));
|
|
35801
36017
|
this.tearDownBodies(toRemove, this.userPromoted);
|
|
35802
36018
|
this.emitUserPromotedCount();
|
|
35803
36019
|
}
|
|
@@ -35858,15 +36074,61 @@ var PromotionRegistry = class {
|
|
|
35858
36074
|
}
|
|
35859
36075
|
emitUserPromotedCount() {
|
|
35860
36076
|
if (!this.deps.onUserPromotedChange) return;
|
|
35861
|
-
|
|
35862
|
-
|
|
35863
|
-
if (focusedId && this.userPromoted.has(focusedId)) count--;
|
|
36077
|
+
let count = 0;
|
|
36078
|
+
for (const id of this.userPromoted) if (!this.deps.inFocus(id)) count++;
|
|
35864
36079
|
this.deps.onUserPromotedChange(count);
|
|
35865
36080
|
}
|
|
35866
36081
|
};
|
|
35867
36082
|
|
|
35868
36083
|
// src/lib/scene/focus/controller.ts
|
|
36084
|
+
function surfaceSite(host, point, hostPos, jd) {
|
|
36085
|
+
const s = cartesianToSpherical([...point], [...hostPos], siteQuat(host, jd));
|
|
36086
|
+
return {
|
|
36087
|
+
latitude: s.latitude,
|
|
36088
|
+
longitude: s.longitude,
|
|
36089
|
+
radius: s.distance
|
|
36090
|
+
};
|
|
36091
|
+
}
|
|
36092
|
+
function siteQuat(host, jd) {
|
|
36093
|
+
const q = host.orientation && bodyQuaternion(host.orientation, jd, host.nutPrec);
|
|
36094
|
+
return q ? [
|
|
36095
|
+
q.x,
|
|
36096
|
+
q.y,
|
|
36097
|
+
q.z,
|
|
36098
|
+
q.w
|
|
36099
|
+
] : void 0;
|
|
36100
|
+
}
|
|
36101
|
+
function underHalfRadius(host, distance) {
|
|
36102
|
+
return distance < kmToScene(effectiveRadiusKm(host.data)) / 2;
|
|
36103
|
+
}
|
|
36104
|
+
function viewOverSite(site, offset, quat) {
|
|
36105
|
+
const { latitude, longitude } = site;
|
|
36106
|
+
const up = new Vector3(...sphericalToCartesian([
|
|
36107
|
+
0,
|
|
36108
|
+
0,
|
|
36109
|
+
0
|
|
36110
|
+
], latitude, longitude, 1, quat));
|
|
36111
|
+
const down = new Vector3(...sphericalToCartesian([
|
|
36112
|
+
0,
|
|
36113
|
+
0,
|
|
36114
|
+
0
|
|
36115
|
+
], latitude + 90, longitude, 1, quat)).negate();
|
|
36116
|
+
const range = offset.length();
|
|
36117
|
+
const level = offset.clone().addScaledVector(up, -offset.dot(up));
|
|
36118
|
+
const tilt = Math.acos(MathUtils.clamp(offset.dot(up) / range, -1, 1));
|
|
36119
|
+
const heading = Math.atan2(new Vector3().crossVectors(down, level).dot(up), down.dot(level));
|
|
36120
|
+
return {
|
|
36121
|
+
latitude,
|
|
36122
|
+
longitude,
|
|
36123
|
+
zoom: site.radius + range,
|
|
36124
|
+
look: {
|
|
36125
|
+
tilt,
|
|
36126
|
+
heading
|
|
36127
|
+
}
|
|
36128
|
+
};
|
|
36129
|
+
}
|
|
35869
36130
|
var _from = new Vector3();
|
|
36131
|
+
var _shift = new Vector3();
|
|
35870
36132
|
var _forward = new Vector3();
|
|
35871
36133
|
var _ground = new Vector3();
|
|
35872
36134
|
var FocusController = class {
|
|
@@ -35875,6 +36137,10 @@ var FocusController = class {
|
|
|
35875
36137
|
readerFocusFilter = null;
|
|
35876
36138
|
focusSets = 0;
|
|
35877
36139
|
focusedBody;
|
|
36140
|
+
subjectBody;
|
|
36141
|
+
onGround = false;
|
|
36142
|
+
subjectSite = null;
|
|
36143
|
+
placedOffSubject = false;
|
|
35878
36144
|
_tmpV3 = new Vector3();
|
|
35879
36145
|
pendingInitialView = null;
|
|
35880
36146
|
heldEpoch = -1;
|
|
@@ -35896,7 +36162,7 @@ var FocusController = class {
|
|
|
35896
36162
|
onBodyClick: (b) => this.handleFocus(b),
|
|
35897
36163
|
assignMapLayerToTrails: deps.assignMapLayerToTrails,
|
|
35898
36164
|
repositionAll: deps.repositionAll,
|
|
35899
|
-
|
|
36165
|
+
inFocus: (id) => id === this.focusedBody?.data.id || id === this.subjectBody?.data.id,
|
|
35900
36166
|
onUserPromotedChange: deps.callbacks.onUserPromotedChange,
|
|
35901
36167
|
onSystemBodies: (bodies) => deps.systemData.loadPlanets(bodies)
|
|
35902
36168
|
});
|
|
@@ -35904,6 +36170,151 @@ var FocusController = class {
|
|
|
35904
36170
|
get current() {
|
|
35905
36171
|
return this.focusedBody;
|
|
35906
36172
|
}
|
|
36173
|
+
get subject() {
|
|
36174
|
+
return this.subjectBody;
|
|
36175
|
+
}
|
|
36176
|
+
get groundView() {
|
|
36177
|
+
return this.onGround;
|
|
36178
|
+
}
|
|
36179
|
+
onGroundAt(body, jd = this.deps.clock.jd) {
|
|
36180
|
+
if (body.data.objectType !== ObjectType.SPACECRAFT) return false;
|
|
36181
|
+
const probe = this.probeRecord(body, jd);
|
|
36182
|
+
return probe && isLandedAt(probe, jd);
|
|
36183
|
+
}
|
|
36184
|
+
landedSite(body, jd = this.deps.clock.jd) {
|
|
36185
|
+
const probe = this.probeRecord(body, jd);
|
|
36186
|
+
if (!probe?.landed || !isLandedAt(probe, jd)) return void 0;
|
|
36187
|
+
const { ctx } = this.deps;
|
|
36188
|
+
const host = ctx.getBody(`naif-${probe.landed.bodyNaifId}`);
|
|
36189
|
+
const pos = ctx.place(body, jd);
|
|
36190
|
+
const hostPos = host && ctx.place(host, jd);
|
|
36191
|
+
if (!host || !pos || !hostPos) return void 0;
|
|
36192
|
+
return {
|
|
36193
|
+
host,
|
|
36194
|
+
site: surfaceSite(host, pos, hostPos, jd)
|
|
36195
|
+
};
|
|
36196
|
+
}
|
|
36197
|
+
probeRecord(body, jd) {
|
|
36198
|
+
if (body.data.objectType !== ObjectType.SPACECRAFT) return void 0;
|
|
36199
|
+
return this.deps.ctx.probeStore?.probeWithCenter(body.data.id, jd)?.probe;
|
|
36200
|
+
}
|
|
36201
|
+
followSubject(jd) {
|
|
36202
|
+
const subject = this.subjectBody;
|
|
36203
|
+
const host = this.focusedBody;
|
|
36204
|
+
const now = subject && this.landedSite(subject, jd);
|
|
36205
|
+
if (!subject || !host || !now || now.host !== host) return;
|
|
36206
|
+
const was = this.subjectSite;
|
|
36207
|
+
this.subjectSite = now.site;
|
|
36208
|
+
if (!was || !this.placedOffSubject && this.pendingInitialView) return;
|
|
36209
|
+
const q = this.focusedBodyQuat(host);
|
|
36210
|
+
const a = sphericalToCartesian([
|
|
36211
|
+
0,
|
|
36212
|
+
0,
|
|
36213
|
+
0
|
|
36214
|
+
], was.latitude, was.longitude, was.radius, q);
|
|
36215
|
+
const b = sphericalToCartesian([
|
|
36216
|
+
0,
|
|
36217
|
+
0,
|
|
36218
|
+
0
|
|
36219
|
+
], now.site.latitude, now.site.longitude, now.site.radius, q);
|
|
36220
|
+
const shift = _shift.set(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
36221
|
+
if (shift.length() < kmToScene(1e-6)) return;
|
|
36222
|
+
this.deps.controls.moveOrbit((orbit) => orbit.add(shift));
|
|
36223
|
+
const held = this.pendingInitialView;
|
|
36224
|
+
if (held) {
|
|
36225
|
+
const at = sphericalToCartesian([
|
|
36226
|
+
0,
|
|
36227
|
+
0,
|
|
36228
|
+
0
|
|
36229
|
+
], held.latitude, held.longitude, held.zoom, q);
|
|
36230
|
+
const moved = cartesianToSpherical([
|
|
36231
|
+
at[0] + shift.x,
|
|
36232
|
+
at[1] + shift.y,
|
|
36233
|
+
at[2] + shift.z
|
|
36234
|
+
], [
|
|
36235
|
+
0,
|
|
36236
|
+
0,
|
|
36237
|
+
0
|
|
36238
|
+
], q);
|
|
36239
|
+
this.pendingInitialView = {
|
|
36240
|
+
...held,
|
|
36241
|
+
latitude: moved.latitude,
|
|
36242
|
+
longitude: moved.longitude,
|
|
36243
|
+
zoom: moved.distance
|
|
36244
|
+
};
|
|
36245
|
+
}
|
|
36246
|
+
}
|
|
36247
|
+
leaveGround() {
|
|
36248
|
+
const subject = this.subjectBody;
|
|
36249
|
+
const host = this.focusedBody;
|
|
36250
|
+
const site = this.subjectSite;
|
|
36251
|
+
if (!subject || !host) return;
|
|
36252
|
+
const pos = this.place(subject);
|
|
36253
|
+
if (!pos || !site) {
|
|
36254
|
+
this.setFocusTarget(subject);
|
|
36255
|
+
return;
|
|
36256
|
+
}
|
|
36257
|
+
const seat = sphericalToCartesian([
|
|
36258
|
+
0,
|
|
36259
|
+
0,
|
|
36260
|
+
0
|
|
36261
|
+
], site.latitude, site.longitude, site.radius, siteQuat(host, this.deps.clock.jd));
|
|
36262
|
+
const cam = this.deps.camera.position;
|
|
36263
|
+
const offset = cartesianToSpherical([
|
|
36264
|
+
cam.x - seat[0],
|
|
36265
|
+
cam.y - seat[1],
|
|
36266
|
+
cam.z - seat[2]
|
|
36267
|
+
], [
|
|
36268
|
+
0,
|
|
36269
|
+
0,
|
|
36270
|
+
0
|
|
36271
|
+
]);
|
|
36272
|
+
this.settleOnBodyInstant(subject, pos);
|
|
36273
|
+
this.snapToBodyFrame(offset.latitude, offset.longitude, offset.distance);
|
|
36274
|
+
}
|
|
36275
|
+
panToSurfacePoint(host, site, height, subject) {
|
|
36276
|
+
const { focus, camera, controls, callbacks, pointClouds } = this.deps;
|
|
36277
|
+
const hostPos = this.place(host);
|
|
36278
|
+
if (!hostPos) return 0;
|
|
36279
|
+
const current = this.focusedBody;
|
|
36280
|
+
if (!(current !== void 0 && (current.data.id === host.data.id || this.landedSite(current)?.host.data.id === host.data.id)) && height !== void 0) return this.arriveAbove(host, site, height, subject);
|
|
36281
|
+
const camWorld = this.cameraTruePos();
|
|
36282
|
+
if (f64dist(focus.focusTruePos, hostPos) > 0) {
|
|
36283
|
+
focus.focusTruePos = [...hostPos];
|
|
36284
|
+
camera.position.set(camWorld[0] - hostPos[0], camWorld[1] - hostPos[1], camWorld[2] - hostPos[2]);
|
|
36285
|
+
this.deps.repositionAll();
|
|
36286
|
+
pointClouds.rebuildBasis();
|
|
36287
|
+
}
|
|
36288
|
+
this.setFocusTarget(host, void 0, subject);
|
|
36289
|
+
this.onGround = true;
|
|
36290
|
+
this.placedOffSubject = true;
|
|
36291
|
+
focus.aim = null;
|
|
36292
|
+
focus.aimLooks = false;
|
|
36293
|
+
focus.aimPose = null;
|
|
36294
|
+
const quat = siteQuat(host, this.deps.clock.jd);
|
|
36295
|
+
const ground = new Vector3(...sphericalToCartesian([
|
|
36296
|
+
0,
|
|
36297
|
+
0,
|
|
36298
|
+
0
|
|
36299
|
+
], site.latitude, site.longitude, site.radius, quat));
|
|
36300
|
+
const from = _from.set(camWorld[0] - hostPos[0], camWorld[1] - hostPos[1], camWorld[2] - hostPos[2]);
|
|
36301
|
+
if (controls.looksOver(from, ground)) {
|
|
36302
|
+
focus.aimPose = controls.lookPose(from, ground, new Quaternion());
|
|
36303
|
+
focus.aim = [
|
|
36304
|
+
ground.x,
|
|
36305
|
+
ground.y,
|
|
36306
|
+
ground.z
|
|
36307
|
+
];
|
|
36308
|
+
focus.aimLooks = true;
|
|
36309
|
+
focus.focusDurationMs = angularDuration(camera.quaternion.angleTo(focus.aimPose), FOCUS_ROT_PACING);
|
|
36310
|
+
return focus.focusDurationMs;
|
|
36311
|
+
}
|
|
36312
|
+
const distance = from.length();
|
|
36313
|
+
callbacks.onCameraPosition?.(site.latitude, site.longitude, distance);
|
|
36314
|
+
const camPos = sphericalToCartesian([...hostPos], site.latitude, site.longitude, distance, quat);
|
|
36315
|
+
prepareFlyToCamera(focus, camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
36316
|
+
return focus.focusDurationMs;
|
|
36317
|
+
}
|
|
35907
36318
|
get focusChanges() {
|
|
35908
36319
|
return this.focusSets;
|
|
35909
36320
|
}
|
|
@@ -35946,12 +36357,12 @@ var FocusController = class {
|
|
|
35946
36357
|
];
|
|
35947
36358
|
}
|
|
35948
36359
|
holdsModel(id) {
|
|
35949
|
-
|
|
35950
|
-
return id === (focused && (isSurfaceFeature(focused) ? focused.featureAnchor.hostId : focused.data.id)) || this.upgradeTargetIds.has(id);
|
|
36360
|
+
return id === this.focusedBody?.data.id || id === this.subjectBody?.data.id || this.upgradeTargetIds.has(id);
|
|
35951
36361
|
}
|
|
35952
36362
|
syncUpgradeTargets(body) {
|
|
35953
36363
|
const { ctx, scene, clickables, meshToBody, bodyObjects, pointClouds, clock, loadTexture, assignMapLayerToTrails } = this.deps;
|
|
35954
36364
|
const targets = upgradeTargets(body, ctx);
|
|
36365
|
+
if (this.subjectBody && !targets.includes(this.subjectBody)) targets.push(this.subjectBody);
|
|
35955
36366
|
const newIds = new Set(targets.map((t) => t.data.id));
|
|
35956
36367
|
for (const id of this.upgradeTargetIds) {
|
|
35957
36368
|
if (newIds.has(id)) continue;
|
|
@@ -36011,6 +36422,8 @@ var FocusController = class {
|
|
|
36011
36422
|
];
|
|
36012
36423
|
}
|
|
36013
36424
|
this.focusedBody = void 0;
|
|
36425
|
+
this.subjectBody = void 0;
|
|
36426
|
+
this.onGround = false;
|
|
36014
36427
|
this.promotion.emitUserPromotedCount();
|
|
36015
36428
|
prepareFocusTarget(focus, [...position], camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
36016
36429
|
const spherical = cartesianToSpherical(camPos ?? camWorld, position, void 0);
|
|
@@ -36051,8 +36464,13 @@ var FocusController = class {
|
|
|
36051
36464
|
}
|
|
36052
36465
|
handleFocus(body) {
|
|
36053
36466
|
if (this.readerFocusFilter && !this.readerFocusFilter(body)) return;
|
|
36054
|
-
if (this.focusedBody?.data.id === body.data.id && !this.deps.focus.aim) {
|
|
36055
|
-
this.deps.callbacks.onFocusChange(body);
|
|
36467
|
+
if ((this.subjectBody ?? this.focusedBody)?.data.id === body.data.id && !this.deps.focus.aim) {
|
|
36468
|
+
this.deps.callbacks.onFocusChange(body, this.focusedBody);
|
|
36469
|
+
return;
|
|
36470
|
+
}
|
|
36471
|
+
const landed = this.landedSite(body);
|
|
36472
|
+
if (landed) {
|
|
36473
|
+
this.panToSurfacePoint(landed.host, landed.site, void 0, body);
|
|
36056
36474
|
return;
|
|
36057
36475
|
}
|
|
36058
36476
|
this.setFocusTarget(body);
|
|
@@ -36062,36 +36480,28 @@ var FocusController = class {
|
|
|
36062
36480
|
const { latitude, longitude, distance } = cartesianToSpherical(camWorld, pos, this.focusedBodyQuat(body));
|
|
36063
36481
|
this.deps.callbacks.onCameraPosition?.(latitude, longitude, distance);
|
|
36064
36482
|
}
|
|
36065
|
-
setFocusTarget(body, camPos) {
|
|
36483
|
+
setFocusTarget(body, camPos, subject) {
|
|
36066
36484
|
const { ctx, bodyObjects, callbacks, focus, camera, systemData, loadTexture } = this.deps;
|
|
36067
36485
|
this.focusSets++;
|
|
36068
|
-
|
|
36069
|
-
|
|
36486
|
+
this.subjectBody = subject;
|
|
36487
|
+
this.subjectSite = (subject && this.landedSite(subject)?.site) ?? null;
|
|
36488
|
+
this.placedOffSubject = false;
|
|
36489
|
+
this.onGround = false;
|
|
36490
|
+
const drawsItself = !isExtrasolar(body) && !isExoplanet(body);
|
|
36491
|
+
this.promotion.ensureBodyObjects(body);
|
|
36070
36492
|
const prev = this.focusedBody;
|
|
36071
|
-
const
|
|
36072
|
-
const newNomBodyId = nomenclatureBodyId(body, bodyObjects);
|
|
36073
|
-
const prevModelId = prev && (isSurfaceFeature(prev) ? prev.featureAnchor.hostId : prev.data.id);
|
|
36074
|
-
const newModelId = isSurfaceFeature(body) ? body.featureAnchor.hostId : body.data.id;
|
|
36493
|
+
const prevId = prev?.data.id;
|
|
36075
36494
|
const newTargetIds = this.syncUpgradeTargets(body);
|
|
36076
|
-
if (
|
|
36077
|
-
const prevBo = bodyObjects.get(
|
|
36078
|
-
if (prevBo) unloadBodyModel(prevBo);
|
|
36079
|
-
|
|
36080
|
-
if (prevNomBodyId && prevNomBodyId !== newNomBodyId) {
|
|
36081
|
-
const prevNomBo = bodyObjects.get(prevNomBodyId);
|
|
36082
|
-
if (prevNomBo) disposeNomenclatureLabels(prevNomBo);
|
|
36495
|
+
if (prevId && prevId !== body.data.id) {
|
|
36496
|
+
const prevBo = bodyObjects.get(prevId);
|
|
36497
|
+
if (prevBo && !newTargetIds.has(prevId)) unloadBodyModel(prevBo);
|
|
36498
|
+
if (prevBo) disposeNomenclatureLabels(prevBo);
|
|
36083
36499
|
}
|
|
36084
36500
|
if (body.data.id !== prev?.data.id) this.pendingInitialView = null;
|
|
36085
36501
|
this.focusedBody = body;
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
ctx.visibility.setFocused(featureHost ?? body);
|
|
36089
|
-
callbacks.onFocusChange(body);
|
|
36502
|
+
ctx.visibility.setFocused(body, subject);
|
|
36503
|
+
callbacks.onFocusChange(subject ?? body, body);
|
|
36090
36504
|
if (drawsItself) loadTexture(body);
|
|
36091
|
-
if (newNomBodyId && newNomBodyId !== body.data.id) {
|
|
36092
|
-
const landingBody = ctx.getBody(newNomBodyId);
|
|
36093
|
-
if (landingBody) loadTexture(landingBody);
|
|
36094
|
-
}
|
|
36095
36505
|
systemData.syncToFocus();
|
|
36096
36506
|
const pos = this.place(body);
|
|
36097
36507
|
if (pos) {
|
|
@@ -36109,6 +36519,19 @@ var FocusController = class {
|
|
|
36109
36519
|
this.setFocusTarget(body);
|
|
36110
36520
|
return 0;
|
|
36111
36521
|
}
|
|
36522
|
+
const landed = this.landedSite(body);
|
|
36523
|
+
if (landed) {
|
|
36524
|
+
const { host, site } = landed;
|
|
36525
|
+
if (zoom === void 0 || underHalfRadius(host, zoom)) return this.panToSurfacePoint(host, site, zoom, body);
|
|
36526
|
+
const view = {
|
|
36527
|
+
latitude: latitude ?? site.latitude,
|
|
36528
|
+
longitude: longitude ?? site.longitude
|
|
36529
|
+
};
|
|
36530
|
+
return this.flyOntoGround(host, {
|
|
36531
|
+
...view,
|
|
36532
|
+
zoom
|
|
36533
|
+
}, body);
|
|
36534
|
+
}
|
|
36112
36535
|
let camPos;
|
|
36113
36536
|
if (zoom !== void 0) {
|
|
36114
36537
|
if (latitude !== void 0 && longitude !== void 0) camPos = sphericalToCartesian(pos, latitude, longitude, zoom, this.focusedBodyQuat(body));
|
|
@@ -36127,7 +36550,7 @@ var FocusController = class {
|
|
|
36127
36550
|
callbacks.onCameraPosition?.(spherical.latitude, spherical.longitude, spherical.distance);
|
|
36128
36551
|
if (zoom !== void 0 && camPos) {
|
|
36129
36552
|
this.pendingInitialView = null;
|
|
36130
|
-
if (this.focusedBody?.data.id === id && f64dist(focus.focusTruePos, pos) < minCameraDistance(body)) {
|
|
36553
|
+
if (this.focusedBody?.data.id === id && !this.subjectBody && !this.onGround && f64dist(focus.focusTruePos, pos) < minCameraDistance(body)) {
|
|
36131
36554
|
focus.focusTruePos = [...pos];
|
|
36132
36555
|
this.deps.repositionAll();
|
|
36133
36556
|
pointClouds.rebuildBasis();
|
|
@@ -36143,9 +36566,55 @@ var FocusController = class {
|
|
|
36143
36566
|
const body = this.deps.ctx.getBody(id);
|
|
36144
36567
|
const pos = body && this.place(body);
|
|
36145
36568
|
if (!body || !pos) return;
|
|
36569
|
+
const landed = this.landedSite(body);
|
|
36570
|
+
if (landed) {
|
|
36571
|
+
const { host, site } = landed;
|
|
36572
|
+
if (underHalfRadius(host, zoom)) this.arriveAbove(host, site, zoom, body, true, look);
|
|
36573
|
+
else this.snapOntoGround(host, {
|
|
36574
|
+
latitude,
|
|
36575
|
+
longitude,
|
|
36576
|
+
zoom,
|
|
36577
|
+
look
|
|
36578
|
+
}, body);
|
|
36579
|
+
return;
|
|
36580
|
+
}
|
|
36146
36581
|
this.settleOnBodyInstant(body, pos);
|
|
36147
36582
|
this.snapToBodyFrame(latitude, longitude, zoom, look);
|
|
36148
36583
|
}
|
|
36584
|
+
arriveAbove(host, site, height, subject, instant = false, look = LEVEL_LOOK) {
|
|
36585
|
+
const view = {
|
|
36586
|
+
latitude: site.latitude,
|
|
36587
|
+
longitude: site.longitude,
|
|
36588
|
+
zoom: site.radius + height
|
|
36589
|
+
};
|
|
36590
|
+
if (!instant) return this.flyOntoGround(host, view, subject);
|
|
36591
|
+
this.snapOntoGround(host, {
|
|
36592
|
+
...view,
|
|
36593
|
+
look
|
|
36594
|
+
}, subject);
|
|
36595
|
+
this.placedOffSubject = true;
|
|
36596
|
+
return 0;
|
|
36597
|
+
}
|
|
36598
|
+
flyOntoGround(host, view, subject) {
|
|
36599
|
+
const { focus, callbacks } = this.deps;
|
|
36600
|
+
const hostPos = this.place(host);
|
|
36601
|
+
if (!hostPos) return 0;
|
|
36602
|
+
const camPos = sphericalToCartesian([...hostPos], view.latitude, view.longitude, view.zoom, siteQuat(host, this.deps.clock.jd));
|
|
36603
|
+
callbacks.onCameraPosition?.(view.latitude, view.longitude, view.zoom);
|
|
36604
|
+
this.pendingInitialView = null;
|
|
36605
|
+
this.setFocusTarget(host, camPos, subject);
|
|
36606
|
+
focus.orbitFly = true;
|
|
36607
|
+
this.onGround = true;
|
|
36608
|
+
this.placedOffSubject = true;
|
|
36609
|
+
return focus.focusDurationMs;
|
|
36610
|
+
}
|
|
36611
|
+
snapOntoGround(host, view, subject) {
|
|
36612
|
+
const pos = this.place(host);
|
|
36613
|
+
if (!pos) return;
|
|
36614
|
+
this.settleOnBodyInstant(host, pos, subject);
|
|
36615
|
+
this.snapToBodyFrame(view.latitude, view.longitude, view.zoom, view.look);
|
|
36616
|
+
this.onGround = true;
|
|
36617
|
+
}
|
|
36149
36618
|
snapToBodyFacing(id, towardId, elevationDeg, distance) {
|
|
36150
36619
|
const { ctx, camera, controls, callbacks } = this.deps;
|
|
36151
36620
|
const body = ctx.getBody(id);
|
|
@@ -36190,9 +36659,9 @@ var FocusController = class {
|
|
|
36190
36659
|
callbacks.onCameraPosition?.(settled.latitude, settled.longitude, settled.distance);
|
|
36191
36660
|
this.pendingInitialView = null;
|
|
36192
36661
|
}
|
|
36193
|
-
settleOnBodyInstant(body, pos) {
|
|
36662
|
+
settleOnBodyInstant(body, pos, subject) {
|
|
36194
36663
|
const { focus, pointClouds } = this.deps;
|
|
36195
|
-
this.setFocusTarget(body);
|
|
36664
|
+
this.setFocusTarget(body, void 0, subject);
|
|
36196
36665
|
focus.focusTruePos = [...pos];
|
|
36197
36666
|
focus.focusOriginWorld = [...pos];
|
|
36198
36667
|
focus.focusTargetWorld = [...pos];
|
|
@@ -36203,6 +36672,10 @@ var FocusController = class {
|
|
|
36203
36672
|
focus.orbitFly = false;
|
|
36204
36673
|
focus.arcOrbit = false;
|
|
36205
36674
|
focus.cameraStaysOnBody = false;
|
|
36675
|
+
focus.aim = null;
|
|
36676
|
+
focus.aimLooks = false;
|
|
36677
|
+
focus.aimPose = null;
|
|
36678
|
+
focus.leavesAim = false;
|
|
36206
36679
|
this.deps.repositionAll();
|
|
36207
36680
|
pointClouds.rebuildBasis();
|
|
36208
36681
|
}
|
|
@@ -36272,10 +36745,6 @@ function framesInBodyAxes(b) {
|
|
|
36272
36745
|
}
|
|
36273
36746
|
function upgradeTargets(focus, ctx) {
|
|
36274
36747
|
if (!focus) return [];
|
|
36275
|
-
if (isSurfaceFeature(focus)) {
|
|
36276
|
-
const host = ctx.getBody(focus.featureAnchor.hostId);
|
|
36277
|
-
return host && isMeshUpgradable(host) ? [host] : [];
|
|
36278
|
-
}
|
|
36279
36748
|
const out = [];
|
|
36280
36749
|
if (isMeshUpgradable(focus)) out.push(focus);
|
|
36281
36750
|
if (focus.data.objectType === ObjectType.MOON || focus.data.orbitalSource === OrbitalSource.SPICE_PROBE) {
|
|
@@ -36736,7 +37205,7 @@ var SPHERE_GROUND = {
|
|
|
36736
37205
|
radiusKm: () => null
|
|
36737
37206
|
};
|
|
36738
37207
|
var _toCamera = new Vector3();
|
|
36739
|
-
function cameraMotionScale(camera, focusTruePos, focused, parent,
|
|
37208
|
+
function cameraMotionScale(camera, focusTruePos, focused, parent, position = camera.position, ground = SPHERE_GROUND) {
|
|
36740
37209
|
const orbitRadius = position.length();
|
|
36741
37210
|
if (!(orbitRadius > 0)) return {
|
|
36742
37211
|
rotate: 1,
|
|
@@ -36747,7 +37216,7 @@ function cameraMotionScale(camera, focusTruePos, focused, parent, seated, positi
|
|
|
36747
37216
|
const floor = focusRadius > 0 ? kmToScene(ground.clearanceKm(sized)) : 0;
|
|
36748
37217
|
let clearance = orbitRadius;
|
|
36749
37218
|
let height = Math.max(orbitRadius - focusRadius, floor);
|
|
36750
|
-
for (const body of [sized,
|
|
37219
|
+
for (const body of [sized, parent]) {
|
|
36751
37220
|
const at = body?.position;
|
|
36752
37221
|
if (!body || !at) continue;
|
|
36753
37222
|
if (!(effectiveRadiusKm(body.data) > 0)) continue;
|
|
@@ -36822,7 +37291,7 @@ var TIGHT_FAR_RELEASE = 2.3;
|
|
|
36822
37291
|
var SUN_PROXY_FAR_FRACTION = .9;
|
|
36823
37292
|
var _sunLiftDir = new Vector3();
|
|
36824
37293
|
var _lookDir = new Vector3();
|
|
36825
|
-
var
|
|
37294
|
+
var _groundDir = new Vector3();
|
|
36826
37295
|
var SPIN_FOLLOW_NONE = 2;
|
|
36827
37296
|
var _spinNow = new Quaternion();
|
|
36828
37297
|
var _spinStep = new Quaternion();
|
|
@@ -36985,7 +37454,6 @@ var SceneRenderer = class SceneRenderer {
|
|
|
36985
37454
|
};
|
|
36986
37455
|
viewportH = 1;
|
|
36987
37456
|
tier = currentRenderTier();
|
|
36988
|
-
landedNomBodyId = null;
|
|
36989
37457
|
rafId = 0;
|
|
36990
37458
|
paused = false;
|
|
36991
37459
|
covered = false;
|
|
@@ -37091,7 +37559,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37091
37559
|
const dust = new StarDust(this.renderer, dustPlan(), this.invalidate);
|
|
37092
37560
|
this.starfield = new StarfieldSystem(ctx, this.scene, starBudget(), this.invalidate, this.pointClouds.pickRegistry, dust, new GlowSky(this.renderer, this.scene, glowPlan(), dust, this.invalidate, () => this.skyboxSource(), new StarlessSky(loadStarlessSky, this.invalidate)));
|
|
37093
37561
|
this.constellations = new ConstellationSystem(ctx, this.scene, SceneRenderer.MAP_LAYER, this.invalidate, canvas, (slug, figure, name) => this.callbacks.onConstellationSelect?.(slug, figure, name), (direction, instant) => this.aimAt(direction, instant), () => this.focus.camOriginWorld !== null);
|
|
37094
|
-
this.controls = new LookOrbitControls(this.camera, canvas, this.lookSurfaceRadius,
|
|
37562
|
+
this.controls = new LookOrbitControls(this.camera, canvas, this.lookSurfaceRadius, () => this.pendingUrlWrite = true);
|
|
37095
37563
|
this.controls.enableDamping = true;
|
|
37096
37564
|
this.orbitDampingFactor = orbitDamping(sceneSettings().resolvedReducedMotion);
|
|
37097
37565
|
Object.defineProperty(this.controls, "minDistance", { get: () => this.minCameraDistance() });
|
|
@@ -37112,7 +37580,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37112
37580
|
}
|
|
37113
37581
|
this.invalidate();
|
|
37114
37582
|
});
|
|
37115
|
-
this.cameraUp = new CameraUpController(this.camera, this.controls, ctx
|
|
37583
|
+
this.cameraUp = new CameraUpController(this.camera, this.controls, ctx);
|
|
37116
37584
|
this.systemData = new SystemDataLoader(this.scene, ctx, this.renderer, this.textureLoader, this.bodyObjects, clock, () => {
|
|
37117
37585
|
this.focusController.reapplyInitialViewIfPending();
|
|
37118
37586
|
this.invalidate();
|
|
@@ -37165,7 +37633,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37165
37633
|
ctx.visibility.updateCamera(framing.zoom, this.clock.jd);
|
|
37166
37634
|
this.viewOpen = !ctx.loading;
|
|
37167
37635
|
this.updateSystemShare(0);
|
|
37168
|
-
callbacks.onFocusChange(focusBody);
|
|
37636
|
+
callbacks.onFocusChange(focusBody, focusBody);
|
|
37169
37637
|
this.buildScene();
|
|
37170
37638
|
if (focusBody) this.focusController.promotion.ensureBodyObjects(focusBody);
|
|
37171
37639
|
if (focusBody) this.focusController.syncUpgradeTargets(focusBody);
|
|
@@ -37176,6 +37644,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37176
37644
|
this.gpuPick = new GpuPickPass(this.renderer, this.camera);
|
|
37177
37645
|
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.ctx.visibility.planetarySystemShare() === 0 ? this.starfield.pickDraws() : [], (pickId) => this.pickStar(pickId));
|
|
37178
37646
|
this.pointerInteraction.attach();
|
|
37647
|
+
if (focusBody && this.focusController.landedSite(focusBody)) this.focusController.snapToBody(focusBody.data.id, framing.latitude, framing.longitude, framing.zoom, framing.look);
|
|
37179
37648
|
this.tick();
|
|
37180
37649
|
}
|
|
37181
37650
|
buildScene() {
|
|
@@ -37450,6 +37919,9 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37450
37919
|
at[1] + offset[1],
|
|
37451
37920
|
at[2] + offset[2]
|
|
37452
37921
|
];
|
|
37922
|
+
const host = this.focusController.landedSite(body, this.sceneJd)?.host;
|
|
37923
|
+
const hostAt = host && this.whereBody(host);
|
|
37924
|
+
if (host && hostAt) return cartesianToSpherical(camera, hostAt, siteQuat(host, this.sceneJd));
|
|
37453
37925
|
return cartesianToSpherical(camera, at, this.focusController.focusedBodyQuat(body));
|
|
37454
37926
|
}
|
|
37455
37927
|
where(id) {
|
|
@@ -37496,7 +37968,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37496
37968
|
this.controls.target.set(0, 0, 0);
|
|
37497
37969
|
this.controls.update();
|
|
37498
37970
|
}
|
|
37499
|
-
this.coverageWatch?.sync(this.focusController.current, this.clock.jd);
|
|
37971
|
+
this.coverageWatch?.sync(this.focusController.subject ?? this.focusController.current, this.clock.jd);
|
|
37500
37972
|
this.clock.tick(performance.now());
|
|
37501
37973
|
if (!this.shouldRender(nowMs)) {
|
|
37502
37974
|
this.controls.update();
|
|
@@ -37522,13 +37994,14 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37522
37994
|
if (!this.dateHeld() && !this.seekAwaitsData) this.clock.placed(this.seenJumps);
|
|
37523
37995
|
this.refreshTravelFocus();
|
|
37524
37996
|
this.callbacks.onFrame?.(this.sceneJd, renderedDtMs);
|
|
37997
|
+
this.followLanding(nowMs);
|
|
37525
37998
|
this.followSpin(nowMs);
|
|
37526
37999
|
const controlsSettled = this.holdingCamera ? this.applyHeldCamera() : stepFocusAnimation(this.focus, this.camera, this.controls, () => this.repositionAll(), () => this.pointClouds.rebuildBasis());
|
|
37527
38000
|
this.applyReaderLimits(nowMs);
|
|
37528
38001
|
const near = this.nearSurfaces();
|
|
37529
38002
|
if (near) {
|
|
37530
38003
|
this.clampToSurfaces(near);
|
|
37531
|
-
this.setMotionScale(cameraMotionScale(this.camera, this.focus.focusTruePos, near.focused, near.parent,
|
|
38004
|
+
this.setMotionScale(cameraMotionScale(this.camera, this.focus.focusTruePos, near.focused, near.parent, this.controls.orbitPosition, this.motionGround));
|
|
37532
38005
|
} else this.setMotionScale({
|
|
37533
38006
|
rotate: 1,
|
|
37534
38007
|
translate: 1
|
|
@@ -37587,7 +38060,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37587
38060
|
}
|
|
37588
38061
|
for (const light of this.ambientLights) light.intensity = ambient;
|
|
37589
38062
|
updateUserLocationOcclusion(this.userLocationMarker, this.bodyObjects, this.camera);
|
|
37590
|
-
const focusedIdLod =
|
|
38063
|
+
const focusedIdLod = this.focusController.current?.data.id;
|
|
37591
38064
|
updateTextureLOD(this.bodyObjects, this.camera, this.renderer, this.ctx, this.textureLoader, focusedIdLod, this.sceneJd, this.appearances, this.invalidate);
|
|
37592
38065
|
updateSphereLOD(this.bodyObjects, this.camera, this.renderer, this.ctx, focusedIdLod, this.invalidate);
|
|
37593
38066
|
updateSunShadowLight(this.bodyObjects, this.focus.focusTruePos, this.ctx, this.shadowLight, starPointLight, distance, this._tmpV3, sceneSettings().realisticLighting && this.starLight.starId === "naif-10", this.sunIntensityScale);
|
|
@@ -37644,6 +38117,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37644
38117
|
if (!body) return POINT_MIN_DISTANCE;
|
|
37645
38118
|
const bo = this.bodyObjects.get(body.data.id);
|
|
37646
38119
|
const lowest = bo && (modelMinRadiusKm(bo) ?? reliefRadiiKm(bo)?.min ?? smallestRadiusKm(bo));
|
|
38120
|
+
if (this.focusController.groundView) return kmToScene((lowest ?? effectiveRadiusKm(body.data)) + LANDED_KEEP_AWAY_KM);
|
|
37647
38121
|
return minCameraDistance(body, lowest, bo && finestTexelKm(bo));
|
|
37648
38122
|
}
|
|
37649
38123
|
clearanceKm(body) {
|
|
@@ -37651,7 +38125,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37651
38125
|
return surfaceClearanceKm(body, bo && finestTexelKm(bo));
|
|
37652
38126
|
}
|
|
37653
38127
|
followSpin(nowMs) {
|
|
37654
|
-
const body = this.
|
|
38128
|
+
const body = this.focusController.current;
|
|
37655
38129
|
const flying = nowMs - this.focus.focusStartTime < this.focus.focusDurationMs;
|
|
37656
38130
|
if (!body?.orientation || flying || this.holdingCamera || this.focus.aim) {
|
|
37657
38131
|
this.spinFrom = null;
|
|
@@ -37665,16 +38139,26 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37665
38139
|
};
|
|
37666
38140
|
if (!from || from.id !== body.data.id) return;
|
|
37667
38141
|
const radiusKm = effectiveRadiusKm(body.data);
|
|
37668
|
-
const height = sceneToKm(this.controls.orbitPosition.length()) / radiusKm -
|
|
38142
|
+
const height = sceneToKm(this.controls.orbitPosition.length()) / radiusKm - 1;
|
|
37669
38143
|
const t = MathUtils.clamp((SPIN_FOLLOW_NONE - height) / 1.5, 0, 1);
|
|
37670
38144
|
const share = t * t * (3 - 2 * t);
|
|
37671
38145
|
if (share === 0) return;
|
|
37672
38146
|
_spinStep.copy(_spinNow).multiply(from.quat.invert());
|
|
37673
38147
|
this.controls.carry(_noTurn.identity().slerp(_spinStep, share));
|
|
37674
38148
|
}
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
38149
|
+
followLanding(nowMs) {
|
|
38150
|
+
if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs || this.holdingCamera || this.dateHeld()) return;
|
|
38151
|
+
const fc = this.focusController;
|
|
38152
|
+
const subject = fc.subject;
|
|
38153
|
+
if (subject) {
|
|
38154
|
+
const onGround = fc.onGroundAt(subject);
|
|
38155
|
+
if (onGround === false) fc.leaveGround();
|
|
38156
|
+
else if (onGround) fc.followSubject(this.sceneJd);
|
|
38157
|
+
return;
|
|
38158
|
+
}
|
|
38159
|
+
const current = fc.current;
|
|
38160
|
+
const landed = current && fc.landedSite(current);
|
|
38161
|
+
if (landed) fc.panToSurfacePoint(landed.host, landed.site, void 0, current);
|
|
37678
38162
|
}
|
|
37679
38163
|
settleCamera() {
|
|
37680
38164
|
this.settleLook();
|
|
@@ -37692,22 +38176,23 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37692
38176
|
return {
|
|
37693
38177
|
focused,
|
|
37694
38178
|
parent: parentId ? this.ctx.getBody(parentId) : void 0,
|
|
37695
|
-
|
|
38179
|
+
onGround: this.focusController.groundView
|
|
37696
38180
|
};
|
|
37697
38181
|
}
|
|
37698
|
-
clampToSurfaces({ focused, parent,
|
|
38182
|
+
clampToSurfaces({ focused, parent, onGround }) {
|
|
37699
38183
|
const { camera } = this;
|
|
37700
38184
|
const at = this.focus.focusTruePos;
|
|
38185
|
+
const own = parent?.data.id === focused.data.id;
|
|
37701
38186
|
if (parent) {
|
|
37702
|
-
const surface = this.surfaceClamp(parent,
|
|
38187
|
+
const surface = this.surfaceClamp(parent, own && onGround);
|
|
37703
38188
|
clampCameraOutsideBody(camera, parent, at, surface, this.clearanceKm(parent));
|
|
37704
38189
|
}
|
|
37705
|
-
if (
|
|
37706
|
-
const surface = this.surfaceClamp(focused,
|
|
38190
|
+
if (own) return;
|
|
38191
|
+
const surface = this.surfaceClamp(focused, onGround);
|
|
37707
38192
|
if (surface) clampCameraOutsideBody(camera, focused, at, surface, this.clearanceKm(focused));
|
|
37708
38193
|
}
|
|
37709
38194
|
motionGround = {
|
|
37710
|
-
clearanceKm: (body) => this.clearanceKm(body),
|
|
38195
|
+
clearanceKm: (body) => this.focusController.groundView && body === this.focusController.current ? LANDED_KEEP_AWAY_KM : this.clearanceKm(body),
|
|
37711
38196
|
radiusKm: (body, direction) => {
|
|
37712
38197
|
const surface = this.surfaceClamp(body, false);
|
|
37713
38198
|
if (!surface) return null;
|
|
@@ -37783,20 +38268,10 @@ var SceneRenderer = class SceneRenderer {
|
|
|
37783
38268
|
}
|
|
37784
38269
|
updateTightFar(distance) {
|
|
37785
38270
|
if (isReversedDepth()) return;
|
|
37786
|
-
const
|
|
37787
|
-
const
|
|
37788
|
-
const anchorBo = anchorId ? this.bodyObjects.get(anchorId) : void 0;
|
|
37789
|
-
const radius = anchorBo?.radiusScene ?? 0;
|
|
37790
|
-
let dist = distance;
|
|
37791
|
-
const anchorAt = anchorBo?.body.position;
|
|
37792
|
-
if (focused && anchorAt && anchorId !== focused.data.id) {
|
|
37793
|
-
const [fx, fy, fz] = this.focus.focusTruePos;
|
|
37794
|
-
const [bx, by, bz] = anchorAt;
|
|
37795
|
-
const cam = this.camera.position;
|
|
37796
|
-
dist = Math.hypot(fx + cam.x - bx, fy + cam.y - by, fz + cam.z - bz);
|
|
37797
|
-
}
|
|
38271
|
+
const id = this.focusController.current?.data.id;
|
|
38272
|
+
const radius = id && this.bodyObjects.get(id)?.radiusScene || 0;
|
|
37798
38273
|
const bound = this.tightFar ? TIGHT_FAR_RELEASE : TIGHT_FAR_ENGAGE;
|
|
37799
|
-
this.tightFar = radius > 0 && this.ctx.visibility.activeSystemId !== null &&
|
|
38274
|
+
this.tightFar = radius > 0 && this.ctx.visibility.activeSystemId !== null && distance < radius * bound;
|
|
37800
38275
|
}
|
|
37801
38276
|
updateDepthFar() {
|
|
37802
38277
|
if (isReversedDepth()) return;
|
|
@@ -38229,34 +38704,23 @@ var SceneRenderer = class SceneRenderer {
|
|
|
38229
38704
|
const cam = this.camera;
|
|
38230
38705
|
return !(cam.position.distanceToSquared(pos) <= cam.position.lengthSq() * 1e-10 && 8 * (1 - Math.abs(cam.quaternion.dot(quat))) <= 1e-8);
|
|
38231
38706
|
}
|
|
38707
|
+
groundUnderCamera() {
|
|
38708
|
+
const body = this.focusController?.current;
|
|
38709
|
+
if (!body) return null;
|
|
38710
|
+
const down = _groundDir.copy(this.controls.orbitPosition).normalize();
|
|
38711
|
+
return {
|
|
38712
|
+
groundKm: this.motionGround.radiusKm(body, down) ?? effectiveRadiusKm(body.data),
|
|
38713
|
+
clearanceKm: this.motionGround.clearanceKm(body)
|
|
38714
|
+
};
|
|
38715
|
+
}
|
|
38232
38716
|
lookSurfaceRadius = (direction) => {
|
|
38233
38717
|
const focused = this.focusController.current;
|
|
38234
38718
|
if (this.holdingCamera || this.focus.aim || !focused) return null;
|
|
38235
|
-
if (this.standingOn(focused)) return 0;
|
|
38236
38719
|
const bo = this.bodyObjects.get(focused.data.id);
|
|
38237
38720
|
if (isModelBearing(focused) || !(bo?.mesh || hasModelSurface(bo))) return null;
|
|
38238
38721
|
const radiusKm = effectiveRadiusKm(focused.data);
|
|
38239
38722
|
return kmToScene(this.motionGround.radiusKm(focused, direction) ?? radiusKm);
|
|
38240
38723
|
};
|
|
38241
|
-
standingOn(focused) {
|
|
38242
|
-
if (isSurfaceFeature(focused)) return this.ctx.getBody(focused.featureAnchor.hostId);
|
|
38243
|
-
if (!this.bodyObjects.get(focused.data.id)?.isLanded) return void 0;
|
|
38244
|
-
const parentId = collisionParentId(focused.data.parentId);
|
|
38245
|
-
return parentId ? this.ctx.getBody(parentId) : void 0;
|
|
38246
|
-
}
|
|
38247
|
-
standingHeight = () => {
|
|
38248
|
-
const focused = this.focusController.current;
|
|
38249
|
-
const host = focused && this.standingOn(focused);
|
|
38250
|
-
const at = host?.position;
|
|
38251
|
-
if (!host || !at) return null;
|
|
38252
|
-
const fp = this.focus.focusTruePos;
|
|
38253
|
-
const cam = this.controls.orbitPosition;
|
|
38254
|
-
const up = _lookUp.set(cam.x + fp[0] - at[0], cam.y + fp[1] - at[1], cam.z + fp[2] - at[2]);
|
|
38255
|
-
const dist = up.length();
|
|
38256
|
-
const groundKm = this.motionGround.radiusKm(host, up.divideScalar(dist));
|
|
38257
|
-
const ground = kmToScene(groundKm ?? effectiveRadiusKm(host.data));
|
|
38258
|
-
return ground > 0 ? (dist - ground) / ground : null;
|
|
38259
|
-
};
|
|
38260
38724
|
onControlsEnd = () => {
|
|
38261
38725
|
this.pendingUrlWrite = true;
|
|
38262
38726
|
this.focusController.clearPendingInitialView();
|
|
@@ -38303,26 +38767,17 @@ var SceneRenderer = class SceneRenderer {
|
|
|
38303
38767
|
}
|
|
38304
38768
|
setSelectedFeature(featureId) {
|
|
38305
38769
|
this.selectedFeatureId = featureId;
|
|
38306
|
-
const nomId =
|
|
38770
|
+
const nomId = this.focusController.current?.data.id;
|
|
38307
38771
|
const bo = nomId ? this.bodyObjects.get(nomId) : void 0;
|
|
38308
38772
|
if (bo) setActiveFeatureLabel(bo, featureId);
|
|
38309
38773
|
}
|
|
38310
|
-
syncLandedNomenclature() {
|
|
38311
|
-
const focused = this.focusController.current;
|
|
38312
|
-
const landingId = (focused ? this.bodyObjects.get(focused.data.id) : void 0)?.isLanded ? focused.data.parentId : null;
|
|
38313
|
-
if (landingId === this.landedNomBodyId) return;
|
|
38314
|
-
this.landedNomBodyId = landingId;
|
|
38315
|
-
if (!landingId) return;
|
|
38316
|
-
const landingBody = this.ctx.getBody(landingId);
|
|
38317
|
-
if (landingBody) this.maybeLoadTexture(landingBody);
|
|
38318
|
-
}
|
|
38319
38774
|
clearUserPromoted() {
|
|
38320
38775
|
this.focusController.promotion.clearUserPromoted();
|
|
38321
38776
|
}
|
|
38322
38777
|
focusedSeatConfigKey() {
|
|
38323
|
-
const focused = this.focusController.current;
|
|
38778
|
+
const focused = this.focusController.subject ?? this.focusController.current;
|
|
38324
38779
|
if (!focused) return null;
|
|
38325
|
-
if (focused.data.objectType !== ObjectType.SPACECRAFT
|
|
38780
|
+
if (focused.data.objectType !== ObjectType.SPACECRAFT) return null;
|
|
38326
38781
|
const parentId = collisionParentId(focused.data.parentId);
|
|
38327
38782
|
const bo = parentId ? this.bodyObjects.get(parentId) : void 0;
|
|
38328
38783
|
if (!bo) return null;
|
|
@@ -38436,7 +38891,6 @@ var SceneRenderer = class SceneRenderer {
|
|
|
38436
38891
|
this.repositionBodies();
|
|
38437
38892
|
this.pointClouds.maybeRebase();
|
|
38438
38893
|
}
|
|
38439
|
-
this.syncLandedNomenclature();
|
|
38440
38894
|
this.seekAwaitsData = seeked && result.focusLoading;
|
|
38441
38895
|
const losingPlace = result.focusUnplaced && !this.focusWasOutOfRange;
|
|
38442
38896
|
this.focusWasOutOfRange = result.focusUnplaced;
|
|
@@ -38454,83 +38908,32 @@ var SceneRenderer = class SceneRenderer {
|
|
|
38454
38908
|
this.focusWasOutOfRange = false;
|
|
38455
38909
|
return this.focusController.focusOnBody(id, zoom, latitude, longitude);
|
|
38456
38910
|
}
|
|
38457
|
-
focusOnFeature(anchor,
|
|
38911
|
+
focusOnFeature(anchor, zoom, move = "turn", view = null, look = LEVEL_LOOK) {
|
|
38458
38912
|
this.disarmLimits();
|
|
38459
38913
|
this.applyJdUpdate();
|
|
38460
38914
|
this.focusWasOutOfRange = false;
|
|
38461
38915
|
const host = this.ctx.getBody(anchor.hostId);
|
|
38462
38916
|
const hostPos = host && this.whereBody(host);
|
|
38463
38917
|
if (!host || !hostPos) return 0;
|
|
38464
|
-
this.focusController
|
|
38465
|
-
|
|
38466
|
-
const fb = makeFeatureBody(anchor, name);
|
|
38467
|
-
this.ctx.bodies.focusFeature = fb;
|
|
38918
|
+
const fc = this.focusController;
|
|
38919
|
+
fc.clearPendingInitialView();
|
|
38468
38920
|
const hostBo = this.bodyObjects.get(host.data.id);
|
|
38469
38921
|
this.selectedFeatureId = anchor.featureId;
|
|
38470
38922
|
if (hostBo) setActiveFeatureLabel(hostBo, anchor.featureId);
|
|
38471
|
-
const seat = this.
|
|
38472
|
-
|
|
38473
|
-
|
|
38474
|
-
if (
|
|
38475
|
-
|
|
38476
|
-
|
|
38477
|
-
|
|
38478
|
-
|
|
38479
|
-
|
|
38480
|
-
|
|
38481
|
-
|
|
38482
|
-
let zz = seat[2] - hostPos[2];
|
|
38483
|
-
const len = Math.hypot(zx, zy, zz);
|
|
38484
|
-
if (len > 1e-9) {
|
|
38485
|
-
zx /= len;
|
|
38486
|
-
zy /= len;
|
|
38487
|
-
zz /= len;
|
|
38488
|
-
} else {
|
|
38489
|
-
zx = 0;
|
|
38490
|
-
zy = 1;
|
|
38491
|
-
zz = 0;
|
|
38492
|
-
}
|
|
38493
|
-
const camPos = mode === "snap" && view ? sphericalToCartesian(seat, view.latitude, view.longitude, view.zoom, quat) : [
|
|
38494
|
-
seat[0] + zx * zoom,
|
|
38495
|
-
seat[1] + zy * zoom,
|
|
38496
|
-
seat[2] + zz * zoom
|
|
38497
|
-
];
|
|
38498
|
-
const s = cartesianToSpherical(camPos, seat, quat);
|
|
38499
|
-
this.callbacks.onCameraPosition?.(s.latitude, s.longitude, s.distance);
|
|
38500
|
-
if (mode === "snap") {
|
|
38501
|
-
this.focusController.setFocusTarget(fb, camPos);
|
|
38502
|
-
const f = this.focus;
|
|
38503
|
-
f.focusTruePos = [...seat];
|
|
38504
|
-
f.focusOriginWorld = [...seat];
|
|
38505
|
-
f.focusTargetWorld = [...seat];
|
|
38506
|
-
f.focusStartTime = -FOCUS_DURATION_MS;
|
|
38507
|
-
f.camOriginWorld = null;
|
|
38508
|
-
f.camTargetWorld = null;
|
|
38509
|
-
f.camTargetOffset = null;
|
|
38510
|
-
f.camOriginOffset = null;
|
|
38511
|
-
f.flyQ0 = null;
|
|
38512
|
-
f.orbitFly = false;
|
|
38513
|
-
f.arcOrbit = false;
|
|
38514
|
-
f.cameraStaysOnBody = false;
|
|
38515
|
-
this.repositionAll();
|
|
38516
|
-
this.pointClouds.rebuildBasis();
|
|
38517
|
-
this.camera.position.set(camPos[0] - seat[0], camPos[1] - seat[1], camPos[2] - seat[2]);
|
|
38518
|
-
this.controls.update();
|
|
38519
|
-
return 0;
|
|
38520
|
-
}
|
|
38521
|
-
if (alreadyOnHost) {
|
|
38522
|
-
const camWorld = this.focusController.cameraTruePos();
|
|
38523
|
-
this.focusController.setFocusTarget(fb);
|
|
38524
|
-
this.focus.focusTruePos = [...seat];
|
|
38525
|
-
this.camera.position.set(camWorld[0] - seat[0], camWorld[1] - seat[1], camWorld[2] - seat[2]);
|
|
38526
|
-
this.repositionAll();
|
|
38527
|
-
this.pointClouds.rebuildBasis();
|
|
38528
|
-
prepareFlyToCamera(this.focus, this.camera, camWorld, camPos, sceneSettings().resolvedReducedMotion);
|
|
38529
|
-
} else {
|
|
38530
|
-
this.focusController.setFocusTarget(fb, camPos);
|
|
38531
|
-
this.focus.orbitFly = true;
|
|
38923
|
+
const seat = featureSeat(anchor, host, hostPos, hostBo, this.sceneJd);
|
|
38924
|
+
const site = surfaceSite(host, seat, hostPos, this.sceneJd);
|
|
38925
|
+
if (move === "turn") return fc.panToSurfacePoint(host, site, zoom);
|
|
38926
|
+
if (move === "frame" || !view) return fc.arriveAbove(host, site, zoom, void 0, move === "snap", look);
|
|
38927
|
+
if (underHalfRadius(host, view.zoom)) {
|
|
38928
|
+
const offset = sphericalToCartesian([
|
|
38929
|
+
0,
|
|
38930
|
+
0,
|
|
38931
|
+
0
|
|
38932
|
+
], view.latitude, view.longitude, view.zoom);
|
|
38933
|
+
view = viewOverSite(site, new Vector3(...offset), siteQuat(host, this.sceneJd));
|
|
38532
38934
|
}
|
|
38533
|
-
|
|
38935
|
+
fc.snapOntoGround(host, view);
|
|
38936
|
+
return 0;
|
|
38534
38937
|
}
|
|
38535
38938
|
pickFocusedModel(ndcX, ndcY) {
|
|
38536
38939
|
if (!this.overlayActive) return null;
|
|
@@ -38551,13 +38954,12 @@ var SceneRenderer = class SceneRenderer {
|
|
|
38551
38954
|
}
|
|
38552
38955
|
overlayModelBo() {
|
|
38553
38956
|
const withModel = (bo) => bo?.model && isDrawn(bo) ? bo : null;
|
|
38554
|
-
const focusBody = this.focusController.current;
|
|
38957
|
+
const focusBody = this.focusController.subject ?? this.focusController.current;
|
|
38555
38958
|
if (!focusBody) {
|
|
38556
38959
|
const centerId = this.travelFocus?.centerId;
|
|
38557
38960
|
return withModel(centerId ? this.bodyObjects.get(centerId) : void 0);
|
|
38558
38961
|
}
|
|
38559
|
-
const
|
|
38560
|
-
const bo = this.bodyObjects.get(modelId);
|
|
38962
|
+
const bo = this.bodyObjects.get(focusBody.data.id);
|
|
38561
38963
|
const own = withModel(bo);
|
|
38562
38964
|
if (own) return own;
|
|
38563
38965
|
if (focusBody.data.orbitalSource === OrbitalSource.SPICE_PROBE && !bo?.isLanded) {
|
|
@@ -41330,6 +41732,14 @@ var SpaceMap = class {
|
|
|
41330
41732
|
set focusedBody(value) {
|
|
41331
41733
|
set(this.#focusedBody, value);
|
|
41332
41734
|
}
|
|
41735
|
+
#cameraBody = state();
|
|
41736
|
+
get cameraBody() {
|
|
41737
|
+
return get(this.#cameraBody);
|
|
41738
|
+
}
|
|
41739
|
+
set cameraBody(value) {
|
|
41740
|
+
set(this.#cameraBody, value);
|
|
41741
|
+
}
|
|
41742
|
+
movingToFeature = false;
|
|
41333
41743
|
attribution = null;
|
|
41334
41744
|
objects = new MapObjectCollection(() => this.requireRenderer().extensions, () => this.requireCanvas());
|
|
41335
41745
|
dragRotate;
|
|
@@ -41426,9 +41836,7 @@ var SpaceMap = class {
|
|
|
41426
41836
|
}
|
|
41427
41837
|
getFocusedBody() {
|
|
41428
41838
|
const focused = this.focusedBody;
|
|
41429
|
-
|
|
41430
|
-
const body = isSurfaceFeature(focused) ? this.ctx.getBody(focused.featureAnchor.hostId) : focused;
|
|
41431
|
-
return body && bodyView(body);
|
|
41839
|
+
return focused && bodyView(focused);
|
|
41432
41840
|
}
|
|
41433
41841
|
getChildren(id) {
|
|
41434
41842
|
return [...this.ctx.bodies.getChildren(id) ?? []];
|
|
@@ -41652,11 +42060,12 @@ var SpaceMap = class {
|
|
|
41652
42060
|
notify: (notice) => this.emit("notice", notice),
|
|
41653
42061
|
dismiss: (topic) => this.emit("noticedismiss", topic)
|
|
41654
42062
|
},
|
|
41655
|
-
onFocusChange: (body) => {
|
|
42063
|
+
onFocusChange: (body, orbited) => {
|
|
41656
42064
|
const initial = this.initialFocusPending;
|
|
41657
42065
|
this.initialFocusPending = false;
|
|
41658
42066
|
this.focusedBody = body;
|
|
41659
|
-
|
|
42067
|
+
this.cameraBody = orbited;
|
|
42068
|
+
const feature = this.movingToFeature;
|
|
41660
42069
|
this.emit("focuschange", {
|
|
41661
42070
|
body: this.getFocusedBody(),
|
|
41662
42071
|
initial,
|
|
@@ -41671,7 +42080,7 @@ var SpaceMap = class {
|
|
|
41671
42080
|
},
|
|
41672
42081
|
onIdle: () => this.emit("idle"),
|
|
41673
42082
|
onCameraPosition: (latitude, longitude, zoom, look = LEVEL_LOOK) => {
|
|
41674
|
-
const body = this.
|
|
42083
|
+
const body = this.cameraBody?.data.id;
|
|
41675
42084
|
if (body === void 0) return;
|
|
41676
42085
|
this.emit("camera", {
|
|
41677
42086
|
body,
|
|
@@ -41904,7 +42313,7 @@ var SpaceMap = class {
|
|
|
41904
42313
|
});
|
|
41905
42314
|
}
|
|
41906
42315
|
panTo(target) {
|
|
41907
|
-
return this.toFeature(this.targetBody(target), target.feature, "
|
|
42316
|
+
return this.toFeature(this.targetBody(target), target.feature, "turn");
|
|
41908
42317
|
}
|
|
41909
42318
|
async jumpTo(target) {
|
|
41910
42319
|
const body = this.targetBody(target);
|
|
@@ -41937,8 +42346,7 @@ var SpaceMap = class {
|
|
|
41937
42346
|
}
|
|
41938
42347
|
if (target.feature) {
|
|
41939
42348
|
const { featureId, lat, lon, diameterM } = target.feature;
|
|
41940
|
-
this.focusOnFeature(body, featureId, lat, lon, diameterM, null,
|
|
41941
|
-
this.renderer?.setLook(lookOf(target));
|
|
42349
|
+
this.focusOnFeature(body, featureId, lat, lon, diameterM, "snap", null, lookOf(target));
|
|
41942
42350
|
return;
|
|
41943
42351
|
}
|
|
41944
42352
|
const camera = this.getCamera();
|
|
@@ -41946,7 +42354,7 @@ var SpaceMap = class {
|
|
|
41946
42354
|
}
|
|
41947
42355
|
getCamera() {
|
|
41948
42356
|
const renderer = this.renderer;
|
|
41949
|
-
const body = this.
|
|
42357
|
+
const body = this.cameraBody?.data.id;
|
|
41950
42358
|
if (!renderer || body === void 0) return null;
|
|
41951
42359
|
const { latitude, longitude, distance } = renderer.getCameraState();
|
|
41952
42360
|
return {
|
|
@@ -41980,6 +42388,9 @@ var SpaceMap = class {
|
|
|
41980
42388
|
getLimits() {
|
|
41981
42389
|
return { ...this.limits };
|
|
41982
42390
|
}
|
|
42391
|
+
getClosestApproach() {
|
|
42392
|
+
return this.renderer?.groundUnderCamera() ?? null;
|
|
42393
|
+
}
|
|
41983
42394
|
applyLimits() {
|
|
41984
42395
|
const l = this.limits;
|
|
41985
42396
|
const band = {};
|
|
@@ -42019,9 +42430,9 @@ var SpaceMap = class {
|
|
|
42019
42430
|
onTouchMove = (e) => {
|
|
42020
42431
|
this.cooperative?.allowsTouchDrag(e.touches.length);
|
|
42021
42432
|
};
|
|
42022
|
-
toFeature(body, feature,
|
|
42433
|
+
toFeature(body, feature, move) {
|
|
42023
42434
|
const { featureId, lat, lon, diameterM } = feature;
|
|
42024
|
-
return this.settled(this.focusOnFeature(body, featureId, lat, lon, diameterM,
|
|
42435
|
+
return this.settled(this.focusOnFeature(body, featureId, lat, lon, diameterM, move));
|
|
42025
42436
|
}
|
|
42026
42437
|
settled(ms) {
|
|
42027
42438
|
return new Promise((resolve) => {
|
|
@@ -42071,7 +42482,7 @@ var SpaceMap = class {
|
|
|
42071
42482
|
if (!renderer || !vantage || vantage.kept) return null;
|
|
42072
42483
|
return (body) => renderer.viewFrom(body.data.id, vantage.offset) ?? {};
|
|
42073
42484
|
}
|
|
42074
|
-
focusOnFeature(bodyId, featureId, lat, lon, diameterM,
|
|
42485
|
+
focusOnFeature(bodyId, featureId, lat, lon, diameterM, move = "turn", view = null, look) {
|
|
42075
42486
|
const hostBody = this.ctx.getBody(bodyId);
|
|
42076
42487
|
if (!hostBody) return 0;
|
|
42077
42488
|
const idealScene = kmToScene(featureDiameterM(diameterM, effectiveRadiusKm(hostBody.data)) * 4 / 1e3);
|
|
@@ -42085,16 +42496,20 @@ var SpaceMap = class {
|
|
|
42085
42496
|
lon,
|
|
42086
42497
|
diameterM
|
|
42087
42498
|
};
|
|
42088
|
-
|
|
42499
|
+
this.movingToFeature = true;
|
|
42500
|
+
try {
|
|
42501
|
+
return this.renderer?.focusOnFeature(anchor, zoom, move, view, look) ?? 0;
|
|
42502
|
+
} finally {
|
|
42503
|
+
this.movingToFeature = false;
|
|
42504
|
+
}
|
|
42089
42505
|
}
|
|
42090
42506
|
setFocusTarget(body, camPos) {
|
|
42091
42507
|
this.renderer?.setFocusTarget(body, camPos);
|
|
42092
42508
|
}
|
|
42093
42509
|
getPose() {
|
|
42094
42510
|
const renderer = this.renderer;
|
|
42095
|
-
const
|
|
42096
|
-
if (!renderer ||
|
|
42097
|
-
const body = focused.data.id;
|
|
42511
|
+
const body = this.cameraBody?.data.id;
|
|
42512
|
+
if (!renderer || body === void 0) return null;
|
|
42098
42513
|
return {
|
|
42099
42514
|
position: {
|
|
42100
42515
|
body,
|