52 KiB
STEP B-Rep 基础几何解释与残差递归算法规范
文档状态
- 状态:Proposed
- 规范版本:0.2
- 目标项目:SimpleCADAPI 2.x
- 输入:STEP 导入后得到的 B-Rep snapshot
- 输出:可重放的几何程序、有限解释证据、评分分量和最终验证指标
- v0.2 实体基础单元:Box、Cylinder、Cone、Sphere、Torus、RawBRep
- v0.2 组合操作:Union、Subtract
- v0.2 NURBS 能力:解析曲面约化、NURBS 面证据匹配、RawBRep 兜底;不从开放 NURBS 面猜测实体
- 非目标:证明原始 CAD feature history、识别 NACA 等领域参数族、保证全局最优、从 mesh 恢复解析 B-Rep
1. 摘要
STEP B-Rep 提供有限数量的 Face、Edge、Vertex、支撑几何及 topology,但通常不提供原始 primitive、Boolean 和 feature tree。本规范把逆向定义为以下有限搜索:
- 将输入 B-Rep 转换为有限的面、边和邻接证据原子。
- 只从输入中出现的有限几何事件枚举 Box、Cylinder、Cone、Sphere 和 Torus 候选。
- 用显式谓词计算每个候选解释的证据原子 bitset。
- 用版本化数值代价表达基础单元常见性和参数复杂度。
- 对候选计算
R+ = T - H和R- = H - T,再递归解释每个 residual solid。 - 用有硬预算的 Beam Search 枚举有限个完整程序。
- 从头重放完整程序,并用 symmetric-difference、双向边界距离和法向误差判定几何等价。
- 在验证通过且所有目标 SurfaceAtom 都有归属的程序中选择总代价最低者。
本文的规范性术语均映射到有限集合、公式、确定排序或返回 OK | FAILED | TIMEOUT | BUDGET_EXHAUSTED 的 kernel 调用。说明性文字不创建额外算法分支。
2. 输入、程序和总函数
2.1 输入
输入实体记为:
[ T=(V,E,F,A) ]
其中 V、E、F 和 topology relation 集合 A 均为有限集合。
2.2 输出程序
Program := Box(parameters)
| Cylinder(parameters)
| Cone(parameters)
| Sphere(parameters)
| Torus(parameters)
| RawBRep(snapshot, byte_length, raw_stats)
| Union(Program, Program)
| Subtract(Program, Program)
内部搜索额外使用占位节点:
Template := Program | Slot(slot_id)
完成程序不允许包含 Slot。
RawBRep 的参数不是 process-local kernel object。它是以下总函数的结果:
SnapshotOf(shape, context) -> KernelResult[BRepSnapshot]
BRepSnapshot
profile = "simplecad-brep-snapshot-0.2"
bytes
sha256 = SHA256(bytes)
该 profile 的 encoder 必须保存重放 shape 所需的 Vertex、Edge curve、Face surface、trim、orientation、tolerance 和 topology relation,并对相同 evaluated B-Rep 和 profile 产生相同 bytes。若 backend 不提供该 profile,SnapshotOf 返回 FAILED(UNSUPPORTED_SNAPSHOT_PROFILE)。RawBRepKey = snapshot.sha256,byte_length = len(snapshot.bytes);反序列化时必须重新计算 SHA-256 和 byte length 并相等。
raw_stats 是从 snapshot 解码结果重新枚举得到的 closed record:
RawStats
analytic_face_count
nurbs_face_records # 每项含 NurbsCost 所需全部整数
edge_count
vertex_count
创建节点时从 ResidualAnalysis 复制;反序列化时从 snapshot 重算并要求相等。
2.3 Kernel 调用总类型
所有可能失败的 kernel 操作统一返回:
KernelResult[T] :=
OK(value: T)
| FAILED(code: FailureCode)
| TIMEOUT
| BUDGET_EXHAUSTED
每次调用前执行:
KernelContext.try_reserve(kind) :=
if monotonic_clock() >= deadline: TIMEOUT
elif total_calls + 1 > max_total_calls: BUDGET_EXHAUSTED
elif kind == BOOLEAN and boolean_calls + 1 > max_boolean_calls: BUDGET_EXHAUSTED
else increment counters and OK
所有 kernel API 都接收 deadline。分析阶段调用失败时,整个输入降级为 RawBRep;候选阶段调用失败时丢弃该候选;最终验证阶段调用失败时,该程序验证失败。
本规范的调用计数单位是本节列出的逻辑 kernel API 调用,不是 CAD kernel 内部函数调用次数。调用包装器必须在进入 backend 前执行一次 try_reserve。纯 CPU 循环每处理 1024 个元素检查一次 deadline;到期后返回 TIMEOUT。
特殊失败动作只有以下四类,后文不得隐式覆盖:
initial/residual analysis failure -> 当前 task 只允许 RawExpansion
candidate construction/measurement failure -> 丢弃当前 candidate
residual generation failure -> 丢弃当前 candidate
validation failure -> 当前 program 的 ValidProgram = false
初始输入 analysis 失败时,只有 SnapshotOf(target) 成功才能返回 RawBRep;否则返回 ANALYSIS_FAILED 且 program = null。
2.4 空 shape
Boolean 结果类型为:
BooleanShape := EMPTY | NONEMPTY(shape)
定义:
volume(EMPTY) = 0
surface_area(EMPTY) = 0
components(EMPTY) = []
ValidSolid(EMPTY) = false
EMPTY 没有 bbox、Face、Edge 或 Vertex,不能成为 Program 叶节点。它只允许出现在 residual 和程序简化过程中。
2.5 几何程序执行
Render(Box/Cylinder/Cone/Sphere/Torus) := kernel_construct_primitive(parameters)
Render(RawBRep(snapshot,byte_length,raw_stats)) := kernel_copy(snapshot)
Render(Union(A,B)) := kernel_regularized_union(Render(A), Render(B))
Render(Subtract(A,B)) := kernel_regularized_cut(Render(A), Render(B))
任一子调用不是 OK 时,父调用返回相同失败状态。
内部验证允许程序简化常量 EMPTY:
Union(EMPTY,X) = X
Union(X,EMPTY) = X
Subtract(EMPTY,X) = EMPTY
Subtract(X,EMPTY) = X
完成输出程序执行常量折叠后不得含 EMPTY。
3. 完整配置
3.1 配置结构
ReverseConfig
tolerance:
eps_linear
eps_angular
eps_radius
eps_surface
eps_normal
eps_volume
eps_area
eps_length
discretization:
face_deflection
face_angular_deflection
edge_step
nurbs_u_samples
nurbs_v_samples
max_mesh_area_relative_error
analysis:
max_analysis_entities
max_analysis_total_kernel_calls
max_event_records
analysis_timeout_seconds
candidates:
max_direction_clusters
max_extent_pairs_per_axis
max_box_frames
max_box_candidates_per_frame
max_candidates_per_type
boolean_prefilter_top_m
min_trigger_entities
min_support_coverage
max_candidate_attempts_per_task
max_candidate_kernel_calls_per_task
score:
reward_support
reward_boundary
reward_edge
reward_adjacency
penalty_leaf_cost
unmatched_support_penalty
unmatched_boundary_penalty
unmatched_edge_penalty
expansion_margin
cost:
type_cost_box
type_cost_cylinder
type_cost_cone
type_cost_sphere
type_cost_torus
type_cost_nurbs_face
type_cost_raw_brep
operation_cost_union
operation_cost_subtract
scalar_parameter_cost
search:
beam_width
max_task_depth
max_expanded_states
max_search_total_kernel_calls
max_search_boolean_calls
search_timeout_seconds
max_completed_programs_to_validate
max_pending_tasks_per_state
validation:
max_validation_total_kernel_calls
max_validation_boolean_calls
validation_timeout_seconds
3.2 默认容差
令输入 bbox 对角线为 D,STEP entity tolerance 最大值为 t_step:
[ \epsilon_{linear}=\max(t_{step},10^{-9}D) ]
[ \epsilon_{surface}=2\epsilon_{linear} ]
[ \epsilon_{radius}=2\epsilon_{linear} ]
[ \epsilon_{angular}=10^{-7} ]
[ \epsilon_{normal}=10^{-6} ]
[ \epsilon_{volume}=\max(10^{-12}D^3,10\epsilon_{linear}^3) ]
[ \epsilon_{area}=\max(10^{-12}D^2,10\epsilon_{linear}^2) ]
[ \epsilon_{length}=\epsilon_{linear} ]
角度单位为 radian,长度单位采用导入后统一的模型单位。
3.3 默认离散化与预算
face_deflection = max(eps_surface / 2, 1e-6 * D)
face_angular_deflection = eps_normal / 2
edge_step = max(eps_length, 1e-4 * D)
nurbs_u_samples = 9
nurbs_v_samples = 9
max_mesh_area_relative_error = 1e-3
max_analysis_entities = 1000000
max_analysis_total_kernel_calls = 1000000
max_event_records = 1000000
analysis_timeout_seconds = 120
max_direction_clusters = 12
max_extent_pairs_per_axis = 8
max_box_frames = 64
max_box_candidates_per_frame = 16
max_candidates_per_type = 24
boolean_prefilter_top_m = 16
min_trigger_entities = 1
min_support_coverage = 0.01
max_candidate_attempts_per_task = 10000
max_candidate_kernel_calls_per_task = 100000
reward_support = 10.0
reward_boundary = 5.0
reward_edge = 2.0
reward_adjacency = 1.0
penalty_leaf_cost = 1.0
unmatched_support_penalty = 5.0
unmatched_boundary_penalty = 2.0
unmatched_edge_penalty = 1.0
expansion_margin = 2.0
type_cost_box = 1.0
type_cost_cylinder = 1.0
type_cost_cone = 1.5
type_cost_sphere = 1.5
type_cost_torus = 2.0
type_cost_nurbs_face = 4.0
type_cost_raw_brep = 8.0
operation_cost_union = 0.5
operation_cost_subtract = 0.5
scalar_parameter_cost = 0.05
beam_width = 8
max_task_depth = 10
max_expanded_states = 1000
max_search_total_kernel_calls = 1000000
max_search_boolean_calls = 500
search_timeout_seconds = 120
max_completed_programs_to_validate = 32
max_pending_tasks_per_state = 256
max_validation_total_kernel_calls = 1000000
max_validation_boolean_calls = 500
validation_timeout_seconds = 120
3.4 配置合法性
ConfigValid(config) 当且仅当:
- 所有浮点配置均为 finite。
- 所有
eps_*、face_deflection、face_angular_deflection、edge_step和 timeout 严格大于0。 0 < eps_angular < pi/2且0 < eps_normal < pi/2。nurbs_u_samples >= 3且nurbs_v_samples >= 3。0 <= max_mesh_area_relative_error < 1。- 所有
max_*、beam_width和boolean_prefilter_top_m是有限正整数。 min_trigger_entities是有限非负整数。0 <= min_support_coverage <= 1。- 所有 reward、penalty 和 cost 是 finite 且大于等于
0。
输入数值合法性:
FiniteBRep(T) :=
every bbox coordinate, Vertex coordinate, curve parameter,
surface parameter, knot, weight and tolerance used by this algorithm is finite
and every radius used by an analytic record is > 0
and every direction consumed by normalize has norm > eps_linear
t_step 为所有 finite entity tolerance 的最大值;没有 entity tolerance 时取 0。FiniteBRep == false 时返回 INVALID_INPUT。
ConfigValid == false 时返回 INVALID_CONFIG,不调用 kernel。
4. 量化与确定顺序
4.1 量化
定义半数向正无穷舍入:
[ Q(x,\epsilon)=\lfloor x/\epsilon+1/2\rfloor ]
[ Q_3(p,\epsilon)=(Q(p_x,\epsilon),Q(p_y,\epsilon),Q(p_z,\epsilon)) ]
单位方向 d 的规范符号:找到绝对值最大的分量;多个分量同大时按 x,y,z 选择第一个;若该分量小于 0,令 d := -d。
[ Q_d(d)=Q_3(d,\sin(\epsilon_{angular})) ]
4.2 基本方向谓词
定义有向夹角:
[ angle(a,b)=acos(clamp(dot(a/|a|,b/|b|),-1,1)) ]
只有 norm(a) > eps_linear 且 norm(b) > eps_linear 时定义;否则调用该谓词的 match 结果为 false。
[ parallel(a,b):=angleAbs(a,b)\leq\epsilon_{angular} ]
其中:
[ angleAbs(a,b)=acos(clamp(|dot(a,b)|,-1,1)) ]
[ perpendicular(a,b):=|dot(a,b)|\leq\sin(\epsilon_{angular}) ]
两轴线 (q1,d1)、(q2,d2) 共轴:
coaxial := parallel(d1,d2)
and norm((q2-q1) - d1*dot(d1,q2-q1)) <= eps_linear
4.3 Bbox 相交
闭区间 bbox A、B 相交当且仅当每个轴 k 满足:
[ A_{min,k}\leq B_{max,k}\land B_{min,k}\leq A_{max,k} ]
expand(B, e) 将每个最小坐标减 e,每个最大坐标加 e。
4.4 Canonical serialization
所有 canonical record 使用 UTF-8 JSON,object key 按 UTF-8 byte order 排序,禁止 NaN 和 Infinity,浮点参数先替换为本文定义的量化整数。
CandidateKey = canonical_json({type, quantized_parameters})
RawBRepKey = snapshot.sha256
ProgramKey = canonical_json(program tree with CandidateKey or RawBRepKey leaves)
程序树不做交换律重排;FoldUnion 的输入排序在第 15 节定义。
5. 输入分析
5.1 初始化顺序
- 从导入 shape 计算 bbox;失败返回
ANALYSIS_FAILED和RawBRep。 - 计算
D;若D <= 0返回INVALID_INPUT。 - 若 bbox 坐标或
D非 finite,返回INVALID_INPUT。 - 计算默认配置或读取覆盖配置。
- 若
ConfigValid == false返回INVALID_CONFIG。 - 创建独立 analysis context,其 deadline 和调用上限来自
analysis.*。 - 若实体总数超过
max_analysis_entities,返回ANALYSIS_FAILED。 - 检查
FiniteBRep。 - 执行本节其余步骤。
5.2 Solid 有效性
ValidSolid(T) :=
kernel_is_valid(T) == OK(true)
and solid_count(T) == 1
and open_shell_count(T) == 0
and closed_shell_count(T) >= 1
and non_manifold_edge_count(T) == 0
and abs(volume(T)) > eps_volume
任一测量调用失败,ValidSolid 为 false。若为 false,返回 RawBRep(T) 和 INVALID_SOLID。v0.2 不隐式 repair。
6. 有限证据原子
6.1 SurfaceAtom
对每张 Face 调用固定参数三角化。调用失败时分析失败。对每个面积严格大于 0 的三角形 (a,b,c) 生成:
SurfaceAtom
atom_id
source_face_id
point = (a+b+c)/3
oriented_normal
area = norm(cross(b-a,c-a))/2
oriented_normal 按 Face orientation 修正。atom_id 按 (source_face_key, Q3(point), Q(area, eps_area), local_triangle_key) 排序后编号。local_triangle_key 是三角形三个量化顶点排序后的 tuple。
记集合为 SA(T):
[ W_S(T)=\sum_{a\in SA(T)}a.area ]
调用 kernel 计算真实表面积 A_kernel。只有满足:
[ W_S(T)>0 ]
且:
[ |W_S(T)-A_{kernel}|\leq\max(\epsilon_{area}, maxMeshAreaRelativeError\cdot A_{kernel}) ]
分析才继续,否则返回 MESH_COVERAGE_FAILED 和 RawBRep。
6.2 EdgeAtom
跳过 kernel 标记为 degenerated 或 seam 的 Edge。对其余 Edge 计算弧长 L。失败时跳过该 Edge 并记录 diagnostic。若 L <= eps_length,跳过。
[ n=\max(1,\lceil L/edgeStep\rceil) ]
按等弧长区间取中点和单位切向,生成:
EdgeAtom
atom_id
source_edge_id
point
unit_tangent
length_weight = L/n
任一中点求值失败时跳过整条 Edge。记集合为 EA(T):
[ W_E(T)=\sum_{e\in EA(T)}e.lengthWeight ]
6.3 AdjacencyAtom
对每条非 seam、非 degenerated Edge,取得 incident oriented Face uses。若恰有两个不同 Face,生成:
AdjacencyAtom
atom_id
source_edge_id
face_a_id = min(face keys)
face_b_id = max(face keys)
weight = edge_length
Edge 长度失败或小于等于 eps_length 时不生成。集合记为 AA(T):
[ W_A(T)=\sum_{r\in AA(T)}r.weight ]
7. 支撑几何 key
7.1 Plane
Plane 用单位法向 n 和 h = dot(n,origin) 表示。按第 4.1 节规范 n 符号;若翻转 n,同时翻转 h。
PlaneKey = (Q_d(n), Q(h, eps_linear))
7.2 Axis
轴线 (p,d) 转换为距原点最近点:
[ q=p-d\cdot dot(d,p) ]
规范 d 符号后:
AxisKey = (Q_d(d), Q3(q, eps_linear))
7.3 解析 support
CylinderKey = (AxisKey, Q(radius, eps_radius))
ConeKey = (AxisKey, Q3(apex, eps_linear), Q(semi_angle, eps_angular))
SphereKey = (Q3(center, eps_linear), Q(radius, eps_radius))
TorusKey = (AxisKey, Q(major_radius, eps_radius), Q(minor_radius, eps_radius))
LineKey = (AxisKey)
CircleKey = (AxisKey, Q3(center, eps_linear), Q(radius, eps_radius))
7.4 NURBS exact key
v0.2 的 exact key 不试图消除参数反转或重参数化。使用 STEP/kernel 提供的参数化顺序,并将控制点转换到 world coordinates:
NurbsExactKey = SHA256(
degree_u,
degree_v,
periodic_u,
periodic_v,
Q(knots_u, 1e-12),
Q(knots_v, 1e-12),
multiplicities_u,
multiplicities_v,
Q(weights, 1e-12),
Q3(world_control_points, eps_linear)
)
7.5 NURBS 解析约化
RecognizeAnalytic(surface, eps_surface) :=
OK(CertifiedAnalytic(PlaneParameters, max_error))
| OK(CertifiedAnalytic(CylinderParameters, max_error))
| OK(CertifiedAnalytic(ConeParameters, max_error))
| OK(CertifiedAnalytic(SphereParameters, max_error))
| OK(CertifiedAnalytic(TorusParameters, max_error))
| OK(NONE)
| FAILED
| TIMEOUT
| BUDGET_EXHAUSTED
只有 backend 能认证整个输入 support surface 在其完整参数域内与返回解析曲面的最大距离不超过 eps_surface,并返回 max_error <= eps_surface 时才允许 CertifiedAnalytic。参数 record 必须包含第 7.1 到 7.3 节 key 所需的全部数值。固定采样只能产生第 12.5 节的近似 evidence,不能把 NONE 升级为解析类型。没有认证接口的 backend 必须返回 OK(NONE)。
8. EvidenceGroup 与有限事件
8.1 Group 代表值
每个原始几何 record 为:
EvidenceRecord
canonical_bucket_key
exact_parameters
weight
source_entity_key
provenance
provenance 是有限的 source entity key 集合。按 canonical_bucket_key 分组。对同一 source entity 只保留排序键最小的 record;组权重是剩余 record weight 之和;组 provenance 是 record provenance 的并集。代表 record 按以下键取最小:
(-weight, source_entity_key, canonical_json(exact_parameters))
候选实际参数只取代表 record 的 exact_parameters;bucket 中其他 record 只增加 trigger provenance 和 group weight。
Face record 的 source_entity_key 和单元素 provenance 为 Face key,weight 为 Face area;Edge 对应 Edge key 和 Edge length;Vertex 对应 Vertex key 和 eps_area;bbox corner 对应 bbox.corner.<0..7> 和 0;world axis 对应 world.x/y/z 和 0。事件的 exact_parameters 包含实际 value 或 direction。事件 group 输出显式字段:
EventGroup
bucket_key
representative_value_or_direction
group_weight
provenance
8.2 DirectionEvent
来源:
- Plane Face normal,权重为 Face area。
- Cylinder/Cone/Torus axis,权重为 Face area。
- LINE Edge direction,权重为 Edge length。
- world axes,权重为
0,source key 固定为world.x/y/z。
按 Q_d 分组。若组总正权重 W > 0:
[ d=normalize(\sum_i w_i d_i) ]
所有 d_i 先规范符号。若 W = 0,代表方向取 source key 最小 record 的 exact direction。按 (-group_weight, Q_d(d)) 排序,保留前 max_direction_clusters 个。
8.3 PositionEvent
给定单位方向 d,产生 record:
- 每个 Vertex:
value = dot(d,p)。 - 每个 normal 与
d满足parallel的 Plane:value = dot(d,origin)。 - bbox 八个 corner:
value = dot(d,corner)。
每个来源按第 8.1 节生成完整 EvidenceRecord;canonical_bucket_key = Q(value,eps_linear)。按第 8.1 节分组并选择 exact representative value。
8.4 AxialEvent
给定轴 (q,d),产生 record:
- 每个 Vertex:
value = dot(d,p-q)。 - 每个与该轴满足
coaxial的 CIRCLE Edge:value = dot(d,center-q),weight 为 Edge length。 - 每个 normal 与
d满足parallel的 Plane:value = dot(d,origin-q)。 - bbox corner:
value = dot(d,corner-q)。
每个来源按第 8.1 节生成完整 EvidenceRecord;canonical_bucket_key = Q(value,eps_linear)。按第 8.1 节分组。
DirectionEvent、PositionEvent、AxialEvent 生成 record 时,每增加一个 record 都递增当前 analysis/task 的 event_record_count。达到 max_event_records 时停止生成更多 record,并返回 BUDGET_EXHAUSTED;初始 analysis 因此降级 RawBRep,residual task 因此只允许 RawExpansion。
8.5 ExtentPair
对排序后的不同 event i < j 生成:
ExtentPair
low = min(event_i.value, event_j.value)
high = max(event_i.value, event_j.value)
weight = event_i.group_weight + event_j.group_weight
provenance = union(event_i.provenance, event_j.provenance)
只保留 high-low > eps_linear。按:
(-weight, -(high-low), Q(low,eps_linear), Q(high,eps_linear))
排序,保留前 max_extent_pairs_per_axis 个。
9. 有限候选枚举
9.0 Primitive 参数 record
所有方向均为 world-coordinate unit vector,所有 frame 均为右手正交 frame,所有 extent 满足 high-low > eps_linear:
BoxParameters
frame_origin
x_axis
y_axis
z_axis = cross(x_axis,y_axis)
x_low, x_high
y_low, y_high
z_low, z_high
CylinderParameters
axis_point # 距 world origin 最近点 q
axis_direction
radius > eps_radius
axial_low, axial_high
ConeParameters
axis_point # 距 world origin 最近点 q
axis_direction
apex_axial_coordinate
semi_angle in (eps_angular, pi/2-eps_angular)
axial_low, axial_high
SphereParameters
center
radius > eps_radius
TorusParameters
axis_point
axis_direction
major_radius
minor_radius
Box 的 world point 为:
[ frameOrigin+x\cdot xAxis+y\cdot yAxis+z\cdot zAxis ]
Cylinder/Cone 的 world axial point 为 axis_point + z*axis_direction。Cone 在轴坐标 z 的半径为:
[ r(z)=|z-apexAxialCoordinate|\tan(semiAngle) ]
候选只允许 r(axial_low) 或 r(axial_high) 至少一个大于 eps_radius。这些 record 的字段顺序就是 CandidateKey 的字段顺序。kernel_construct_primitive 只接受这些 record。
9.1 CandidateValid
CandidateValid(H,T) :=
construction returned OK
and kernel_is_valid(H) == OK(true)
and solid_count(H) == 1
and volume(H) > eps_volume
and BBox(H) intersects expand(BBox(T), eps_linear)
and TriggerEntityCount(H) >= min_trigger_entities
其中每个 kernel 测量按第 2.3 节执行;任一不是 OK 时结果为 false。
TriggerEntityCount 是候选使用的 extent、direction、axis、radius 等 event provenance 中不同 Face/Edge/Vertex key 的数量;world.* 和 bbox provenance 不计数。
9.2 Box
- 从 DirectionEvent 枚举无序三元组,三元组内部按
Q_d排序。 - 只保留三对方向均满足
perpendicular的三元组。 - 令第一个方向为
x0,从第二个方向减去在x0上的投影后规范化为y0,令z0 = normalize(cross(x0,y0))。 - 若
angleAbs(z0, third_direction) > eps_angular,丢弃。 - frame 固定为右手系
(x0,y0,z0)。 - frame 按三个 direction group weight 总和降序、frame key 升序排序,只保留前
max_box_frames个。 - 对每个 frame axis 生成 PositionEvent 和 ExtentPair。
frame_origin = (0,0,0),三个 extent 均为 world origin 在对应 frame axis 上的投影坐标;枚举三个 ExtentPair 集合的笛卡尔积并构造 oriented Box。- 每个 frame 内按
(-trigger_weight,-volume,CandidateKey)排序,保留前max_box_candidates_per_frame个。
trigger_weight 是所有 trigger provenance 对应 record weight 的去重和。
9.3 Cylinder
候选轴和半径来源:
- 每个 CylinderKey Face group 的代表参数。
- 每个 CircleKey group;同一 AxisKey 和 radius bucket 下至少存在两个不同 Circle center axial bucket。
对每个 (axis,radius) 生成 AxialEvent 和 ExtentPair。Cylinder Face group 只提供 axis/radius,有限 axial extent 一律来自 ExtentPair。每个 pair 构造 CylinderParameters 并按 CandidateValid 过滤。
9.4 Cone
来源一:每个 ConeKey Face group 的代表参数。
来源二:同一 AxisKey 的两个 Circle group (z1,r1)、(z2,r2),要求:
[ |z_1-z_2|>\epsilon_{linear} ]
且:
[ |r_1-r_2|>\epsilon_{radius} ]
计算:
[ k=(r_2-r_1)/(z_2-z_1) ]
[ z_{apex}=z_1-r_1/k ]
[ semiAngle=atan(|k|) ]
Cone Face group 只提供 axis/apex/semi-angle,有限 extent 一律来自 AxialEvent 的 ExtentPair。来源二的 apex_axial_coordinate = z_apex。截面半径小于 eps_radius 时置为 0。构造 ConeParameters 后按 CandidateValid 过滤。
9.5 Sphere
每个 SphereKey Face group 的代表参数生成一个完整 Sphere。v0.2 不从任意圆环拟合 Sphere。
9.6 Torus
每个 TorusKey Face group 的代表参数生成一个完整 Torus,且要求:
major_radius > minor_radius > eps_radius
v0.2 不生成 horn torus 或 spindle torus,不从曲率样本拟合 Torus。
9.7 NURBS 证据记录,不是实体候选
NURBS 不进入本节的实体候选列表,不参与 PrimitiveCost、signed residual 或 Beam Search。分析阶段只生成:
NurbsEvidenceRecord
face_ids sharing the same NurbsExactKey
NurbsCost
v0.2 只按 NurbsExactKey 分组,不计算一般 NURBS 的 pairwise 等价关系。
9.8 去重和预算
候选只按 CandidateKey 去重。相同 key 合并 trigger provenance。v0.2 不调用 Boolean 做候选几何去重。
每种类型按第 13.3 节排序后保留前 max_candidates_per_type 个。
每个 task 在尝试构造候选前递增 candidate_attempt_count;在候选专属 kernel 调用前递增 candidate_kernel_call_count。达到 max_candidate_attempts_per_task 或 max_candidate_kernel_calls_per_task 后,停止该 task 的候选枚举,并继续保留其无条件 RawExpansion。该规则同时限制 Circle pair、Cone pair 和 Box extent 笛卡尔积的实际尝试数。
10. 先验与描述代价
10.1 PrimitiveCost
ParameterCodeUnits(Box) = 9
ParameterCodeUnits(Cylinder) = 8
ParameterCodeUnits(Cone) = 9
ParameterCodeUnits(Sphere) = 4
ParameterCodeUnits(Torus) = 9
PrimitiveCost(H) =
configured_type_cost(H.type)
+ scalar_parameter_cost * ParameterCodeUnits(H.type)
ParameterCodeUnits 是 v0.2 先验表中的版本化整数,不声称等于参数 record 字段数或最小几何自由度。
10.2 NurbsCost
NurbsCost(face) =
type_cost_nurbs_face
+ 0.02 * control_point_count
+ 0.01 * (knot_u_count + knot_v_count)
+ 0.01 * non_unit_weight_count
+ 0.02 * degree_u * degree_v
+ 0.05 * trim_edge_count
同一 NurbsExactKey group 的代价为一张 support 的 NurbsCost 加 0.1 * (face_count-1)。
10.3 RawBRepCost
RawBRepCost(analysis_or_raw_stats) =
type_cost_raw_brep
+ sum(NurbsCost(record) for nurbs_face_records)
+ 0.20 * analytic_face_count
+ 0.05 * edge_count
+ 0.01 * vertex_count
10.4 LeafCost
LeafCost(primitive) = PrimitiveCost(primitive)
LeafCost(RawBRep(...,raw_stats)) = RawBRepCost(raw_stats)
这些代价是“常见性”的唯一规范表示。数值越低,先验偏好越高。
11. 点、法向和 Edge 投影谓词
11.1 ProjectFace
ProjectFace(point, face) := kernel_nearest_point_on_trimmed_face(point, face)
成功值包含:
distance
projected_point
all_outward_normals_at_projection
投影位于 smooth interior 时 normal 集合大小为 1;位于 Edge/Vertex 时包含所有 incident Face outward normals;无法取得 normal 时调用失败。
11.2 BoundaryExplainsOnFace
BoundaryExplainsOnFace(atom, face) :=
ProjectFace(atom.point, face) == OK(p)
and p.distance <= eps_surface
and exists n in p.all_outward_normals_at_projection:
angle(atom.oriented_normal, n) <= eps_normal
11.3 ProjectEdge
EdgeExplainsOnEdge(atom, edge) :=
kernel_nearest_point_on_edge(atom.point, edge) == OK(p)
and p.distance <= eps_surface
and angleAbs(atom.unit_tangent, p.unit_tangent) <= eps_normal
12. 支撑面匹配
12.1 无向 support 匹配
SupportExplains 故意忽略材料方向,因为同一个 Cylinder support 可以是凸台外壁,也可以在 Subtract 后成为孔壁。方向只在候选实际边界和最终 leaf ownership 中检查。
Plane:
[ |dot(n,a.point)-h|\leq\epsilon_{surface} ]
且 angleAbs(n,a.oriented_normal) <= eps_normal。
Cylinder:令:
[ v=a.point-q-d\cdot dot(d,a.point-q) ]
要求:
[ ||v|-r|\leq\epsilon_{surface} ]
且 norm(v) > eps_linear,并满足 angleAbs(v/norm(v),a.oriented_normal) <= eps_normal。
Cone、Sphere、Torus:调用无限支撑面的 kernel projection,要求 distance 不超过 eps_surface,且 projected normal 与 atom normal 的 angleAbs 不超过 eps_normal。
SupportExplains(H,a) :=
exists support s of primitive H: support_match(s,a)
12.2 实际边界匹配
BoundaryExplains(H,a) :=
exists boundary Face f of H: BoundaryExplainsOnFace(a,f)
12.3 Edge 匹配
NaturalEdges(H) 是 primitive 构造结果中排除 seam 和 degenerated Edge 后的有限 Edge 集合。
EdgeExplains(H,e) :=
exists g in NaturalEdges(H): EdgeExplainsOnEdge(e,g)
12.4 FaceMap
对目标 Face f 和候选边界 Face b:
[ M(f,b)=\sum_{a\in SA(T),a.sourceFace=f} a.area\cdot I[BoundaryExplainsOnFace(a,b)] ]
FaceMap(f,H) 取 M(f,b) 最大的候选 Face;最大值小于 eps_area 时为 NONE。同分时按:
BoundaryFaceKey = (support_key,
Q3(face_centroid, eps_linear),
Q(face_area, eps_area),
construction_local_face_id)
取 key 最小者。construction_local_face_id 由 primitive builder 按固定面角色分配,不使用 kernel 枚举序号。
13. 候选解释与局部分数
13.1 Bitset
CandidateExplanation
support_surface_atom_bits[a] = SupportExplains(H,a)
boundary_surface_atom_bits[a] = BoundaryExplains(H,a)
edge_atom_bits[e] = EdgeExplains(H,e)
adjacency_atom_bits[r] = AdjacencyExplains(H,r)
AdjacencyExplains(H,r) 当且仅当:
FaceMap(r.face_a,H)和FaceMap(r.face_b,H)均不是NONE。- 两个映射结果不同。
- 两个候选 Face 在候选 B-Rep 中共享至少一条非 degenerated Edge。
13.2 覆盖率
[ C_S=\frac{\sum_a a.area\cdot I[supportBits[a]]}{W_S(T)} ]
[ C_B=\frac{\sum_a a.area\cdot I[boundaryBits[a]]}{W_S(T)} ]
若 W_E(T)>0:
[ C_E=\frac{\sum_e e.lengthWeight\cdot I[edgeBits[e]]}{W_E(T)} ]
否则 C_E=0 且局部分数中的 Edge reward 置 0。
若 W_A(T)>0:
[ C_A=\frac{\sum_r r.weight\cdot I[adjacencyBits[r]]}{W_A(T)} ]
否则 C_A=0 且局部分数中的 adjacency reward 置 0。
13.3 LocalScore
[ LocalScore(H,T)= rewardSupport\cdot C_S+ rewardBoundary\cdot C_B+ rewardEdge'\cdot C_E+ rewardAdjacency'\cdot C_A- penaltyLeafCost\cdot PrimitiveCost(H) ]
候选只在 C_S >= min_support_coverage 时保留。排序键:
(-LocalScore, -C_S, -C_B, PrimitiveCost, CandidateKey)
每种类型保留前 max_candidates_per_type 个,合并后保留前 boolean_prefilter_top_m 个。
14. Signed residual
对当前任务 solid S 和候选 H,连续执行两个 kernel Boolean:
RplusResult: BooleanShape = regularized_cut(S,H)
RminusResult: BooleanShape = regularized_cut(H,S)
调用前必须为两个 Boolean 分别成功 reserve budget。任一结果失败时淘汰候选。
对 EMPTY 使用空 component list;对 NONEMPTY(shape) 调用 components(shape)。调用失败时淘汰候选。每个非空 component 必须满足 ValidSolid。体积小于等于 eps_volume 的 component 不进入列表,删除体积累加为 discardedVolume。若:
[ discardedVolume>\epsilon_{volume} ]
则淘汰候选。
剩余 component 分别记为:
P = sorted components of Rplus
M = sorted components of Rminus
每个剩余 component 立即执行第 15.1 节 AnalyzeResidual。任一分析失败时淘汰候选。排序键为 analysis 中的 snapshot.sha256。
15. 程序模板状态转移
15.1 ResidualAnalysis
ResidualAnalysis
shape # 只在当前进程计算期间使用
snapshot: BRepSnapshot # 可序列化、可重放
surface_atoms
edge_atoms
adjacency_atoms
support_groups
direction_events
face_count
edge_count
vertex_count
analytic_face_count
nurbs_face_count
raw_stats
volume
surface_area
AnalyzeResidual(S,config,context) :=
ValidSolid(S)
+ SnapshotOf(S)
+ 第 6 至 8 节全部分析
结果是 KernelResult[ResidualAnalysis]。所有测量只在这里执行并缓存;候选排序、cost、proxy、task 选择和 StateKey 不允许再次隐式调用 kernel。初始 target 也产生相同结构的 ResidualAnalysis。
15.2 FoldUnion
统一排序函数:
UnionOrderKey(primitive leaf) = (0, CandidateKey)
UnionOrderKey(RawBRep leaf) = (1, RawBRepKey)
UnionOrderKey(Slot) = (2, slot_id)
UnionOrderKey(composite Program) = (3, ProgramKey)
输入按 UnionOrderKey 排序。
FoldUnion([]) = EMPTY
FoldUnion([x]) = x
FoldUnion([x1,...,xn]) = Union(...Union(Union(x1,x2),x3)...,xn)
EMPTY 只用于构造规则,不是 Program 节点。
15.3 CandidateExpansion
设 P 有 p 个 components,M 有 m 个 components。为每个 component 创建唯一 Slot,ID 为:
SHA256(parent_slot_id, sign, component_analysis.snapshot.sha256,
same_fingerprint_ordinal)
其中 sign 只用于 ID,取 PLUS 或 MINUS;ordinal 是排序后同 fingerprint 的从零计数。
base = FoldUnion([H] + plus_slots)
replacement = base if m == 0
replacement = Subtract(base,
FoldUnion(minus_slots)) if m > 0
将当前 Slot 替换为 replacement。每个新 Slot 对应:
ResidualTask
slot_id
analysis: ResidualAnalysis
depth = parent.depth + 1
若新 task 数使状态的 pending_task_count > max_pending_tasks_per_state,丢弃该 CandidateExpansion,只保留父 task 的 RawExpansion。
新插入 Boolean 节点数:
union_count = p + max(m-1, 0)
subtract_count = 1 if m > 0 else 0
立即增加的 committed cost:
PrimitiveCost(H)
+ union_count * operation_cost_union
+ subtract_count * operation_cost_subtract
15.4 RawExpansion
task.analysis 已含 SnapshotOf(task.analysis.shape) 的结果。将当前 Slot 替换为 RawBRep(task.analysis.snapshot, len(snapshot.bytes), task.analysis.raw_stats),删除该 task,立即增加:
RawBRepCost(task.analysis)
15.5 成本不变量
对任一搜索状态:
committed_cost =
sum(cost of every committed leaf)
+ sum(cost of every committed Boolean node)
当没有 Slot 时:
committed_cost == ProgramCost(completed_program)
16. Residual 代理代价和展开门槛
16.1 ResidualProxyCost
ResidualProxyCost(analysis) =
1.00 * solid_count
+ 0.20 * analytic_face_count
+ 0.50 * nurbs_face_count
+ 0.02 * edge_count
+ 0.01 * vertex_count
component list 的 proxy 是每个 component proxy 之和。
16.2 ExpansionEstimate
[ ExpansionEstimate= PrimitiveCost(H)+ unionCount\cdot operationCostUnion+ subtractCount\cdot operationCostSubtract+ Proxy(P)+Proxy(M)+ unmatchedSupportPenalty\cdot(1-C_S)+ unmatchedBoundaryPenalty\cdot(1-C_B)+ unmatchedEdgePenalty'\cdot(1-C_E) ]
若 W_E=0,最后一项为 0。
只有满足:
[ ExpansionEstimate\leq RawBRepCost(currentTask.analysis)+expansionMargin ]
的候选才执行第 15.3 节状态转移。
17. 有界 Beam Search
17.1 SearchState
SearchState
template
tasks_by_slot_id
committed_cost
estimated_remaining_cost
state_key
estimated_remaining_cost =
sum(MinLeafCost for each pending task)
MinLeafCost = min(
type_cost_box + 9*scalar_parameter_cost,
type_cost_cylinder + 8*scalar_parameter_cost,
type_cost_cone + 9*scalar_parameter_cost,
type_cost_sphere + 4*scalar_parameter_cost,
type_cost_torus + 9*scalar_parameter_cost,
type_cost_raw_brep
)
该值只用于排序;不声称是包含 Boolean 操作的严格数学下界。
17.2 StateKey
state_key = SHA256(
canonical serialization of template,
sorted tuples(slot_id, task.depth, task.analysis.snapshot.sha256)
)
同一 state_key 只保留 committed_cost 最小者;同分保留 template serialization 字典序最小者。
17.3 任务选择
从 pending task 中按以下键取最小:
(-RawBRepCost(task.analysis),
-task.analysis.volume,
task.analysis.snapshot.sha256,
slot_id)
17.4 单状态展开
if task.depth >= max_task_depth:
children = [RawExpansion(state, task)]
else:
children = every accepted CandidateExpansion
plus one unconditional RawExpansion
展开一个状态前检查 expanded_states + 1 <= max_expanded_states。每个 kernel 调用由 KernelContext.try_reserve 单独检查,因此 Boolean 和总调用硬上限不会超出。
17.5 Frontier
算法每次从 frontier 取排序键最小的一个状态展开,再将 children 放回 frontier。排序键:
(
committed_cost + estimated_remaining_cost,
sum(RawBRepCost(task.analysis) for pending tasks),
pending_task_count,
state_key
)
插入 children、去重、排序后只保留前 beam_width 个状态。
17.6 搜索终止
搜索上下文保存唯一 termination_reason:
FRONTIER_EMPTY
STATE_LIMIT
BOOLEAN_LIMIT
KERNEL_CALL_LIMIT
SEARCH_TIMEOUT
ENOUGH_COMPLETED_PROGRAMS
循环开始及每次 kernel reserve 前更新该值。达到任何预算后不再生成候选。若一个状态已从 frontier 弹出但尚未完成展开,将该未修改状态加入 fallbackStates;已经完整生成的 children 保留在 frontier。把 frontier union fallbackStates 中每个状态的全部 pending Slot 按 slot ID 升序执行 RawExpansion,得到完整程序。若 frontier 正常变空且 termination reason 尚未设置,则设置为 FRONTIER_EMPTY。
完成程序按 (ProgramCost,ProgramKey) 去重,只保留前 max_completed_programs_to_validate 个。
17.7 显式调用上界
若实际展开状态数为 S <= max_expanded_states,每状态经过预筛选的候选数为 P <= boolean_prefilter_top_m,signed residual 最多调用:
[ 2SP ]
次 Boolean。实际数量同时受 max_search_boolean_calls 限制。候选构造、投影和测量计入 max_search_total_kernel_calls。最终程序重放和验证使用独立 validation context,不消费搜索预算。
18. 完整程序代价
ProgramCost(primitive) = PrimitiveCost(primitive)
ProgramCost(RawBRep(...,raw_stats)) = RawBRepCost(raw_stats)
ProgramCost(Union(A,B)) = ProgramCost(A)+ProgramCost(B)+operation_cost_union
ProgramCost(Subtract(A,B)) = ProgramCost(A)+ProgramCost(B)+operation_cost_subtract
19. 完整程序验证
19.1 重放
整个 validation 阶段创建一个共享 validationContext;所有完成程序和最终 RawBRep fallback 共用它的 deadline 和调用计数。依次执行按 (ProgramCost,ProgramKey) 排序的程序;context 到期或预算耗尽后不再验证后续程序。
对一个程序只执行一次 Render(P) 并保存 ReplayResult = R。失败时:
ValidProgram(P,T) = false
19.2 Symmetric difference
成功重放结果为 R:
D1: BooleanShape = regularized_cut(T,R)
D2: BooleanShape = regularized_cut(R,T)
symdiff_volume = volume(D1)+volume(D2) # volume(EMPTY)=0
任一调用失败则验证失败。
VolumeEquivalent := symdiff_volume <= eps_volume
19.3 双向边界距离和法向
用第 6.1 节相同配置生成 SA(R),并对 mesh area 执行相同覆盖检查。对 SA(T) 中每个 atom 投影到 boundary(R),对 SA(R) 中每个 atom 投影到 boundary(T)。
每次 projection 必须成功。距离集合 Dists 和最小有向法向误差集合 Angles:
distance = nearest boundary distance
angle = min(angle(atom.oriented_normal,n)
for n in all outward normals at projection)
两个 atom 集合都非空,因此:
[ maxBoundaryDistance=\max(Dists) ]
[ rmsBoundaryDistance=\sqrt{ \frac{\sum_i area_i\cdot distance_i^2}{\sum_i area_i}} ]
[ maxNormalError=\max(Angles) ]
BoundaryEquivalent := maxBoundaryDistance <= eps_surface
NormalEquivalent := maxNormalError <= eps_normal
rmsBoundaryDistance 只作为输出指标,不参与 v0.2 acceptance。
19.4 可计算 topology signature
v0.2 不用原始 V-E+F 计算 Euler characteristic。定义:
TopologySignature(S) = (
solid_count(S),
closed_shell_count(S),
open_shell_count(S),
non_manifold_edge_count(S)
)
任一计数调用失败则验证失败。
TopologySignatureMatches := TopologySignature(T) == TopologySignature(R)
19.5 ValidProgram
ValidProgram(P,T) :=
ReplayResult R exists
and kernel_is_valid(R) == OK(true)
and VolumeEquivalent
and BoundaryEquivalent
and NormalEquivalent
and TopologySignatureMatches
ValidProgram 是 acceptance profile 名称,含义仅为本节 finite tests 在给定配置下通过,不表示符号等价或完整 Hausdorff/topology 证明。
20. SurfaceAtom 归属与有效解释
20.1 Leaf polarity
从程序根向叶递归:
polarity(root) = +1
polarity(Union.left/right) = polarity(parent)
polarity(Subtract.left) = polarity(parent)
polarity(Subtract.right) = -polarity(parent)
20.2 LeafSupportEvidence
对 primitive 或 RawBRep 叶节点 L,将其支撑面投影法向乘 polarity(L):
LeafSupportEvidence(L,a) :=
exists support face f of L:
projection of a.point to support(f) succeeds
and distance <= eps_surface
and exists normal n at projection:
angle(a.oriented_normal, polarity(L)*n) <= eps_normal
支撑面使用未裁剪 support,因此 Boolean 改变 trim 后仍可产生解释证据。这个 predicate 不声称该叶节点是原始历史来源,也不证明该 support 在 Boolean 中对最终边界有唯一贡献。
20.3 唯一 owner
对每个目标 SurfaceAtom 枚举有限叶节点集合。满足 LeafSupportEvidence 的叶节点按:
(
0 if primitive else 1,
LeafCost,
-leaf_depth,
leaf_program_key
)
取最小者。输出字段名为 support_evidence_owner;没有匹配者时为 NONE。
20.4 ValidExplanation
ValidExplanation(P,T) :=
ValidProgram(P,T)
and every a in SA(T) has support_evidence_owner != NONE
本文中的“100% 解释”严格指:第 6.1 节有限 SurfaceAtom 集合全部有 support evidence,并且第 19 节 acceptance profile 通过;不表示连续曲面上的数学全称命题。第 6.1 节同时限制离散 surface area 与 kernel surface area 的差值。
21. Edge 和 adjacency 节点归属
Edge 和 adjacency 可能由 Boolean 生成,因此不强制归属到叶节点。
21.1 节点重放缓存
完整程序验证时缓存每个语法树节点的 Render(node)。节点深度从根为 0 向下递增。
21.2 EdgeOwner
NodeExplainsEdge(node,e) :=
exists non-seam, non-degenerated Edge g in Render(node):
EdgeExplainsOnEdge(e,g)
候选 node 按 (-node_depth,node_program_key) 取最小。最终 root 若仍不能解释,则 owner 为 UNATTRIBUTED。
21.3 AdjacencyOwner
对每个 node 的 rendered shape,用第 12.4 节方式把目标两个 Face 映射到 node result Face:
NodeExplainsAdjacency(node,r) :=
both FaceMap values exist and differ
and the mapped result Faces share a non-degenerated Edge
按 (-node_depth,node_program_key) 选择 owner。失败时为 UNATTRIBUTED。
Edge 或 adjacency 的 UNATTRIBUTED 数量进入输出,但 v0.2 不把它们加入 ValidExplanation;几何正确性由第 19 节约束。
22. 最终选择
先对最多 max_completed_programs_to_validate 个完成程序执行第 19、20 节。令:
VP = finite set of programs with ValidExplanation == true
若 VP 非空,排序键为:
(
ProgramCost,
RawBRep_leaf_count,
total_NURBS_face_count_inside_RawBRep,
Boolean_node_count,
ProgramKey
)
返回最小者,status 为 VALID_EXPLANATION。
若 VP 为空,验证 RawBRep(T)。若其 ValidExplanation 为 true,返回它,status 为 RAW_BREP_ONLY;否则返回 FINAL_VALIDATION_FAILED。
23. 主算法伪代码
def reverse_step(target, overrides=None):
bbox = kernel_bbox(target)
if not bbox.ok:
return snapshot_or_failure(target, "ANALYSIS_FAILED")
if not bbox.finite or bbox.diagonal <= 0:
return snapshot_or_failure(target, "INVALID_INPUT")
config = resolve_config(target, bbox, overrides)
if not config_valid(config):
return failure("INVALID_CONFIG")
analysis_ctx = KernelContext.for_analysis(config)
analysis = analyze_residual(target, config, analysis_ctx)
if not analysis.ok:
return snapshot_or_failure(target, analysis.status)
search_ctx = KernelContext.for_search(config)
initial_slot = Slot(id=sha256("root"))
initial_task = ResidualTask(
slot_id=initial_slot.id,
analysis=analysis.value,
depth=0,
)
frontier = [SearchState(
template=initial_slot,
tasks={initial_slot.id: initial_task},
committed_cost=0,
)]
completed = []
fallback_states = []
expanded_states = 0
while frontier and not search_ctx.stopped:
state = pop_min(frontier, key=frontier_sort_key)
if not state.tasks:
completed.append(state.template)
if len(completed) >= config.search.max_completed_programs_to_validate:
search_ctx.stop("ENOUGH_COMPLETED_PROGRAMS")
continue
if expanded_states >= config.search.max_expanded_states:
search_ctx.stop("STATE_LIMIT")
fallback_states.append(state)
break
task = select_task(state.tasks)
expanded_states += 1
children = []
if task.depth < config.search.max_task_depth:
candidates = enumerate_candidates(task.analysis, config, search_ctx)
explanations = compute_explanations(candidates, task.analysis, config, search_ctx)
ranked = rank_and_limit(explanations, config)
for candidate, explanation in ranked:
residuals = signed_residuals(task.analysis, candidate, config, search_ctx)
if not residuals.ok:
if search_ctx.stopped:
break
continue
if expansion_estimate(candidate, explanation, residuals, config) \
> raw_brep_cost(task.analysis, config) + config.score.expansion_margin:
continue
children.append(candidate_expansion(
state, task, candidate, residuals, config
))
if search_ctx.stopped:
fallback_states.append(state)
break
children.append(raw_expansion(state, task, config))
frontier = deduplicate(frontier + children)
frontier.sort(key=frontier_sort_key)
frontier = frontier[:config.search.beam_width]
if not search_ctx.termination_reason:
search_ctx.stop("FRONTIER_EMPTY")
completed.extend(materialize_all_slots_as_raw(
deduplicate(frontier + fallback_states), config
))
completed = deduplicate_and_sort_programs(completed, config)
completed = completed[:config.search.max_completed_programs_to_validate]
valid = []
validation_ctx = KernelContext.for_validation(config)
for program in completed:
if validation_ctx.stopped:
break
metrics = validate_program_and_assign_atoms(
program, target, analysis, config, validation_ctx
)
if metrics.valid_explanation:
valid.append((final_program_sort_key(program, metrics), program, metrics))
if valid:
valid.sort(key=lambda x: x[0])
return result(valid[0], search_ctx.termination_reason)
return validate_and_return_raw_brep(
target, analysis, config, validation_ctx
)
snapshot_or_failure 创建一个独立、只允许一次 SnapshotOf 的 emergency context;成功时返回 RawBRep,失败时 program = null。这条 emergency 调用及其 deadline 单独写入结果,不能复用已经耗尽的阶段 context。
24. 状态与诊断枚举
INVALID_CONFIG
INVALID_INPUT
ANALYSIS_FAILED
INVALID_SOLID
MESH_COVERAGE_FAILED
CANDIDATE_CONSTRUCTION_FAILED
BOOLEAN_FAILED
BOOLEAN_TIMEOUT
NUMERICALLY_UNSTABLE
STATE_LIMIT
BOOLEAN_LIMIT
KERNEL_CALL_LIMIT
SEARCH_TIMEOUT
FRONTIER_EMPTY
ENOUGH_COMPLETED_PROGRAMS
RAW_BREP_ONLY
FINAL_VALIDATION_FAILED
VALID_EXPLANATION
自由文本 diagnostic 不参与控制流。
25. 输出结构
ExplanationResult
status
search_termination_reason
input_fingerprint
config
program
program_key
program_cost
candidate_records
surface_atom_owners
edge_atom_owners
adjacency_atom_owners
validation_metrics
kernel_call_counts
diagnostics
validation_metrics 至少包含:
symdiff_volume
max_boundary_distance
rms_boundary_distance
max_normal_error
topology_signature_input
topology_signature_result
surface_atom_count
owned_surface_atom_count
unattributed_edge_atom_count
unattributed_adjacency_atom_count
valid_program
valid_explanation
26. MVP 模块
实现模块和唯一输入输出如下:
| 模块 | 输入 | 输出 |
|---|---|---|
BRepAnalyzer |
B-Rep、Config、KernelContext | descriptors、SA、EA、AA、support groups |
CandidateEnumerator |
analysis、Config | finite primitive candidates |
CandidateScorer |
candidate、current residual analysis | bitsets、coverage、LocalScore |
ResidualEngine |
residual、candidate、KernelContext | sorted Rplus/Rminus components |
ProgramSearch |
root residual、Config | finite completed programs |
ProgramValidator |
program、target atoms、Config | validation metrics、atom owners |
27. 测试要求
27.1 确定性
对相同 STEP bytes、backend version 和 config 重复运行两次,以下 bytes 必须相同:
- atom records canonical JSON。
- event records canonical JSON。
- CandidateKey 序列。
- ProgramKey。
- 非 timeout 情况下的最终 ExplanationResult canonical JSON;运行时间字段除外。
27.2 单 primitive
对 Box、Cylinder、Cone、Sphere 和 ring Torus fixture:
ValidExplanation(expected_primitive_program, fixture) == true
ProgramCost(expected_primitive_program) < RawBRepCost(fixture)
27.3 Boolean
对 Box 减 Cylinder、Box 加 Cylinder、同轴 Cylinder 相减 fixture:
- 返回
VALID_EXPLANATION。 symdiff_volume <= eps_volume。max_boundary_distance <= eps_surface。max_normal_error <= eps_normal。owned_surface_atom_count == surface_atom_count。
27.4 Face split
对几何相同但 Face split 不同的两个 STEP:
- 最优程序的
ProgramKey相同。 - 最优程序的
ProgramCost相同。 - 两者各自
ValidExplanation == true。
不比较 atom 数量,因为三角化可能跟随输入 Face split 变化。
27.5 NURBS
- 可约化 NURBS Cylinder:
RecognizeAnalytic返回完整 Cylinder 参数且通过采样验证。 - exact-key 相同的 NURBS Face:产生同一个 exact group。
- pairwise sampled match 不取传递闭包。
- 全自由曲面 closed solid:在预算内返回
RawBRep或包含 RawBRep residual 的有效程序。
27.6 硬预算
每次测试断言:
search_boolean_calls <= max_search_boolean_calls
search_total_kernel_calls <= max_search_total_kernel_calls
expanded_states <= max_expanded_states
each_task_depth <= max_task_depth
validation_boolean_calls <= max_validation_boolean_calls
validation_total_kernel_calls <= max_validation_total_kernel_calls
28. 算法总流程
STEP B-Rep
|
v
解析并验证 Config
|
v
ValidSolid + 固定参数三角化/边离散
|
v
构造有限 SA / EA / AA 和 support groups
|
v
从有限 Direction/Position/Axial/Extent 事件枚举 primitive candidates
|
v
计算 explanation bitsets、coverage 和 LocalScore
|
v
Top-M 候选计算 R+ 与 R-
|
v
ExpansionEstimate 门槛
|
v
固定模板替换规则 + 有界 Beam Search
| \
| +--> 每个 Slot 无条件可替换为 RawBRep
v
有限个完成 Program
|
v
Render + symmetric difference + 双向边界/法向 + topology signature
|
v
SurfaceAtom polarity owner
|
v
在 ValidExplanation 集合中最小化 ProgramCost
29. 结论
v0.2 将原始设想翻译为以下有限优化问题。给定输入 T、固定配置 C 和搜索实际枚举到的有限程序集合 Programs(T,C):
[ P^*=argmin_{P\in Programs(T,C)} ProgramCost(P) ]
约束:
[ ValidExplanation(P,T)=true ]
基础单元常见性由 PrimitiveCost、NurbsCost 和 RawBRepCost 表示;解释多少边和面由 CandidateExplanation bitset 及加权覆盖率表示;“基础几何元素 + 剩余部分”由两个 regularized cut 和第 15.3 节固定程序模板表示;递归由有限事件、候选上限、Beam Width、任务深度、状态数、kernel 调用数、Boolean 调用数和 deadline 限制。
因此该规范不需要枚举任意空间中的全部几何体,也不需要对一般 NURBS 求不可控的全局拟合;在所有搜索路径失败或预算耗尽时,RawBRep 分支给出有限、可重放的兜底程序。