0%

05 - 平面类

1. 定义

1.1 平面定义 —— 隐式定义

  • 可以用类似于定义 2D 直线的方法来定义平面。
  • 平面的隐式定义由所有满足平面方程的点 p=(x,y,z)\vec{p} = (x,y,z) 给出

平面方程的两种表示法:

{ax+by+cz=dpn=dis\begin{cases} ax + by + cz = d \\ \vec{p} \cdot \vec{n} = dis \end{cases}

其中有以下关系:

{n=(a,b,c)a2+b2+c2=(aa2+b2+c2,ba2+b2+c2,ca2+b2+c2)dis=da2+b2+c2\begin{cases} \vec{n} = \cfrac{(a,b,c)}{\sqrt {a^2 + b^2 + c^2}} = (\cfrac{a}{\sqrt {a^2 + b^2 + c^2}},\cfrac{b}{\sqrt {a^2 + b^2 + c^2}},\cfrac{c}{\sqrt {a^2 + b^2 + c^2}}) \\[2ex] dis = \cfrac{d}{\sqrt {a^2 + b^2 + c^2}} \end{cases}

  • 向量 n\vec{n} 即为平面的法向量,垂直于整个平面

同时,

{a=b=c=d=\begin{cases} a = \\[2ex] b = \\[2ex] c = \\[2ex] d = \end{cases}

1.2 平面定义 —— 三点定面

screenShot.png

  • 通过平面上的三个点 p1p_1p2p_2p3p_3 来计算 n\vec{n}disdis
  • 右手坐标系下,当从平面正面看时,三个点 p1p_1p2p_2p3p_3 逆时针列出。

<1> 求向量 n\vec{n}

{e3=p2p1e1=p3p2n=e3×e1e3×e1\begin{cases} \vec{e_3} = \vec{p_2} - \vec{p_1} \\[2ex] \vec{e_1} = \vec{p_3} - \vec{p_2} \end{cases} \Rightarrow \vec{n} = \cfrac{\vec{e_3} \times \vec{e_1}}{\Vert \vec{e_3} \times \vec{e_1} \Vert}

<2>disdis

dis=p1n=p2n=p3ndis = \vec{p_1} \cdot \vec{n} = \vec{p_2} \cdot \vec{n} = \vec{p_3} \cdot \vec{n}

1.3 多于三个点的“最佳”平面

从一组三个以上的点集出平面方程,这种点集最常见的例子就是多边形的顶点,这些顶点绕多边形 逆时针 列出。

  • 会遇到的问题
    1. 多边形是凹的,这些点恰好在凹处,从而构成了顺时针,导致法向量方向错误;
    2. 可能由于浮点数精确问题或者错误的生成多边形的方法,使得多边形的顶点可能会出现不共面的情况。

因此需要从点集中求出“最佳”,该平面应综合考虑所有的点:

p1=[x1y1z1]p1=[x2y2z2]p1=[xn1yn1zn1]p1=[xnynzn]\begin{aligned} &\vec{p_1} = \begin{bmatrix} x_1 & y_1 & z_1 \end{bmatrix} \\ &\vec{p_1} = \begin{bmatrix} x_2 & y_2 & z_2 \end{bmatrix} \\ &\vdots \\ &\vec{p_1} = \begin{bmatrix} x_{n-1} & y_{n-1} & z_{n-1} \end{bmatrix} \\ &\vec{p_1} = \begin{bmatrix} x_n & y_n & z_n \end{bmatrix} \end{aligned}

1.3.1 最佳平面的法向量 n\vec{n}

nx=(z1+z2)(y1y2)+(z2+z3)(y2y3)++(zn1+zn)(yn1yn)+(zn+z1)(yny1)ny=(x1+x2)(z1z2)+(x2+x3)(z2z3)++(xn1+xn)(zn1zn)+(xn+x1)(znz1)nz=(y1+y2)(x1x2)+(y2+y3)(x2x3)++(yn1+yn)(xn1xn)+(yn+y1)(xnx1)\begin{aligned} &\vec{n_x} = (z_1 + z_2)(y_1 - y_2) + (z_2 + z_3)(y_2 - y_3) + \cdots + (z_{n-1} + z_n)(y_{n-1} - y_n) + (z_n + z_1)(y_n - y_1) \\ &\vec{n_y} = (x_1 + x_2)(z_1 - z_2) + (x_2 + x_3)(z_2 - z_3) + \cdots + (x_{n-1} + x_n)(z_{n-1} - z_n) + (x_n + x_1)(z_n - z_1) \\ &\vec{n_z} = (y_1 + y_2)(x_1 - x_2) + (y_2 + y_3)(x_2 - x_3) + \cdots + (y_{n-1} + y_n)(x_{n-1} - x_n) + (y_n + y_1)(x_n - x_1) \end{aligned}

若设 pn+1=p1\vec{p_{n+1}} = \vec{p_1} ,使用求和符号,则有:

nx=i=1n(zi+zi+1)(yiyi+1)ny=i=1n(xi+xi+1)(zizi+1)nz=i=1n(yi+yi+1)(xixi+1)\begin{aligned} &\vec{n_x} = \sum_{i = 1}^{n}(z_i + z_{i+1})(y_i - y_{i+1}) \\ &\vec{n_y} = \sum_{i = 1}^{n}(x_i + x_{i+1})(z_i - z_{i+1}) \\ &\vec{n_z} = \sum_{i = 1}^{n}(y_i + y_{i+1})(x_i - x_{i+1}) \end{aligned}

若需要限制 n\vec{n} 必须为单位向量,则该向量必须正则化。

1.3.2 最佳平面的距离 disdis

dis=1ni=1n(pin)=1n(i=1npi)n\begin{aligned} dis &= \frac{1}{n} \sum_{i=1}^{n} (\vec{p_i} \cdot \vec{n}) \\[4ex] &= \frac{1}{n} \left( \sum_{i=1}^{n} \vec{p_i} \right) \cdot \vec{n} \end{aligned}

1.3.2 计算点集的最佳平面:

  • 计算法向量 n\vec{n}
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
Vector3 computeBestFitNormal (const Vector3 v[], int n)
{
// 和置零
Vector3 result = kZeroVector;

// 从最后一个顶点开始,避免在循环中做 if 判断
const Vector3 *p = &v[n-1];

// 迭代所有顶点
for(int i = 0; i < n; ++i)
{
// 得到“当前”顶点
const Vector3 *c = &v[i];

// 边向量乘积相加
result.x += (p->z + c->z) * (p->y - c->y);
result.y += (p->x + c->x) * (p->z - c->z);
result.z += (p->y + c->y) * (p->x - c->x);

// 下一个顶点
p = c;
}

// 正则化结果并返回
result.normalize();
return result;
}
  • 计算平均距离 disdis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float computeBestDis (const Vector3 v[], int n, const Vector3& normalVec)
{
// 和置零
float disRes = 0;
Vector3 sumVec = kZeroVector;

// 从最后一个顶点开始,避免在循环中做 if 判断
const Vector3 *p = &v[n-1];

// 迭代所有顶点
for(int i = 0; i < n; ++i)
{
// 相加
sumVec += *p;
}

disRes = (sumVec * normalVec) / n;
return disRes;
}

1.4 点到平面的距离

1.4.1 函数式 ax+by+cz=dax + by + cz = d

q(x0,y0,z0)q(x_0,y_0,z_0) 到平面 PP 的距离为:

dis(q,P)=ax0+by0+cz0da2+b2+c2dis(q,P) = \cfrac{\vert {ax_0 + by_0 + cz_0 - d} \vert}{\sqrt {a^2 + b^2 + c^2}}

1
2
3
4
5
6
float computetDis (float a, float b, float c, float d, const Vector3& q)
{
float disRes = fabs(a * q.x + b * q.y + c * q.z - d) / sqrt(a*a + b*b + c*c);

return disRes;
}

1.4.2 点和法向量表达式 pn=dis\vec{p} \cdot \vec{n} = dis

设想一个平面和一个不在平面上的点 q\vec{q} 。平面上存在一个点 p\vec{p} ,它到 q\vec{q} 的距离最短。显然,从 p\vec{p}q\vec{q} 的向量垂直于平面,且形式为 ana \cdot \vec{n}

screenShot.png

p+an=q(p+an)n=qnpn+ann=qndis+a=qna=qndis\begin{aligned} \vec{p} + a \cdot \vec{n} &= \vec{q} \\ (\vec{p} + a \cdot \vec{n}) \cdot \vec{n} &= \vec{q} \cdot \vec{n} \\ \vec{p} \cdot \vec{n} + a \cdot \vec{n} \cdot \vec{n} &= \vec{q} \cdot \vec{n} \\ dis + a &= \vec{q} \cdot \vec{n} \\ a &= \vec{q} \cdot \vec{n} - dis \end{aligned}

计算任意点到平面的有符号距离

1
2
3
4
5
6
float computetDis (float dis, const Vector3& normalVec, const Vector3& q)
{
float disRes = q * normalVec - dis;

return disRes;
}