/** * 几何工具类 * 提供法向量计算、按法线偏移、线段分割、点到线段投影、角点校正、分割线位置交错等常用几何方法 */ 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]) } }) } }