
本文详解如何在三维球面几何中准确计算三个单位向量构成的球面三角形各顶点处的球面角(即切平面内两弧边的夹角),而非欧氏空间中的平面角;核心是利用向量叉积构造切向量,并通过三重正交关系求解真实球面角。
本文详解如何在三维球面几何中准确计算三个单位向量构成的球面三角形各顶点处的**球面角**(即切平面内两弧边的夹角),而非欧氏空间中的平面角;核心是利用向量叉积构造切向量,并通过三重正交关系求解真实球面角。
在球面几何中,当三个点位于单位球面上(即均为单位向量),它们两两之间沿球面大圆连接,形成一个球面三角形。此时每个顶点处的内角——称为球面角——并非简单由三点在三维空间中构成的平面角决定,而应定义为:在该顶点处的球面切平面上,两条大圆弧对应切向量之间的夹角。
原始函数 ang3D2 的问题在于:它直接对弦向量(如 V1−V2 和 V3−V2)做点积与反余弦,本质上计算的是以 V2 为顶点、V1 和 V3 为端点的空间折线角(即欧氏三角形角),隐含假设三点共面。但在球面上,V1, V2, V3 一般不共面,该角既不反映球面弧的局部方向,也不满足球面三角形角和 > 180° 的基本性质(球面过剩角 = 角和 − 180°)。
✅ 正确解法的关键洞察是:
- 球面上点
V2处的切平面垂直于径向向量V2(即球心到该点的单位法向); - 过
V2的大圆弧在V2处的切向量,必然同时垂直于V2和该大圆所在平面的法向; - 因此,从
V2指向V1的大圆弧切向量 ∝V1 × V2(因为V1 × V2垂直于V2且位于V1–V2平面内,正是该大圆在V2处的切方向);同理,V3 × V2是另一条弧的切向量。
于是,顶点 V2 处的球面角 ∠V1V2V3 即为向量 a = V1 × V2 与 b = V3 × V2 在切平面内的夹角,可由下式精确计算:
function ang3D(v1, v2, v3) {
// 输入为 p5.Vector,已归一化(若非单位向量,务必先 normalize!)
const V1 = v1.copy().normalize();
const V2 = v2.copy().normalize();
const V3 = v3.copy().normalize();
// 构造两个切向量:分别沿 V2→V1 和 V2→V3 的大圆弧方向
const a = p5.Vector.cross(V1, V2); // ∈ 切平面,⊥ V2
const b = p5.Vector.cross(V3, V2); // ∈ 切平面,⊥ V2
// 计算两切向量夹角(使用点积 + 模长)
const dot = p5.Vector.dot(a, b);
const magA = a.mag();
const magB = b.mag();
// 防止浮点误差导致 |dot/(magA*magB)| > 1
const cosTheta = Math.max(-1, Math.min(1, dot / (magA * magB)));
const angleRad = Math.acos(cosTheta);
const angleDeg = (angleRad * 180.0) / Math.PI;
return Math.round(angleDeg * 1000) / 1000;
}? 使用说明与注意事项:
- 该函数需调用三次以获取球面三角形全部三个角:
const ang1 = ang3D(cyl2V, cyl1V, cyl3V); // ∠cyl2-cyl1-cyl3,在 cyl1 处 const ang2 = ang3D(cyl1V, cyl2V, cyl3V); // ∠cyl1-cyl2-cyl3,在 cyl2 处 const ang3 = ang3D(cyl1V, cyl3V, cyl2V); // ∠cyl1-cyl3-cyl2,在 cyl3 处 const sphericalExcess = ang1 + ang2 + ang3 - 180; // 球面过剩角(≥ 0)
- ✅ 严格要求输入向量为单位向量(即位于单位球面上)。若原始坐标是经纬度
(lat, lon),请先转换为笛卡尔单位向量:function latLonToVec(lat, lon) { const phi = radians(lat); // 纬度 → 弧度(-π/2 ~ π/2) const theta = radians(lon); // 经度 → 弧度(-π ~ π) return createVector( cos(phi) * cos(theta), cos(phi) * sin(theta), sin(phi) ); } - ⚠️
V1 × V2与V2 × V1方向相反,但因夹角取绝对值(acos定义域[0, π]),不影响结果;但若需有向角(如用于定向面积),需统一叉积顺序。 - ? 数值稳定性:加入
Math.max(-1, Math.min(1, ...))防止因浮点误差使dot/(magA*magB)超出[-1,1]区间,避免NaN。
总结而言,球面角的本质是切空间中的夹角,而非三维位置向量构成的空间角。通过将原始向量投影至球面切平面(借助叉积消去法向分量),再在该二维子空间中计算夹角,即可严格满足球面三角学要求。此方法简洁、几何意义清晰,且完全兼容 p5.js 的向量运算体系,是实现交互式球面几何可视化(如球面三角剖分、天球导航、地球测地分析)的可靠基础。

















