/**
|
* 轴交互管理器
|
* 职责:为已绘制的面提供 XYZ 轴与圆轴的可视化与交互(旋转/平移/悬停高亮),
|
* 在交互结束时可触发外部更新(如重新生成航线),并维护距离标签与控制器几何。
|
*/
|
export class AxisInteractionManager {
|
/**
|
* 构造函数
|
* @param {Cesium.Viewer} viewer Cesium Viewer 实例
|
* @param {EntityFactory} entityFactory 实体工厂
|
* @param {LabelManager} labelManager 标签管理器
|
* @param {GeometryUtils} geometryUtils 几何工具
|
* @param {EventHub} eventHub 事件中心
|
*/
|
constructor(viewer, entityFactory, labelManager, geometryUtils, eventHub) {
|
this.viewer = viewer
|
this.entityFactory = entityFactory
|
this.labelManager = labelManager
|
this.geometryUtils = geometryUtils
|
this.eventHub = eventHub
|
|
this.axisPosition = {}
|
this.centerPoint = null
|
this.axisEntyX = null
|
this.axisEntyY = null
|
this.axisEntyZ = null
|
this.sphereEntyX = null
|
this.sphereEntyY = null
|
this.sphereEntyZ = null
|
this.overlayModels = { axisX: null, axisY: null, axisZ: null, sphereX: null, sphereY: null, sphereZ: null }
|
this.lastOrientation = { axisX: null, axisY: null, axisZ: null, sphereX: null, sphereY: null, sphereZ: null }
|
this.centerVectorXC = null
|
this.centerVectorYC = null
|
this.centerVectorZC = null
|
this.dashLineList = []
|
this.flip = { x: false, y: false, z: false }
|
|
this.planeCtx = { activeShape: null, polygonPositionList: [], centerPoint: null }
|
this.axisModelUris = { axisX: 'gltf/green-arrows.gltf', axisY: 'gltf/red-arrows.gltf', axisZ: 'gltf/blue-arrows.gltf' }
|
this.axisColors = { axisX: Cesium.Color.fromCssColorString('#9ed81c'), axisY: Cesium.Color.fromCssColorString('#ec4346'), axisZ: Cesium.Color.fromCssColorString('#47dce4') }
|
this.axisLocalBasis = {
|
axisX: { forwardAxis: '-X', upAxis: 'Z' },
|
axisY: { forwardAxis: '-Z', upAxis: 'Y' },
|
axisZ: { forwardAxis: 'Y', upAxis: 'Z' }
|
}
|
this.circleModelUris = { sphereX: 'gltf/green-circle.gltf', sphereY: 'gltf/red-circle.gltf', sphereZ: 'gltf/blue-circle.gltf' }
|
this.circleLocalBasis = {
|
sphereX: { forwardAxis: 'Z', upAxis: 'X' },
|
sphereY: { forwardAxis: 'X', upAxis: 'Z' },
|
sphereZ: { forwardAxis: 'X', upAxis: 'Y' }
|
}
|
}
|
|
/**
|
* 注入当前平面上下文(面实体、顶点列表、中心点)
|
* @param {{activeShape:Cesium.Entity,polygonPositionList:Cesium.Cartesian3[],centerPoint:Cesium.Cartesian3}} ctx 上下文
|
*/
|
setPlaneContext (ctx) {
|
this.planeCtx = ctx
|
this.centerPoint = ctx.centerPoint
|
}
|
|
/**
|
* 依据四个顶点计算控制器几何(轴线、圆轴、虚线、中心向量等)
|
* @param {Cesium.Cartesian3} point1 顶点1
|
* @param {Cesium.Cartesian3} point2 顶点2
|
* @param {Cesium.Cartesian3} point3 顶点3
|
* @param {Cesium.Cartesian3} point4 顶点4
|
* @returns {Object} 几何集(包含轴、圆轴、虚线、中心点与中心向量)
|
*/
|
computeAxisGeometry (point1, point2, point3, point4) {
|
const mid = (a, b) => Cesium.Cartesian3.midpoint(a, b, new Cesium.Cartesian3())
|
const c14 = mid(point1, point4)
|
const c23 = mid(point2, point3)
|
const c12 = mid(point1, point2)
|
const c34 = mid(point3, point4)
|
const centerPoint = mid(point1, point3)
|
const d12 = Cesium.Cartesian3.distance(point2, point3, new Cesium.Cartesian3())
|
const v1423 = Cesium.Cartesian3.subtract(c14, c23, new Cesium.Cartesian3())
|
const v1234 = Cesium.Cartesian3.subtract(c12, c34, new Cesium.Cartesian3())
|
const nZ = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(v1423, v1234, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const axisZEnd = Cesium.Cartesian3.add(Cesium.Cartesian3.multiplyByScalar(nZ, d12 / 2, new Cesium.Cartesian3()), centerPoint, new Cesium.Cartesian3())
|
const nX = Cesium.Cartesian3.normalize(v1423, new Cesium.Cartesian3())
|
const axisXEnd = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nX, -d12 / 2, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const nY = Cesium.Cartesian3.normalize(v1234, new Cesium.Cartesian3())
|
const axisYEnd = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nY, -d12 / 2, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const bs = Cesium.BoundingSphere.fromPoints([point1, point2, point3, point4])
|
const dashLen = bs.radius * 1000
|
const dashXPos = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nX, dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const dashXNeg = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nX, -dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const dashYPos = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nY, dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const dashYNeg = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nY, -dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const dashZPos = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nZ, dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const dashZNeg = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nZ, -dashLen, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const zRef = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nX, -d12 / 3, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const xRef = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nY, -d12 / 3, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const yRef = Cesium.Cartesian3.add(centerPoint, Cesium.Cartesian3.multiplyByScalar(nZ, -d12 / 3, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const rotationNormalZ = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(axisZEnd, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const rotationNormalX = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(c14, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const rotationNormalY = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(c12, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const refDirZ = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(zRef, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const refDirX = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(xRef, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const refDirY = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(yRef, centerPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const sphereZ = this.axisAngleSphere(rotationNormalZ, zRef, centerPoint)
|
const sphereX = this.axisAngleSphere(rotationNormalX, xRef, centerPoint)
|
const sphereY = this.axisAngleSphere(rotationNormalY, yRef, centerPoint)
|
return {
|
centerPoint,
|
axisX: [centerPoint, axisXEnd],
|
axisY: [centerPoint, axisYEnd],
|
axisZ: [centerPoint, axisZEnd],
|
sphereX, sphereY, sphereZ,
|
dashX: [dashXNeg, dashXPos],
|
dashY: [dashYNeg, dashYPos],
|
dashZ: [dashZNeg, dashZPos],
|
centerVectorXC: Cesium.Cartesian3.subtract(axisXEnd, centerPoint, new Cesium.Cartesian3()),
|
centerVectorYC: Cesium.Cartesian3.subtract(axisYEnd, centerPoint, new Cesium.Cartesian3()),
|
centerVectorZC: Cesium.Cartesian3.subtract(axisZEnd, centerPoint, new Cesium.Cartesian3())
|
, rotationNormalX, rotationNormalY, rotationNormalZ, refDirX, refDirY, refDirZ
|
}
|
}
|
|
/**
|
* 应用几何到实体(若不存在则创建,存在则更新 positions),并缓存必要向量
|
* @param {Object} g 几何集(来自 computeAxisGeometry)
|
*/
|
applyAxisGeometry (g) {
|
this.centerPoint = g.centerPoint
|
if (this.dashLineList[0]) { this.dashLineList[0].polyline.positions = g.dashX } else { this.dashLineList[0] = this.entityFactory.drawDashLine(g.dashX, 'dashX') }
|
if (this.dashLineList[1]) { this.dashLineList[1].polyline.positions = g.dashY } else { this.dashLineList[1] = this.entityFactory.drawDashLine(g.dashY, 'dashY') }
|
if (this.dashLineList[2]) { this.dashLineList[2].polyline.positions = g.dashZ } else { this.dashLineList[2] = this.entityFactory.drawDashLine(g.dashZ, 'dashZ') }
|
|
const xDir = this.flip.x ? Cesium.Cartesian3.multiplyByScalar(g.centerVectorXC, -1, new Cesium.Cartesian3()) : g.centerVectorXC
|
const qX = this._getAxisOrientation(g.centerPoint, xDir, this.axisLocalBasis.axisX)
|
const qY = this._getAxisOrientation(g.centerPoint, g.rotationNormalY, this.axisLocalBasis.axisY)
|
const zDir = this.flip.z ? Cesium.Cartesian3.multiplyByScalar(g.rotationNormalZ, -1, new Cesium.Cartesian3()) : g.rotationNormalZ
|
const qZ = this._getAxisOrientation(g.centerPoint, zDir, this.axisLocalBasis.axisZ)
|
this.lastOrientation.axisX = qX
|
this.lastOrientation.axisY = qY
|
this.lastOrientation.axisZ = qZ
|
if (this.axisEntyX) {
|
this.axisEntyX.position = g.centerPoint
|
this.axisEntyX.orientation = qX
|
} else {
|
this.axisEntyX = this.viewer.entities.add({ name: 'controller', id: 'axisX', position: g.centerPoint, orientation: qX, model: { uri: this.axisModelUris.axisX, scale: 9, minimumPixelSize: 120, silhouetteColor: this.axisColors.axisX, silhouetteSize: 0 } })
|
}
|
if (this.axisEntyY) {
|
this.axisEntyY.position = g.centerPoint
|
this.axisEntyY.orientation = qY
|
} else {
|
this.axisEntyY = this.viewer.entities.add({ name: 'controller', id: 'axisY', position: g.centerPoint, orientation: qY, model: { uri: this.axisModelUris.axisY, scale: 9, minimumPixelSize: 120, silhouetteColor: this.axisColors.axisY, silhouetteSize: 0 } })
|
}
|
if (this.axisEntyZ) {
|
this.axisEntyZ.position = g.centerPoint
|
this.axisEntyZ.orientation = qZ
|
} else {
|
this.axisEntyZ = this.viewer.entities.add({ name: 'controller', id: 'axisZ', position: g.centerPoint, orientation: qZ, model: { uri: this.axisModelUris.axisZ, scale: 9, minimumPixelSize: 120, silhouetteColor: this.axisColors.axisZ, silhouetteSize: 0 } })
|
}
|
const qsX = this._getOrientationWithUp(g.centerPoint, g.refDirX, g.rotationNormalX, this.circleLocalBasis.sphereX)
|
const qsY = this._getOrientationWithUp(g.centerPoint, g.refDirY, g.rotationNormalY, this.circleLocalBasis.sphereY)
|
const zUp = this.flip.z ? Cesium.Cartesian3.multiplyByScalar(g.rotationNormalZ, -1, new Cesium.Cartesian3()) : g.rotationNormalZ
|
const qsZ = this._getOrientationWithUp(g.centerPoint, g.refDirZ, zUp, this.circleLocalBasis.sphereZ)
|
this.lastOrientation.sphereX = qsX
|
this.lastOrientation.sphereY = qsY
|
this.lastOrientation.sphereZ = qsZ
|
if (this.sphereEntyX) { this.sphereEntyX.position = g.centerPoint; this.sphereEntyX.orientation = qsX } else { this.sphereEntyX = this.viewer.entities.add({ name: 'controller', id: 'sphereX', position: g.centerPoint, orientation: qsX, model: { uri: this.circleModelUris.sphereX, scale: 18, minimumPixelSize: 200, silhouetteColor: this.axisColors.axisX, silhouetteSize: 0 }, renderOrder: 999 }) }
|
if (this.sphereEntyY) { this.sphereEntyY.position = g.centerPoint; this.sphereEntyY.orientation = qsY } else { this.sphereEntyY = this.viewer.entities.add({ name: 'controller', id: 'sphereY', position: g.centerPoint, orientation: qsY, model: { uri: this.circleModelUris.sphereY, scale: 18, minimumPixelSize: 200, silhouetteColor: this.axisColors.axisY, silhouetteSize: 0 } }) }
|
if (this.sphereEntyZ) { this.sphereEntyZ.position = g.centerPoint; this.sphereEntyZ.orientation = qsZ } else { this.sphereEntyZ = this.viewer.entities.add({ name: 'controller', id: 'sphereZ', position: g.centerPoint, orientation: qsZ, model: { uri: this.circleModelUris.sphereZ, scale: 18, minimumPixelSize: 200, silhouetteColor: this.axisColors.axisZ, silhouetteSize: 0 } }) }
|
|
const mAxisX = this._getEntityModelMatrix(this.axisEntyX)
|
const mAxisY = this._getEntityModelMatrix(this.axisEntyY)
|
const mAxisZ = this._getEntityModelMatrix(this.axisEntyZ)
|
const mSphereX = this._getEntityModelMatrix(this.sphereEntyX)
|
const mSphereY = this._getEntityModelMatrix(this.sphereEntyY)
|
const mSphereZ = this._getEntityModelMatrix(this.sphereEntyZ)
|
this._ensureOverlayModel('axisX', this.axisModelUris.axisX, mAxisX, this.axisColors.axisX, 9, 120)
|
this._ensureOverlayModel('axisY', this.axisModelUris.axisY, mAxisY, this.axisColors.axisY, 9, 120)
|
this._ensureOverlayModel('axisZ', this.axisModelUris.axisZ, mAxisZ, this.axisColors.axisZ, 9, 120)
|
this._ensureOverlayModel('sphereX', this.circleModelUris.sphereX, mSphereX, this.axisColors.axisX, 18, 200)
|
this._ensureOverlayModel('sphereY', this.circleModelUris.sphereY, mSphereY, this.axisColors.axisY, 18, 200)
|
this._ensureOverlayModel('sphereZ', this.circleModelUris.sphereZ, mSphereZ, this.axisColors.axisZ, 18, 200)
|
|
this.axisPosition['axisX'] = g.axisX
|
this.axisPosition['axisY'] = g.axisY
|
this.axisPosition['axisZ'] = g.axisZ
|
this.axisPosition['sphereX'] = g.sphereX
|
this.axisPosition['sphereY'] = g.sphereY
|
this.axisPosition['sphereZ'] = g.sphereZ
|
this.axisPosition['dashX'] = g.dashX
|
this.axisPosition['dashY'] = g.dashY
|
this.axisPosition['dashZ'] = g.dashZ
|
this.centerVectorXC = g.centerVectorXC
|
this.centerVectorYC = g.centerVectorYC
|
this.centerVectorZC = g.centerVectorZC
|
}
|
|
/**
|
* 便捷方法:计算并应用控制器几何
|
*/
|
createAxisEntity (point1, point2, point3, point4) {
|
const g = this.computeAxisGeometry(point1, point2, point3, point4)
|
this.applyAxisGeometry(g)
|
}
|
|
setReversalMode (flag) {
|
this.flip.z = !!flag
|
this.flip.x = !!flag
|
const p = this.planeCtx.polygonPositionList
|
if (Array.isArray(p) && p.length >= 4) this.createAxisEntity(p[0], p[1], p[2], p[3])
|
}
|
|
/**
|
* 生成围绕指定法向的圆轴折线点集合
|
* @param {Cesium.Cartesian3} normalize 单位法向量
|
* @param {Cesium.Cartesian3} centerVector 圆上参考向量(以 centerPoint 为基)
|
* @param {Cesium.Cartesian3} centerPoint 圆心
|
* @returns {Cesium.Cartesian3[]} 圆轴折线点(闭合)
|
*/
|
axisAngleSphere (normalize, centerVector, centerPoint) {
|
let angleList = []
|
for (let i = 0; i < 360; i += 10) {
|
const axis = Cesium.Quaternion.fromAxisAngle(normalize, Cesium.Math.toRadians(i))
|
const quater = Cesium.Matrix3.fromQuaternion(axis)
|
const fromRotater = Cesium.Matrix4.fromRotationTranslation(quater)
|
const subtract = Cesium.Cartesian3.subtract(centerVector, centerPoint, new Cesium.Cartesian3())
|
const matrix4 = Cesium.Matrix4.multiplyByPoint(fromRotater, subtract, new Cesium.Cartesian3())
|
const matrixPoint = Cesium.Cartesian3.add(matrix4, centerPoint, new Cesium.Cartesian3())
|
angleList.push(matrixPoint)
|
}
|
angleList.push(angleList[0])
|
return angleList
|
}
|
|
/**
|
* 更新控制器实体位置(通过回调属性绑定平移向量)
|
* @param {Cesium.Cartesian3} multiplyByScalar 平移向量
|
*/
|
updateAxisEntities (multiplyByScalar) {
|
const add = (p) => Cesium.Cartesian3.add(p, multiplyByScalar, new Cesium.Cartesian3())
|
const viewer = this.viewer
|
const ap = this.axisPosition
|
const axisCenterX = add(ap['axisX'][0])
|
const axisCenterY = add(ap['axisY'][0])
|
const axisCenterZ = add(ap['axisZ'][0])
|
this.axisEntyX.position = new Cesium.CallbackProperty(() => axisCenterX, false)
|
this.axisEntyY.position = new Cesium.CallbackProperty(() => axisCenterY, false)
|
this.axisEntyZ.position = new Cesium.CallbackProperty(() => axisCenterZ, false)
|
|
this.sphereEntyX.position = new Cesium.CallbackProperty(() => axisCenterX, false)
|
this.sphereEntyY.position = new Cesium.CallbackProperty(() => axisCenterY, false)
|
this.sphereEntyZ.position = new Cesium.CallbackProperty(() => axisCenterZ, false)
|
|
const mAxisX = this._getEntityModelMatrix(this.axisEntyX)
|
const mAxisY = this._getEntityModelMatrix(this.axisEntyY)
|
const mAxisZ = this._getEntityModelMatrix(this.axisEntyZ)
|
const mSphereX = this._getEntityModelMatrix(this.sphereEntyX)
|
const mSphereY = this._getEntityModelMatrix(this.sphereEntyY)
|
const mSphereZ = this._getEntityModelMatrix(this.sphereEntyZ)
|
if (this.overlayModels.axisX) this.overlayModels.axisX.modelMatrix = mAxisX
|
if (this.overlayModels.axisY) this.overlayModels.axisY.modelMatrix = mAxisY
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.modelMatrix = mAxisZ
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.modelMatrix = mSphereX
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.modelMatrix = mSphereY
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.modelMatrix = mSphereZ
|
|
const axisDashX = ap['dashX'].map(add)
|
const axisDashY = ap['dashY'].map(add)
|
const axisDashZ = ap['dashZ'].map(add)
|
this.dashLineList[0].polyline.positions = new Cesium.CallbackProperty(() => axisDashX, false)
|
this.dashLineList[1].polyline.positions = new Cesium.CallbackProperty(() => axisDashY, false)
|
this.dashLineList[2].polyline.positions = new Cesium.CallbackProperty(() => axisDashZ, false)
|
}
|
|
/**
|
* 围绕指定轴旋转整个面(以中心点为参考)并重建控制器几何
|
* @param {number} angle 角度(度)
|
* @param {'x'|'y'|'z'} axis 旋转轴
|
*/
|
rotatePlaneByAxis (angle, axis) {
|
let normal
|
switch (axis) {
|
case 'x': normal = Cesium.Cartesian3.normalize(this.centerVectorXC, new Cesium.Cartesian3()); break
|
case 'y': normal = Cesium.Cartesian3.normalize(this.centerVectorYC, new Cesium.Cartesian3()); break
|
case 'z': normal = Cesium.Cartesian3.normalize(this.centerVectorZC, new Cesium.Cartesian3()); break
|
}
|
const quaternion = Cesium.Quaternion.fromAxisAngle(normal, Cesium.Math.toRadians(angle))
|
const rotationMatrix = Cesium.Matrix3.fromQuaternion(quaternion)
|
const transformMatrix = Cesium.Matrix4.fromRotationTranslation(rotationMatrix)
|
this.planeCtx.polygonPositionList = this.planeCtx.polygonPositionList.map(point => {
|
const translated = Cesium.Cartesian3.subtract(point, this.centerPoint, new Cesium.Cartesian3())
|
const rotated = Cesium.Matrix4.multiplyByPoint(transformMatrix, translated, new Cesium.Cartesian3())
|
return Cesium.Cartesian3.add(rotated, this.centerPoint, new Cesium.Cartesian3())
|
})
|
this.planeCtx.activeShape.polygon.hierarchy = new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(this.planeCtx.polygonPositionList), false)
|
const p = this.planeCtx.polygonPositionList
|
const g = this.computeAxisGeometry(p[0], p[1], p[2], p[3])
|
this.applyAxisGeometry(g)
|
}
|
|
/**
|
* 沿指定轴平移整个面并重建控制器几何
|
* @param {number} distance 平移距离(米)
|
* @param {'x'|'y'|'z'} axis 平移轴
|
*/
|
translatePlaneByAxis (distance, axis) {
|
let axisVector
|
switch (axis) {
|
case 'x': axisVector = Cesium.Cartesian3.normalize(this.centerVectorXC, new Cesium.Cartesian3()); break
|
case 'y': axisVector = Cesium.Cartesian3.normalize(this.centerVectorYC, new Cesium.Cartesian3()); break
|
case 'z': axisVector = Cesium.Cartesian3.normalize(this.centerVectorZC, new Cesium.Cartesian3()); break
|
default: return
|
}
|
const translationVector = Cesium.Cartesian3.multiplyByScalar(axisVector, distance, new Cesium.Cartesian3())
|
this.planeCtx.polygonPositionList = this.planeCtx.polygonPositionList.map((point) => Cesium.Cartesian3.add(point, translationVector, new Cesium.Cartesian3()))
|
this.centerPoint = Cesium.Cartesian3.add(this.centerPoint, translationVector, new Cesium.Cartesian3())
|
this.planeCtx.activeShape.polygon.hierarchy = new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(this.planeCtx.polygonPositionList), false)
|
const p2 = this.planeCtx.polygonPositionList
|
const g2 = this.computeAxisGeometry(p2[0], p2[1], p2[2], p2[3])
|
this.applyAxisGeometry(g2)
|
}
|
|
/**
|
* 绑定轴交互(按下后进入旋转或平移,移动中更新几何,抬起时恢复并触发外部更新)
|
* @param {(positions:Cesium.Cartesian3[])=>void} onUpdate 交互完成后的更新回调
|
*/
|
bindAxisInteractions (onUpdate) {
|
const viewer = this.viewer
|
const eventHub = this.eventHub
|
let masterController = eventHub.get('axisMaster')
|
let moveDashLine = false
|
let rotationController = this._rotationController()
|
let translationController = this._translationController()
|
|
masterController.setInputAction((e) => {
|
const results = viewer.scene.drillPick(e.position)
|
let hit = null
|
let id = null
|
if (Array.isArray(results) && results.length > 0) {
|
for (let k = 0; k < results.length; k++) {
|
const rid = (results[k] && (results[k].id?.id || results[k].id || results[k].primitive?.id))
|
if (!rid) continue
|
const s = String(rid)
|
if (s === 'polygon') continue
|
if (s.slice(0, 6) === 'sphere' || s.slice(0, 4) === 'axis') { hit = results[k]; id = rid; break }
|
}
|
}
|
if (!hit || !id || !this.axisPosition[id]) return
|
|
moveDashLine = true
|
eventHub.enableRotate(false)
|
eventHub.off('hover', Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
|
let isRotation = false
|
let isTranslation = false
|
|
if (id.slice(0, 6) === 'sphere') {
|
if (rotationController.startRotation(hit, this.centerPoint)) { isRotation = true; this._setDragRotationFocus(rotationController.state.currentAxis) }
|
} else if (id.slice(0, 4) === 'axis') {
|
if (translationController.startTranslation(hit, this.centerPoint)) { isTranslation = true; translationController.state.lastMousePosition = new Cesium.Cartesian2(e.position.x, e.position.y); this._setDragTranslationHighlight(translationController.state.currentAxis) }
|
}
|
if (!isRotation && !isTranslation) return
|
this.setDistanceLabelsVisibility(false)
|
|
masterController.setInputAction((movement) => {
|
const mousePosition = new Cesium.Cartesian2(movement.endPosition.x, movement.endPosition.y)
|
if (isRotation) {
|
const angleDelta = rotationController.updateRotation(mousePosition, this.centerPoint)
|
if (Math.abs(angleDelta) > 0.1) {
|
this._setDragRotationFocus(rotationController.state.currentAxis)
|
this.rotatePlaneByAxis(angleDelta, rotationController.state.currentAxis)
|
}
|
} else if (isTranslation) {
|
const distanceDelta = translationController.updateTranslation(mousePosition, this.centerPoint)
|
if (Math.abs(distanceDelta) > 0.01) { this._setDragTranslationHighlight(translationController.state.currentAxis); this.translatePlaneByAxis(distanceDelta, translationController.state.currentAxis) }
|
}
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
|
masterController.setInputAction(() => {
|
masterController.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
masterController.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)
|
this.axisEntyX.show = true
|
this.axisEntyY.show = true
|
this.axisEntyZ.show = true
|
this.sphereEntyX.show = true
|
this.sphereEntyY.show = true
|
this.sphereEntyZ.show = true
|
if (this.overlayModels.axisX) this.overlayModels.axisX.show = true
|
if (this.overlayModels.axisY) this.overlayModels.axisY.show = true
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.show = true
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.show = true
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.show = true
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.show = true
|
moveDashLine = false
|
eventHub.enableRotate(true)
|
this.axisEntyX.model.silhouetteSize = 0
|
this.axisEntyY.model.silhouetteSize = 0
|
this.axisEntyZ.model.silhouetteSize = 0
|
this.sphereEntyX.model.silhouetteSize = 0
|
this.sphereEntyY.model.silhouetteSize = 0
|
this.sphereEntyZ.model.silhouetteSize = 0
|
this.dashLineList.forEach(ele => { ele.show = false })
|
this.bindHover()
|
if (isRotation) rotationController.endRotation()
|
if (isTranslation) translationController.endTranslation()
|
if (typeof onUpdate === 'function') onUpdate(this.planeCtx.polygonPositionList)
|
this.createAxisEntity(...this.planeCtx.polygonPositionList)
|
this.updateDistanceLabels()
|
this.setDistanceLabelsVisibility(true)
|
}, Cesium.ScreenSpaceEventType.LEFT_UP)
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
}
|
|
_setDragRotationFocus (axis) {
|
this.setRotationAxisFocus(axis)
|
if (axis === 'x') {
|
this.axisEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
if (this.overlayModels.axisX) this.overlayModels.axisX.silhouetteSize = 0
|
this.sphereEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.silhouetteSize = 1
|
|
if (this.dashLineList[0]) this.dashLineList[0].show = true
|
}
|
if (axis === 'y') {
|
this.axisEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
if (this.overlayModels.axisY) this.overlayModels.axisY.silhouetteSize = 0
|
this.sphereEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.silhouetteSize = 1
|
|
if (this.dashLineList[1]) this.dashLineList[1].show = true
|
}
|
if (axis === 'z') {
|
this.axisEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.silhouetteSize = 0
|
this.sphereEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.silhouetteSize = 1
|
|
if (this.dashLineList[2]) this.dashLineList[2].show = true
|
}
|
}
|
|
_setDragTranslationHighlight (axis) {
|
if (axis === 'x') {
|
this.axisEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.axisX) this.overlayModels.axisX.silhouetteSize = 1
|
|
if (this.dashLineList[0]) this.dashLineList[0].show = true
|
}
|
if (axis === 'y') {
|
this.axisEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.axisY) this.overlayModels.axisY.silhouetteSize = 1
|
|
if (this.dashLineList[1]) this.dashLineList[1].show = true
|
}
|
if (axis === 'z') {
|
this.axisEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => 1, false)
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.silhouetteSize = 1
|
|
if (this.dashLineList[2]) this.dashLineList[2].show = true
|
}
|
}
|
|
/**
|
* 绑定悬停高亮(通过 CallbackProperty 动态调整线宽,并在悬停时显示对应虚线)
|
*/
|
bindHover () {
|
const handler = this.eventHub.get('hover')
|
handler.setInputAction((arg) => {
|
const results = this.viewer.scene.drillPick(arg.endPosition)
|
let id = null
|
if (Array.isArray(results) && results.length > 0) {
|
for (let k = 0; k < results.length; k++) {
|
const rid = (results[k] && (results[k].id?.id || results[k].id || results[k].primitive?.id))
|
if (!rid) continue
|
const s = String(rid)
|
if (s.slice(0, 6) === 'sphere' || s.slice(0, 4) === 'axis') { id = rid; break }
|
}
|
}
|
if (id) {
|
this.axisEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'axisZ' ? 1 : 0), false)
|
this.axisEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'axisX' ? 1 : 0), false)
|
this.axisEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'axisY' ? 1 : 0), false)
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.silhouetteSize = (id === 'axisZ' ? 1 : 0)
|
if (this.overlayModels.axisX) this.overlayModels.axisX.silhouetteSize = (id === 'axisX' ? 1 : 0)
|
if (this.overlayModels.axisY) this.overlayModels.axisY.silhouetteSize = (id === 'axisY' ? 1 : 0)
|
this.sphereEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'sphereX' ? 1 : 0), false)
|
this.sphereEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'sphereY' ? 1 : 0), false)
|
this.sphereEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => (id === 'sphereZ' ? 1 : 0), false)
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.silhouetteSize = (id === 'sphereX' ? 1 : 0)
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.silhouetteSize = (id === 'sphereY' ? 1 : 0)
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.silhouetteSize = (id === 'sphereZ' ? 1 : 0)
|
this.dashLineList.forEach(ele => { ele.show = (id === 'axisZ' && ele.id === 'dashZ') || (id === 'axisX' && ele.id === 'dashX') || (id === 'axisY' && ele.id === 'dashY') })
|
} else {
|
this.axisEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
this.axisEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
this.axisEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.silhouetteSize = 0
|
if (this.overlayModels.axisX) this.overlayModels.axisX.silhouetteSize = 0
|
if (this.overlayModels.axisY) this.overlayModels.axisY.silhouetteSize = 0
|
this.sphereEntyX.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
this.sphereEntyY.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
this.sphereEntyZ.model.silhouetteSize = new Cesium.CallbackProperty(() => 0, false)
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.silhouetteSize = 0
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.silhouetteSize = 0
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.silhouetteSize = 0
|
this.dashLineList.forEach(ele => { ele.show = false })
|
}
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
}
|
|
/**
|
* 更新四边中心的距离标签位置与文本(使用回调属性保持动态)
|
*/
|
updateDistanceLabels () {
|
const positionsRef = () => this.planeCtx.polygonPositionList
|
const formatDistance = (m) => (m < 1000 ? `${m.toFixed(2)} m` : `${(m / 1000).toFixed(2)} km`)
|
const updateOne = (id, i, j) => {
|
const ent = this.viewer.entities.getById(id)
|
if (!ent) return
|
ent.position = new Cesium.CallbackProperty(() => {
|
const pos = positionsRef()
|
if (!pos || pos.length < 4) return ent.position
|
return Cesium.Cartesian3.midpoint(pos[i], pos[j], new Cesium.Cartesian3())
|
}, false)
|
ent.label.text = new Cesium.CallbackProperty(() => {
|
const pos = positionsRef()
|
if (!pos || pos.length < 4) return ent.label.text
|
const d = Cesium.Cartesian3.distance(pos[i], pos[j])
|
return formatDistance(d)
|
}, false)
|
}
|
updateOne('distanceLabel01', 0, 1)
|
updateOne('distanceLabel12', 1, 2)
|
updateOne('distanceLabel23', 2, 3)
|
updateOne('distanceLabel34', 3, 0)
|
}
|
|
/**
|
* 设置距离标签的可见性
|
* @param {boolean} show 是否显示
|
*/
|
setDistanceLabelsVisibility (show) {
|
const ids = ['distanceLabel01', 'distanceLabel12', 'distanceLabel23', 'distanceLabel34']
|
ids.forEach(id => {
|
const ent = this.viewer.entities.getById(id)
|
if (ent) ent.show = !!show
|
})
|
}
|
|
/**
|
* 旋转时仅聚焦当前轴与圆轴,隐藏其他轴与对应虚线
|
* @param {'x'|'y'|'z'} axis 当前旋转轴
|
*/
|
setRotationAxisFocus (axis) {
|
const xOn = axis === 'x'
|
const yOn = axis === 'y'
|
const zOn = axis === 'z'
|
if (this.axisEntyX) this.axisEntyX.show = xOn
|
if (this.axisEntyY) this.axisEntyY.show = yOn
|
if (this.axisEntyZ) this.axisEntyZ.show = zOn
|
if (this.sphereEntyX) this.sphereEntyX.show = xOn
|
if (this.sphereEntyY) this.sphereEntyY.show = yOn
|
if (this.sphereEntyZ) this.sphereEntyZ.show = zOn
|
if (this.overlayModels.axisX) this.overlayModels.axisX.show = xOn
|
if (this.overlayModels.axisY) this.overlayModels.axisY.show = yOn
|
if (this.overlayModels.axisZ) this.overlayModels.axisZ.show = zOn
|
if (this.overlayModels.sphereX) this.overlayModels.sphereX.show = xOn
|
if (this.overlayModels.sphereY) this.overlayModels.sphereY.show = yOn
|
if (this.overlayModels.sphereZ) this.overlayModels.sphereZ.show = zOn
|
if (this.dashLineList[0]) this.dashLineList[0].show = xOn
|
if (this.dashLineList[1]) this.dashLineList[1].show = yOn
|
if (this.dashLineList[2]) this.dashLineList[2].show = zOn
|
}
|
|
_getAxisOrientation (centerPoint, dir, basis) {
|
const fwd = Cesium.Cartesian3.normalize(dir, new Cesium.Cartesian3())
|
let upWorld = Cesium.Cartesian3.normalize(centerPoint, new Cesium.Cartesian3())
|
const dotUF = Cesium.Cartesian3.dot(upWorld, fwd)
|
const proj = Cesium.Cartesian3.multiplyByScalar(fwd, dotUF, new Cesium.Cartesian3())
|
upWorld = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(upWorld, proj, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (Cesium.Cartesian3.magnitude(upWorld) < 1e-6) upWorld = Cesium.Cartesian3.UNIT_Z
|
const rightWorld = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(fwd, upWorld, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
let xCol, yCol, zCol
|
const assign = (code, vec) => {
|
if (code === 'X') xCol = vec
|
else if (code === '-X') xCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
else if (code === 'Y') yCol = vec
|
else if (code === '-Y') yCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
else if (code === 'Z') zCol = vec
|
else if (code === '-Z') zCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
}
|
assign(basis.forwardAxis, fwd)
|
assign(basis.upAxis, upWorld)
|
if (!xCol && yCol && zCol) xCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(yCol, zCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (!yCol && zCol && xCol) yCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(zCol, xCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (!zCol && xCol && yCol) zCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(xCol, yCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const rot = new Cesium.Matrix3()
|
Cesium.Matrix3.setColumn(rot, 0, xCol, rot)
|
Cesium.Matrix3.setColumn(rot, 1, yCol, rot)
|
Cesium.Matrix3.setColumn(rot, 2, zCol, rot)
|
return Cesium.Quaternion.fromRotationMatrix(rot)
|
}
|
|
|
_getOrientationWithUp (centerPoint, forward, up, basis) {
|
let upWorld = Cesium.Cartesian3.normalize(up, new Cesium.Cartesian3())
|
let f0 = Cesium.Cartesian3.normalize(forward, new Cesium.Cartesian3())
|
const proj = Cesium.Cartesian3.multiplyByScalar(upWorld, Cesium.Cartesian3.dot(f0, upWorld), new Cesium.Cartesian3())
|
let fwd = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(f0, proj, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (Cesium.Cartesian3.magnitude(fwd) < 1e-6) {
|
const alt = Math.abs(Cesium.Cartesian3.dot(upWorld, Cesium.Cartesian3.UNIT_X)) < 0.9 ? Cesium.Cartesian3.UNIT_X : Cesium.Cartesian3.UNIT_Y
|
fwd = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(upWorld, alt, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
}
|
const side = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(fwd, upWorld, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
let xCol, yCol, zCol
|
const assign = (code, vec) => {
|
if (code === 'X') xCol = vec
|
else if (code === '-X') xCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
else if (code === 'Y') yCol = vec
|
else if (code === '-Y') yCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
else if (code === 'Z') zCol = vec
|
else if (code === '-Z') zCol = Cesium.Cartesian3.multiplyByScalar(vec, -1, new Cesium.Cartesian3())
|
}
|
assign(basis.forwardAxis, fwd)
|
assign(basis.upAxis, upWorld)
|
if (!xCol && yCol && zCol) xCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(yCol, zCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (!yCol && zCol && xCol) yCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(zCol, xCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (!zCol && xCol && yCol) zCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(xCol, yCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
if (!xCol) xCol = side
|
if (!yCol) yCol = upWorld
|
if (!zCol) zCol = Cesium.Cartesian3.normalize(Cesium.Cartesian3.cross(xCol, yCol, new Cesium.Cartesian3()), new Cesium.Cartesian3())
|
const rot = new Cesium.Matrix3()
|
Cesium.Matrix3.setColumn(rot, 0, xCol, rot)
|
Cesium.Matrix3.setColumn(rot, 1, yCol, rot)
|
Cesium.Matrix3.setColumn(rot, 2, zCol, rot)
|
return Cesium.Quaternion.fromRotationMatrix(rot)
|
}
|
|
_composeModelMatrix (centerPoint, quaternion) {
|
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(centerPoint)
|
const rot3 = Cesium.Matrix3.fromQuaternion(quaternion)
|
const rot4 = Cesium.Matrix4.fromRotationTranslation(rot3)
|
return Cesium.Matrix4.multiply(enu, rot4, new Cesium.Matrix4())
|
}
|
|
_getEntityModelMatrix (entity) {
|
if (!entity) return new Cesium.Matrix4()
|
return entity.computeModelMatrix(this.viewer.clock.currentTime, new Cesium.Matrix4())
|
}
|
|
_ensureOverlayModel (key, url, modelMatrix, color, scale, minimumPixelSize) {
|
const existing = this.overlayModels[key]
|
if (existing) { existing.modelMatrix = modelMatrix; if (color) existing.color = color; return }
|
Cesium.Model.fromGltfAsync({ id: key, url, modelMatrix, opaquePass: Cesium.Pass.OVERLAY, allowPicking: true, shadows: Cesium.ShadowMode.DISABLED, minimumPixelSize: minimumPixelSize, scale: scale, color }).then(m => {
|
m.silhouetteColor = color
|
m.silhouetteSize = 0
|
this.overlayModels[key] = this.viewer.scene.primitives.add(m)
|
})
|
}
|
|
/**
|
* 计算面在指定角度的轴向平面(保留旧方法以兼容部分逻辑)
|
* @param {Cesium.Cartesian3[]} positionList 顶点列表
|
* @param {number} angletext 角度(度)
|
* @returns {Cesium.Cartesian3[]} 线段列表
|
*/
|
axisAnglePlane (positionList, angletext) {
|
const subtract = Cesium.Cartesian3.subtract(positionList[0], positionList[1], new Cesium.Cartesian3())
|
const normal = Cesium.Cartesian3.normalize(subtract, new Cesium.Cartesian3())
|
const fromAxisAngle = Cesium.Quaternion.fromAxisAngle(normal, Cesium.Math.toRadians(angletext))
|
const fromQuaternion = Cesium.Matrix3.fromQuaternion(fromAxisAngle)
|
const fromRotationTranslation = Cesium.Matrix4.fromRotationTranslation(fromQuaternion)
|
var centerVector1 = Cesium.Cartesian3.subtract(positionList[2], positionList[1], new Cesium.Cartesian3())
|
const multiplyByPoint1 = Cesium.Matrix4.multiplyByPoint(fromRotationTranslation, centerVector1, new Cesium.Cartesian3())
|
var centerVector2 = Cesium.Cartesian3.subtract(positionList[3], positionList[0], new Cesium.Cartesian3())
|
const multiplyByPoint2 = Cesium.Matrix4.multiplyByPoint(fromRotationTranslation, centerVector2, new Cesium.Cartesian3())
|
const lineList = [positionList[0], positionList[1], Cesium.Cartesian3.add(multiplyByPoint1, positionList[1], new Cesium.Cartesian3()), Cesium.Cartesian3.add(multiplyByPoint2, positionList[0], new Cesium.Cartesian3())]
|
return lineList
|
}
|
|
/**
|
* 创建旋转控制器(内部类),处理鼠标投影到旋转平面并计算增量角度
|
* @returns {RotationController} 旋转控制器实例
|
*/
|
_rotationController () {
|
const self = this
|
class RotationController {
|
constructor() { this.state = { isRotating: false, currentAxis: null, rotationPlane: null, lastProjectedPoint: null, accumulatedAngle: 0 } }
|
startRotation (pick, centerPoint) {
|
const axis = this.getAxisFromPick(pick); if (!axis) return false
|
this.state.isRotating = true; this.state.currentAxis = axis; this.state.rotationPlane = this.createRotationPlane(centerPoint, axis); this.state.accumulatedAngle = 0; this.state.lastProjectedPoint = null; return true
|
}
|
getAxisFromPick (pick) {
|
const id = (pick && (pick.id?.id || pick.id || pick.primitive?.id))
|
if (!id) return null
|
if (id === 'sphereX') return 'x'
|
if (id === 'sphereY') return 'y'
|
if (id === 'sphereZ') return 'z'
|
return null
|
}
|
createRotationPlane (centerPoint, axis) {
|
let normal
|
switch (axis) {
|
case 'x': if (!self.centerVectorXC) return null; normal = Cesium.Cartesian3.normalize(self.centerVectorXC, new Cesium.Cartesian3()); break
|
case 'y': if (!self.centerVectorYC) return null; normal = Cesium.Cartesian3.normalize(self.centerVectorYC, new Cesium.Cartesian3()); break
|
case 'z': if (!self.centerVectorZC) return null; normal = Cesium.Cartesian3.normalize(self.centerVectorZC, new Cesium.Cartesian3()); break
|
default: return null
|
}
|
return Cesium.Plane.fromPointNormal(centerPoint, normal)
|
}
|
updateRotation (mousePosition, centerPoint) {
|
if (!this.state.isRotating || !this.state.rotationPlane) return 0
|
const ray = self.viewer.camera.getPickRay(mousePosition); if (!ray) return 0
|
const projectedPoint = Cesium.IntersectionTests.rayPlane(ray, this.state.rotationPlane); if (!projectedPoint) return 0
|
if (!this.state.lastProjectedPoint) { this.state.lastProjectedPoint = projectedPoint; return 0 }
|
const vectorA = Cesium.Cartesian3.subtract(this.state.lastProjectedPoint, centerPoint, new Cesium.Cartesian3())
|
const vectorB = Cesium.Cartesian3.subtract(projectedPoint, centerPoint, new Cesium.Cartesian3())
|
const lengthA = Cesium.Cartesian3.magnitude(vectorA); const lengthB = Cesium.Cartesian3.magnitude(vectorB)
|
if (lengthA < 1e-10 || lengthB < 1e-10) return 0
|
Cesium.Cartesian3.normalize(vectorA, vectorA); Cesium.Cartesian3.normalize(vectorB, vectorB)
|
const dot = Cesium.Cartesian3.dot(vectorA, vectorB)
|
const angle = Math.acos(Cesium.Math.clamp(dot, -1.0, 1.0))
|
if (angle < 1e-5) return 0
|
const cross = Cesium.Cartesian3.cross(vectorA, vectorB, new Cesium.Cartesian3())
|
const axisVector = this.getAxisVector(this.state.currentAxis)
|
Cesium.Cartesian3.normalize(axisVector, axisVector)
|
const direction = Cesium.Cartesian3.dot(cross, axisVector) >= 0 ? 1 : -1
|
const degrees = Cesium.Math.toDegrees(angle) * direction
|
this.state.accumulatedAngle += degrees; this.state.lastProjectedPoint = projectedPoint
|
return degrees
|
}
|
getAxisVector (axis) { switch (axis) { case 'x': return Cesium.Cartesian3.normalize(self.centerVectorXC, new Cesium.Cartesian3()); case 'y': return Cesium.Cartesian3.normalize(self.centerVectorYC, new Cesium.Cartesian3()); case 'z': return Cesium.Cartesian3.normalize(self.centerVectorZC, new Cesium.Cartesian3()); default: return new Cesium.Cartesian3(0, 0, 1) } }
|
endRotation () { const finalAngle = this.state.accumulatedAngle; this.state = { isRotating: false, currentAxis: null, rotationPlane: null, lastProjectedPoint: null, accumulatedAngle: 0 }; return finalAngle }
|
}
|
return new RotationController()
|
}
|
|
/**
|
* 创建平移控制器(内部类),将鼠标在相机朝向平面上的位移投影到轴向并计算世界距离
|
* @returns {TranslationController} 平移控制器实例
|
*/
|
_translationController () {
|
const self = this
|
class TranslationController {
|
constructor() { this.state = { isTranslating: false, currentAxis: null, transformData: null, lastOffset: 0, accumulatedDistance: 0 } }
|
startTranslation (pick, centerPoint) {
|
const axis = this.getAxisFromPick(pick); if (!axis) return false
|
this.state.isTranslating = true; this.state.currentAxis = axis; this.state.transformData = this.createTranslationTransform(centerPoint, axis); this.state.lastOffset = 0; this.state.accumulatedDistance = 0; return true
|
}
|
getAxisFromPick (pick) {
|
const id = (pick && (pick.id?.id || pick.id || pick.primitive?.id))
|
if (!id) return null
|
if (id === 'axisX') return 'x'
|
if (id === 'axisY') return 'y'
|
if (id === 'axisZ') return 'z'
|
return null
|
}
|
createTranslationTransform (centerPoint, axis) {
|
const cameraDirection = self.viewer.camera
|
const axisVectors = { x: Cesium.Cartesian3.normalize(self.centerVectorXC, new Cesium.Cartesian3()), y: Cesium.Cartesian3.normalize(self.centerVectorYC, new Cesium.Cartesian3()), z: Cesium.Cartesian3.normalize(self.centerVectorZC, new Cesium.Cartesian3()) }
|
const origin = centerPoint; const axisVector = axisVectors[axis]
|
const plane = Cesium.Plane.fromPointNormal(origin, Cesium.Cartesian3.normalize(cameraDirection.directionWC, new Cesium.Cartesian3()))
|
const axisEndPoint = Cesium.Cartesian3.add(origin, axisVector, new Cesium.Cartesian3())
|
const projectedAxisEnd = Cesium.Plane.projectPointOntoPlane(plane, axisEndPoint)
|
const planeDirection = Cesium.Cartesian3.subtract(projectedAxisEnd, origin, new Cesium.Cartesian3())
|
const normalizedDirection = Cesium.Cartesian3.normalize(planeDirection, new Cesium.Cartesian3())
|
const rate = 1 / Cesium.Cartesian3.magnitude(planeDirection)
|
return { direction: axisVector, operation: axis, origin, plane, directionOnPlane: normalizedDirection, startOffset: 0, rate }
|
}
|
updateTranslation (mousePosition) {
|
if (!this.state.isTranslating || !this.state.transformData) return 0
|
const ray = self.viewer.camera.getPickRay(mousePosition); if (!ray) return 0
|
const intersection = Cesium.IntersectionTests.rayPlane(ray, this.state.transformData.plane); if (!intersection) return 0
|
const vectorToIntersection = Cesium.Cartesian3.subtract(intersection, this.state.transformData.origin, new Cesium.Cartesian3())
|
const currentOffset = Cesium.Cartesian3.dot(vectorToIntersection, this.state.transformData.directionOnPlane) * this.state.transformData.rate
|
if (this.state.lastOffset === 0) { this.state.startOffset = currentOffset; this.state.lastOffset = currentOffset; return 0 }
|
const offsetDelta = currentOffset - this.state.lastOffset
|
this.state.lastOffset = currentOffset; this.state.accumulatedDistance += offsetDelta
|
const worldDistance = offsetDelta / this.state.transformData.rate
|
return worldDistance
|
}
|
endTranslation () { const finalDistance = this.state.accumulatedDistance; this.state = { isTranslating: false, currentAxis: null, transformData: null, lastOffset: 0, accumulatedDistance: 0 }; return finalDistance }
|
}
|
return new TranslationController()
|
}
|
}
|