release: bump version to 0.31.3

This commit is contained in:
rayd1o
2026-04-22 03:52:09 +08:00
parent 003a46ac30
commit 437efc848c
19 changed files with 1516 additions and 321 deletions

View File

@@ -10,6 +10,7 @@ import {
BGP_CONFIG,
CRUISE_CONFIG,
ROTATION_MODE,
SCENE_LIGHT_CONFIG,
} from "./constants.js";
import { vector3ToLatLon, screenToEarthCoords } from "./utils.js";
import {
@@ -36,7 +37,7 @@ import {
clearEarthTexture,
setEarthSunDirection,
} from "./earth.js";
import { registerTerrainMesh, clearTerrainData } from "./terrain.js";
import { registerTerrainMesh, clearTerrainData, sampleElevationAt } from "./terrain.js";
import {
initCelestialLayer,
updateCelestialLayer,
@@ -45,6 +46,7 @@ import {
getSunDirection,
setCelestialOrientation,
setCelestialFollow,
setCelestialDayNightEnabled,
} from "./celestial.js";
import {
loadGeoJSONFromPath,
@@ -129,12 +131,17 @@ import {
getAutoRotate,
getRotationMode,
getShowTerrain,
getStartupLoadLayers,
setAutoRotate,
applyImmediateView,
focusEarthView,
getZoomLevel,
teardownControls,
} from "./controls.js";
import {
createLayerStartupTaskMap,
resolveStartupMessage,
} from "./layer-startup-tasks.js";
import {
setLayerButtonState,
} from "./layer-button-state.js";
@@ -1224,10 +1231,13 @@ export function init() {
registerTerrainMesh(createTerrain(earthObj));
initCelestialLayer(scene, {
camera,
ambientLight: sceneLights?.ambientLight ?? null,
sunLight: sceneLights?.sunLight ?? null,
backLight: sceneLights?.backLight ?? null,
pointLight: sceneLights?.pointLight ?? null,
earth: earthObj,
});
setCelestialDayNightEnabled(true);
createGridLines(scene, earthObj);
createSatellites(scene, earthObj);
@@ -1258,21 +1268,43 @@ function registerGlobalApi() {
}
function addLights() {
const ambientLight = new THREE.AmbientLight(0x404060);
const ambientLight = new THREE.AmbientLight(SCENE_LIGHT_CONFIG.ambient.color);
ambientLight.intensity = SCENE_LIGHT_CONFIG.ambient.intensity;
scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xffffff, 1.2);
sunLight.position.set(5, 3, 5);
const sunLight = new THREE.DirectionalLight(
SCENE_LIGHT_CONFIG.sun.color,
SCENE_LIGHT_CONFIG.sun.intensity,
);
sunLight.position.set(
SCENE_LIGHT_CONFIG.sun.position.x,
SCENE_LIGHT_CONFIG.sun.position.y,
SCENE_LIGHT_CONFIG.sun.position.z,
);
sunLight.target.position.set(0, 0, 0);
scene.add(sunLight);
scene.add(sunLight.target);
const backLight = new THREE.DirectionalLight(0x446688, 0.3);
backLight.position.set(-5, 0, -5);
const backLight = new THREE.DirectionalLight(
SCENE_LIGHT_CONFIG.back.color,
SCENE_LIGHT_CONFIG.back.intensity,
);
backLight.position.set(
SCENE_LIGHT_CONFIG.back.position.x,
SCENE_LIGHT_CONFIG.back.position.y,
SCENE_LIGHT_CONFIG.back.position.z,
);
scene.add(backLight);
const pointLight = new THREE.PointLight(0xffffff, 0.4);
pointLight.position.set(10, 10, 10);
const pointLight = new THREE.PointLight(
SCENE_LIGHT_CONFIG.point.color,
SCENE_LIGHT_CONFIG.point.intensity,
);
pointLight.position.set(
SCENE_LIGHT_CONFIG.point.position.x,
SCENE_LIGHT_CONFIG.point.position.y,
SCENE_LIGHT_CONFIG.point.position.z,
);
scene.add(pointLight);
return {
@@ -1364,100 +1396,54 @@ async function loadData() {
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
await yieldFrame(16);
// Step 2 — Landing points
if (cablesEnabled) {
setLoadingMessage("正在加载登陆点...");
await yieldFrame(12);
try {
await loadLandingPoints(scene, earth, { silent: true });
} catch (err) {
errors.push({ label: "登陆点", reason: err });
}
const startupLoaders = createLayerStartupTaskMap({
scene,
earth,
setLoadingMessage,
yieldFrame,
refreshLegend,
setLegendItems,
updateCableToggleUi,
updateSatelliteToggleUi,
updateBGPHud,
getShowBGP,
getInitialSatelliteLoadLimit,
shouldHydrateFullSatelliteSet,
scheduleSatellitePositionWarmup,
hydrateAllSatellitesInBackground,
syncBGPKnownEventIds: () => ensureBGPCruiseAdapter().syncKnownEventIds(),
isCancelled: () => loadToken !== currentLoadToken || destroyed,
isCablesEnabled: () => cablesEnabled,
isSatellitesEnabled: () => satellitesEnabled,
nextSatelliteHydrationToken: () => ++satelliteHydrationToken,
getSatelliteHydrationToken: () => satelliteHydrationToken,
reportError: (label, reason) => {
errors.push({ label, reason });
},
});
const startupLayers = getStartupLoadLayers();
const startupLoadQueue = startupLayers
.map((layer) => ({
layer,
run: startupLoaders[layer.id],
}))
.filter((entry) => typeof entry.run === "function");
for (const entry of startupLoadQueue) {
await entry.run(entry.layer);
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
await yieldFrame(16);
}
// Step 3 — Cables
if (cablesEnabled) {
setLoadingMessage("正在加载海缆...");
await yieldFrame(12);
try {
const cableCount = await loadGeoJSONFromPath(scene, earth, {
silent: true,
});
if (loadToken === currentLoadToken && cablesEnabled) {
toggleCables(true);
updateCableToggleUi(true);
setLegendItems("cables", getCableLegendItems());
refreshLegend();
}
} catch (err) {
errors.push({ label: "海缆", reason: err });
}
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
await yieldFrame(16);
}
// Step 4 — Satellites
if (satellitesEnabled) {
setLoadingMessage("正在加载卫星...");
await yieldFrame(12);
try {
clearSatelliteData();
const loadResult = await loadSatellites({
limit: getInitialSatelliteLoadLimit(),
});
if (loadToken === currentLoadToken && satellitesEnabled) {
updateSatelliteToggleUi(true, loadResult.count);
setLegendItems("satellites", getSatelliteLegendItems());
refreshLegend();
scheduleSatellitePositionWarmup(() => {
if (
loadToken === currentLoadToken &&
satellitesEnabled &&
!destroyed
) {
toggleSatellites(true);
}
});
if (shouldHydrateFullSatelliteSet(loadResult)) {
const hydrationToken = ++satelliteHydrationToken;
hydrateAllSatellitesInBackground(
() =>
hydrationToken === satelliteHydrationToken &&
loadToken === currentLoadToken &&
satellitesEnabled &&
!destroyed,
);
}
}
} catch (err) {
errors.push({ label: "卫星", reason: err });
}
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
await yieldFrame(16);
}
// Step 5 — BGP
setLoadingMessage("正在加载BGP态势...");
await yieldFrame(12);
try {
const bgpResult = await loadBGPAnomalies(scene, earth);
if (loadToken === currentLoadToken) {
toggleBGP(true);
updateBGPHud(bgpResult);
ensureBGPCruiseAdapter().syncKnownEventIds();
}
} catch (err) {
errors.push({ label: "BGP态势", reason: err });
}
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
await yieldFrame(16);
// Step 6 — Terrain (if enabled)
if (getShowTerrain()) {
setLoadingMessage("正在渲染地形...");
const terrainLayer = startupLayers.find((layer) => layer.id === "terrain");
const terrainMessage = resolveStartupMessage(
terrainLayer,
"load",
"正在渲染地形...",
);
setLoadingMessage(terrainMessage);
await yieldFrame(24);
}
@@ -1493,7 +1479,14 @@ export async function reloadData() {
await loadData();
}
export async function setCablesEnabled(enabled) {
export function getSatellitesEnabled() {
return satellitesEnabled;
}
export async function setCablesEnabled(
enabled,
{ suppressStatus = false, suppressLoadingUi = false } = {},
) {
if (enabled === cablesEnabled) {
updateCableToggleUi(enabled);
return getCableLines().length;
@@ -1502,32 +1495,47 @@ export async function setCablesEnabled(enabled) {
if (!enabled) {
clearSelectionAndInfo();
disableCables();
showStatusMessage("线缆已隐藏", "info");
if (!suppressStatus) {
showStatusMessage("线缆已隐藏", "info");
}
return 0;
}
setLoadingMessage("正在加载线缆数据...");
setLoading(true);
hideError();
if (!suppressLoadingUi) {
setLoadingMessage("正在加载线缆数据...");
setLoading(true);
hideError();
}
try {
const cableCount = await ensureCablesEnabled();
showStatusMessage("线缆已显示", "info");
if (!suppressStatus) {
showStatusMessage("线缆已显示", "info");
}
return cableCount;
} catch (error) {
cablesEnabled = false;
clearCableData(getEarth());
updateCableToggleUi(false);
const message = `线缆加载失败: ${error?.message || String(error)}`;
showError(message);
showStatusMessage(message, "error");
if (!suppressLoadingUi) {
showError(message);
}
if (!suppressStatus) {
showStatusMessage(message, "error");
}
throw error;
} finally {
setLoading(false);
if (!suppressLoadingUi) {
setLoading(false);
}
}
}
export async function setSatellitesEnabled(enabled) {
export async function setSatellitesEnabled(
enabled,
{ suppressStatus = false, suppressLoadingUi = false } = {},
) {
if (enabled === satellitesEnabled) {
updateSatelliteToggleUi(enabled);
return getSatelliteCount();
@@ -1539,24 +1547,34 @@ export async function setSatellitesEnabled(enabled) {
return 0;
}
setLoadingMessage("正在加载卫星数据...");
setLoading(true);
hideError();
if (!suppressLoadingUi) {
setLoadingMessage("正在加载卫星数据...");
setLoading(true);
hideError();
}
try {
const satelliteCount = await ensureSatellitesEnabled();
showStatusMessage("卫星已显示", "info");
if (!suppressStatus) {
showStatusMessage("卫星已显示", "info");
}
return satelliteCount;
} catch (error) {
satellitesEnabled = false;
resetSatelliteState();
updateSatelliteToggleUi(false, 0);
const message = `卫星加载失败: ${error?.message || String(error)}`;
showError(message);
showStatusMessage(message, "error");
if (!suppressLoadingUi) {
showError(message);
}
if (!suppressStatus) {
showStatusMessage(message, "error");
}
throw error;
} finally {
setLoading(false);
if (!suppressLoadingUi) {
setLoading(false);
}
}
}
@@ -1618,7 +1636,10 @@ function getFrontFacingCables(cableLines) {
scratchCableDirection
.subVectors(scratchCableCenter, earth.position)
.normalize();
return scratchCameraToEarth.dot(scratchCableDirection) > 0;
return (
scratchCameraToEarth.dot(scratchCableDirection) >
SATELLITE_CONFIG.frontFacingDotThreshold
);
});
}
@@ -1634,7 +1655,10 @@ function getFrontFacingBGPMarkers(markers) {
scratchBGPDirection
.subVectors(scratchBGPWorldPosition, earth.position)
.normalize();
return scratchCameraToEarth.dot(scratchBGPDirection) > 0;
return (
scratchCameraToEarth.dot(scratchBGPDirection) >
SATELLITE_CONFIG.frontFacingDotThreshold
);
});
}
@@ -1793,10 +1817,16 @@ function onMouseMove(event) {
if (earthPoint) {
const coords = vector3ToLatLon(earthPoint);
updateCoordinatesDisplay(coords.lat, coords.lon, coords.alt);
const elevMeters = sampleElevationAt(coords.lat, coords.lon);
const elevText = elevMeters !== null
? elevMeters >= 1000
? `${(elevMeters / 1000).toFixed(2)} km`
: `${Math.round(elevMeters)} m`
: "—";
showTooltip(
event.clientX + TOOLTIP_COORDS_OFFSET,
event.clientY + TOOLTIP_COORDS_OFFSET,
`纬度: ${coords.lat}°<br>经度: ${coords.lon}°<br>海拔: ${coords.alt.toFixed(1)} km`,
`纬度: ${coords.lat}°<br>经度: ${coords.lon}°<br>海拔: ${elevText}`,
);
} else {
hideTooltip();