diff --git a/src/scene/CesiumGlobe.js b/src/scene/CesiumGlobe.js index 4a6bde8..ae3239a 100644 --- a/src/scene/CesiumGlobe.js +++ b/src/scene/CesiumGlobe.js @@ -26,11 +26,6 @@ const IMAGERY_URL = const TERRAIN_URL = 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer'; -// 右键环视角度限制与灵敏度 -const MAX_LOOK_ROT = (40 * Math.PI) / 180; // 左右旋转 ±40° -const MAX_LOOK_TILT = (20 * Math.PI) / 180; // 上下倾角 ±20° -const LOOK_RATE = Math.PI / 720; // 每像素约 0.25° - // 计算多边形集合的球面面积(km²),用于按国土面积分级国家名字号 function sphericalAreaKm2(polys) { let area = 0; @@ -217,6 +212,7 @@ export class CesiumGlobe { Cesium.CameraEventType.PINCH, ]; controller.tiltEventTypes = [ + Cesium.CameraEventType.RIGHT_DRAG, // 右键 = 原生 tilt(传统环视) Cesium.CameraEventType.PINCH, { eventType: Cesium.CameraEventType.LEFT_DRAG, @@ -259,7 +255,6 @@ export class CesiumGlobe { this._buildMarkers(); this._setupPick(); - this._setupLookAround(); this._loadBoundaries(); this._buildCityLabels(); @@ -360,172 +355,6 @@ export class CesiumGlobe { viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); } - // 右键环视:绕相机注视点(地形表面)做轨道倾倒。左右 = 经度方向、上下 = 纬度方向,强制北朝上。 - // 关键:把轨道后的视线方向转到 ENU 后直接求 heading/pitch 传给 setView, - // 绕过 setView 在近天底处用 up 反推 heading 的奇点(否则小角度倾斜会被翻转)。 - _setupLookAround() { - const scene = this.viewer.scene; - const camera = this.viewer.camera; - const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); - this._lookState = null; - this._orbitScratch = { - offset: new Cesium.Cartesian3(), - upAxis: new Cesium.Cartesian3(), - eastAxis: new Cesium.Cartesian3(), - northAxis: new Cesium.Cartesian3(), - qAz: new Cesium.Quaternion(), - qEl: new Cesium.Quaternion(), - q: new Cesium.Quaternion(), - rotMat: new Cesium.Matrix3(), - newOffset: new Cesium.Cartesian3(), - newPos: new Cesium.Cartesian3(), - dir: new Cesium.Cartesian3(), - enu: new Cesium.Matrix4(), - invEnu: new Cesium.Matrix4(), - localDir: new Cesium.Cartesian3(), - enuUp: new Cesium.Cartesian3(), - u0: new Cesium.Cartesian3(), - un: new Cesium.Cartesian3(), - cross: new Cesium.Cartesian3(), - }; - - handler.setInputAction((movement) => { - // 取消左键旋转/缩放/倾斜的惯性余力,避免右键拖动时叠加成“又转又倾斜” - const ctrl = scene.screenSpaceCameraController; - const inertiaStates = [ - '_lastInertiaSpinMovement', - '_lastInertiaZoomMovement', - '_lastInertiaTiltMovement', - ]; - for (let i = 0; i < inertiaStates.length; i++) { - const st = ctrl[inertiaStates[i]]; - if (st) st.inertiaEnabled = false; - } - // 旋转中心 = 相机当前注视的屏幕中心点(地形表面),而不是点击点 - const canvas = scene.canvas; - const center = Cesium.Cartesian2.fromElements( - canvas.clientWidth / 2, - canvas.clientHeight / 2, - ); - const ray = camera.getPickRay(center); - let pivot = scene.globe.pick(ray, scene); - if (!Cesium.defined(pivot)) { - pivot = camera.pickEllipsoid(center, scene.globe.ellipsoid); - } - if (!Cesium.defined(pivot)) return; - const sc = this._orbitScratch; - const offset = Cesium.Cartesian3.subtract(camera.position, pivot, sc.offset); - const upAxis = Cesium.Cartesian3.normalize(pivot, sc.upAxis); - const eastAxis = Cesium.Cartesian3.cross(Cesium.Cartesian3.UNIT_Z, pivot, sc.eastAxis); // 上下拖动(纬度方向) - if (Cesium.Cartesian3.magnitudeSquared(eastAxis) < 1e-12) { - eastAxis.x = 1; - eastAxis.y = 0; - eastAxis.z = 0; // 极点退化兜底 - } else { - Cesium.Cartesian3.normalize(eastAxis, eastAxis); - } - const northAxis = Cesium.Cartesian3.normalize( - Cesium.Cartesian3.cross(upAxis, eastAxis, sc.northAxis), // 左右拖动(经度方向) - sc.northAxis, - ); - this._lookState = { - pivot: Cesium.Cartesian3.clone(pivot), - startOffset: Cesium.Cartesian3.clone(offset), - eastAxis: Cesium.Cartesian3.clone(eastAxis), - northAxis: Cesium.Cartesian3.clone(northAxis), - position: Cesium.Cartesian3.clone(camera.position), - direction: Cesium.Cartesian3.clone(camera.direction), - up: Cesium.Cartesian3.clone(camera.up), - azimuth: 0, - elevation: 0, - lastX: movement.position.x, - lastY: movement.position.y, - }; - }, Cesium.ScreenSpaceEventType.RIGHT_DOWN); - - handler.setInputAction((movement) => { - if (!this._lookState) return; - const s = this._lookState; - const dx = movement.endPosition.x - s.lastX; - const dy = movement.endPosition.y - s.lastY; - s.lastX = movement.endPosition.x; - s.lastY = movement.endPosition.y; - s.azimuth = Cesium.Math.clamp(s.azimuth + dx * LOOK_RATE, -MAX_LOOK_ROT, MAX_LOOK_ROT); - s.elevation = Cesium.Math.clamp(s.elevation + dy * LOOK_RATE, -MAX_LOOK_TILT, MAX_LOOK_TILT); - this._orbitCamera(s); - }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); - - handler.setInputAction(() => { - if (!this._lookState) return; - const s = this._lookState; - this._lookState = null; - camera.setView({ - destination: s.position, - orientation: { direction: s.direction, up: s.up }, - }); - }, Cesium.ScreenSpaceEventType.RIGHT_UP); - - this._lookHandler = handler; - } - - // 轨道倾倒:左右绕南北轴、上下绕东西轴,位置沿轨道移动且始终看向注视点;直接算 heading/pitch 传 setView - _orbitCamera(s) { - const camera = this.viewer.camera; - const sc = this._orbitScratch; - Cesium.Quaternion.fromAxisAngle(s.northAxis, s.azimuth, sc.qAz); - Cesium.Quaternion.fromAxisAngle(s.eastAxis, s.elevation, sc.qEl); - Cesium.Quaternion.multiply(sc.qEl, sc.qAz, sc.q); - Cesium.Matrix3.fromQuaternion(sc.q, sc.rotMat); - Cesium.Matrix3.multiplyByVector(sc.rotMat, s.startOffset, sc.newOffset); - Cesium.Cartesian3.add(s.pivot, sc.newOffset, sc.newPos); - const dir = Cesium.Cartesian3.normalize( - Cesium.Cartesian3.subtract(s.pivot, sc.newPos, sc.dir), - sc.dir, - ); - // 视线方向转到 newPos 处的 ENU,直接求 heading/pitch(避开 setView 近天底用 up 反推 heading 的翻转) - Cesium.Transforms.eastNorthUpToFixedFrame( - sc.newPos, - this.viewer.scene.globe.ellipsoid, - sc.enu, - ); - Cesium.Matrix4.inverseTransformation(sc.enu, sc.invEnu); - Cesium.Matrix4.multiplyByPointAsVector(sc.invEnu, dir, sc.localDir); - const heading = Math.atan2(sc.localDir.x, sc.localDir.y); - const pitch = Math.asin(sc.localDir.z); - - // 计算“北朝上”所需的 roll:把 roll=0 的 up(水平线方向)滚转到“世界 Z 投影”的 up, - // 这样倾斜时 up 不随方向转动(否则会又倾斜又旋转) - const dz = dir.z; - sc.un.x = -dir.x * dz; - sc.un.y = -dir.y * dz; - sc.un.z = 1 - dz * dz; - if (Cesium.Cartesian3.magnitudeSquared(sc.un) < 1e-12) { - Cesium.Cartesian3.clone(s.up, sc.un); - } else { - Cesium.Cartesian3.normalize(sc.un, sc.un); - } - Cesium.Cartesian3.normalize(sc.newPos, sc.enuUp); // 径向 = newPos 处的“上” - const d0 = Cesium.Cartesian3.dot(sc.enuUp, dir); - sc.u0.x = sc.enuUp.x - d0 * dir.x; - sc.u0.y = sc.enuUp.y - d0 * dir.y; - sc.u0.z = sc.enuUp.z - d0 * dir.z; - let roll = 0; - if (Cesium.Cartesian3.magnitudeSquared(sc.u0) > 1e-12) { - Cesium.Cartesian3.normalize(sc.u0, sc.u0); - Cesium.Cartesian3.cross(sc.u0, sc.un, sc.cross); - const sin = - Cesium.Cartesian3.magnitude(sc.cross) * - Math.sign(Cesium.Cartesian3.dot(sc.cross, dir)); - const cos = Cesium.Cartesian3.dot(sc.u0, sc.un); - roll = Math.atan2(sin, cos); - } - - camera.setView({ - destination: Cesium.Cartesian3.clone(sc.newPos), - orientation: { heading, pitch, roll }, - }); - } - // 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变 flyToEntry(index) { const entry = ENTRIES[index];