三角形重心坐标

本文最后更新于:2025年4月17日 晚上

三角形

想要定义一个三角形只需列出其 3 个顶点即可,但是列出这三个顶点的顺序非常重要。在左手坐标系并且从三角形正面看时,通常按照顺时针顺序枚举顶点。我们将这 3 个顶点分别称为 v1\bf v_1v2\bf v_2v3\bf v_3

其中的边长和长度可以表示为:

e1=v3v2e2=v1v3e3=v2v1l1=e1l2=e2l3=e3\begin{aligned} {\bf e_1} &= {\bf v_3} - {\bf v_2} & {\bf e_2} &= {\bf v_1} - {\bf v_3} & {\bf e_3} &= {\bf v_2} - {\bf v_1}\\ l_1 &= \lVert {\bf e_1} \rVert & l_2 &= \lVert {\bf e_2} \rVert & l_3 &= \lVert {\bf e_3} \rVert \end{aligned}

正弦定理:

sin(θ1)l1=sin(θ2)l2=sin(θ3)l3\frac{\sin(\theta_1)}{l_1} = \frac{\sin(\theta_2)}{l_2} = \frac{\sin(\theta_3)}{l_3}

余弦定理:

l12=l22+l322l2l3cosθ1l22=l12+l322l1l3cosθ2l32=l12+l222l1l2cosθ3\begin{aligned} l_1^2 = l_2^2 + l_3^2 - 2l_2l_3\cos\theta_1\\ l_2^2 = l_1^2 + l_3^2 - 2l_1l_3\cos\theta_2\\ l_3^2 = l_1^2 + l_2^2 - 2l_1l_2\cos\theta_3\\ \end{aligned}

三角形周长:

p=l1+l2+l3p = l_1 + l_2 + l_3

三角形面积

三角形面积:

A=bh/2A = bh/2

如果高度未知,则可以使用海伦公式,该公式只需要三边的边长即可求三角形的面积:

s=l1+l2+l32=p2A=s(sl1)(sl2)(sl3)\begin{aligned} s = \frac{l_1 + l_2 + l_3}{2} = \frac{p}{2} A = \sqrt{s(s - l_1)(s - l_2)(s - l_3)} \end{aligned}

我们还可以单独通过顶点坐标来计算三角形的面积,其基本思想是,为三角形的三条边中的每一条边计算梯形的有符号的面积,该面积由边和下方的 x 轴所界定,如下图所示

“有符号的面积”是指如果边的指向是从左到右,则该面积为正;如果边的指向是从右到左,则面积为负。请注意,无论三角形如何定向,总会有至少一条边的面积为正值并且至少一条边的面积为负值。垂直边的面积为零。每条边下的面积公式为:

A(e1)=(y3+y2)(x3x2)2A(e2)=(y1+y3)(x1x3)2A(e3)=(y2+y1)(x2x1)2\begin{aligned} A({\bf e_1}) = \frac{(y_3 + y_2)(x_3 - x_2)}{2}\\ A({\bf e_2}) = \frac{(y_1 + y_3)(x_1 - x_3)}{2}\\ A({\bf e_3}) = \frac{(y_2 + y_1)(x_2 - x_1)}{2}\\ \end{aligned}

通过对 3 个梯形的有符号面积求和,可以获得三角形本身的面积。假设三角形周围的顶点按顺时针排序(如果按相反的顺序排序会反转面积的符号),可以通过下式对梯形的面积求和来计算三角形的有符号面积:

A=A(e1)+A(e2)+A(e3)=(y3+y2)(x3x2)+(y1+y3)(x1x3)+(y2+y1)(x2x1)2=y3x3y3x2+y2x3y2x2+y1x1y1x3+y3x1y3x3+y2x2y2x1+y1x2y1x12=y3x2+y2x3y1x3+y3x1y2x1+y1x22=y1(x2x3)+y2(x3x1)+y3(x1x2)2\begin{aligned} A &= A({\bf e_1}) + A({\bf e_2}) + A({\bf e_3})\\ &= \frac{(y_3 + y_2)(x_3 - x_2) + (y_1 + y_3)(x_1 - x_3) + (y_2 + y_1)(x_2 - x_1)}{2}\\ &= \frac{y_3x_3 - y_3x_2 + y_2x_3 - y_2x_2 + y_1x_1 - y_1x_3 + y_3x_1 - y_3x_3 + y_2x_2 - y_2x_1 + y_1x_2 - y_1x_1}{2}\\ &= \frac{-y_3x_2 + y_2x_3 - y_1x_3 + y_3x_1 - y_2x_1 + y_1x_2}{2}\\ &= \frac{y_1(x_2 - x_3) + y_2(x_3 - x_1) + y_3(x_1 - x_2)}{2} \end{aligned}

我们还可以进一步化简这个式子,因为我们可以在不影响面积的情况下平移三角形,比如将三角形垂直移动直到一点落在 x 轴上。例如当第三个点落到 x 轴上时,我们可以从每个 y 坐标中减去 y3y_3

A=y1(x2x3)+y2(x3x1)+y3(x1x2)2=(y1y3)(x2x3)+(y2y3)(x3x1)+(y3y3)(x1x2)2=(y1y3)(x2x3)+(y2y3)(x3x1)2\begin{aligned} A &= \frac{y_1(x_2 - x_3) + y_2(x_3 - x_1) + y_3(x_1 - x_2)}{2}\\ &= \frac{(y_1 - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - x_1) + (y_3 - y_3)(x_1 - x_2)}{2}\\ &= \frac{(y_1 - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - x_1)}{2} \end{aligned}

在三维中,可以使用叉积来计算三角形的面积,两个矢量 a{\bf a}b{\bf b} 的叉积的大小等于通过 a{\bf a}b{\bf b} 这两条边形成的平行四边形的面积。由于三角形的面积是封闭的平行四边形面积的一半,那么,给定来自三角形的两个边矢量 e1{\bf e_1}e2{\bf e_2},该三角形的面积为:

A=e1×e22A = \frac{\lVert{\bf e_1 \times e_2}\rVert}{2}

重心空间

三角形平面中的任何点都可以表示为顶点的加权平均值。这些加权称为重心坐标。从重心坐标 (b1b_1, b2b_2, b3b_3) 到标准三维空间的转换可由下式定义:

(b1,b2,b3)b1v1+b2v2+b3v3(b_1, b_2, b_3) \equiv b_1{\bf v_1} + b_2{\bf v_2} + b_3{\bf v_3}

重心坐标是一些矢量的线性组合,而普通笛卡尔坐标也可以解释为基矢量的线性组合,但重心坐标和普通笛卡尔坐标之间有一个细微的区别,那就是重心坐标的坐标之和被限制为 1,其定义如下:

b1+b2+b3=1b_1 + b_2 + b_3 = 1

b1b_1b2b_2b3b_3 分别是每个顶点对该点的“贡献”或“权重”。下图显示了点及其重心坐标的一些示例。

上图中的三角形的 3 个顶点在重心坐标中具有以下很简单的形式:

(1,0,0)v1(0,1,0)v2(0,0,1)v3(1, 0, 0) \equiv {\bf v_1} \qquad (0, 1, 0) \equiv {\bf v_2} \qquad (0, 0, 1) \equiv {\bf v_3}

而且,与顶点相对的一条边上的所有点对应于该顶点的重心坐标将具有零值。例如,对于包含 e1{\bf e_1}(与 v1{\bf v_1} 相对)的直线上的所有点,b1=0b_1 = 0

平面中的任何点都可以用重心坐标来描述,而不仅仅是三角形内的点。三角形内部的点的重心坐标都在 [0, 1] 范围内,三角形外的任何点都将至少有一个负坐标值。重心空间可以将平面以与原始三角形大小相同的三角形来网格化,如下图所示。

还有另一种思考重心坐标的方法。丢弃 b3b_3,我们可以将 (b1b_1, b2b_2) 解释为常规的 (x, y) 二维坐标,其中,原点位于 v3{\bf v_3},x 轴是 v1v3{\bf v_1 - v_3},y 轴是 v2v3{\bf v_2 - v_3}

(b1,b2,b3)b1v1+b2v2+b3v3b1v1+b2v2+(1b1b2)v3b1v1+b2v2+v3b1v3b2v3v3+b1(v1v3)+b2(v2v3)\begin{aligned} (b_1, b_2, b_3) &\equiv b_1{\bf v_1} + b_2{\bf v_2} + b_3{\bf v_3}\\ &\equiv b_1{\bf v_1} + b_2{\bf v_2} + (1 - b_1 - b_2){\bf v_3}\\ &\equiv b_1{\bf v_1} + b_2{\bf v_2} + {\bf v_3} - b_1{\bf v_3} - b_2{\bf v_3}\\ &\equiv {\bf v_3} + b_1({\bf v_1 - v_3}) + b_2({\bf v_2 - v_3}) \end{aligned}

计算重心坐标

现在来看一看如何确定笛卡尔坐标的重心坐标。先从下图的二维开始,它显示了 v1{\bf v_1}v2{\bf v_2}v3{\bf v_3} 这三个顶点以及点 p{\bf p}。我们还标记了 3 个分割出来的子三角形 T1T_1T2T_2T3T_3,它们与同一索引的顶点相对。

已知 3 个顶点和点 p{\bf p} 的笛卡尔坐标,求重心坐标 b1b_1b2b_2b3b_3。以下给了 3 个方程和 3 个未知数:

b1x1+b2x2+b3x3=px,b1y1+b2y2+b3y3=py,b1+b2+b3=1\begin{aligned} b_1x_1 + b_2x_2 + b_3x_3 &= p_x,\\ b_1y_1 + b_2y_2 + b_3y_3 &= p_y,\\ b_1 + b_2 + b_3 &= 1 \end{aligned}

求解该方程组得出以下结果:

b1=(pyy3)(x2x3)+(y2y3)(x3px)(y1y3)(x2x3)+(y2y3)(x3x1),b2=(pyy1)(x3x1)+(y3y1)(x1px)(y1y3)(x2x3)+(y2y3)(x3x1),b3=(pyy2)(x1x2)+(y1y2)(x2px)(y1y3)(x2x3)+(y2y3)(x3x1)\begin{aligned} b_1 &= \frac{(p_y - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - p_x)}{(y_1 - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - x_1)},\\ b_2 &= \frac{(p_y - y_1)(x_3 - x_1) + (y_3 - y_1)(x_1 - p_x)}{(y_1 - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - x_1)},\\ b_3 &= \frac{(p_y - y_2)(x_1 - x_2) + (y_1 - y_2)(x_2 - p_x)}{(y_1 - y_3)(x_2 - x_3) + (y_2 - y_3)(x_3 - x_1)}\\ \end{aligned}

可以看到每个表达式中的分母是相同的,而且由海伦公式的变形可知它等于三角形面积的两倍。而对于每个重心坐标 bib_i 来说,其分子等于子三角形 TiT_i 的面积的两倍,换言之:

b1=A(T1)/A(T),b2=A(T2)/A(T),b3=A(T3)/A(T)b_1 = A(T_1)/A(T),\qquad b_2 = A(T_2)/A(T),\qquad b_3 = A(T_3)/A(T)

请注意,即使 p{\bf p} 在三角形之外,此解释也是适用的,因为如果顶点以逆时针顺序枚举,则计算面积的公式会产生负值结果。如果三角形的三个顶点是共线的,那么分母中的面积将为零,因此不能计算重心坐标。

计算三维中任意点 p{\bf p} 的重心坐标比在二维中更复杂。我们不能像以前一样求解方程组,因为有 3 个未知数和 4 个方程。另一个复杂因素是 p{\bf p} 可能不在包含三角形的平面中,在这种情况下,重心坐标是不确定的。现在,先假设 p{\bf p} 位于包含三角形的平面中的情形。

一个有效的技巧是,通过丢弃 x、y 或 z 中的一个来将三维问题转变为二维问题。这具有将三角形投影到 3 个基本平面之一上的效果。以直觉而言,这是有效的,因为投影面积与原始面积成正比。

但是,应该丢弃哪一个坐标呢?我们不能总丢弃相同的,因为如果三角形垂直于投影平面,投影点将是共线的。如果三角形几乎垂直于投影平面,那么将遇到浮点精度问题。解决这个问题的方法是选择投影平面,以便最大化投影三角形的面积。这可以通过检查平面法线来完成,具有最大绝对值的坐标就是我们将要丢弃的坐标。例如,如果法线是 [0.267, -0.802, 0.535],那么将丢弃顶点和 p{\bf p} 点的 y 值,投影到 xz 平面上。

下面代码展示如何计算任意三维点的重心坐标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
bool computeBarycentricCoords3d(
const Vector3 v[3], // vertices of the triangle
const Vector3 &p, // point that we wish to compute coords for
float b[3] // barycentric coords returned here
) {

// First, compute two clockwise edge vectors
Vector3 d1 = v[1] - v[0];
Vector3 d2 = v[2] - v[1];

// Compute surface normal using cross product. In many cases
// this step could be skipped, since we would have the surface
// normal precomputed. We do not need to normalize it, although
// if a precomputed normal was normalized, it would be OK.
Vector3 n = crossProduct(d1, d2);

// Locate dominant axis of normal, and select plane of projection
float u1, u2, u3, u4;
float v1, v2, v3, v4;
if ((fabs(n.x) >= fabs(n.y)) && (fabs(n.x) >= fabs(n.z))) {

// Discard x, project onto yz plane
u1 = v[0].y - v[2].y;
u2 = v[1].y - v[2].y;
u3 = p.y - v[0].y;
u4 = p.y - v[2].y;

v1 = v[0].z - v[2].z;
v2 = v[1].z - v[2].z;
v3 = p.z - v[0].z;
v4 = p.z - v[2].z;

} else if (fabs(n.y) >= fabs(n.z)) {

// Discard y, project onto xz plane
u1 = v[0].z - v[2].z;
u2 = v[1].z - v[2].z;
u3 = p.z - v[0].z;
u4 = p.z - v[2].z;

v1 = v[0].x - v[2].x;
v2 = v[1].x - v[2].x;
v3 = p.x - v[0].x;
v4 = p.x - v[2].x;

} else {

// Discard z, project onto xy plane
u1 = v[0].x - v[2].x;
u2 = v[1].x - v[2].x;
u3 = p.x - v[0].x;
u4 = p.x - v[2].x;

v1 = v[0].y - v[2].y;
v2 = v[1].y - v[2].y;
v3 = p.y - v[0].y;
v4 = p.y - v[2].y;
}

// Compute denominator, check for invalid
float denom = v1*u2 - v2*u1;
if (denom == 0.0f) {

// Bogus triangle - probably triangle has zero area
return false;
}

// Compute barycentric coordinates
float oneOverDenom = 1.0f / denom;
b[0] = (v4*u2 - v2*u4) * oneOverDenom;
b[1] = (v1*u3 - v3*u1) * oneOverDenom;
b[2] = 1.0f - b[0] - b[1];

// OK
return true;
}

还有一种技术可以用于计算三维中的重心坐标,那就是使用叉积来计算三维三角形的面积的方法。给定三角形的两个边矢量 e1\bf e_1e2\bf e_2,可以将三角形的面积计算为 e1×e2/2\lVert{\bf e_1 \times e_2}\rVert /2。只要得到整个三角形的面积和 3 个子三角形的面积,就可以计算出其重心坐标。

当然,这里有一个小问题:叉积的大小对顶点的排序不敏感——按照定义,其大小总是正的。那么三角形之外的点就不适用了,因为三角形之外的点必须始终至少有一个负重心坐标。看起来我们真正需要的是一种计算叉积矢量长度的方法,如果顶点以“不正确”的顺序枚举,则会产生负值。事实证明,使用点积有一种非常简单的方法。

假设将 c\bf c 指定为三角形的两个边矢量的叉积,c\bf c 的大小将等于三角形面积的两倍。假设有一个单位长度的法线 n^\bf \hat{n}。现在 c\bf cn^\bf \hat{n} 是平行的,因为它们都垂直于三角形所在的平面。但是,它们可能指向相反的方向,所以:

cn^=cn^cosθ=c(1)(±1)=±c\begin{aligned} {\bf c} \cdot {\bf \hat{n}} &= \lVert {\bf c} \rVert \lVert {\bf \hat{n}} \rVert \cos\theta\\ &= \lVert {\bf c} \rVert(1)(\pm 1)\\ &= \pm \lVert {\bf c} \rVert \end{aligned}

将此结果除以 2,就可以计算三维中三角形的“有符号面积”。

在上图中,每个顶点都有一个从 vi\bf v_ip\bf p 的矢量,名为 di\bf d_i

e1=v3v2e2=v1v3e3=v2v1d1=pv1d2=pv2d3=pv3\begin{aligned} {\bf e_1} &= {\bf v_3} - {\bf v_2} & {\bf e_2} &= {\bf v_1} - {\bf v_3} & {\bf e_3} &= {\bf v_2} - {\bf v_1}\\ {\bf d_1} &= {\bf p} - {\bf v_1} & {\bf d_2} &= {\bf p} - {\bf v_2} & {\bf d_3} &= {\bf p} - {\bf v_3} \end{aligned}

我们还需要一个表面法线,可以通过下式计算:

n^=e1×e2e1×e2\bf \hat{n} = \frac{e_1 \times e_2}{\lVert e_1 \times e_2 \rVert}

现在整个三角形(简称为 TT)的面积和 3 个子三角形的面积可由下式给出:

A(T)=((e1×e2)n^)/2A(T1)=((e1×d3)n^)/2A(T2)=((e2×d1)n^)/2A(T3)=((e3×d2)n^)/2\begin{aligned} A(T) &= (({\bf e_1 \times e_2})\cdot{\bf\hat{n}})/2\\ A(T_1) &= (({\bf e_1 \times d_3})\cdot{\bf\hat{n}})/2\\ A(T_2) &= (({\bf e_2 \times d_1})\cdot{\bf\hat{n}})/2\\ A(T_3) &= (({\bf e_3 \times d_2})\cdot{\bf\hat{n}})/2 \end{aligned}

每个重心坐标 bib_i 可由 A(Ti)/A(T)A(T_i)/A(T) 给出:

b1=A(T1)/A(T)=(e1×d3)n^(e1×e2)n^b2=A(T2)/A(T)=(e2×d1)n^(e1×e2)n^b3=A(T3)/A(T)=(e3×d2)n^(e1×e2)n^\begin{aligned} b_1 = A(T_1)/A(T) = \frac{({\bf e_1 \times d_3})\cdot{\bf\hat{n}}}{({\bf e_1 \times e_2})\cdot{\bf\hat{n}}}\\ b_2 = A(T_2)/A(T) = \frac{({\bf e_2 \times d_1})\cdot{\bf\hat{n}}}{({\bf e_1 \times e_2})\cdot{\bf\hat{n}}}\\ b_3 = A(T_3)/A(T) = \frac{({\bf e_3 \times d_2})\cdot{\bf\hat{n}}}{({\bf e_1 \times e_2})\cdot{\bf\hat{n}}}\\ \end{aligned}

请注意,n^\bf \hat{n} 被用于所有的分子和分母,所以它不一定必须是单位矢量。

特殊点

重心

重心是三角形完美的平衡点,它是中线的交点(中线是从一个顶点到对边中点的直线)。

重心是 3 个顶点的几何平均值:

cGrav=v1+v2+v33{\bf c_{Grav}} = \frac{\bf v_1 + v_2 + v_3}{3}

重心坐标为:

(13,13,13)(\frac{1}{3}, \frac{1}{3}, \frac{1}{3})

重心也被称为质心。

内心

三角形的内心是三角形 3 条角平分线的焦点,它之所以被称为内心,是因为它也是三角形内切圆的圆心。由该特性可知,内心与三角形各条边的距离是相等的。

内心可通过下式计算:

cIn=l1v1+l2v2+l3v3p{\bf c_{In}} = \frac{l_1{\bf v_1} + l_2{\bf v_2} + l_3{\bf v_3}}{p}

其中,p=l1+l2+l3p = l_1 + l_2 + l_3 是三角形的周长。因此,内心的重心坐标为:

(l1p,l2p,l3p)(\frac{l_1}{p}, \frac{l_2}{p}, \frac{l_3}{p})

可以通过下式将三角形的面积除以其周长来计算内切圆的半径:

rIn=Apr_{In} = \frac{A}{p}

内切圆解决了找到与 3 条线相切的圆的问题。

外心

外心是三角形中与顶点等距的点,它是围绕三角形的外接圆的圆心。外心构造为各条边的垂直平分线的交点。

要计算外心,需要先定义以下的中间值:

d1=e2e3,d2=e3e1,d3=e1e2,c1=d2d3,c2=d3d1,c3=d1d2,c=c1+c2+c3\begin{aligned} d_1 &= -{\bf e_2}\cdot{\bf e_3},\\ d_2 &= -{\bf e_3}\cdot{\bf e_1},\\ d_3 &= -{\bf e_1}\cdot{\bf e_2},\\ c_1 &= d_2d_3,\\ c_2 &= d_3d_1,\\ c_3 &= d_1d_2,\\ c &= c_1 + c_2 + c_3 \end{aligned}

在取得这些中间值后,即可按下式给出外心的重心坐标:

(c2+c32c,c3+c12c,c1+c22c)(\frac{c_2 + c_3}{2c}, \frac{c_3 + c_1}{2c}, \frac{c_1 + c_2}{2c})

由此,外心可通过下式计算:

cCirc=(c2+c3)v1+(c3+c1)v2+(c1+c2)v32c{\bf c_{Circ}} = \frac{(c_2 + c_3){\bf v_1} + (c_3 + c_1){\bf v_2} + (c_1 + c_2){\bf v_3}}{2c}

外接圆半径的计算公式为:

rCirc=(d1+d2)(d2+d3)(d3+d1)/c2r_{Circ} = \frac{\sqrt{(d_1 + d_2)(d_2 + d_3)(d_3 + d_1)/c}}{2}

外接圆半径和外心解决了找到通过三个点的圆的问题。


三角形重心坐标
http://example.com/posts/三角形重心坐标/
作者
祭零小白
发布于
2022年7月31日
许可协议