import * as THREE from 'three'; import { createNoise3D } from 'simplex-noise'; import { CONFIG } from './constants.js'; import { latLonToVector3, vector3ToLatLon, screenToEarthCoords } from './utils.js'; import { showStatusMessage, updateCoordinatesDisplay, updateZoomDisplay, updateEarthStats, updateCableDetails, setLoading, showTooltip, hideTooltip } from './ui.js'; import { createEarth, createClouds, createTerrain, createStars, createGridLines, toggleTerrain, getEarth } from './earth.js'; import { loadGeoJSONFromPath, loadLandingPoints, handleCableClick, clearCableSelection, getCableLines } from './cables.js'; import { setupControls, getAutoRotate, getShowTerrain, zoomLevel, setAutoRotate, toggleAutoRotate } from './controls.js'; export let scene, camera, renderer; let simplex; let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; let hoveredCable = null; let lockedCable = null; window.addEventListener('error', (e) => { console.error('全局错误:', e.error); showStatusMessage('加载错误: ' + e.error?.message, 'error'); }); window.addEventListener('unhandledrejection', (e) => { console.error('未处理的Promise错误:', e.reason); }); export function init() { simplex = createNoise3D(); scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = CONFIG.defaultCameraZ; renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, powerPreference: 'high-performance' }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0x0a0a1a, 1); renderer.setPixelRatio(window.devicePixelRatio); document.getElementById('container').appendChild(renderer.domElement); addLights(); const earthObj = createEarth(scene); createClouds(scene, earthObj); createTerrain(scene, earthObj, simplex); createStars(scene); createGridLines(scene, earthObj); setupControls(camera, renderer, scene, earthObj); setupEventListeners(camera, renderer); loadData(); animate(); } function addLights() { const ambientLight = new THREE.AmbientLight(0x404060); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); directionalLight.position.set(5, 3, 5); scene.add(directionalLight); const backLight = new THREE.DirectionalLight(0x446688, 0.3); backLight.position.set(-5, 0, -5); scene.add(backLight); const pointLight = new THREE.PointLight(0xffffff, 0.4); pointLight.position.set(10, 10, 10); scene.add(pointLight); } async function loadData() { setLoading(true); try { console.log('开始加载电缆数据...'); await loadGeoJSONFromPath(scene, getEarth()); console.log('电缆数据加载完成'); await loadLandingPoints(scene, getEarth()); console.log('登陆点数据加载完成'); } catch (error) { console.error('加载数据失败:', error); showStatusMessage('加载数据失败: ' + error.message, 'error'); } setLoading(false); } function setupEventListeners(camera, renderer) { window.addEventListener('resize', () => onWindowResize(camera, renderer)); renderer.domElement.addEventListener('mousemove', (e) => onMouseMove(e, camera)); renderer.domElement.addEventListener('mousedown', onMouseDown); renderer.domElement.addEventListener('mouseup', onMouseUp); renderer.domElement.addEventListener('click', (e) => onClick(e, camera, renderer)); } function onWindowResize(camera, renderer) { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function getFrontFacingCables(cableLines, camera) { const earth = getEarth(); if (!earth) return cableLines; const cameraDir = new THREE.Vector3(); camera.getWorldDirection(cameraDir); return cableLines.filter(cable => { const cablePos = new THREE.Vector3(); cable.geometry.computeBoundingBox(); const boundingBox = cable.geometry.boundingBox; if (boundingBox) { boundingBox.getCenter(cablePos); cable.localToWorld(cablePos); } const toCamera = new THREE.Vector3().subVectors(camera.position, earth.position).normalize(); const toCable = new THREE.Vector3().subVectors(cablePos, earth.position).normalize(); return toCamera.dot(toCable) > 0; }); } function onMouseMove(event, camera) { const earth = getEarth(); if (!earth) return; const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2( (event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1 ); raycaster.setFromCamera(mouse, camera); const allCableLines = getCableLines(); const frontCables = getFrontFacingCables(allCableLines, camera); const intersects = raycaster.intersectObjects(frontCables); if (hoveredCable && hoveredCable !== lockedCable) { if (hoveredCable.userData.originalColor !== undefined) { hoveredCable.material.color.setHex(hoveredCable.userData.originalColor); } hoveredCable = null; } if (intersects.length > 0) { const cable = intersects[0].object; if (cable !== lockedCable) { cable.material.color.setHex(0xffffff); cable.material.opacity = 1; hoveredCable = cable; } const userData = cable.userData; document.getElementById('cable-name').textContent = userData.name || userData.shortname || '未命名电缆'; document.getElementById('cable-owner').textContent = userData.owner || '-'; document.getElementById('cable-status').textContent = userData.status || '-'; document.getElementById('cable-length').textContent = userData.length || '-'; document.getElementById('cable-coords').textContent = '-'; document.getElementById('cable-rfs').textContent = userData.rfs || '-'; hideTooltip(); } else { if (!lockedCable) { document.getElementById('cable-name').textContent = '点击电缆查看详情'; document.getElementById('cable-owner').textContent = '-'; document.getElementById('cable-status').textContent = '-'; document.getElementById('cable-length').textContent = '-'; document.getElementById('cable-coords').textContent = '-'; document.getElementById('cable-rfs').textContent = '-'; } const earthPoint = screenToEarthCoords(event.clientX, event.clientY, camera, earth); if (earthPoint) { const coords = vector3ToLatLon(earthPoint); updateCoordinatesDisplay(coords.lat, coords.lon, coords.alt); if (!isDragging) { showTooltip(event.clientX + 10, event.clientY + 10, `纬度: ${coords.lat}°
经度: ${coords.lon}°
海拔: ${coords.alt.toFixed(1)} km`); } } } if (isDragging) { const deltaX = event.clientX - previousMousePosition.x; const deltaY = event.clientY - previousMousePosition.y; earth.rotation.y += deltaX * 0.005; earth.rotation.x += deltaY * 0.005; previousMousePosition = { x: event.clientX, y: event.clientY }; } } function onMouseDown(event) { isDragging = true; previousMousePosition = { x: event.clientX, y: event.clientY }; document.getElementById('container').classList.add('dragging'); hideTooltip(); } function onMouseUp() { isDragging = false; document.getElementById('container').classList.remove('dragging'); } function onClick(event, camera, renderer) { const earth = getEarth(); if (!earth) return; const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2( (event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1 ); raycaster.setFromCamera(mouse, camera); const allCableLines = getCableLines(); const frontCables = getFrontFacingCables(allCableLines, camera); const intersects = raycaster.intersectObjects(frontCables); if (intersects.length > 0) { if (lockedCable && lockedCable !== intersects[0].object) { if (lockedCable.userData.originalColor !== undefined) { lockedCable.material.color.setHex(lockedCable.userData.originalColor); } } lockedCable = intersects[0].object; lockedCable.material.color.setHex(0xffffff); setAutoRotate(false); handleCableClick(intersects[0].object); } else { if (lockedCable) { if (lockedCable.userData.originalColor !== undefined) { lockedCable.material.color.setHex(lockedCable.userData.originalColor); } lockedCable = null; } setAutoRotate(true); clearCableSelection(); } } function animate() { requestAnimationFrame(animate); const earth = getEarth(); if (getAutoRotate() && earth) { earth.rotation.y += CONFIG.rotationSpeed; } if (lockedCable) { const pulse = (Math.sin(Date.now() * 0.003) + 1) * 0.5; lockedCable.material.opacity = 0.6 + pulse * 0.4; const glowIntensity = 0.7 + pulse * 0.3; lockedCable.material.color.setRGB(glowIntensity, glowIntensity, glowIntensity); } renderer.render(scene, camera); } window.clearLockedCable = function() { if (lockedCable) { if (lockedCable.userData.originalColor !== undefined) { lockedCable.material.color.setHex(lockedCable.userData.originalColor); lockedCable.material.opacity = 1.0; } lockedCable = null; } clearCableSelection(); }; document.addEventListener('DOMContentLoaded', init);