文档
Mesh 和 CSG 内核
来自参数化实体的闭合三角形网格、具有凸快速路径的 BSP 布尔、清理和拓扑检查。
从英文自动翻译。 查看英文原文
什么是网格内核
网格内核适用于闭合三角形网格:它从参数基元构建它们(meshOf),测量它们(meshVolume, meshBounds, meshTopology),并将它们与布尔值 (booleanMesh, booleanAll)。它是视口显示的内容、CAM 生成器加工的内容以及 STL 导出写入的内容。
它不是精确的边界表示。被切割的圆柱体仍然是多面圆柱体,并且没有可供圆角或测量的面拓扑。对于精确的面、圆角、质量属性和 STEP, B-代表内核 是一段单独的代码。
| 文件 | 线路 | 角色 |
|---|---|---|
cad/solid.ts | 659 | 实体模式(长方体、圆柱体、球体、圆锥体、环面、拉伸、旋转)、meshOf、meshVolume、meshBounds |
cad/csg.ts | 459 | BSP 布尔值、凸分类器、平移/旋转/合并 |
cad/mesh-clean.ts | 626 | 焊接、T 形接头修复、共面合并、重新三角测量 |
cad/mesh-topology.ts | 110 | 流形、壳、属、体积、面积 |
cad/triangulate.ts | 383 | 带孔多边形三角剖分 |
网格类型和基元
export interface Mesh {
readonly positions: Float32Array // x, y, z per vertex
readonly indices: Uint32Array // three per triangle, counter-clockwise seen from outside
}
export const meshOf: (solid: Solid, tolerance?: number /* 0.1 mm */) => Mesh
export const meshVolume: (mesh: Mesh) => number // signed; positive when outward-facing
export const meshBounds: (mesh: Mesh) => SolidBounds | 坚硬的 | 领域 | 限制 |
|---|---|---|
| 盒子 | 原点,尺寸 {x, y, z} | 尺寸 > 0 |
| 圆柱 | 原点、半径、高度、线段、轴? | 段 3–512; z 轴 | x| y |
| 领域 | 中心、半径、环 | 铃声 2–256 |
| 锥体 | 原点、baseRadius、topRadius、高度、线段、轴? | 顶部半径≥0;段 3–512 |
| 环面 | 中心、majorRadius、minorRadius、旋转?、majorSegments、minorSegments、轴? | 3–512 和 3–256 |
| 拉伸 | 轮廓(≥ 3 点,不闭合),高度,原点? | xY 轮廓 |
| 围绕 | 轮廓(≥ XZ 中的 2 点)、角度、线段? | 关于Z;段 3–512,默认 16 |
segments 和 rings 是最小值: meshOf 提高它们直到和弦误差在范围内 tolerance, using step = 4·asin(√(min(1, tol/r)/2)). Two budgets stop runaway tolerances.
| 警卫 | 价值 | 错误信息 |
|---|---|---|
| 公差必须是正的且有限的 | — | 网格公差必须为正且有限 |
| 每条曲线的线段 | 65 536 | 网格公差超出细分预算 |
| 网格行×列(球体、环面、旋转) | 1 000 000 | 网格容差超出顶点预算 |
Ø6 圆柱体 segments: 24 在公差 0 处。05 得到 96 个三角形(24 条边,两个 24 边形帽); a Ø20 具有 128,因为较大的半径需要 32 段才能保持在 0.05 毫米内。
通过二进制空间划分进行布尔运算
每个操作数成为一个分类器。通用网格得到一棵BSP树;凸网格(封闭的、连通的、每条边都是凸的)只是其不同的面平面,因此一个盒子有六个平面,一万个三角形的球体可以在不构建凸实体 BSP 退化为的二次链的情况下进行分类。多边形被分类为列表,并且保留或删除没有其他操作数边界框的多边形,而不走任何东西。
- If either mesh is empty, or the bounding boxes do not meet (within tolerance), answer directly: union = merge, subtract = a, intersect = empty.
- 构建 A 和 B 的分类器(凸平面或 BSP)。
- 并集:将 A 保留在 B 之外,将 B 保留在 A 之外,并将 B 的面与 A 的指向相同的方向重合,因此保留一次共享面。
- Subtract and intersect follow the classic a.clipTo(b), b.clipTo(a), b.invert() sequence on polygon lists, without copying trees.
- 清理多边形汤:焊接 max(10·eps, 1e-9·size) 内的点,关闭 T 形接头,关闭条子之间最多 1e-4·size 的间隙,合并共面片段并重新三角化。
export type BooleanOp = "union" | "subtract" | "intersect"
export interface CsgOptions {
/** Distance below which a point counts as on a plane, mm. Default 1e-7 × the larger extent. */
readonly tolerance?: number
/** Merge coplanar fragments and re-triangulate (default true). */
readonly mergeCoplanar?: boolean
}
export const booleanMesh: (a: Mesh, b: Mesh, op: BooleanOp, options?: CsgOptions) => Mesh
/** Union reduced as a balanced tree; subtract and intersect fold left to right. */
export const booleanAll: (meshes: ReadonlyArray<Mesh>, op: BooleanOp, options?: CsgOptions) => Mesh
export const translateMesh: (mesh: Mesh, dx: number, dy: number, dz: number) => Mesh
export const rotateMesh: (mesh: Mesh, axis: V3, radians: number, origin?: V3) => Mesh
export const mergeMeshes: (meshes: ReadonlyArray<Mesh>) => Mesh 三角形数量随着特征而增长,而不是随着布尔值的数量而增长:合并步骤将共面的条带回整个面。在上面的板中,盒子有 12 个三角形,在四个孔之后 412,在凸台之后 540,在孔之后 640;整个链在 96 毫秒内运行。
对曲线输入进行并集、相减、相交
A 20 mm cube at the origin against a sphere of radius 12 centred on its far corner (20, 20, 20), meshed at tolerance 0.2 (624 triangles). Real volumes:
| 结果 | 体积,立方毫米 | 三角形 |
|---|---|---|
| 单独的球体 | 7 063.84(确切地说是 7 238.23) | 624 |
| 立方体∩球体 | 882.98 | 112 |
| 立方体∪球体 | 14 180.86 | — |
| 立方体-球体 | 7 117.02 | — |
检查:8 000 + 7 063.84 − 882.98 = 14 180.86 和 8 000 − 882.98 = 7 117.02,因此三个结果彼此一致第一百。球体网格本身是精确球体下的 2.4 %,这是粗略公差的分面,而不是布尔误差。
拓扑检查
meshTopology 计算顶点、边和三角形的数量,查找边界、非流形和错误定向的边、退化三角形和壳,并导出欧拉特征和亏格。 describeTopology 打印助手和视口使用的一行摘要。
{"vertices":312,"triangles":640,"edges":960,"boundaryEdges":0,"nonManifoldEdges":0,
"misorientedEdges":0,"degenerateTriangles":0,"shells":1,"eulerCharacteristic":-8,"genus":5,
"closed":true,"solid":true,"volume":23981.43509870267,"area":8238.640705862534}
// describeTopology → "closed solid, 1 shell, genus 5, 640 triangles, V 23981.4 mm³, A 8238.6 mm²" V − E + F = 312 − 960 + 640 = −8 = 2 − 2g, so g = 5: the four drilled holes plus the bore, which runs through the boss and the plate.
多边形三角剖分
triangulatePolygon(outer, holes) 将索引三元组返回到外环和孔的串联中。带有 4 × 4 孔的 2 × 2 正方形可得出八个三角形:
triangulatePolygon(
[{x:0,y:0},{x:4,y:0},{x:4,y:4},{x:0,y:4}],
[[{x:1,y:1},{x:1,y:3},{x:3,y:3},{x:3,y:1}]])
// [[0,4,5],[7,4,0],[3,0,5],[7,0,1],[2,3,5],[6,7,1],[2,5,6],[6,1,2]] triangulatePolygonChecked 回报 undefined 当输入无法进行三角测量时(自相交环、外环外侧的孔),而不是部分结果。
故障排除
| 症状 | 原因 | 使固定 |
|---|---|---|
| 布尔结果有洞或翻转面 | 输入开路或顺时针缠绕 | 使用 meshTopology 检查两个输入:boundaryEdges 和 MisorientedEdges 必须为 0 |
| 两个操作数共享的面丢失或重复 | 面几乎但不完全重合(距离超出公差) | 将操作数对齐到同一平面,或提高 CsgOptions.tolerance |
| RangeError:网格容差超出细分预算 | 对于半径来说公差太细 | 使用公差预设(0.1 至 0.0005 毫米) |
| 体积与公式不同 | 粗公差刻面 | 网格更细,或使用 B-rep 内核获得精确的质量属性 |
| 许多工具上的布尔运算速度很慢 | 首先构建的工具联盟 | 逐一减去工具(booleanAll 执行此操作):每个凸工具都是几个平面 |