自动关联目录:离散曲面与共形算法
本章目标
连续理论给出度量、曲率、Laplacian 和共形结构;算法必须把它们转成有限维数据。本章从三角网格出发,逐步推导:
边长→三角形角度与面积→角度亏损→余切能量→稀疏 Laplace 矩阵→调和/共形参数化.
1. 三角剖分曲面
一个定向三角网格记为
M=(V,E,F),
其中V 是顶点,E 是边,F 是有向三角形。几何嵌入给每个顶点位置pi∈R3。
1.1 组合结构与几何结构要分开
- 组合结构:哪些顶点、边、面相邻;
- 分片线性几何:每条边的长度lij;
- 外在嵌入:顶点在R3 中的位置。
离散内在度量只需边长,并要求每个三角形满足严格三角不等式:
lij+ljk>lki.
同一组边长可以有不同三维折叠,但内在角度、面积和离散曲率相同。
1.2 半边结构
每条无向边拆成两个定向半边h=(i→j) 与h∗=(j→i)。每个半边至少记录:
origin(h) : 起点
next(h) : 同一面中的下一条半边
twin(h) : 反向半边;边界处可为空
face(h) : 所属面
edge(h) : 所属无向边
常数时间可完成:
- 沿面循环:
h -> next(h); - 穿过边:
h -> twin(h); - 绕顶点:
h -> twin(next(h))(具体公式随约定变化)。
2. 从边长恢复单个三角形
三角形(i,j,k) 的边长记为
a=ljk,b=lki,c=lij.
余弦定理:
cosθi=2bcb2+c2−a2.
Heron 公式:
s=2a+b+c,A=s(s−a)(s−b)(s−c).
也可在局部二维坐标中放置
qi=(0,0),qj=(c,0),
qk=(2cb2+c2−a2,b2−xk2).
所有三角形内的梯度、Jacobian 和能量都可在这个局部坐标中计算。
3. 分片平坦度量与锥奇点
每个三角形内部是欧氏平面,沿边按等长方式粘合。曲率只集中在顶点,顶点邻域像一个欧氏圆锥。
内部顶点i 的总角为
Θi=t∋i∑θit.
离散高斯曲率(角度亏损)为
Ki=2π−Θi.
边界顶点的边界曲率为
ki=π−Θi.
这里Ki 是积分曲率,不是除以面积后的曲率密度。若需近似点值曲率,可除以 barycentric 或 Voronoi dual area。
4. 离散 Gauss–Bonnet 的推导
设网格有边界。将所有顶点曲率求和:
i∈V∘∑(2π−Θi)+i∈∂V∑(π−Θi).
每个三角形内角和为π,所以
i∑Θi=πF.
记内部顶点数Vi、边界顶点数Vb:
∑Ki=2πVi+πVb−πF.
对三角网格,数面-边关联:
3F=2Ei+Eb,
每条边界分支是闭合多边形,所以Eb=Vb。代数化简得到
i∈V∘∑Ki+i∈∂V∑ki=2π(V−E+F)=2πχ(M).
离散 Gauss–Bonnet 对任意三角剖分严格成立,不是收敛意义下的近似。
5. 分片线性有限元基函数
对每个顶点i,定义帽函数ϕi:
ϕi(pj)=δij,
并在每个三角形内线性。任意分片线性函数
uh=i∑uiϕi.
在每个三角形内∇ϕi 为常向量。
5.1 单个三角形上的梯度
在二维有向三角形(qi,qj,qk) 中,令R90(x,y)=(−y,x),有
∇ϕi=2AR90(qk−qj),
以及循环置换。因为ϕi 在对边(j,k) 上为 0,梯度垂直于该边;大小为“高度的倒数”。
6. 余切权重的有限元推导
连续 Dirichlet 能量的双线性型为
a(u,v)=∫M⟨∇u,∇v⟩dA.
离散 stiffness matrix
Lij=∫M⟨∇ϕi,∇ϕj⟩dA.
在包含边(i,j) 的单个三角形(i,j,k) 上:
∫t⟨∇ϕi,∇ϕj⟩dA=−21cotθk.
6.1 计算过程
由梯度公式:
⟨∇ϕi,∇ϕj⟩=4A2(qk−qj)⋅(qi−qk).
分子对应角k 两条边的负点积:
(qk−qj)⋅(qi−qk)=−∥qj−qk∥∥qi−qk∥cosθk.
又
2A=∥qj−qk∥∥qi−qk∥sinθk.
乘以三角形面积A 后得到−21cotθk。
对于内部边(i,j) 两侧对角αij,βij:
wij=21(cotαij+cotβij),
Lij=−wij,i=j,
Lii=j∈N(i)∑wij.
7. 离散 Dirichlet 能量
ED(u)=21uTLu=21(i,j)∈E∑wij(ui−uj)2.
由于它来自积分∫∥∇uh∥2,即使某些余切权重为负,总矩阵仍为半正定(对无退化网格)。但负权重会破坏离散最大值原理。
7.1 零空间
每行和为 0:
L1=0.
对有c 个连通分支的网格,dimkerL=c。求解 Poisson 方程Lu=b 时必须满足每个分支上
1Tb=0
或施加 Dirichlet/锚点约束。
8. 质量矩阵与强形式 Laplacian
stiffness matrixL 表示弱形式。要近似点值Δu,还需质量矩阵
Mij=∫MϕiϕjdA.
单个三角形的 consistent mass matrix:
Mt=12At211121112.
lumped mass 把每个三角形At/3 分给三个顶点,使M 对角。
强形式近似为
Δhu≈−M−1Lu
(符号取决于约定)。谱问题是广义特征问题
Lϕ=λMϕ.
9. 离散 Dirichlet 问题
将顶点分成内部I 和边界B:
[LIILBILIBLBB][uIuB]=0.
给定边界uB,内部满足
LIIuI=−LIBuB.
若权重非负,内部值是邻居的凸组合:
ui=∑jwij∑jwijuj.
因此离散最大值原理成立。
10. Tutte 嵌入定理
设平面图三连通,把外边界固定为严格凸多边形,每个内部顶点放为邻居的严格凸组合:
ui=j∈N(i)∑λijuj,λij>0,quadj∑λij=1.
则所得直线嵌入没有边交叉,内部面不翻转。
10.1 证明思想
- 每个内部顶点位于邻居凸包内;
- 任意方向投影后,内部最大值只能沿连到边界的“最大值平台”传播;
- 若发生折叠或交叉,可构造内部极值/分离结构,与三连通及凸边界矛盾。
10.2 为什么余切权重不自动满足定理
钝角的余切为负,因此wij 可能为负。Tutte 的凸组合假设失效。常见选择:
- uniform weightwij=1:正,但畸变较大;
- mean value coordinates:对合适网格为正;
- intrinsic Delaunay triangulation:使内部边余切和非负;
- 直接用带 barrier 的无翻转优化。
11. 调和参数化
给边界顶点二维位置zB=(uB,vB),分别解
LIIuI=−LIBuB,
LIIvI=−LIBvB.
这最小化
ED(z)=ED(u)+ED(v)
且固定边界。它是调和映射,但一般不严格共形,因为固定边界形状可能迫使内部发生角度畸变。
12. 单个三角形的参数化 Jacobian
设原三角形局部二维矩阵
X=[xj−xiyj−yixk−xiyk−yi],
UV 三角形矩阵
U=[uj−uivj−viuk−uivk−vi].
仿射映射 Jacobian 为
Jt=UX−1.
用 SVDJt=R1diag(σ1,σ2)R2T 可计算:
- 共形畸变:Kt=σ1/σ2;
- 面积缩放:JtA=detJt=σ1σ2;
- symmetric Dirichlet:σ12+σ22+σ1−2+σ2−2;
- 翻转:detJt<0。
13. 共形能量与 Cauchy–Riemann 残差
平面映射f=(u,v) 的共形条件:
ux−vy=0,uy+vx=0.
定义 least-squares conformal energy
EC(f)=21∫M[(ux−vy)2+(uy+vx)2]dA.
展开:
2EC=∫M(ux2+uy2+vx2+vy2),dA−2∫M(uxvy−uyvx),dA.
所以
EC=ED−A(f),
其中A(f) 是有向映射面积(具体1/2 系数随定义约定)。这再次给出:Dirichlet 能量减面积正是非共形误差。
14. LSCM 的单三角形组装
在局部复坐标zi=xi+iyi 中,线性 UV 函数w=u+iv 的反全纯导数在三角形内为常数。可将每个三角形贡献写成
Et=4At1∣(wj−wi)(zk−zi)−(wk−wi)(zj−zi)∣anti2,
更方便的实现形式是直接离散 Cauchy–Riemann 残差。设
∇u=a∈{i,j,k}∑ua∇ϕa,∇v=a∑va∇ϕa.
三角形残差:
rt=[ux−vyuy+vx]=Ctxt,
xt=(ui,uj,uk,vi,vj,vk)T.
局部能量
Et=At∥Ctxt∥2,
局部 Hessian
Ht=2AtCtTCt.
把Ht scatter-add 到全局矩阵H。因为平移、旋转和缩放不改变共形性,系统有相似变换自由度;通常固定两个不同顶点的 UV 坐标,或添加规范化约束。
15. LSCM 约束系统
将所有未知量排列为
x=(u1,…,un,v1,…,vn)T.
问题:
xmin21xTHxs.t.Cx=d.
KKT 系统:
[HCCT0][xλ]=[0d].
或者消去固定变量,只解自由变量:
HFFxF=−HFCxC.
锚点应尽量相隔较远,否则尺度规范化病态。
16. LSCM 不保证无翻转
LSCM 最小化二次能量,但可行域没有包含detJt>0 约束。特别是:
- 网格不是圆盘;
- 锚点选择差;
- 三角形退化;
- 边界高度非凸;
- 能量极小值经过退化状态;
都可能出现翻转。需要显式 barrier,例如对detJt 加
−μlogdetJt
或使用本身在detJ→0+ 时发散的 symmetric Dirichlet 能量并配合 line search。
17. 离散共形等价的另一种定义:顶点缩放
参数化算法把网格映到平面;离散共形几何还研究“哪些分片平坦度量属于同一共形类”。常用 vertex scaling 定义:
l~ij=e(ui+uj)/2lij.
ui 是离散共形因子。改变u 会改变角度和顶点曲率,但保留一个离散意义下的共形结构。
当三角不等式失效或需要跨越不同 Delaunay 区域时,执行 intrinsic edge flip。四边形翻边的边长更新由 Ptolemy 关系联系,这是离散 Möbius 几何的重要结构。
18. 曲率对共形因子的导数
离散曲率
Ki(u)=2π−t∋i∑θit(u).
在一个固定 Delaunay 区域内,其 Jacobian 与余切 Laplacian 密切相关:
∂uj∂Ki∼−wij,i=j.
因此处方目标曲率Kˉ 可定义势能
∇E(u)=K(u)−Kˉ,
Hessian 是半正定 Laplace 型矩阵。固定整体尺度后,可用 Newton 法求解。这是离散 Ricci flow 章节的基础。
19. 误差评估必须分开
| 指标 | 公式 | 测量内容 |
|---|
| 翻转 | detJt≤0 | 局部不可逆 |
| 共形畸变 | Kt=σ1/σ2 | 各向异性 |
| Beltrami | $ | \mu_t |
| 面积畸变 | log(σ1σ2) | 局部密度变化 |
| Dirichlet | σ12+σ22 | 总拉伸,但偏好收缩 |
| symmetric Dirichlet | ∣J∣F2+∣J−1∣F2 | 同时惩罚压缩和拉伸 |
全局统计不要只报告平均值。应至少给:最大值、面积加权中位数、95% 分位数、翻转数和退化数。
20. Python:组装余切 Laplacian
import numpy as np
from scipy.sparse import coo_matrix
def cotangent(u, v, eps=1e-15):
cross = np.linalg.norm(np.cross(u, v))
if cross <= eps:
raise ValueError("degenerate triangle")
return float(np.dot(u, v) / cross)
def cotangent_laplacian(vertices, faces):
n = len(vertices)
weights = {}
for i, j, k in np.asarray(faces, dtype=int):
pi, pj, pk = vertices[[i, j, k]]
cot_i = cotangent(pj - pi, pk - pi)
cot_j = cotangent(pk - pj, pi - pj)
cot_k = cotangent(pi - pk, pj - pk)
for (a, b), value in [
((j, k), 0.5 * cot_i),
((k, i), 0.5 * cot_j),
((i, j), 0.5 * cot_k),
]:
edge = tuple(sorted((a, b)))
weights[edge] = weights.get(edge, 0.0) + value
rows, cols, data = [], [], []
diagonal = np.zeros(n)
for (i, j), w in weights.items():
rows += [i, j]
cols += [j, i]
data += [-w, -w]
diagonal[i] += w
diagonal[j] += w
rows.extend(range(n))
cols.extend(range(n))
data.extend(diagonal)
return coo_matrix((data, (rows, cols)), shape=(n, n)).tocsr()
def validate_laplacian(L, atol=1e-10):
symmetry = np.max(np.abs((L - L.T).data), initial=0.0)
row_sum = np.max(np.abs(np.asarray(L.sum(axis=1)).ravel()))
if symmetry > atol or row_sum > atol:
raise ValueError((symmetry, row_sum))
21. Python:单三角形 Jacobian 与畸变
def local_triangle_2d(p0, p1, p2):
e1 = p1 - p0
length = np.linalg.norm(e1)
if length <= 1e-15:
raise ValueError("zero edge")
x_axis = e1 / length
normal = np.cross(e1, p2 - p0)
area2 = np.linalg.norm(normal)
if area2 <= 1e-15:
raise ValueError("degenerate triangle")
normal /= area2
y_axis = np.cross(normal, x_axis)
return np.array([
[0.0, 0.0],
[length, 0.0],
[np.dot(p2 - p0, x_axis), np.dot(p2 - p0, y_axis)],
])
def triangle_distortion(points_3d, uv):
x = local_triangle_2d(*points_3d)
X = np.column_stack((x[1] - x[0], x[2] - x[0]))
U = np.column_stack((uv[1] - uv[0], uv[2] - uv[0]))
J = U @ np.linalg.inv(X)
sigma = np.linalg.svd(J, compute_uv=False)
det = np.linalg.det(J)
K = np.inf if sigma[1] <= 1e-15 else sigma[0] / sigma[1]
return {"J": J, "det": det, "sigma": sigma, "K": K}
22. 选算法的决策表
| 目标 | 首选方法 | 理论保证 | 主要风险 |
|---|
| 简单、保证嵌入 | Tutte + 正权重 + 凸边界 | 无交叉/翻转 | 边界和角度畸变大 |
| 低角度畸变 | LSCM | 二次优化、快速 | 不保证无翻转 |
| 同时控制拉伸压缩 | symmetric Dirichlet | barrier 型局部可逆性 | 非线性、需好初值 |
| 目标曲率/标准域 | discrete Ricci/Yamabe | 能量凸性(相应设定下) | 三角不等式、需翻边 |
| 高亏格全局参数 | Hodge/Abel differential | 周期结构正确 | 需同调基和切图 |
23. 习题
计算题
- 对一个等边三角形手算局部 stiffness matrix。
- 对两个三角形组成的正方形组装全局L,验证L1=0。
- 构造一个钝角三角形,观察哪条边的余切贡献为负。
- 对J=diag(2,1/2) 计算所有表中畸变指标。
- 从顶点角度亏损推导四面体表面的总曲率。
证明题
- 完整推导∫t∇ϕi⋅∇ϕj=−21cotθk。
- 证明离散 Dirichlet 能量对常数平移不变。
- 在所有权重为正时证明离散最大值原理。
- 推导EC=ED−A。
- 说明为何K=1 不能排除反射,必须检查行列式。
实现题
- 实现质量矩阵、stiffness matrix,并通过线性函数 patch test。
- 实现 uniform Tutte 与 cotangent harmonic 两种参数化,对比翻转和Kt。
- 实现 LSCM 局部矩阵组装,用两个锚点求解。
- 输出每个三角形的logKt、log∣detJt∣ 热图。
- 对同一网格执行 intrinsic Delaunay flips,比较负权重数量。
24. 本章结论
余切 Laplacian、调和参数化和 LSCM 不是三个孤立配方:
PL 度量→有限元梯度→Dirichlet/共形能量→稀疏二次优化
每个算法保证都附带假设:正权重、凸边界、圆盘拓扑、非退化三角形或方向保持初值。实际代码必须显式检查这些假设。
下一篇用完整代码把拓扑检查、矩阵组装、边界条件、求解与畸变统计串起来:Python 实验:三角网格共形参数化。