feat(earth): cable state management, hover/lock visual separation, fix isSameCable undefined bug
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
Reference in New Issue
Block a user