feat(earth): cable state management, hover/lock visual separation, fix isSameCable undefined bug

This commit is contained in:
rayd1o
2026-03-19 16:46:40 +08:00
parent 869d661a94
commit 0ecc1bc537
3 changed files with 113 additions and 77 deletions

View File

@@ -2,7 +2,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { CONFIG, CABLE_COLORS, PATHS } from './constants.js'; import { CONFIG, CABLE_COLORS, PATHS, CABLE_STATE } from './constants.js';
import { latLonToVector3 } from './utils.js'; import { latLonToVector3 } from './utils.js';
import { updateEarthStats, showStatusMessage } from './ui.js'; import { updateEarthStats, showStatusMessage } from './ui.js';
import { showInfoCard } from './info-card.js'; import { showInfoCard } from './info-card.js';
@@ -340,3 +340,25 @@ export function getCablesById(cableId) {
export function getLandingPoints() { export function getLandingPoints() {
return landingPoints; return landingPoints;
} }
const cableStates = new Map();
export function getCableState(cableId) {
return cableStates.get(cableId) || CABLE_STATE.NORMAL;
}
export function setCableState(cableId, state) {
cableStates.set(cableId, state);
}
export function clearAllCableStates() {
cableStates.clear();
}
export function getCableStateInfo() {
const states = {};
cableStates.forEach((state, cableId) => {
states[cableId] = state;
});
return states;
}

View File

@@ -47,6 +47,12 @@ export const CABLE_CONFIG = {
pulseCoefficient: 0.4 pulseCoefficient: 0.4
}; };
export const CABLE_STATE = {
NORMAL: 'normal',
HOVERED: 'hovered',
LOCKED: 'locked'
};
export const GRID_CONFIG = { export const GRID_CONFIG = {
latitudeStep: 10, latitudeStep: 10,
longitudeStep: 30, longitudeStep: 30,

View File

@@ -1,7 +1,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { createNoise3D } from 'simplex-noise'; import { createNoise3D } from 'simplex-noise';
import { CONFIG, CABLE_CONFIG } from './constants.js'; import { CONFIG, CABLE_CONFIG, CABLE_STATE } from './constants.js';
import { latLonToVector3, vector3ToLatLon, screenToEarthCoords } from './utils.js'; import { latLonToVector3, vector3ToLatLon, screenToEarthCoords } from './utils.js';
import { import {
showStatusMessage, showStatusMessage,
@@ -13,7 +13,7 @@ import {
hideTooltip hideTooltip
} from './ui.js'; } from './ui.js';
import { createEarth, createClouds, createTerrain, createStars, createGridLines, toggleTerrain, getEarth } from './earth.js'; import { createEarth, createClouds, createTerrain, createStars, createGridLines, toggleTerrain, getEarth } from './earth.js';
import { loadGeoJSONFromPath, loadLandingPoints, handleCableClick, clearCableSelection, getCableLines, getCablesById, lockedCable as cableLocked } from './cables.js'; import { loadGeoJSONFromPath, loadLandingPoints, handleCableClick, clearCableSelection, getCableLines, getCablesById, lockedCable as cableLocked, getCableState, setCableState, clearAllCableStates } from './cables.js';
import { createSatellites, loadSatellites, updateSatellitePositions, toggleSatellites, toggleTrails, getShowSatellites, selectSatellite, getSatelliteData, getSatellitePoints } from './satellites.js'; import { createSatellites, loadSatellites, updateSatellitePositions, toggleSatellites, toggleTrails, getShowSatellites, selectSatellite, getSatelliteData, getSatellitePoints } from './satellites.js';
import { setupControls, getAutoRotate, getShowTerrain, zoomLevel, setAutoRotate, toggleAutoRotate, resetView } from './controls.js'; import { setupControls, getAutoRotate, getShowTerrain, zoomLevel, setAutoRotate, toggleAutoRotate, resetView } from './controls.js';
import { initInfoCard, showInfoCard, hideInfoCard, getCurrentType, setInfoCardNoBorder } from './info-card.js'; import { initInfoCard, showInfoCard, hideInfoCard, getCurrentType, setInfoCardNoBorder } from './info-card.js';
@@ -33,6 +33,7 @@ let isLongDrag = false;
function clearLockedObject() { function clearLockedObject() {
hoveredCable = null; hoveredCable = null;
clearAllCableStates();
lockedObject = null; lockedObject = null;
lockedObjectType = null; lockedObjectType = null;
lockedSatellite = null; lockedSatellite = null;
@@ -40,25 +41,61 @@ function clearLockedObject() {
} }
function isSameCable(cable1, cable2) { function isSameCable(cable1, cable2) {
return cable1 && cable2 && cable1.userData.cableId === cable2.userData.cableId; if (!cable1 || !cable2) return false;
const id1 = cable1.userData?.cableId;
const id2 = cable2.userData?.cableId;
if (id1 === undefined || id2 === undefined) return false;
return id1 === id2;
}
function showCableInfo(cable) {
showInfoCard('cable', {
name: cable.userData.name,
owner: cable.userData.owner,
status: cable.userData.status,
length: cable.userData.length,
coords: cable.userData.coords,
rfs: cable.userData.rfs
});
}
function showSatelliteInfo(props) {
const meanMotion = props?.mean_motion || 0;
const period = meanMotion > 0 ? (1440 / meanMotion).toFixed(1) : '-';
const ecc = props?.eccentricity || 0;
const perigee = (6371 * (1 - ecc)).toFixed(0);
const apogee = (6371 * (1 + ecc)).toFixed(0);
showInfoCard('satellite', {
name: props?.name || '-',
norad_id: props?.norad_cat_id,
inclination: props?.inclination ? props.inclination.toFixed(2) : '-',
period: period,
perigee: perigee,
apogee: apogee
});
} }
function applyCableVisualState() { function applyCableVisualState() {
const allCables = getCableLines(); const allCables = getCableLines();
const pulse = (Math.sin(Date.now() * CABLE_CONFIG.pulseSpeed) + 1) * 0.5; const pulse = (Math.sin(Date.now() * CABLE_CONFIG.pulseSpeed) + 1) * 0.5;
const isLocked = lockedObjectType === 'cable' && lockedObject;
allCables.forEach(c => { allCables.forEach(c => {
const cableIsLocked = isSameCable(c, lockedObject); const cableId = c.userData.cableId;
const cableIsHovered = isSameCable(c, hoveredCable); const state = getCableState(cableId);
if (cableIsLocked) { switch (state) {
case CABLE_STATE.LOCKED:
c.material.opacity = CABLE_CONFIG.lockedOpacityMin + pulse * CABLE_CONFIG.pulseCoefficient; c.material.opacity = CABLE_CONFIG.lockedOpacityMin + pulse * CABLE_CONFIG.pulseCoefficient;
c.material.color.setRGB(1, 1, 1); c.material.color.setRGB(1, 1, 1);
} else if (cableIsHovered) { break;
case CABLE_STATE.HOVERED:
c.material.opacity = 1; c.material.opacity = 1;
c.material.color.setRGB(1, 1, 1); c.material.color.setRGB(1, 1, 1);
} else if (isLocked) { break;
case CABLE_STATE.NORMAL:
default:
if (lockedObjectType === 'cable' && lockedObject) {
c.material.opacity = CABLE_CONFIG.otherOpacity; c.material.opacity = CABLE_CONFIG.otherOpacity;
const origColor = c.userData.originalColor; const origColor = c.userData.originalColor;
const brightness = CABLE_CONFIG.otherBrightness; const brightness = CABLE_CONFIG.otherBrightness;
@@ -71,6 +108,7 @@ function applyCableVisualState() {
c.material.opacity = 1; c.material.opacity = 1;
c.material.color.setHex(c.userData.originalColor); c.material.color.setHex(c.userData.originalColor);
} }
}
}); });
} }
@@ -231,10 +269,6 @@ function onMouseMove(event, camera) {
const frontCables = getFrontFacingCables(allCableLines, camera); const frontCables = getFrontFacingCables(allCableLines, camera);
const intersects = raycaster.intersectObjects(frontCables); const intersects = raycaster.intersectObjects(frontCables);
if (hoveredCable && (lockedObjectType !== 'cable' || !isSameCable(hoveredCable, lockedObject))) {
hoveredCable = null;
}
const hasHoveredCable = intersects.length > 0; const hasHoveredCable = intersects.length > 0;
let hoveredSat = null; let hoveredSat = null;
if (getShowSatellites()) { if (getShowSatellites()) {
@@ -249,57 +283,35 @@ function onMouseMove(event, camera) {
} }
const hasHoveredSatellite = hoveredSat && hoveredSat.properties; const hasHoveredSatellite = hoveredSat && hoveredSat.properties;
if (hoveredCable) {
if (!hasHoveredCable || !isSameCable(intersects[0]?.object, hoveredCable)) {
if (!isSameCable(hoveredCable, lockedObject)) {
setCableState(hoveredCable.userData.cableId, CABLE_STATE.NORMAL);
}
hoveredCable = null;
}
}
if (hasHoveredCable) { if (hasHoveredCable) {
const cable = intersects[0].object; const cable = intersects[0].object;
if (!isSameCable(cable, lockedObject)) {
hoveredCable = cable; hoveredCable = cable;
setCableState(cable.userData.cableId, CABLE_STATE.HOVERED);
} else {
hoveredCable = cable;
}
showInfoCard('cable', { showCableInfo(cable);
name: cable.userData.name,
owner: cable.userData.owner,
status: cable.userData.status,
length: cable.userData.length,
coords: cable.userData.coords,
rfs: cable.userData.rfs
});
setInfoCardNoBorder(true); setInfoCardNoBorder(true);
hideTooltip(); hideTooltip();
} else if (hasHoveredSatellite) { } else if (hasHoveredSatellite) {
hoveredSatellite = hoveredSat; hoveredSatellite = hoveredSat;
const props = hoveredSat.properties; showSatelliteInfo(hoveredSat.properties);
const meanMotion = props.mean_motion || 0;
const period = meanMotion > 0 ? (1440 / meanMotion).toFixed(1) : '-';
const ecc = props.eccentricity || 0;
const perigee = (6371 * (1 - ecc)).toFixed(0);
const apogee = (6371 * (1 + ecc)).toFixed(0);
showInfoCard('satellite', {
name: props.name,
norad_id: props.norad_cat_id,
inclination: props.inclination ? props.inclination.toFixed(2) : '-',
period: period,
perigee: perigee,
apogee: apogee
});
setInfoCardNoBorder(true); setInfoCardNoBorder(true);
} else if (lockedObjectType === 'cable') { } else if (lockedObjectType === 'cable' && lockedObject) {
handleCableClick(lockedObject); showCableInfo(lockedObject);
} else if (lockedObjectType === 'satellite') { } else if (lockedObjectType === 'satellite' && lockedSatellite) {
const props = lockedSatellite.properties; showSatelliteInfo(lockedSatellite.properties);
const meanMotion = props?.mean_motion || 0;
const period = meanMotion > 0 ? (1440 / meanMotion).toFixed(1) : '-';
const ecc = props?.eccentricity || 0;
const perigee = (6371 * (1 - ecc)).toFixed(0);
const apogee = (6371 * (1 + ecc)).toFixed(0);
showInfoCard('satellite', {
name: props?.name || '-',
norad_id: props?.norad_cat_id,
inclination: props?.inclination ? props.inclination.toFixed(2) : '-',
period: period,
perigee: perigee,
apogee: apogee
});
} else { } else {
hideInfoCard(); hideInfoCard();
} }
@@ -368,12 +380,8 @@ function onClick(event, camera, renderer) {
const clickedCable = intersects[0].object; const clickedCable = intersects[0].object;
const cableId = clickedCable.userData.cableId; const cableId = clickedCable.userData.cableId;
const sameCables = getCablesById(cableId);
sameCables.forEach(c => { setCableState(cableId, CABLE_STATE.LOCKED);
c.material.color.setHex(0xffffff);
c.material.opacity = 1;
});
lockedObject = clickedCable; lockedObject = clickedCable;
lockedObjectType = 'cable'; lockedObjectType = 'cable';