/**
|
* 几何工具类
|
* 提供法向量计算、按法线偏移、线段分割、点到线段投影、角点校正、分割线位置交错等常用几何方法
|
*/
|
export class GeometryUtils {
|
/**
|
* 计算四点组成平面的法向量(单位化)
|
* @param {Cesium.Cartesian3[]} positions 四个顶点 [p0,p1,p2,p3]
|
* @returns {Cesium.Cartesian3} 单位法向量
|
*/
|
computeNormal (positions) {
|
// 边向量:p0->p2 与 p1->p3 的叉积得到法向量
|
const v13 = Cesium.Cartesian3.subtract(positions[0], positions[2], new Cesium.Cartesian3())
|
const v24 = Cesium.Cartesian3.subtract(positions[1], positions[3], new Cesium.Cartesian3())
|
const n = Cesium.Cartesian3.cross(v13, v24, new Cesium.Cartesian3())
|
return Cesium.Cartesian3.normalize(n, new Cesium.Cartesian3())
|
}
|
/**
|
* 按法向量将平面上的所有点整体偏移指定高度
|
* @param {Cesium.Cartesian3[]} positions 原始顶点数组
|
* @param {number} relativeHeight 相对高度(米),沿法线正向偏移
|
* @returns {Cesium.Cartesian3[]} 偏移后的顶点数组
|
*/
|
computeOffsetPositions (positions, relativeHeight) {
|
const normal = this.computeNormal(positions)
|
const offset = Cesium.Cartesian3.multiplyByScalar(normal, relativeHeight, new Cesium.Cartesian3())
|
const out = new Array(positions.length)
|
for (let i = 0; i < positions.length; i++) { out[i] = Cesium.Cartesian3.add(positions[i], offset, new Cesium.Cartesian3()) }
|
return out
|
}
|
/**
|
* 按指定间距分割线段,返回分割点序列
|
* @param {Cesium.Cartesian3} point1 起点
|
* @param {Cesium.Cartesian3} point2 终点
|
* @param {number} s 目标分割间距(米)
|
* @param {boolean} reverse 是否反向输出(从终点到起点)
|
* @returns {Cesium.Cartesian3[]} 分割点数组(含端点)
|
*/
|
splitSegment (point1, point2, s, reverse) {
|
const distance = Cesium.Cartesian3.distance(point1, point2)
|
const segments = Math.max(1, Math.ceil(distance / s))
|
const points = new Array(segments + 1)
|
if (reverse) {
|
for (let i = segments; i >= 0; i--) { const fraction = i / segments; const pt = new Cesium.Cartesian3(); Cesium.Cartesian3.lerp(point1, point2, fraction, pt); points[segments - i] = pt }
|
} else {
|
for (let i = 0; i <= segments; i++) { const fraction = i / segments; const pt = new Cesium.Cartesian3(); Cesium.Cartesian3.lerp(point1, point2, fraction, pt); points[i] = pt }
|
}
|
return points
|
}
|
/**
|
* 将点 A 在由 B->C 定义的方向上进行投影,返回投影点坐标(不裁剪到线段范围)
|
* @param {Cesium.Cartesian3} pointA 待投影点
|
* @param {Cesium.Cartesian3} pointB 基点(投影起点)
|
* @param {Cesium.Cartesian3} pointC 方向参考点(形成向量 B->C)
|
* @returns {Cesium.Cartesian3} 投影点(位于经过 B 且方向为 B->C 的直线上)
|
*/
|
projectPointOnSegment (pointA, pointB, pointC) {
|
const vectorBC = Cesium.Cartesian3.subtract(pointC, pointB, new Cesium.Cartesian3())
|
const vectorBA = Cesium.Cartesian3.subtract(pointA, pointB, new Cesium.Cartesian3())
|
const projection = new Cesium.Cartesian3()
|
Cesium.Cartesian3.projectVector(vectorBA, vectorBC, projection)
|
return Cesium.Cartesian3.add(pointB, projection, new Cesium.Cartesian3())
|
}
|
/**
|
* 基于投影校正四角点位置,使得边保持相对一致与闭合
|
* @param {Cesium.Cartesian3[]} positionOffset 四角点(已按法线偏移)数组 [p0,p1,p2,p3]
|
* @param {number} distance01 边 p0-p1 的长度,用于阈值判断
|
* @returns {{newPointList: Cesium.Cartesian3[], distanceP2:number, distanceP3:number, distanceP20:number, distanceP21:number, distanceP30:number, distanceP31:number}}
|
*/
|
adjustCornersByProjection (positionOffset, distance01) {
|
const projectionPoint2 = this.projectPointOnSegment(positionOffset[2], positionOffset[1], positionOffset[0])
|
const projectionPoint3 = this.projectPointOnSegment(positionOffset[3], positionOffset[0], positionOffset[1])
|
const distanceP2 = Cesium.Cartesian3.distance(projectionPoint2, positionOffset[2])
|
const distanceP3 = Cesium.Cartesian3.distance(projectionPoint3, positionOffset[3])
|
const distanceP30 = Cesium.Cartesian3.distance(projectionPoint3, positionOffset[0])
|
const distanceP31 = Cesium.Cartesian3.distance(projectionPoint3, positionOffset[1])
|
const newPoint0 = distanceP30 + distanceP31 > distance01 ? projectionPoint3 : positionOffset[0]
|
const distanceP20 = Cesium.Cartesian3.distance(projectionPoint2, positionOffset[0])
|
const distanceP21 = Cesium.Cartesian3.distance(projectionPoint2, positionOffset[1])
|
const newPoint1 = distanceP20 + distanceP21 > distance01 ? projectionPoint2 : positionOffset[1]
|
let newPoint2, newPoint3
|
if (distanceP2 > distanceP3) {
|
if (distanceP20 + distanceP21 > distance01) { newPoint2 = positionOffset[2] } else { const vector = Cesium.Cartesian3.subtract(positionOffset[1], projectionPoint2, new Cesium.Cartesian3()); newPoint2 = Cesium.Cartesian3.add(positionOffset[2], vector, new Cesium.Cartesian3()) }
|
const vector01 = Cesium.Cartesian3.subtract(newPoint0, newPoint1, new Cesium.Cartesian3())
|
newPoint3 = Cesium.Cartesian3.add(newPoint2, vector01, new Cesium.Cartesian3())
|
} else {
|
if (distanceP30 + distanceP31 > distance01) { newPoint3 = positionOffset[3] } else { const vector = Cesium.Cartesian3.subtract(positionOffset[0], projectionPoint3, new Cesium.Cartesian3()); newPoint3 = Cesium.Cartesian3.add(positionOffset[3], vector, new Cesium.Cartesian3()) }
|
const vector01 = Cesium.Cartesian3.subtract(newPoint1, newPoint0, new Cesium.Cartesian3())
|
newPoint2 = Cesium.Cartesian3.add(newPoint3, vector01, new Cesium.Cartesian3())
|
}
|
return { newPointList: [newPoint0, newPoint1, newPoint2, newPoint3, newPoint0], distanceP2, distanceP3, distanceP20, distanceP21, distanceP30, distanceP31 }
|
}
|
/**
|
* 交错合并两段分割点序列,形成折线的交错点序列
|
* @param {Cesium.Cartesian3[]} arr1 序列一
|
* @param {Cesium.Cartesian3[]} arr2 序列二
|
* @param {Cesium.Cartesian3[]} out 输出数组引用,将被填充
|
*/
|
handleSplitLinePosition (arr1, arr2, out) {
|
arr1.forEach((item, i) => { if (i % 2 === 0) { out.push(arr2[i], item) } else { out.push(item, arr2[i]) } })
|
}
|
}
|