release: bump version to 0.40.3
This commit is contained in:
@@ -283,7 +283,9 @@ export const SATELLITE_CONFIG = {
|
||||
displayAltitudeOffset: 8,
|
||||
frontFacingDotThreshold: 0.015,
|
||||
overlayRenderOrder: 12,
|
||||
dotSize: 2.8,
|
||||
dotBaseSize: 2.8,
|
||||
dotBackdropScale: 1.28,
|
||||
dotZoomScalePower: 1,
|
||||
ringSize: 0.07,
|
||||
apiPath: '/api/v1/visualization/geo/satellites',
|
||||
breathingSpeed: 0.08,
|
||||
|
||||
@@ -81,8 +81,10 @@ 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_HOVER_SCALE = 1.32;
|
||||
const LOCKED_RING_HOVER_LINE_WIDTH = 5;
|
||||
const HOVER_RING_LINE_WIDTH = 3;
|
||||
const LOCKED_RING_IDLE_OPACITY = 0.92;
|
||||
const DOT_ZOOM_SCALE_POWER = 1;
|
||||
const EARTH_RADIUS_KM = 6378.137;
|
||||
const GROUND_FOOTPRINT_MIN_ELEVATION_DEG = 25;
|
||||
const GROUND_FOOTPRINT_RADIUS_OFFSET = 0.72;
|
||||
@@ -112,6 +114,71 @@ const satelliteSunDirection = new THREE.Vector3(1, 0.2, 0.4).normalize();
|
||||
|
||||
export let breathingPhase = 0;
|
||||
|
||||
function getPointPixelRatio() {
|
||||
return typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
||||
}
|
||||
|
||||
function getSatelliteDotBaseSize() {
|
||||
return SATELLITE_CONFIG.dotBaseSize;
|
||||
}
|
||||
|
||||
function getSatelliteDotBackdropSize() {
|
||||
return getSatelliteDotBaseSize() * SATELLITE_CONFIG.dotBackdropScale;
|
||||
}
|
||||
|
||||
function getSatelliteDotZoomScale(cameraDistance) {
|
||||
return Math.pow(CONFIG.defaultCameraZ / Math.max(cameraDistance, 1), SATELLITE_CONFIG.dotZoomScalePower);
|
||||
}
|
||||
|
||||
function createSatellitePointMaterial({
|
||||
texture,
|
||||
size,
|
||||
opacity = 1,
|
||||
useVertexColor = true,
|
||||
baseColor = 0xffffff,
|
||||
alphaTest = 0.04,
|
||||
}) {
|
||||
return new THREE.ShaderMaterial({
|
||||
uniforms: {
|
||||
pointTexture: { value: texture },
|
||||
size: { value: size * getPointPixelRatio() },
|
||||
opacity: { value: opacity },
|
||||
baseColor: { value: new THREE.Color(baseColor) },
|
||||
},
|
||||
transparent: true,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
vertexShader: `
|
||||
uniform float size;
|
||||
uniform vec3 baseColor;
|
||||
attribute float alpha;
|
||||
${useVertexColor ? "attribute vec3 color;" : ""}
|
||||
varying float vAlpha;
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vAlpha = alpha;
|
||||
vColor = ${useVertexColor ? "color" : "baseColor"};
|
||||
gl_PointSize = size;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform sampler2D pointTexture;
|
||||
uniform float opacity;
|
||||
varying float vAlpha;
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture2D(pointTexture, gl_PointCoord);
|
||||
float alphaValue = texel.a * opacity * vAlpha;
|
||||
if (alphaValue < ${alphaTest.toFixed(3)}) discard;
|
||||
gl_FragColor = vec4(vColor * texel.rgb, alphaValue);
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
const SATELLITE_LEGEND_RULES = [
|
||||
{
|
||||
key: "equatorial",
|
||||
@@ -197,9 +264,22 @@ export function updateBreathingPhase(deltaTime = 16) {
|
||||
export function updateSatellitePointSize() {
|
||||
if (!satellitePoints || !cameraRef) return;
|
||||
const camDist = cameraRef.position.length();
|
||||
const zoomScale = Math.pow(CONFIG.defaultCameraZ / Math.max(camDist, 1), DOT_ZOOM_SCALE_POWER);
|
||||
satellitePoints.material.size = SATELLITE_CONFIG.dotSize * zoomScale;
|
||||
satelliteBackdropPoints.material.size = SATELLITE_CONFIG.dotSize * 1.28 * zoomScale;
|
||||
const zoomScale = getSatelliteDotZoomScale(camDist);
|
||||
const pointPixelRatio = getPointPixelRatio();
|
||||
const dotSize = getSatelliteDotBaseSize() * zoomScale;
|
||||
const backdropSize = getSatelliteDotBackdropSize() * zoomScale;
|
||||
const shaderDotSize = dotSize * pointPixelRatio;
|
||||
const shaderBackdropSize = backdropSize * pointPixelRatio;
|
||||
if (satellitePoints.material.uniforms?.size) {
|
||||
satellitePoints.material.uniforms.size.value = shaderDotSize;
|
||||
} else {
|
||||
satellitePoints.material.size = dotSize;
|
||||
}
|
||||
if (satelliteBackdropPoints.material.uniforms?.size) {
|
||||
satelliteBackdropPoints.material.uniforms.size.value = shaderBackdropSize;
|
||||
} else {
|
||||
satelliteBackdropPoints.material.size = backdropSize;
|
||||
}
|
||||
}
|
||||
|
||||
function getBreathingPulse(phase) {
|
||||
@@ -338,7 +418,7 @@ function createBackdropDotTexture() {
|
||||
return texture;
|
||||
}
|
||||
|
||||
function createRingTexture(innerRadius, outerRadius, color = "#ffffff") {
|
||||
function createRingTexture(innerRadius, outerRadius, color = "#ffffff", lineWidth = 3) {
|
||||
const size = DOT_TEXTURE_SIZE * 2;
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = size;
|
||||
@@ -347,7 +427,7 @@ function createRingTexture(innerRadius, outerRadius, color = "#ffffff") {
|
||||
const center = size / 2;
|
||||
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.beginPath();
|
||||
ctx.arc(center, center, (innerRadius + outerRadius) / 2, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
@@ -384,26 +464,21 @@ export function createSatellites(scene, earthObj) {
|
||||
const pointsGeometry = new THREE.BufferGeometry();
|
||||
const backdropGeometry = new THREE.BufferGeometry();
|
||||
|
||||
const backdropMaterial = new THREE.PointsMaterial({
|
||||
size: SATELLITE_CONFIG.dotSize * 1.28,
|
||||
map: backdropTexture,
|
||||
color: 0x0b1626,
|
||||
transparent: true,
|
||||
const backdropMaterial = createSatellitePointMaterial({
|
||||
size: getSatelliteDotBackdropSize(),
|
||||
texture: backdropTexture,
|
||||
useVertexColor: false,
|
||||
baseColor: 0x0b1626,
|
||||
opacity: 0.42,
|
||||
sizeAttenuation: false,
|
||||
alphaTest: 0.04,
|
||||
depthWrite: false,
|
||||
});
|
||||
|
||||
const pointsMaterial = new THREE.PointsMaterial({
|
||||
size: SATELLITE_CONFIG.dotSize,
|
||||
map: dotTexture,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
const pointsMaterial = createSatellitePointMaterial({
|
||||
size: getSatelliteDotBaseSize(),
|
||||
texture: dotTexture,
|
||||
useVertexColor: true,
|
||||
opacity: 0.9,
|
||||
sizeAttenuation: false,
|
||||
alphaTest: 0.1,
|
||||
depthWrite: false,
|
||||
});
|
||||
|
||||
satelliteBackdropPoints = new THREE.Points(backdropGeometry, backdropMaterial);
|
||||
@@ -492,6 +567,9 @@ function ensureSatelliteCapacity(count) {
|
||||
const previousBackdropPositions =
|
||||
satelliteBackdropPoints.geometry.attributes.position?.array || null;
|
||||
const previousColors = satellitePoints.geometry.attributes.color?.array || null;
|
||||
const previousPointAlphas = satellitePoints.geometry.attributes.alpha?.array || null;
|
||||
const previousBackdropAlphas =
|
||||
satelliteBackdropPoints.geometry.attributes.alpha?.array || null;
|
||||
const previousTrailPositions =
|
||||
satelliteTrails.geometry.attributes.position?.array || null;
|
||||
const previousTrailColors =
|
||||
@@ -502,6 +580,10 @@ function ensureSatelliteCapacity(count) {
|
||||
const positions = new Float32Array(nextCapacity * 3);
|
||||
const backdropPositions = new Float32Array(nextCapacity * 3);
|
||||
const colors = new Float32Array(nextCapacity * 3);
|
||||
const pointAlphas = new Float32Array(nextCapacity);
|
||||
const backdropAlphas = new Float32Array(nextCapacity);
|
||||
pointAlphas.fill(1);
|
||||
backdropAlphas.fill(1);
|
||||
if (previousPointPositions) {
|
||||
positions.set(
|
||||
previousPointPositions.subarray(0, Math.min(previousPointPositions.length, positions.length)),
|
||||
@@ -518,10 +600,27 @@ function ensureSatelliteCapacity(count) {
|
||||
if (previousColors) {
|
||||
colors.set(previousColors.subarray(0, Math.min(previousColors.length, colors.length)));
|
||||
}
|
||||
if (previousPointAlphas) {
|
||||
pointAlphas.set(
|
||||
previousPointAlphas.subarray(0, Math.min(previousPointAlphas.length, pointAlphas.length)),
|
||||
);
|
||||
}
|
||||
if (previousBackdropAlphas) {
|
||||
backdropAlphas.set(
|
||||
previousBackdropAlphas.subarray(
|
||||
0,
|
||||
Math.min(previousBackdropAlphas.length, backdropAlphas.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
satelliteBackdropPoints.geometry.setAttribute(
|
||||
"position",
|
||||
new THREE.BufferAttribute(backdropPositions, 3),
|
||||
);
|
||||
satelliteBackdropPoints.geometry.setAttribute(
|
||||
"alpha",
|
||||
new THREE.BufferAttribute(backdropAlphas, 1),
|
||||
);
|
||||
satelliteBackdropPoints.geometry.setDrawRange(
|
||||
0,
|
||||
Math.min(previousCapacity, nextCapacity),
|
||||
@@ -534,6 +633,10 @@ function ensureSatelliteCapacity(count) {
|
||||
"color",
|
||||
new THREE.BufferAttribute(colors, 3),
|
||||
);
|
||||
satellitePoints.geometry.setAttribute(
|
||||
"alpha",
|
||||
new THREE.BufferAttribute(pointAlphas, 1),
|
||||
);
|
||||
satellitePoints.geometry.setDrawRange(0, Math.min(previousCapacity, nextCapacity));
|
||||
|
||||
const trailPositions = new Float32Array(nextCapacity * TRAIL_LENGTH * 3);
|
||||
@@ -579,6 +682,25 @@ function ensureSatelliteCapacity(count) {
|
||||
satelliteCapacity = nextCapacity;
|
||||
}
|
||||
|
||||
function shouldHideSatellitePoint(index) {
|
||||
return index === hoveredSatelliteIndex || index === lockedSatelliteIndex;
|
||||
}
|
||||
|
||||
function updateSatellitePointVisibilityAttributes(count = satelliteData.length) {
|
||||
const pointAlphaAttr = satellitePoints?.geometry?.attributes?.alpha;
|
||||
const backdropAlphaAttr = satelliteBackdropPoints?.geometry?.attributes?.alpha;
|
||||
if (!pointAlphaAttr?.array || !backdropAlphaAttr?.array) return;
|
||||
|
||||
const visibleCount = Math.min(count, pointAlphaAttr.array.length, backdropAlphaAttr.array.length);
|
||||
for (let i = 0; i < visibleCount; i++) {
|
||||
const alpha = shouldHideSatellitePoint(i) ? 0 : 1;
|
||||
pointAlphaAttr.array[i] = alpha;
|
||||
backdropAlphaAttr.array[i] = alpha;
|
||||
}
|
||||
pointAlphaAttr.needsUpdate = true;
|
||||
backdropAlphaAttr.needsUpdate = true;
|
||||
}
|
||||
|
||||
function computeSatellitePosition(satellite, time) {
|
||||
try {
|
||||
const props = satellite.properties;
|
||||
@@ -824,6 +946,8 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
const backdropPositions =
|
||||
satelliteBackdropPoints.geometry.attributes.position.array;
|
||||
const colors = satellitePoints.geometry.attributes.color.array;
|
||||
const pointAlphas = satellitePoints.geometry.attributes.alpha.array;
|
||||
const backdropAlphas = satelliteBackdropPoints.geometry.attributes.alpha.array;
|
||||
const trailPositions = satelliteTrails.geometry.attributes.position.array;
|
||||
const trailColors = satelliteTrails.geometry.attributes.color.array;
|
||||
const baseTime = new Date(Date.now() + elapsedMs);
|
||||
@@ -871,6 +995,9 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
colors[i * 3] = r * pointBrightness;
|
||||
colors[i * 3 + 1] = g * pointBrightness;
|
||||
colors[i * 3 + 2] = b * pointBrightness;
|
||||
const pointAlpha = shouldHideSatellitePoint(i) ? 0 : 1;
|
||||
pointAlphas[i] = pointAlpha;
|
||||
backdropAlphas[i] = pointAlpha;
|
||||
|
||||
const satPosition = satellitePositions[i];
|
||||
for (let j = 0; j < TRAIL_LENGTH; j++) {
|
||||
@@ -909,6 +1036,8 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
backdropPositions[i * 3] = 0;
|
||||
backdropPositions[i * 3 + 1] = 0;
|
||||
backdropPositions[i * 3 + 2] = 0;
|
||||
pointAlphas[i] = 0;
|
||||
backdropAlphas[i] = 0;
|
||||
|
||||
for (let j = 0; j < TRAIL_LENGTH; j++) {
|
||||
const trailIdx = (i * TRAIL_LENGTH + j) * 3;
|
||||
@@ -920,8 +1049,10 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
|
||||
satellitePoints.geometry.attributes.position.needsUpdate = true;
|
||||
satellitePoints.geometry.attributes.color.needsUpdate = true;
|
||||
satellitePoints.geometry.attributes.alpha.needsUpdate = true;
|
||||
satellitePoints.geometry.setDrawRange(0, count);
|
||||
satelliteBackdropPoints.geometry.attributes.position.needsUpdate = true;
|
||||
satelliteBackdropPoints.geometry.attributes.alpha.needsUpdate = true;
|
||||
satelliteBackdropPoints.geometry.setDrawRange(0, count);
|
||||
|
||||
satelliteTrails.geometry.attributes.position.needsUpdate = true;
|
||||
@@ -1014,10 +1145,12 @@ export function setSatelliteSunDirection(direction) {
|
||||
|
||||
export function setLockedSatelliteIndex(index) {
|
||||
lockedSatelliteIndex = index;
|
||||
updateSatellitePointVisibilityAttributes();
|
||||
}
|
||||
|
||||
export function setHoveredSatelliteIndex(index) {
|
||||
hoveredSatelliteIndex = index;
|
||||
updateSatellitePointVisibilityAttributes();
|
||||
}
|
||||
|
||||
function normalizeSatelliteDisplayStyle(nextStyle) {
|
||||
@@ -1190,7 +1323,7 @@ export function isSatelliteFrontFacing(index, camera = cameraRef) {
|
||||
function createLockedHaloMaterial(color = "#ffbf47") {
|
||||
return new THREE.ShaderMaterial({
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
side: THREE.DoubleSide,
|
||||
uniforms: {
|
||||
@@ -1397,6 +1530,7 @@ function clearLockedSatelliteStyleVisuals() {
|
||||
|
||||
function updateLockedDotWorldTransform(position) {
|
||||
if (!lockedDotSprite || !position || !earthObjRef) return;
|
||||
earthObjRef.updateMatrixWorld(true);
|
||||
const worldPosition = position.clone().applyMatrix4(earthObjRef.matrixWorld);
|
||||
lockedDotSprite.position.copy(worldPosition);
|
||||
if (cameraRef) {
|
||||
@@ -1417,6 +1551,7 @@ function updateLockedDotWorldTransform(position) {
|
||||
|
||||
function updateLockedHaloWorldTransform(position) {
|
||||
if (!position || !earthObjRef || !lockedHaloMesh) return;
|
||||
earthObjRef.updateMatrixWorld(true);
|
||||
const worldPosition = position.clone().applyMatrix4(earthObjRef.matrixWorld);
|
||||
const viewDirection = cameraRef
|
||||
? scratchToCamera.subVectors(cameraRef.position, worldPosition).normalize()
|
||||
@@ -1764,7 +1899,7 @@ function showSelfGlowStyle(position, color = "#ffd25a") {
|
||||
color: new THREE.Color(color),
|
||||
transparent: true,
|
||||
opacity: 0.96,
|
||||
depthTest: false,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
@@ -1824,6 +1959,7 @@ function createRingSprite(position, isLocked = false, color = "#ffcc00") {
|
||||
8,
|
||||
12,
|
||||
isLocked ? color : "#ffffff",
|
||||
isLocked ? LOCKED_RING_HOVER_LINE_WIDTH : HOVER_RING_LINE_WIDTH,
|
||||
);
|
||||
const filledTexture = isLocked
|
||||
? createFilledCircleTexture(color)
|
||||
@@ -1832,7 +1968,9 @@ function createRingSprite(position, isLocked = false, color = "#ffcc00") {
|
||||
map: ringTexture,
|
||||
transparent: true,
|
||||
opacity: 0.8,
|
||||
depthTest: false,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
alphaTest: 0.01,
|
||||
sizeAttenuation: false,
|
||||
});
|
||||
|
||||
@@ -1962,7 +2100,7 @@ export function updateLockedRingPosition(position) {
|
||||
1 +
|
||||
(ringPulse * 2 - 1) * SATELLITE_CONFIG.breathingScaleAmplitude;
|
||||
const markerSize = isHovered
|
||||
? SATELLITE_CONFIG.ringSize
|
||||
? SATELLITE_CONFIG.ringSize * LOCKED_RING_HOVER_SCALE
|
||||
: SATELLITE_CONFIG.ringSize * LOCKED_RING_IDLE_SCALE;
|
||||
lockedRingSprite.scale.set(
|
||||
markerSize * breathScale,
|
||||
@@ -2026,26 +2164,38 @@ export function setSatelliteRingState(index, state, position) {
|
||||
hoveredSatelliteIndex = index;
|
||||
hideHoverRings();
|
||||
showHoverRing(position, false);
|
||||
updateSatellitePointVisibilityAttributes();
|
||||
break;
|
||||
case "locked":
|
||||
hoveredSatelliteIndex = null;
|
||||
hideHoverRings();
|
||||
showHoverRing(position, true);
|
||||
updateSatellitePointVisibilityAttributes();
|
||||
break;
|
||||
case "none":
|
||||
hoveredSatelliteIndex = null;
|
||||
hideHoverRings();
|
||||
hideLockedRing();
|
||||
updateSatellitePointVisibilityAttributes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function applyDimMaterialState(isDimmed) {
|
||||
if (satellitePoints) {
|
||||
satellitePoints.material.opacity = isDimmed ? DIMMED_SATELLITE_POINT_OPACITY : 0.9;
|
||||
const opacity = isDimmed ? DIMMED_SATELLITE_POINT_OPACITY : 0.9;
|
||||
if (satellitePoints.material.uniforms?.opacity) {
|
||||
satellitePoints.material.uniforms.opacity.value = opacity;
|
||||
} else {
|
||||
satellitePoints.material.opacity = opacity;
|
||||
}
|
||||
}
|
||||
if (satelliteBackdropPoints) {
|
||||
satelliteBackdropPoints.material.opacity = isDimmed ? DIMMED_SATELLITE_BACKDROP_OPACITY : 0.42;
|
||||
const opacity = isDimmed ? DIMMED_SATELLITE_BACKDROP_OPACITY : 0.42;
|
||||
if (satelliteBackdropPoints.material.uniforms?.opacity) {
|
||||
satelliteBackdropPoints.material.uniforms.opacity.value = opacity;
|
||||
} else {
|
||||
satelliteBackdropPoints.material.opacity = opacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2242,6 +2392,7 @@ export function clearSatelliteData() {
|
||||
if (satellitePoints) {
|
||||
const positionAttr = satellitePoints.geometry.attributes.position;
|
||||
const colorAttr = satellitePoints.geometry.attributes.color;
|
||||
const alphaAttr = satellitePoints.geometry.attributes.alpha;
|
||||
if (positionAttr?.array) {
|
||||
positionAttr.array.fill(0);
|
||||
positionAttr.needsUpdate = true;
|
||||
@@ -2250,16 +2401,26 @@ export function clearSatelliteData() {
|
||||
colorAttr.array.fill(0);
|
||||
colorAttr.needsUpdate = true;
|
||||
}
|
||||
if (alphaAttr?.array) {
|
||||
alphaAttr.array.fill(0);
|
||||
alphaAttr.needsUpdate = true;
|
||||
}
|
||||
satellitePoints.geometry.setDrawRange(0, 0);
|
||||
}
|
||||
|
||||
if (satelliteBackdropPoints) {
|
||||
const backdropPositionAttr =
|
||||
satelliteBackdropPoints.geometry.attributes.position;
|
||||
const backdropAlphaAttr =
|
||||
satelliteBackdropPoints.geometry.attributes.alpha;
|
||||
if (backdropPositionAttr?.array) {
|
||||
backdropPositionAttr.array.fill(0);
|
||||
backdropPositionAttr.needsUpdate = true;
|
||||
}
|
||||
if (backdropAlphaAttr?.array) {
|
||||
backdropAlphaAttr.array.fill(0);
|
||||
backdropAlphaAttr.needsUpdate = true;
|
||||
}
|
||||
satelliteBackdropPoints.geometry.setDrawRange(0, 0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user