fix(earth-bgp): consolidate visual constants and bump version to 0.22.12

This commit is contained in:
linkong
2026-04-02 17:31:26 +08:00
parent 8bd8e3966a
commit 135ec01223
10 changed files with 273 additions and 84 deletions

View File

@@ -15,3 +15,4 @@
- [ ] 把 RIR delegated / `inetnum` / `inet6num` whois 设计成 prefix geography 的 fallback而不是主来源
- [x] 在 activity layer 之后继续补 `route leak``path instability / flap` detector
- [ ] 对 [frontend/public/earth/js/bgp.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp.js) 做按职责拆分的小重构,拆成 data / markers / overlays / animation降低后续维护复杂度
- [ ] 可选优化(非必做):将 BGP incident/collector 标点改为 HTML marker参考 worldmonitor 的 `htmlElementsData` 思路),实现近乎固定屏幕尺寸与更高密度可点击性

View File

@@ -1 +1 @@
0.22.11
0.22.12

View File

@@ -7,6 +7,32 @@ This project follows the repository versioning rule:
- `feature` -> `+0.1.0`
- `bugfix` -> `+0.0.1`
## 0.22.12
Released: 2026-04-02
### Highlights
- Completed Earth `BGP / cables / landing points` visual-parameter consolidation so the latest overlap/size fixes are no longer scattered as magic numbers.
- Shipped a bugfix version bump for the constant-extraction and marker-size stabilization work (`+0.0.1`).
### Improved
- Improved [constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js) by introducing structured `BGP_CONFIG` groups (`marker`, `pulse`, `ring`, `halo`, `sizeStabilization`) and by extracting additional cable/landing-point render parameters into `CABLE_CONFIG`.
- Improved [bgp.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp.js) by fully switching BGP marker/ring/pulse logic to the new nested constants, including collector status-core scale floors and distance/FOV-based size-stabilization knobs.
- Improved [cables.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cables.js) by removing hardcoded landing-point visual constants and centralizing base scale, pulse, dimming, opacity, emissive, and stabilization thresholds.
- Improved [main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) by passing `camera` through landing-point visual update/reset flows so stabilized sizing remains consistent during normal render-loop interactions.
- Improved planning continuity in [TODO.md](/home/ray/dev/linkong/planet/TODO.md) by documenting the optional `HTML marker` migration path for near-fixed screen-size BGP markers as a non-blocking follow-up.
### Fixed
- Fixed drift from earlier iterative tuning where BGP/cable size and animation behavior depended on multiple dispersed literals, which made overlap and readability regressions more likely during subsequent tweaks.
- Fixed a partially applied refactor state where some BGP collector/status-core scaling still bypassed shared config, preventing clean global tuning.
### Notes
- During this round, an over-tight marker-size clamp path was intentionally not kept; the final shipped config uses wider stabilization bounds so zoom-level response remains visible while still reducing overlap blow-up.
## 0.22.11
Released: 2026-04-02

View File

@@ -1,6 +1,6 @@
{
"name": "planet-frontend",
"version": "0.22.11",
"version": "0.22.12",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.2.6",

View File

@@ -277,13 +277,39 @@ function blendHexColors(fromHex, toHex, ratio) {
}
function getCollectorDistanceScale(marker, camera) {
if (!marker || !camera) return 1;
if (!marker || !camera || BGP_CONFIG.sizeStabilization?.enabled === false) return 1;
marker.getWorldPosition(collectorWorldPosition);
const distanceToCamera = camera.position.distanceTo(collectorWorldPosition);
const referenceDistance = CONFIG.defaultCameraZ - CONFIG.earthRadius + BGP_CONFIG.collectorAltitudeOffset;
const referenceFovRad = (75 * Math.PI) / 180;
const cameraFovRad = ((camera.fov || 75) * Math.PI) / 180;
const min = Number(BGP_CONFIG.sizeStabilization?.collectorMin ?? 0.6);
const max = Number(BGP_CONFIG.sizeStabilization?.collectorMax ?? 1.9);
const worldPerPixel =
distanceToCamera * Math.tan(cameraFovRad / 2);
const referenceWorldPerPixel =
referenceDistance * Math.tan(referenceFovRad / 2);
return clamp(distanceToCamera / referenceDistance, 0.6, 1.9);
return clamp(worldPerPixel / referenceWorldPerPixel, min, max);
}
function getEventDistanceScale(marker, camera) {
if (!marker || !camera || BGP_CONFIG.sizeStabilization?.enabled === false) return 1;
marker.getWorldPosition(collectorWorldPosition);
const distanceToCamera = camera.position.distanceTo(collectorWorldPosition);
const referenceDistance = CONFIG.defaultCameraZ - CONFIG.earthRadius + BGP_CONFIG.altitudeOffset;
const referenceFovRad = (75 * Math.PI) / 180;
const cameraFovRad = ((camera.fov || 75) * Math.PI) / 180;
const min = Number(BGP_CONFIG.sizeStabilization?.eventMin ?? 0.7);
const max = Number(BGP_CONFIG.sizeStabilization?.eventMax ?? 1.9);
const worldPerPixel =
distanceToCamera * Math.tan(cameraFovRad / 2);
const referenceWorldPerPixel =
referenceDistance * Math.tan(referenceFovRad / 2);
return clamp(worldPerPixel / referenceWorldPerPixel, min, max);
}
function orientCollectorMarkerToSurface(marker, position) {
@@ -332,13 +358,13 @@ function getCollectorActivityProfile(markerData) {
const scaleBoost = clamp(1 + Math.log2(activityScore + 1) * 0.12, 1, 1.55);
const haloScale =
BGP_CONFIG.collectorHaloScale +
BGP_CONFIG.halo.collectorScale +
clamp(Math.log2(recent24h + prefixes + 1) * 2.2, 0, 12);
const pulseHaloScale =
BGP_CONFIG.collectorPulseHaloScale +
BGP_CONFIG.halo.collectorPulseScale +
clamp(Math.log2(recent24h + recent7d + 1) * 2.8, 0, 14);
const coverageHaloScale =
BGP_CONFIG.collectorCoverageHaloScale +
BGP_CONFIG.halo.collectorCoverageScale +
clamp(Math.log2(prefixes + origins + 1) * 3.4, 0, 18);
return {
@@ -994,7 +1020,7 @@ function createCollectorMarker(markerData) {
);
marker.position.copy(position);
marker.scale.set(BGP_CONFIG.collectorScale * 0.88 * activity.scaleBoost, BGP_CONFIG.collectorScale * 1.08 * activity.scaleBoost, 1);
marker.scale.set(BGP_CONFIG.marker.collectorBaseScale * 0.88 * activity.scaleBoost, BGP_CONFIG.marker.collectorBaseScale * 1.08 * activity.scaleBoost, 1);
marker.renderOrder = 3;
marker.visible = showBGP;
orientCollectorMarkerToSurface(marker, position);
@@ -1018,7 +1044,11 @@ function createCollectorMarker(markerData) {
const statusCore = createOverlaySprite({
color: activity.color,
opacity: 0.0,
scale: Math.max(BGP_CONFIG.collectorScale * 0.12, 1.2),
scale: Math.max(
BGP_CONFIG.marker.collectorBaseScale *
BGP_CONFIG.marker.collectorStatusCoreBaseScale,
BGP_CONFIG.marker.collectorStatusCoreMinScale,
),
});
statusCore.position.set(0, 0, 0.02);
statusCore.renderOrder = 4;
@@ -1036,7 +1066,7 @@ function createCollectorMarker(markerData) {
marker.userData = {
type: "bgp_collector",
state: "normal",
baseScale: BGP_CONFIG.collectorScale * activity.scaleBoost,
baseScale: BGP_CONFIG.marker.collectorBaseScale * activity.scaleBoost,
baseColor,
idleColor,
pulseOffset: Math.random() * Math.PI * 2,
@@ -1072,7 +1102,7 @@ function createAnomalyMarker(markerData) {
CONFIG.earthRadius + BGP_CONFIG.altitudeOffset,
);
const baseScale = BGP_CONFIG.baseScale * getSeverityScale(markerData.severity);
const baseScale = BGP_CONFIG.marker.eventBaseScale * getSeverityScale(markerData.severity);
sprite.position.copy(position);
sprite.scale.setScalar(baseScale);
sprite.renderOrder = 5;
@@ -1097,7 +1127,7 @@ function createAnomalyMarker(markerData) {
blending: THREE.AdditiveBlending,
}),
);
ringA.scale.setScalar(baseScale * BGP_CONFIG.eventRingScaleA);
ringA.scale.setScalar(baseScale * BGP_CONFIG.ring.scaleA);
ringA.position.set(0, 0, -0.01);
sprite.add(ringA);
@@ -1112,7 +1142,7 @@ function createAnomalyMarker(markerData) {
blending: THREE.AdditiveBlending,
}),
);
ringB.scale.setScalar(baseScale * BGP_CONFIG.eventRingScaleB);
ringB.scale.setScalar(baseScale * BGP_CONFIG.ring.scaleB);
ringB.position.set(0, 0, -0.02);
sprite.add(ringB);
@@ -1338,7 +1368,7 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
0.5 +
0.5 *
Math.sin(
now * BGP_CONFIG.collectorPulseSpeed + marker.userData.pulseOffset,
now * BGP_CONFIG.pulse.collectorSpeed + marker.userData.pulseOffset,
);
let scale = marker.userData.baseScale * getCollectorDistanceScale(marker, camera);
@@ -1411,7 +1441,11 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
isLocked ? 0.58 : isHovered ? 0.4 : hasLockedLayer ? 0.0 : 0.18;
marker.userData.statusCore.material.color.setHex(marker.userData.baseColor || BGP_CONFIG.collectorColor);
marker.userData.statusCore.scale.setScalar(
Math.max(BGP_CONFIG.collectorScale * 0.12, 1.2) * (isLocked ? 1.08 : isHovered ? 1.04 : 0.92),
Math.max(
BGP_CONFIG.marker.collectorBaseScale *
BGP_CONFIG.marker.collectorStatusCoreBaseScale,
BGP_CONFIG.marker.collectorStatusCoreMinScale,
) * (isLocked ? 1.08 : isHovered ? 1.04 : 0.92),
);
}
if (marker.userData.coverageHalo) {
@@ -1433,33 +1467,35 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
const isHovered = marker.userData.state === "hover";
const pulse =
0.5 +
0.5 * Math.sin(now * BGP_CONFIG.pulseSpeed + marker.userData.pulseOffset);
0.5 * Math.sin(now * BGP_CONFIG.pulse.eventSpeed + marker.userData.pulseOffset);
let scale = marker.userData.baseScale;
const iconAnchorScale =
marker.userData.baseScale * getEventDistanceScale(marker, camera);
let scale = iconAnchorScale;
let opacity = BGP_CONFIG.opacity.normal;
let markerColor = marker.userData.baseColor || getSeverityColor(marker.userData.severity);
const isIncidentMarker = marker.userData.source === "bgp_incident";
let ringBaseOpacity = isIncidentMarker
? BGP_CONFIG.eventRingOpacity
: BGP_CONFIG.eventRingOpacity * 0.45;
? BGP_CONFIG.ring.opacity
: BGP_CONFIG.ring.opacity * 0.45;
if (isLocked || isLinkedCollectorLocked) {
scale *= 1 + BGP_CONFIG.lockedPulseAmplitude * pulse;
scale *= 1 + BGP_CONFIG.pulse.lockedAmplitude * pulse;
opacity =
BGP_CONFIG.opacity.lockedMin +
(BGP_CONFIG.opacity.lockedMax - BGP_CONFIG.opacity.lockedMin) * pulse;
ringBaseOpacity *= 1.2;
} else if (isHovered) {
scale *= BGP_CONFIG.hoverScale;
scale *= BGP_CONFIG.marker.hoverScale;
opacity = BGP_CONFIG.opacity.hover;
ringBaseOpacity *= 1.05;
} else if (isOtherLocked) {
scale *= BGP_CONFIG.dimmedScale;
scale *= BGP_CONFIG.marker.dimmedScale;
opacity = 0.1;
markerColor = 0x7d8ca3;
ringBaseOpacity = 0.02;
} else {
scale *= 1 + BGP_CONFIG.normalPulseAmplitude * pulse;
scale *= 1 + BGP_CONFIG.pulse.normalAmplitude * pulse;
opacity = isIncidentMarker ? 0.7 : 0.62;
}
@@ -1468,13 +1504,13 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
marker.material.opacity = opacity;
marker.visible = showBGP;
const ringPhaseA = (now * BGP_CONFIG.eventRingSpeed + marker.userData.pulseOffset) % 1;
const ringPhaseA = (now * BGP_CONFIG.ring.speed + marker.userData.pulseOffset) % 1;
const applyRingState = (ring, phase, maxScale) => {
if (!ring) return;
const progress = Math.max(0, Math.min(1, phase));
const minScale = 1.28;
const desiredWorldScale =
marker.userData.baseScale * (minScale + progress * (maxScale - minScale));
iconAnchorScale * (minScale + progress * (maxScale - minScale));
const parentScale = Math.max(scale, 0.0001);
const localRingScale = desiredWorldScale / parentScale;
const fadeIn = Math.max(0, Math.min(1, (progress - 0.08) / 0.14));
@@ -1486,7 +1522,7 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
ring.visible = showBGP;
};
applyRingState(marker.userData.ringA, ringPhaseA, BGP_CONFIG.eventRingScaleA);
applyRingState(marker.userData.ringA, ringPhaseA, BGP_CONFIG.ring.scaleA);
if (marker.userData.ringB) {
marker.userData.ringB.material.opacity = 0;
marker.userData.ringB.visible = false;

View File

@@ -2,7 +2,13 @@
import * as THREE from "three";
import { CONFIG, CABLE_COLORS, PATHS, CABLE_STATE } from "./constants.js";
import {
CONFIG,
CABLE_COLORS,
PATHS,
CABLE_STATE,
CABLE_CONFIG,
} from "./constants.js";
import { latLonToVector3 } from "./utils.js";
import { updateEarthStats, showStatusMessage } from "./ui.js";
import { showInfoCard } from "./info-card.js";
@@ -14,6 +20,37 @@ export let lockedCable = null;
let cableIdMap = new Map();
let cableStates = new Map();
let cablesVisible = true;
const landingPointWorldPosition = new THREE.Vector3();
function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
function getLandingPointDistanceScale(point, camera) {
if (
!point ||
!camera ||
CABLE_CONFIG.landingPointSizeStabilization?.enabled === false
) return 1;
point.getWorldPosition(landingPointWorldPosition);
const distanceToCamera = camera.position.distanceTo(landingPointWorldPosition);
const referenceDistance =
CONFIG.defaultCameraZ -
CONFIG.earthRadius +
CABLE_CONFIG.landingPoint.altitudeOffset;
const referenceFovDeg =
CABLE_CONFIG.landingPointSizeStabilization?.referenceFov || 75;
const referenceFovRad = (referenceFovDeg * Math.PI) / 180;
const cameraFovRad =
(((camera.fov || referenceFovDeg)) * Math.PI) / 180;
const worldPerPixel = distanceToCamera * Math.tan(cameraFovRad / 2);
const referenceWorldPerPixel = referenceDistance * Math.tan(referenceFovRad / 2);
return clamp(
worldPerPixel / referenceWorldPerPixel,
CABLE_CONFIG.landingPointSizeStabilization?.min ?? 0.12,
CABLE_CONFIG.landingPointSizeStabilization?.max ?? 3.0,
);
}
function disposeMaterial(material) {
if (!material) return;
@@ -83,9 +120,9 @@ function createCableLine(points, color, properties) {
const lineMaterial = new THREE.LineBasicMaterial({
color,
linewidth: 1,
linewidth: CABLE_CONFIG.line.lineWidth,
transparent: true,
opacity: 1.0,
opacity: CABLE_CONFIG.line.opacity,
depthTest: true,
depthWrite: true,
});
@@ -115,7 +152,7 @@ function createCableLine(points, color, properties) {
localCenter:
lineGeometry.boundingSphere?.center?.clone() || new THREE.Vector3(),
};
cableLine.renderOrder = 1;
cableLine.renderOrder = CABLE_CONFIG.line.renderOrder;
if (!cableIdMap.has(cableId)) {
cableIdMap.set(cableId, []);
@@ -149,7 +186,7 @@ function calculateGreatCirclePoints(
let delta = Math.acos(Math.max(-1, Math.min(1, cosDelta)));
if (delta < 0.01) {
if (delta < CABLE_CONFIG.line.nearPointThreshold) {
const p1 = latLonToVector3(lat1, lon1, radius);
const p2 = latLonToVector3(lat2, lon2, radius);
return [p1, p2];
@@ -248,8 +285,8 @@ export async function loadGeoJSONFromPath(scene, earthObj) {
lon1,
lat2,
lon2,
CONFIG.earthRadius + 0.2,
50,
CONFIG.earthRadius + CABLE_CONFIG.line.altitudeOffset,
CABLE_CONFIG.line.greatCircleSegments,
);
points.push(...(i === 0 ? segment : segment.slice(1)));
}
@@ -273,8 +310,8 @@ export async function loadGeoJSONFromPath(scene, earthObj) {
lon1,
lat2,
lon2,
CONFIG.earthRadius + 0.2,
50,
CONFIG.earthRadius + CABLE_CONFIG.line.altitudeOffset,
CABLE_CONFIG.line.greatCircleSegments,
);
points.push(...(i === 0 ? segment : segment.slice(1)));
}
@@ -324,7 +361,11 @@ export async function loadLandingPoints(scene, earthObj) {
clearLandingPoints(earthObj);
const sphereGeometry = new THREE.SphereGeometry(0.4, 16, 16);
const sphereGeometry = new THREE.SphereGeometry(
CABLE_CONFIG.landingPoint.radius,
CABLE_CONFIG.landingPoint.widthSegments,
CABLE_CONFIG.landingPoint.heightSegments,
);
let validCount = 0;
try {
@@ -345,7 +386,11 @@ export async function loadLandingPoints(scene, earthObj) {
continue;
}
const position = latLonToVector3(lat, lon, CONFIG.earthRadius + 0.1);
const position = latLonToVector3(
lat,
lon,
CONFIG.earthRadius + CABLE_CONFIG.landingPoint.altitudeOffset,
);
if (
Number.isNaN(position.x) ||
Number.isNaN(position.y) ||
@@ -357,11 +402,11 @@ export async function loadLandingPoints(scene, earthObj) {
const sphere = new THREE.Mesh(
sphereGeometry.clone(),
new THREE.MeshStandardMaterial({
color: 0xffaa00,
emissive: 0x442200,
emissiveIntensity: 0.5,
color: CABLE_CONFIG.landingPoint.color,
emissive: CABLE_CONFIG.landingPoint.emissive,
emissiveIntensity: CABLE_CONFIG.landingPoint.emissiveIntensity,
transparent: true,
opacity: 1,
opacity: CABLE_CONFIG.landingPoint.opacity,
}),
);
sphere.position.copy(position);
@@ -371,6 +416,7 @@ export async function loadLandingPoints(scene, earthObj) {
cableNames: properties.cable_names || [],
country: properties.country || "未知国家",
status: properties.status || "Unknown",
baseScale: CABLE_CONFIG.landingPoint.baseScale,
};
earthObj.add(sphere);
@@ -493,9 +539,10 @@ export function getAllLandingPoints() {
return landingPoints;
}
export function applyLandingPointVisualState(lockedCableName, dimAll = false) {
const pulse = (Math.sin(Date.now() * 0.003) + 1) * 0.5;
const brightness = 0.3;
export function applyLandingPointVisualState(lockedCableName, dimAll = false, camera = null) {
const pulse =
(Math.sin(Date.now() * CABLE_CONFIG.landingPointVisual.pulseSpeed) + 1) * 0.5;
const brightness = CABLE_CONFIG.landingPointVisual.dimBrightness;
const relatedNames = Array.isArray(lockedCableName)
? lockedCableName.filter(Boolean)
: lockedCableName
@@ -509,30 +556,48 @@ export function applyLandingPointVisualState(lockedCableName, dimAll = false) {
lp.userData.cableNames.some((name) => relatedNames.includes(name));
if (isRelated) {
lp.material.color.setHex(0xffaa00);
lp.material.emissive.setHex(0x442200);
lp.material.emissiveIntensity = 0.5 + pulse * 0.5;
lp.material.opacity = 0.8 + pulse * 0.2;
lp.scale.setScalar(1.2 + pulse * 0.3);
lp.material.color.setHex(CABLE_CONFIG.landingPoint.color);
lp.material.emissive.setHex(CABLE_CONFIG.landingPoint.emissive);
lp.material.emissiveIntensity =
CABLE_CONFIG.landingPointVisual.related.emissiveIntensityBase +
pulse * CABLE_CONFIG.landingPointVisual.related.emissiveIntensityPulse;
lp.material.opacity =
CABLE_CONFIG.landingPointVisual.related.opacityBase +
pulse * CABLE_CONFIG.landingPointVisual.related.opacityPulse;
const distanceScale = getLandingPointDistanceScale(lp, camera);
const baseScale = lp.userData?.baseScale || CABLE_CONFIG.landingPoint.baseScale;
lp.scale.setScalar(
(CABLE_CONFIG.landingPointVisual.related.scaleBase +
pulse * CABLE_CONFIG.landingPointVisual.related.scalePulse) *
baseScale *
distanceScale,
);
} else {
const r = 255 * brightness;
const g = 170 * brightness;
lp.material.color.setRGB(r / 255, g / 255, 0);
lp.material.emissive.setHex(0x000000);
lp.material.emissiveIntensity = 0;
lp.material.opacity = 0.3;
lp.scale.setScalar(1.0);
const dimColor = CABLE_CONFIG.landingPointVisual.dimmed.colorRGB;
const r = dimColor.r * brightness;
const g = dimColor.g * brightness;
const b = dimColor.b * brightness;
lp.material.color.setRGB(r / 255, g / 255, b / 255);
lp.material.emissive.setHex(CABLE_CONFIG.landingPointVisual.dimmed.emissive);
lp.material.emissiveIntensity =
CABLE_CONFIG.landingPointVisual.dimmed.emissiveIntensity;
lp.material.opacity = CABLE_CONFIG.landingPointVisual.dimmed.opacity;
const distanceScale = getLandingPointDistanceScale(lp, camera);
const baseScale = lp.userData?.baseScale || CABLE_CONFIG.landingPoint.baseScale;
lp.scale.setScalar(baseScale * distanceScale);
}
});
}
export function resetLandingPointVisualState() {
export function resetLandingPointVisualState(camera = null) {
landingPoints.forEach((lp) => {
lp.material.color.setHex(0xffaa00);
lp.material.emissive.setHex(0x442200);
lp.material.emissiveIntensity = 0.5;
lp.material.opacity = 1.0;
lp.scale.setScalar(1.0);
lp.material.color.setHex(CABLE_CONFIG.landingPoint.color);
lp.material.emissive.setHex(CABLE_CONFIG.landingPoint.emissive);
lp.material.emissiveIntensity = CABLE_CONFIG.landingPoint.emissiveIntensity;
lp.material.opacity = CABLE_CONFIG.landingPoint.opacity;
const distanceScale = getLandingPointDistanceScale(lp, camera);
const baseScale = lp.userData?.baseScale || CABLE_CONFIG.landingPoint.baseScale;
lp.scale.setScalar(baseScale * distanceScale);
});
}

View File

@@ -45,7 +45,50 @@ export const CABLE_CONFIG = {
otherOpacity: 0.5,
otherBrightness: 0.6,
pulseSpeed: 0.008,
pulseCoefficient: 0.4
pulseCoefficient: 0.4,
line: {
altitudeOffset: 0.2,
greatCircleSegments: 50,
nearPointThreshold: 0.01,
lineWidth: 1,
opacity: 1.0,
renderOrder: 1,
},
landingPoint: {
altitudeOffset: 0.1,
radius: 0.4,
widthSegments: 16,
heightSegments: 16,
baseScale: 2.5,
color: 0xffaa00,
emissive: 0x442200,
emissiveIntensity: 0.5,
opacity: 1.0,
},
landingPointSizeStabilization: {
enabled: true,
referenceFov: 75,
min: 0.12,
max: 3.0,
},
landingPointVisual: {
pulseSpeed: 0.003,
dimBrightness: 0.3,
related: {
emissiveIntensityBase: 0.5,
emissiveIntensityPulse: 0.5,
opacityBase: 0.8,
opacityPulse: 0.2,
scaleBase: 1.2,
scalePulse: 0.3,
},
dimmed: {
colorRGB: { r: 255, g: 170, b: 0 },
emissive: 0x000000,
emissiveIntensity: 0,
opacity: 0.3,
},
},
};
export const CABLE_STATE = {
@@ -75,15 +118,21 @@ export const BGP_CONFIG = {
maxRenderedMarkers: 200,
altitudeOffset: 2.1,
collectorAltitudeOffset: 1.6,
baseScale: 6.2,
collectorScale: 7.4,
hoverScale: 1.16,
dimmedScale: 0.92,
pulseSpeed: 0.0045,
collectorPulseSpeed: 0.0024,
marker: {
eventBaseScale: 6.2,
collectorBaseScale: 7.4,
hoverScale: 1.16,
dimmedScale: 0.92,
collectorStatusCoreBaseScale: 0.12,
collectorStatusCoreMinScale: 1.2,
},
pulse: {
eventSpeed: 0.0045,
collectorSpeed: 0.0024,
normalAmplitude: 0.03,
lockedAmplitude: 0.16,
},
regionScale: 11.5,
normalPulseAmplitude: 0.08,
lockedPulseAmplitude: 0.28,
opacity: {
normal: 0.78,
hover: 1.0,
@@ -132,13 +181,24 @@ export const BGP_CONFIG = {
dimmedColor: 0x7d8ca3,
},
regionColor: 0x2dd4bf,
eventRingScaleA: 2.5,
eventRingScaleB: 3.4,
eventRingOpacity: 0.5,
eventRingSpeed: 0.001,
collectorHaloScale: 11.5,
collectorPulseHaloScale: 16.5,
collectorCoverageHaloScale: 22.5
ring: {
scaleA: 2.5,
scaleB: 3.4,
opacity: 0.5,
speed: 0.001,
},
halo: {
collectorScale: 11.5,
collectorPulseScale: 16.5,
collectorCoverageScale: 22.5,
},
sizeStabilization: {
enabled: true,
collectorMin: 0.12,
collectorMax: 3.0,
eventMin: 0.12,
eventMax: 3.0,
},
};
export const PREDICTED_ORBIT_CONFIG = {

View File

@@ -1537,11 +1537,11 @@ function animate() {
updateBGPVisualState(lockedObjectType, lockedObject, camera);
if (lockedObjectType === "cable" && lockedObject) {
applyLandingPointVisualState(lockedObject.userData.name, false);
applyLandingPointVisualState(lockedObject.userData.name, false, camera);
} else if (
lockedObjectType === "satellite" && lockedSatellite
) {
applyLandingPointVisualState(null, true);
applyLandingPointVisualState(null, true, camera);
} else if (lockedObjectType === "bgp" && lockedObject) {
const relatedCableNames = getBGPRelatedCableNames(lockedObject);
clearAllCableStates();
@@ -1555,12 +1555,13 @@ function animate() {
applyLandingPointVisualState(
relatedCableNames.length > 0 ? relatedCableNames : null,
relatedCableNames.length === 0,
camera,
);
} else if (lockedObjectType === "bgp_collector" && lockedObject) {
clearAllCableStates();
resetLandingPointVisualState();
resetLandingPointVisualState(camera);
} else {
resetLandingPointVisualState();
resetLandingPointVisualState(camera);
}
updateSatellitePositions(deltaTime);

View File

@@ -1,6 +1,6 @@
[project]
name = "planet"
version = "0.22.11"
version = "0.22.12"
description = "智能星球计划 - 态势感知系统"
requires-python = ">=3.14"
dependencies = [

2
uv.lock generated
View File

@@ -475,7 +475,7 @@ wheels = [
[[package]]
name = "planet"
version = "0.22.11"
version = "0.22.12"
source = { virtual = "." }
dependencies = [
{ name = "aiofiles" },