跳到内容
CADCAMFUN
文档

文档

精确的 B-rep 内核

精确边界表示:解析和 NURBS 几何、布尔、圆角、孔识别、质量属性、验证、曲面细分和 STEP。

从英文自动翻译。 查看英文原文

B-rep 内核是什么

确切的内核在 packages/core/src/cad/brep (24 个源文件,大约 8 660 行,加上测试)通过精确边界表示实体:解析曲面和 NURBS 曲面,通过精确边缘的循环进行修剪。一切都是纯数据和纯函数,因此 B-rep 序列化为 JSON,跨越工作人员边界,并且项目历史记录中的差异保持不变。布尔运算、圆角和倒角、孔识别、质量属性、验证、无懈可击的曲面细分和 STEP 交换都适用于这一结构。

减去和圆角后的精确板 拖动进行轨道旋转·滚动进行缩放·右键拖动进行平移
A 60 × 40 × 15 box minus a Ø12 cylinder (booleanBrep), then the four vertical edges filleted R4 (blendBrep). Mesh from brepToMesh at chordTol 0.05. 18 vertices, 27 edges, 11 faces; exact volume 34 097.522 mm³.

拓扑表

A Brep 是一组索引表。实体通过索引相互引用,而不是通过对象引用。

实体持有步骤对应
顶点点、公差VERTEX_POINT
边缘exact curve, [t0, t1] with t0 < t1, v0 = C(t0), v1 = C(t1), toleranceEDGE_CURVE
科埃奇循环使用边缘的一种方式:边缘索引、反向、面中的可选 p 曲线 (u, v)ORIENTED_EDGE + PCURVE
环形共边闭循环;从外部看到的左侧材料(外部逆时针,孔顺时针)EDGE_LOOP
脸一个表面的区域;反转表示向外法线为 −(S_u × S_v)ADVANCED_FACE
壳连接的面集;封闭的壳限制了体积CLOSED_SHELL

Seams (an edge used twice by the same face, such as the generator line of a full cylinder) and poles (a loop through a degenerate point, such as a cone apex) need no special entities. That is why the exact sphere has 2 vertices, 1 edge and 1 face, and the torus 1 vertex, 2 edges and 1 face.

原语计数 - 实际输出
makeBox(60, 40, 15)    {"V":8,"E":12,"F":6,"S":1,"surfaces":["plane"]}
makeSphere(10)         {"V":2,"E":1,"F":1,"S":1,"surfaces":["sphere"]}
makeTorus(20, 5)       {"V":1,"E":2,"F":1,"S":1,"surfaces":["torus"]}
makeCone(10, 4, 20)    {"V":2,"E":3,"F":3,"S":1,"surfaces":["cone","plane"]}

曲线和曲面

分析类型使用 STEP (ISO 10303-42) 参数化,因此从 STEP 读取和写入的面会保留其参数。

类型参数化
线C(t) = O + t·d,d单位,t弧长
圆圈C(t) = O + r(cos t·x + sin t·y)
椭圆C(t) = O + a cos t·x + b sin t·y
飞机S(u,v) = O + u·x + v·y
圆柱S(u,v) = O + r(cos u·x + sin u·y) + v·z
锥体S(u,v) = O + (R + v tan α)(cos u·x + sin u·y) + v·z
领域S(u,v) = O + r cos v(cos u·x + sin u·y) + r sin v·z
环面S(u,v) = O + (R + r cos v)(cos u·x + sin u·y) + r sin v·z
革命S(u,v) = rotate(C(v), axis, u)
挤压S(u,v) = C(u) + v·d
努布斯有理 B 样条曲线或曲面(nurbs.ts 中的 Piegl & Tiller 算法)
精确的基元,细分 拖动进行轨道旋转·滚动进行缩放·右键拖动进行平移
makeBox(10,10,10), makeCylinder(5,10), makeCone(5,2,10) and makeSphere(5), each tessellated by brepToMesh at chordTol 0.2 (12, 430, 122 and 1810 triangles) and placed side by side.
环面 R20 r5 拖动进行轨道旋转·滚动进行缩放·右键拖动进行平移
makeTorus(20, 5):具有接缝且没有其他边缘的单个环形面,在chordTol 1和30°处进行镶嵌。 MassProperties 返回的精确体积为 2π²Rr² = 9 869.604 mm³。

精确布尔值

  1. 相交。 每个实体相对于另一个实体面的边(牛顿精确点)、位于另一个实体表面上的边以及每对不重合面的相交曲线,在两个表面上进行细化并识别为直线和圆,否则在拟合公差内拟合为三次 B 样条曲线。
  2. 印记。 每个交点都成为两个实体的一个注册表中的顶点,边在此处分割,并且每个面通过其 (u, v) 域中的平面排列被其上的曲线切割。
  3. 分类。 每个片段内的一个点相对于另一个实体定位:以相同或相反的方向向内、向外或向上。
  4. 缝。 片段按照操作规则保留,重合的片段成为一条边,共面的邻居合并,并且结果得到验证。不是封闭流形固体的结果抛出 BooleanError;它永远不会被返回。
boolean.ts
export const booleanBrep: (A: Brep, B: Brep, op: BooleanOp, options?: BooleanOptions) => BooleanResult
export interface BooleanOptions {
  tol?: number          // coincidence, default 1e-8 × model size, at least 1e-7 mm
  fitTol?: number       // fitted intersection curves, default 1e-6 × size, at least 1e-6 mm
  seedAngleDeg?: number // normal turn per seed-mesh triangle, default 10
  debug?: (stage: string, data: unknown) => void
}
export interface BooleanResult {
  brep: Brep; warnings: string[]
  stats: { intersectionCurves: number; splitPoints: number; fragments: number; keptFaces: number }
}
盒子减去圆柱体——实际输出
import { brep } from "@cadcamfun/core"
const { makeBox, makeCylinder, frame, booleanBrep, validateBrep, massProperties } = brep

const box = makeBox(60, 40, 15)
const hole = makeCylinder(6, 25, frame({ x: 30, y: 20, z: -5 }))
const r = booleanBrep(box, hole, "subtract")          // 141 ms
r.stats     // {"intersectionCurves":2,"splitPoints":12,"fragments":13,"keptFaces":7}
r.warnings  // []
validateBrep(r.brep)
// {"valid":true,"manifoldSolid":true,"genus":1,"volume":34303.53996706151,"issues":[]}
massProperties(r.brep).volume   // 34303.53996706151  = 60·40·15 − π·6²·15 exactly

质量特性

体积、面积、质心和惯性张量(单位密度,关于质心,行长)来自边界面上的散度定理。平面通过格林定理沿着精确的边缘曲线精确积分;曲面被集成在精确表面的参数步细分上。

身体massProperties封闭式
盒子 - Ø12 孔,体积34303.5399670615134303.539967061515
相同,面积8139.2920065876848139.292006587698
相同,质心(30.000, 20.000, 7.500)(30, 20, 7.5)
球体 r10,体积4188.7902047863794188.790204786391
环面 20/5,体积9869.604401094759869.604401089358
圆角板,体积34097.5222039230234097.522203923065

The filleted-plate closed form is the subtracted plate minus four corner slivers of (r² − πr²/4)·h = (16 − 4π)·15 each.

圆角、倒角和孔识别

签名
export const blendCandidates: (b: Brep, minAngleDegrees?: number /* 0.5 */) => BlendCandidate[]
export const selectBlendEdges: (edges: ReadonlyArray<BlendCandidate>, s?: BlendSelector) => BlendCandidate[]
// BlendSelector: parallel?: "x"|"y"|"z", within?: box, kind?: "convex"|"concave"|"both" (default convex), minLength?, curved?
export const blendBrep: (b: Brep, edges: ReadonlyArray<BlendCandidate>, size: number,
  style?: "fillet" | "chamfer", options?: BlendOptions) => BlendResult
// BlendResult: brep, blended, vertices (rolling-ball corners), rejected (edge ids), ...
export const recognizeHoles: (b: Brep, options?: HoleRecognitionOptions) => ReadonlyArray<RecognizedHole>

在盒子 - 圆柱体上有 14 个混合候选者; selectBlendEdges(c, { parallel: "z" }) 选取 4 垂直外边缘,并且 blendBrep(b, sel, 4) 已对所有 4 进行圆角处理,且在 94 毫秒内没有拒绝任何内容。这个洞只从面孔上读回:

recognizeHoles — 实际输出
[{"entry":{"x":30,"y":20,"z":15},"direction":{"x":0,"y":0,"z":-1},"depth":15,"diameter":12,
  "form":"simple","bottom":"through",
  "segments":[{"kind":"cylinder","start":0,"end":15,"diameterStart":12,"diameterEnd":12,"faces":[6]}]}]

表格有 simple, counterbore 和 countersink;底部是 through, flat 和 drill-point。识别读取的是面孔,而不是历史记录,因此 STEP 导入、布尔结果和参数部分的识别方式是相似的。

防水镶嵌

每条边都在其精确曲线上采样一次,并且使用它的每个面共享这些样本,因此焊接网格没有裂纹。面由约束 Delaunay 在其 (u, v) 域中进行三角剖分。

选项默认意义
chordTol模型对角线的 1/2000,夹紧到 [0.001, 0.05] mm网格与精确表面之间的最大距离
angleTolDeg15穿过三角形的最大正常转弯(0 禁用)
maxEdge0(无)边长目标
maxPointsPerFace200 000安全帽
paramStep—massProperties 使用的集成模式

对于圆角板 defaultChordTol 返回 0.0368 毫米并且 tessellateBrep 生成了 652 个三角形以及每个面的网格、边缘折线和警告。

步骤交换

writeStepBreps 以毫米和弧度写入 AP214 (AUTOMOTIVE_DESIGN) 文件,每个零件一个 ADVANCED_BREP_SHAPE_REPRESENTATION。 readStepBreps 读取 AP203、AP214 和 AP242 几何形状和拓扑,包括组件及其转换和产品名称。未知实体报告于 warnings 他们的脸都跳了起来;没有什么是猜测的。
writeStepBreps([{ name: "plate", brep }]) — first lines of the real 358-line file
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('CADCAMFUN exact B-rep'),'2;1');
FILE_NAME('plate','2026-10-15T04:49:21',(''),(''),'CADCAMFUN B-rep kernel','CADCAMFUN','');
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
ENDSEC;
DATA;
#1=APPLICATION_CONTEXT('core data for automotive mechanical design processes');
#2=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#1);
#3=PRODUCT_CONTEXT('',#1,'mechanical');
#4=PRODUCT_DEFINITION_CONTEXT('part definition',#1,'design');
#5=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));

读回该文件,得到一个体积为 34097.52220392303 mm3 的零件,等于写入的正文,精确到 11 有效数字。

时间预算

内核是同步的并且在调用者的线程上运行。 withBudget(ms, f) 设定最后期限;长循环(布尔值的面对、面分割、表面相交、曲面细分)调用 checkBudget,这会抛出 BudgetExceeded。嵌套预算保留较早的截止日期;没有预算,一张支票就需要一次比较。

实际输出
withBudget(1, () => booleanBrep(makeSphere(10), makeBox(10, 10, 10), "union"))
// BudgetExceeded: the exact model exceeded its time budget of 1 ms

故障排除

症状原因使固定
BooleanError缝合结果不是闭合流形实体(相切或接近重合的面)将操作数稍微偏离相切;首先使用 validateBrep 检查两者
BudgetExceeded行动花费的时间超过了预算简化,或给呼叫者更大的预算
来自原语的 RangeError非正大小,或小圆环 ≥ 大圆环例如“圆环小半径必须小于大半径”
混合边缘 rejected面对于半径、刀刃或倾斜的壁来说太窄较小的半径,或倒角
STEP 部分缺失面文件中不支持的实体读 warnings 来自 readStepBreps
validateBrep 报告问题构建体中的索引、方向或表面边缘误差该问题命名了该实体;没有任何内容会自动修复