release: bump version to 0.40.1

This commit is contained in:
linkong
2026-04-24 17:28:03 +08:00
parent 86807f6af6
commit e9464a9833
8 changed files with 115 additions and 41 deletions

View File

@@ -79,6 +79,9 @@ const LOCKED_HALO_BASE_OPACITY = 0.54;
const LOCKED_HALO_OFFSET = 0.0014;
const LOCKED_HALO_CORE_PIXEL_RADIUS = 8;
const LOCKED_HALO_PIXEL_RADIUS = 24;
const FILLED_TEXTURE_RADIUS_RATIO = 0.28;
const LOCKED_RING_IDLE_SCALE = 0.68;
const LOCKED_RING_IDLE_OPACITY = 0.92;
const EARTH_RADIUS_KM = 6378.137;
const GROUND_FOOTPRINT_MIN_ELEVATION_DEG = 25;
const GROUND_FOOTPRINT_RADIUS_OFFSET = 0.72;
@@ -345,6 +348,25 @@ function createRingTexture(innerRadius, outerRadius, color = "#ffffff") {
return texture;
}
function createFilledCircleTexture(color = "#ffcc00") {
const size = DOT_TEXTURE_SIZE * 2;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
const center = size / 2;
const radius = size * FILLED_TEXTURE_RADIUS_RATIO;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(center, center, radius, 0, Math.PI * 2);
ctx.fill();
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
}
export function createSatellites(scene, earthObj) {
initSatelliteScene(scene, earthObj);
const dotTexture = createDotTexture();
@@ -1156,14 +1178,14 @@ export function isSatelliteFrontFacing(index, camera = cameraRef) {
);
}
function createLockedHaloMaterial() {
function createLockedHaloMaterial(color = "#ffbf47") {
return new THREE.ShaderMaterial({
transparent: true,
depthTest: false,
depthWrite: false,
side: THREE.DoubleSide,
uniforms: {
uColor: { value: new THREE.Color(0xffbf47) },
uColor: { value: new THREE.Color(color) },
uOpacity: { value: LOCKED_HALO_BASE_OPACITY },
},
vertexShader: `
@@ -1196,10 +1218,8 @@ function createGroundFootprintMaterial() {
return new THREE.ShaderMaterial({
transparent: true,
side: THREE.DoubleSide,
depthTest: false,
depthWrite: false,
polygonOffset: true,
polygonOffsetFactor: -2,
polygonOffsetUnits: -2,
uniforms: {
uColor: { value: new THREE.Color(0xffffff) },
uOpacity: { value: 0.46 },
@@ -1288,6 +1308,12 @@ function createGroundFootprintMaterial() {
}
vec3 worldNormal = normalize(vWorldPosition);
vec3 toCam = normalize(cameraPosition - vWorldPosition);
float facing = dot(worldNormal, toCam);
float limbFade = smoothstep(-0.08, 0.12, facing);
if (limbFade <= 0.0) discard;
alpha *= limbFade;
vec3 sunDir = normalize(uSunDirectionWorld);
float sunFacing = dot(worldNormal, sunDir);
float daylight = clamp(sunFacing * 0.5 + 0.5, 0.0, 1.0);
@@ -1720,13 +1746,13 @@ function updateGroundFootprintTransform(position) {
}
}
function showSelfGlowStyle(position) {
function showSelfGlowStyle(position, color = "#ffd25a") {
const dotGeometry = new THREE.CircleGeometry(
LOCKED_HALO_CORE_RADIUS,
LOCKED_HALO_CORE_SEGMENTS,
);
const dotMaterial = new THREE.MeshBasicMaterial({
color: 0xffd25a,
color: new THREE.Color(color),
transparent: true,
opacity: 0.96,
depthTest: false,
@@ -1740,7 +1766,7 @@ function showSelfGlowStyle(position) {
lockedHaloMesh = new THREE.Mesh(
new THREE.CircleGeometry(LOCKED_HALO_RADIUS, 64),
createLockedHaloMaterial(),
createLockedHaloMaterial(color),
);
lockedHaloMesh.renderOrder = SATELLITE_CONFIG.overlayRenderOrder + 1;
sceneRef?.add(lockedHaloMesh);
@@ -1750,7 +1776,7 @@ function showSelfGlowStyle(position) {
function showGroundFootprintStyle(position) {
if (!earthObjRef) return;
lockedGroundFootprintMesh = new THREE.Group();
lockedGroundFootprintMesh.renderOrder = SATELLITE_CONFIG.overlayRenderOrder - 1;
lockedGroundFootprintMesh.renderOrder = 0;
const fill = new THREE.Mesh(
new THREE.BufferGeometry(),
createGroundFootprintMaterial(),
@@ -1766,7 +1792,7 @@ function showIridiumReservedStyle(position) {
lockedIridiumFootprintMesh = createIridiumFootprintAdapter({
earthObj: earthObjRef,
earthRadiusWorld: CONFIG.earthRadius,
renderOrder: SATELLITE_CONFIG.overlayRenderOrder - 1,
renderOrder: 0,
});
updateIridiumReservedStyle(position);
}
@@ -1782,14 +1808,17 @@ function updateIridiumReservedStyle(position) {
});
}
function createRingSprite(position, isLocked = false) {
function createRingSprite(position, isLocked = false, color = "#ffcc00") {
if (!earthObjRef) return null;
const ringTexture = createRingTexture(
8,
12,
isLocked ? "#ffcc00" : "#ffffff",
isLocked ? color : "#ffffff",
);
const filledTexture = isLocked
? createFilledCircleTexture(color)
: null;
const spriteMaterial = new THREE.SpriteMaterial({
map: ringTexture,
transparent: true,
@@ -1799,6 +1828,8 @@ function createRingSprite(position, isLocked = false) {
});
const sprite = new THREE.Sprite(spriteMaterial);
sprite.userData.ringTexture = ringTexture;
sprite.userData.filledTexture = filledTexture;
sprite.position.copy(position);
sprite.scale.set(SATELLITE_CONFIG.ringSize, SATELLITE_CONFIG.ringSize, 1);
sprite.renderOrder = SATELLITE_CONFIG.overlayRenderOrder;
@@ -1806,6 +1837,26 @@ function createRingSprite(position, isLocked = false) {
return sprite;
}
function isLockedSatelliteHovered() {
return (
lockedSatelliteIndex !== null &&
hoveredSatelliteIndex !== null &&
hoveredSatelliteIndex === lockedSatelliteIndex
);
}
function updateLockedMarkerVisual(isHovered) {
if (!lockedRingSprite?.material) return;
const nextTexture = isHovered
? lockedRingSprite.userData.ringTexture
: lockedRingSprite.userData.filledTexture || lockedRingSprite.userData.ringTexture;
if (lockedRingSprite.material.map !== nextTexture) {
lockedRingSprite.material.map = nextTexture;
lockedRingSprite.material.needsUpdate = true;
}
}
function createRelatedSatelliteSprite(position, color = "#7dd3fc") {
if (!earthObjRef) return null;
@@ -1830,11 +1881,12 @@ export function showHoverRing(position, isLocked = false) {
if (!earthObjRef || !position) return null;
if (isLocked) {
const lockedProps = getLockedSatelliteProperties() || {};
const legendColor = getSatelliteLegendRule(lockedProps).color;
hideLockedRing();
lockedRingSprite = createRingSprite(position, true);
const presentationMode = getSatellitePresentationMode(
getLockedSatelliteProperties() || {},
);
lockedRingSprite = createRingSprite(position, true, legendColor);
updateLockedMarkerVisual(isLockedSatelliteHovered());
const presentationMode = getSatellitePresentationMode(lockedProps);
if (
presentationMode ===
SATELLITE_PRESENTATION_MODES.STARLINK_GROUND_FOOTPRINT
@@ -1845,7 +1897,7 @@ export function showHoverRing(position, isLocked = false) {
) {
showIridiumReservedStyle(position);
} else {
showSelfGlowStyle(position);
showSelfGlowStyle(position, legendColor);
}
return lockedRingSprite;
}
@@ -1864,7 +1916,15 @@ export function hideHoverRings() {
export function hideLockedRing() {
if (lockedRingSprite) {
const ringTexture = lockedRingSprite.userData?.ringTexture;
const filledTexture = lockedRingSprite.userData?.filledTexture;
disposeObject3D(lockedRingSprite);
if (ringTexture && ringTexture !== lockedRingSprite.material?.map) {
ringTexture.dispose();
}
if (filledTexture && filledTexture !== lockedRingSprite.material?.map) {
filledTexture.dispose();
}
lockedRingSprite = null;
}
clearLockedSatelliteStyleVisuals();
@@ -1885,21 +1945,28 @@ export function updateLockedRingPosition(position) {
showHoverRing(position, true);
}
if (lockedRingSprite) {
const isHovered = isLockedSatelliteHovered();
updateLockedMarkerVisual(isHovered);
lockedRingSprite.position.copy(position);
const ringPulse = getBreathingPulse(breathingPhase);
const breathScale =
1 +
(ringPulse * 2 - 1) * SATELLITE_CONFIG.breathingScaleAmplitude;
const markerSize = isHovered
? SATELLITE_CONFIG.ringSize
: SATELLITE_CONFIG.ringSize * LOCKED_RING_IDLE_SCALE;
lockedRingSprite.scale.set(
SATELLITE_CONFIG.ringSize * breathScale,
SATELLITE_CONFIG.ringSize * breathScale,
markerSize * breathScale,
markerSize * breathScale,
1,
);
lockedRingSprite.material.opacity =
SATELLITE_CONFIG.breathingOpacityMin +
ringPulse *
(SATELLITE_CONFIG.breathingOpacityMax -
SATELLITE_CONFIG.breathingOpacityMin);
isHovered
? SATELLITE_CONFIG.breathingOpacityMin +
ringPulse *
(SATELLITE_CONFIG.breathingOpacityMax -
SATELLITE_CONFIG.breathingOpacityMin)
: LOCKED_RING_IDLE_OPACITY;
}
if (lockedDotSprite) {