Compare commits

..

1 Commits

Author SHA1 Message Date
rayd1o
dd41588527 release: bump version to 0.76.2
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
2026-10-01 02:29:49 +08:00
11 changed files with 80 additions and 40 deletions

View File

@@ -1 +1 @@
0.76.1
0.76.2

View File

@@ -8,6 +8,20 @@ This project follows the repository versioning rule:
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
- `bugfix` -> `+0.0.1`
## [0.76.2] — 2026-10-01
Released: 2026-10-01
### Highlights
- 优化光缆密集区的卫星点拾取,并将点状交互物件优先级泛化到其他密集图层。
### Added / Fixed / Improved
- 屏幕命中先统一比较卫星、BGP、算力和其他点状对象的距离,海缆线作为兜底候选,保护穿插在密集线缆中的小面积对象。
- hover 与 click 共用同一候选排序,避免鼠标提示和最终选中对象不一致。
- 补充密集区域拾取回归测试,清理中间态代码并同步渲染层说明。
---
## [0.76.1] — 2026-10-01
Released: 2026-10-01

View File

@@ -253,7 +253,7 @@ All material, layer, satellite, BGP, cable, terrain, celestial, and other style
## Render hot paths
`cable-batches.js` batches the complete cable and landing-point sets. `cables.js` still owns original business objects, picking, occlusion, and selection state. Do not make these interaction proxies draw individually again or return a render batch as the detail-card business object.
`cable-batches.js` batches the complete cable and landing-point sets. `cables.js` still owns original business objects, picking, occlusion, and selection state. Do not make these interaction proxies draw individually again or return a render batch as the detail-card business object. In dense cable areas, screen-distance picking compares satellites, BGP, compute and other point objects before using cable lines as the fallback; hover and click share the same ordering so small objects threaded through a line bundle remain selectable.
Satellite breathing runs in vertex shaders. `satellite-position-worker.js` moves full SGP4 position and initial trail calculations off the main thread; the main thread applies snapshots to interaction coordinates and render buffers. `satellite-propagation.js` supplies the same mathematics to the Worker, predicted orbits, and synchronous fallback. Reload, clear, and altitude changes must terminate prior work so stale snapshots cannot resurrect cleared layers.

View File

@@ -233,7 +233,7 @@ TV 预览需要尽量复用 Earth 运行时的直播卡片结构和状态标签
## 渲染热路径
`cable-batches.js` 将全部海缆线段与全部登陆点分别合批绘制;`cables.js` 继续拥有原始业务对象、拾取、遮挡和选择状态。避免在后续功能中把这些代理对象重新加入逐对象绘制,或把绘制批次当作业务对象返回给详情卡。
`cable-batches.js` 将全部海缆线段与全部登陆点分别合批绘制;`cables.js` 继续拥有原始业务对象、拾取、遮挡和选择状态。避免在后续功能中把这些代理对象重新加入逐对象绘制,或把绘制批次当作业务对象返回给详情卡。密集线缆区的拾取先在屏幕距离上比较卫星、BGP、算力和其他点状对象,海缆线作为没有更近点状对象时的兜底;hover 与 click 共用该候选顺序,保护穿插在线缆中的小面积物件。
卫星的逐点呼吸在 GPU 中计算。全量 SGP4 位置和初始轨迹计算通过 `satellite-position-worker.js` 离开主线程;主线程只接收位置快照、更新共享交互坐标及绘制缓冲。轨道数学由 `satellite-propagation.js` 同时供 Worker、预测轨道和同步降级路径使用,避免多套公式漂移。数据重载、清空和高度切换必须同步终止旧计算,不能让旧快照恢复已清空的图层。

View File

@@ -16,12 +16,13 @@
## Current Version
- `main` 当前主线历史推导到:`0.16.5`
- `dev` 当前开发分支历史推导到:`0.76.1`
- `dev` 当前开发分支历史推导到:`0.76.2`
## Timeline
| Version | Type | Branch | Commit | Summary |
| --- | --- | --- | --- | --- |
| `0.76.2` | bugfix | `dev` | `v0.76.2` | 优化光缆密集区卫星点拾取,统一点状对象与线缆候选排序并补齐回归验证 |
| `0.76.1` | bugfix | `dev` | `v0.76.1` | 修复选中 BGP 事件静态光晕地表遮挡,统一事件锚点显隐与前景偏移,清理中间态逻辑并补齐验证 |
| `0.76.0` | feature | `dev` | `v0.76.0` | 详情卡默认配图与后台配置、五类算力中心、统一地表拾取与选中置顶、卡片交互修复及 Earth 性能优化 |
| `0.75.4` | improvement | `dev` | `v0.75.4` | 图例统一筛选、卫星 Points 点径与性能优化、FPS、菜单 DPI/hover、星空 sRGB、地表对齐及新闻编码修复,清理废弃代码文档 |

View File

@@ -1,6 +1,6 @@
{
"name": "planet-frontend",
"version": "0.76.1",
"version": "0.76.2",
"private": true,
"packageManager": "bun@1",
"dependencies": {

View File

@@ -27,3 +27,9 @@ export function pickNearestInteractiveHit(groups = []) {
);
return candidates[0] || null;
}
export function pickNearestPointCandidate(candidates = []) {
return candidates
.filter(candidate => candidate?.object && Number.isFinite(candidate.distancePxSq))
.sort((left, right) => left.distancePxSq - right.distancePxSq)[0] || null;
}

View File

@@ -1,5 +1,5 @@
import { expect, test } from "bun:test";
import { pickNearestInteractiveHit } from "./interactive-picking.js";
import { pickNearestInteractiveHit, pickNearestPointCandidate } from "./interactive-picking.js";
const hit = (object, distancePxSq, cluster = false) => ({ object, distancePxSq, cluster });
@@ -23,3 +23,10 @@ test("cluster hits are ignored while pointer eligibility remains true for real m
{ type: "bgp_collector", hits: [hit("rrc22", 9)] },
]).object).toBe("rrc22");
});
test("nearest point candidate is resolved before the line fallback", () => {
expect(pickNearestPointCandidate([
{ type: "compute_center", object: "compute", distancePxSq: 16 },
{ type: "satellite", object: "satellite", distancePxSq: 0 },
]).type).toBe("satellite");
});

View File

@@ -13,7 +13,7 @@ import { applyBGPLegendFilter } from "./bgp.js";
import { applyCountryBoundaryLegendFilter } from "./country-boundaries.js";
import { isSatelliteCategoryVisible, updateSatelliteLabels } from "./satellites.js";
import { beginFrameStats, finishFrameStats } from "./frame-stats.js";
import { pickNearestInteractiveHit } from "./interactive-picking.js";
import { pickNearestInteractiveHit, pickNearestPointCandidate } from "./interactive-picking.js";
import { createScreenSpaceDrag, globeDragRotationFactor } from "./globe-navigation.js";
import * as THREE from "three";
@@ -5816,23 +5816,33 @@ function onMouseMove(event) {
vesselIntersects,
earthInteractableIntersects,
});
const satelliteCandidate = satIntersects.length > 0
? {
type: "satellite",
index: satIntersects[0].index,
object: getSatelliteAt(satIntersects[0].index),
screen: { x: event.clientX, y: event.clientY },
distancePxSq: satIntersects[0].distanceSq,
}
: null;
const pointPick = pickNearestPointCandidate([interactivePick, satelliteCandidate]);
if (renderer?.domElement) {
renderer.domElement.style.cursor =
interactivePick || satIntersects?.length || cableIntersects?.length
pointPick || cableIntersects?.length
? "pointer"
: "default";
}
let hoveredSat = null;
let hoveredSatIndexFromIntersect = null;
if (satIntersects.length > 0) {
const satIndex = satIntersects[0].index;
if (pointPick?.type === "satellite") {
const satIndex = pointPick.index;
hoveredSatIndexFromIntersect = satIndex;
hoveredSat = selectSatellite(satIndex);
}
const hoveredBGPMarker =
interactivePick?.type === "bgp" || interactivePick?.type === "bgp_collector"
pointPick?.type === "bgp" || pointPick?.type === "bgp_collector"
? getPrimaryBGPHoverTarget(bgpAnomalyIntersects, bgpCollectorIntersects)
: null;
@@ -5842,11 +5852,11 @@ function onMouseMove(event) {
}
const hoveredComputeCenterMarker =
interactivePick?.type === "compute_center" ? interactivePick.object : null;
pointPick?.type === "compute_center" ? pointPick.object : null;
const hoveredVesselMarker =
interactivePick?.type === "vessel" && vesselPick.checked ? interactivePick.object : null;
pointPick?.type === "vessel" && vesselPick.checked ? pointPick.object : null;
const hoveredEarthInteractableMarker =
interactivePick?.type === "earth_interactable" ? interactivePick.object : null;
pointPick?.type === "earth_interactable" ? pointPick.object : null;
const earthPoint = screenToEarthCoords(
event.clientX,
event.clientY,
@@ -5962,15 +5972,7 @@ function onMouseMove(event) {
getEarthInteractableBriefHtml(hoveredEarthInteractableMarker),
);
objectTooltipShown = true;
} else if (cableIntersects.length > 0 && getShowCables()) {
const cable = cableIntersects[0].object;
hoveredCable = cable;
if (!isSameCable(cable, lockedObject)) {
setCableState(cable.userData.cableId, CABLE_STATE.HOVERED);
}
showTooltip(event.clientX + TOOLTIP_CURSOR_OFFSET, event.clientY + TOOLTIP_CURSOR_OFFSET, getCableBriefHtml(cable));
objectTooltipShown = true;
} else if (hoveredSat?.properties) {
} else if (hoveredSat?.properties && pointPick?.type === "satellite") {
hoveredSatellite = hoveredSat;
hoveredSatelliteIndex = hoveredSatIndexFromIntersect;
if (hoveredSatelliteIndex !== lockedSatelliteIndex) {
@@ -5986,6 +5988,14 @@ function onMouseMove(event) {
setHoveredSatelliteIndex(hoveredSatelliteIndex);
showTooltip(event.clientX + TOOLTIP_CURSOR_OFFSET, event.clientY + TOOLTIP_CURSOR_OFFSET, getSatelliteBriefHtml(hoveredSat.properties));
objectTooltipShown = true;
} else if (cableIntersects.length > 0 && getShowCables()) {
const cable = cableIntersects[0].object;
hoveredCable = cable;
if (!isSameCable(cable, lockedObject)) {
setCableState(cable.userData.cableId, CABLE_STATE.HOVERED);
}
showTooltip(event.clientX + TOOLTIP_CURSOR_OFFSET, event.clientY + TOOLTIP_CURSOR_OFFSET, getCableBriefHtml(cable));
objectTooltipShown = true;
} else if (lockedObjectType === "bgp" && lockedObject) {
applyBGPHoverState(lockedObject);
} else if (lockedObjectType === "bgp_collector" && lockedObject) {
@@ -6239,6 +6249,23 @@ function onClick(event) {
vesselIntersects,
earthInteractableIntersects,
});
const satelliteCandidate = satIntersects.length > 0
? {
type: "satellite",
index: satIntersects[0].index,
object: getSatelliteAt(satIntersects[0].index),
screen: { x: event.clientX, y: event.clientY },
distancePxSq: satIntersects[0].distanceSq,
}
: null;
const pointPick = pickNearestPointCandidate([interactivePick, satelliteCandidate]);
if (pointPick?.type === "satellite") {
if (confirmInteractionCandidate(pointPick, { showConfirmationFeedback: false })) {
showSelectionStatus(getInteractionCandidateLabel(pointPick));
}
return;
}
if (interactivePick) {
let marker = interactivePick.object;
@@ -6272,21 +6299,6 @@ function onClick(event) {
return;
}
if (satIntersects.length > 0) {
const hit = satIntersects[0];
const candidate = {
type: "satellite",
index: hit.index,
object: getSatelliteAt(hit.index),
screen: { x: event.clientX, y: event.clientY },
distancePxSq: hit.distancePxSq,
};
if (confirmInteractionCandidate(candidate, { showConfirmationFeedback: false })) {
showSelectionStatus(getInteractionCandidateLabel(candidate));
}
return;
}
if (isCruiseModeActive()) interruptCruisePresentation({ resetLoop: true });
clearLockedObject();
hideInfoCard();

View File

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

2
uv.lock generated
View File

@@ -757,7 +757,7 @@ wheels = [
[[package]]
name = "planet"
version = "0.76.1"
version = "0.76.2"
source = { virtual = "." }
dependencies = [
{ name = "aiofiles" },