- 桌面应用
- 3D建模
【免费下载链接】CAD_Sketcher
Constraint-based geometry sketcher for blender
CAD Sketcher 的建模工具(Modeling Tools)作用于对象而非草图实体:它们在被操作对象上添加一个节点组(Geometry Nodes)修改器,使生成的实体始终跟随其来源草图变化,之后仍可在修改器面板中随时调整。读完本文,你将掌握 Extrude、Revolve、Linear/Circular Array、Boolean 四类工具的操作流程、全部参数含义与默认值,以及它们背后的节点图构建、布尔求解器选择与 Auto Boolean 自动检测机制的源码级原理。
建模工具的共同工作方式
建模工具与草图绘图工具的关键区别在于作用层级:草图工具编辑约束求解器管理的 2D 实体,而建模工具把 2D 截面"抬升"成 3D 实体,且以非破坏性的节点修改器形式实现。
- 入口:在无活动草图时从视口工具栏(toolbar)触达,或在 Object Mode 下任意位置使用其全局快捷键(见 docs/content/tools.md 的 Global Shortcuts 一节:
Ctrl+Shift+EExtrude、Ctrl+Shift+RRevolve、Ctrl+Shift+DArray、Ctrl+Shift+BBoolean)。 - 选取语义:每个工具优先拾取当前已选对象;完成后自动返回 Select 工具(源码中即
return_to_tool = BLENDER_SELECT_TOOL,见 operators/modifiers.py)。 - 可修正性:所有交互中设置的数值,既可以在工具结束后的 redo panel 中立即修改,之后也可以在对象修改器堆栈里继续编辑。
- 作用对象是 body:修改器挂在 body(草图实体化的承载对象)上而非草图 Curves 对象上。从源码结构看,这是刻意的:
operators/modifiers.py中solid_target()会把选中的草图解析为其 body,因为 Blender 对 Curves 对象上产出网格的节点堆栈拒绝执行 apply 操作(源码注释引用了 issue #723)。
五个工具操作符均在 operators/modifiers.py 中定义,共享基类NodeOperator(L547),并在 declarations.py 中注册了各自的bl_idname:
| 工具 | 类名 | bl_idname | 节点组名 |
|---|---|---|---|
| Extrude | View3D_OT_node_extrude(L790) | view3d.slvs_node_extrude | CAD Sketcher Extrude |
| Revolve | View3D_OT_node_revolve(L1295) | view3d.slvs_node_revolve | CAD Sketcher Revolve |
| Linear Array | View3D_OT_node_array_linear(L1130) | view3d.slvs_node_array_linear | CAD Sketcher Linear Array |
| Circular Array | View3D_OT_node_array_circular(L1422) | view3d.slvs_node_array_circular | CAD Sketcher Circular Array |
| Boolean | View3D_OT_node_boolean(L1557) | view3d.slvs_node_boolean | CAD Sketcher Boolean |
NodeOperator基类还实现了两个通用能力:
- redo panel 编辑回灌:
read_props()(L676)在目标对象已带有本工具修改器时被调用,从修改器输入种子化操作符属性——因此对同一对象重复调用工具会基于当前值继续编辑,而不是重置为默认值。 - eyedropper 重定向:
editable = True(L561)让 redo panel 中的指针字段支持重新拾取;_prepare_edit()(L745)记录旧对象与修改器名,main()会把修改器从旧目标搬走,避免留下孤儿修改器。
Extrude:拉伸
操作:选取一个截面(草图的 body 或一条曲线),拖动深度或输入数值。验证目标由is_2d_profile()决定:曲线对象或草图 body(operators/modifiers.py)。
参数(redo panel 中由draw_settings(),L864-L871 绘制):
| 参数 | 属性 | 默认值 | 说明 |
|---|---|---|---|
| Offset | offset | 拖动确定 | 拉伸深度;get_offset()(L822)把光标在对象局部空间的 Z 分量作为深度,所以负值向反方向拉伸 |
| Mirror Extrude | mirror | False | 实体向截面两侧对称生长 |
| Asymmetric | asymmetry | False | 允许两侧厚度不同 |
| Asymmetry Distance | asymmetry_distance | — | Asymmetric 开启时另一侧的深度 |
实现要点(utilities/extrude_nodes.py):
- 节点组按代码构建而非随附二进制资产,带版本号
EXTRUDE_VERSION = 2;重建时先_snapshot_modifier_inputs()按 socket 名快照所有绑定修改器的输入值,重建后再按名字恢复——否则从旧资产升级会静默重置所有已有拉伸。 - 输入接口为
Size(默认 1.0)、Mirror Extrude、Asymmetry Override、Asymmetry Distance(默认 1.0)。Size的符号决定哪一侧被翻转,保证端帽法线始终朝外。 - 未填充截面变成开放壁面:
ensure_extrude_edge_walls()(L301)幂等地给组打补丁——当输入没有面(wire profile)时,把边界边沿局部 +Z 按Size挤出为开口壁,并遵守Mirror Extrude;已填充的输入走原面挤出路径不受影响。这就是文档所说"A profile that isn't filled is extruded into open walls rather than a solid"。 - 接缝焊接在 Blender 5.2+ 上按 identity(
Merge Points的 Merge ID)执行,完全与模型尺度无关;旧版本回退到 1 微米距离焊接——源码注释指出旧资产硬编码的 1 mm 会吞掉小模型上的亚毫米细节(issue #670)。
Revolve:旋转
操作:先选截面,再选旋转轴——可以是网格边、草图/曲线线段,或部件自身的轴(选轴过程中这些轴会画出来)。轴的拾取由PickedAxisMixin(operators/modifiers.py)实现:pick_element()(L1002)优先命中画出的基础平面轴(hit_test_axis),其次网格边,再次是曲线段(curve_segment_under_cursor,屏幕空间距离 12 px × UI scale);resolve_axis()(L1078)把拾取结果换算为对象局部空间的(origin, direction)并持久化到axis_origin/axis_direction属性,redo 无需重选轴。
参数:
| 参数 | 属性 | 默认值/范围 | 说明 |
|---|---|---|---|
| Angle | angle | 2π,范围[-2π, 2π] | 扫掠角 |
| Flip Direction | flip | False | 反转旋转方向。set_props()(L1403)在写入时才对存储的轴取反,以便 redo panel 中切换生效 |
| Angular Resolution | angular_resolution | 2°,min 0.5°,soft_max 90° | 每段最大角;步数随扫掠角自适应,圆度保持一致 |
实现要点(utilities/revolve_nodes.py,REVOLVE_VERSION = 7):该模块文档字符串完整解释了节点组策略——先把任意截面归一化为边界环(网格取其相邻面少于 2 的边界边转成曲线;曲线直接通过),从而无论草图的Fill开关状态如何,扫掠出的侧面都一致(修复了 issue #634:填充截面被三角化后内部边被扫进表面)。侧面用逐环挤出+逐角旋转扫掠而非单个网格,因此带孔截面能旋出真正贯通孔洞的实体;仅当截面已填充且扫掠小于 360° 时才补端帽;最后用有向体积(散度定理)判断朝向并整体翻正,法线恒朝外。
Arrays:线性阵列与圆形阵列
两种阵列共享一个工具栏按钮和一个快捷键Ctrl+Shift+D,启动时执行工具栏当前显示的那一个(keymaps.py 中NODE_TOOL_KEYS把NodeArrayLinear标记为GROUP键)。两者都用节点组复制被选对象,因此副本随原始对象联动变化。
Linear Array
操作:拖出方向和间距。拖动过程中,如果光标经过坐标轴、网格边或草图线段,行列会贴合该方向,从而可以把阵列"放"在已有方向上而非目测对齐——get_offset()(operators/modifiers.py)先检查axis_under_cursor()命中,用offset_along_axis()把拖动投影到该轴上;无命中时投影到过原点、面向视图的平面上自由拖动。
参数:
| 参数 | 属性 | 默认值 | 说明 |
|---|---|---|---|
| Count | count | 2,min 2 | 副本数;拖动计数用get_count()(L1241),取abs(值)+2保证至少 2 |
| Use Total Distance | use_total_distance | False | 开启后距离解释为整排跨度而非副本间距 |
| Count 2 / Offset 2 | count_2/offset_2 | 1 / 零向量 | 第二方向;count_2 = 1保持单行。offset_2为零时,second_array_axis()(L1101)在对象 XY 平面内取垂直方向、沿用第一间距,得到正方形网格 |
| Align Rotation | align_rotation | False | 让副本随阵列方向旋转 |
| Merge by Distance | merge/merge_distance | False/ 0.001 | 焊接相遇的副本 |
节点组CAD Sketcher Linear Array由 utilities/array_nodes.py 构建;set_props()(L1245-L1272)把offset分解为方向向量与长度,分别写入Direction与Spacing / Total distance两个输入。
Circular Array
操作:先拾取旋转轴(与 Revolve 完全相同的轴拾取规则:网格边、草图线段或部件自身轴),再拖动出副本数。
参数:
| 参数 | 属性 | 默认值 | 说明 |
|---|---|---|---|
| Count | count | 6,min 2 | 副本数 |
| Angle / Total Angle | angle | 2π,范围[-2π, 2π] | 副本分布的总扫掠角;use_total_angle关闭时则为相邻副本间的转角 |
| Use Total Angle | use_total_angle | True | 上表 Angle 的语义开关 |
| Align Rotation | align_rotation | True | 像螺栓圆一样让每个副本随阵列旋转;关闭时副本绕行时保持原朝向 |
| Merge by Distance | merge/merge_distance | False/ 0.001 | 焊接相遇的副本 |
节点组构建见 utilities/circular_array_nodes.py;轴与中心分别写入Axis/Center输入(set_props(),operators/modifiers.py)。
Boolean:布尔运算
操作:选中接收布尔的 body 与 cutter(刀具),可以事先同时选中两者(body 为 active),也可以在视口中依次拾取——操作符声明了两个对象状态:Object(body)与Cutter(operators/modifiers.py)。
参数:
| 参数 | 属性 | 默认值 | 说明 |
|---|---|---|---|
| Operation | operation | Difference | Difference/Union/Intersect |
| Boolean Solver | boolean_solver | 由偏好决定(见下) | Exact(稳健但慢)或Manifold(快,仅接受干净闭包);选项来自 utilities/boolean_nodes.py 的SOLVER_ITEMS |
| Self Intersection | self_intersection | False | 仅 Exact 求解器有效,代价约为 5 倍求值时间 |
| Hole Tolerant | hole_tolerant | False | 仅 Exact 求解器有效 |
| Cutter Display | cutter_display | AUTO | AUTO自动规则 /WIRE线框 /SOLID保持实体着色 |
关键实现:
- 修改器挂在 body 上、每个 cutter 一个:
boolean_modifier_name()生成CAD_Sketcher Boolean {cutter.name}(operators/modifiers.py),多个布尔在同一 body 上叠放而非互相覆盖;read_props()还能识别既有布尔并回灌参数。 - 循环依赖防护:
creates_boolean_cycle()(L211)沿 body 已有的 cutter 链做 DFS,若 cutter 已(间接)依赖 body——包括cutter is body的零长度环——则拒绝创建。源码注释说明:闭合 depsgraph 依赖环会导致 Blender 崩溃,main()(L1708)在创建修改器前就拦截并给出 WARNING。 - 求解器默认值:新布尔的默认求解器是扩展偏好 base/preferences.py 中的
boolean_solver,当前仓库默认值为Manifold(源码注释称其约快 7 倍,且处理不了的操作数会自动回退);但View3D_OT_node_boolean的invoke()(L1643)每次从偏好取新值,避免沿用操作符上次记住的值。_solver_for()(L277)在运行期做最终裁决:若选了 Manifold 但 body 或 cutter 任一不是闭包(is_closed_solid(),utilities/boolean_targets.py 逐边校验每条边恰好被两个面共享),强制改用Exact——否则 Manifold 会静默丢弃非闭包操作数,让"用实体切平面截面"变成空结果。已保存文件在载入时由repair_solver_choice()(utilities/boolean_nodes.py)执行同样的修复。 - 修改器输入的双版本兼容:Blender ≤ 5.1 把节点修改器输入存为 ID 属性(
modifier["Input_2"]),5.2 起改为 RNA 接口modifier.properties.inputs.<id>.value;set/get/draw_modifier_input()(operators/modifiers.py)统一封装了这个分叉,这也是所有工具都能兼容两个版本读写输入的原因。 - Cutter 显示规则:实体 cutter 会遮住布尔结果,所以一旦参与切割,其视口显示默认切换为线框或隐藏(
cutter_display默认AUTO);设为Solid可保持实体着色。实际执行规则在 utilities/part.py 的update_cutter_display():切割归属于部件的 cutter 被hide_viewport隐藏(眼形hide_set不够,因为实例化副本仍会画出它),跨多个部件切割的 cutter 或"想切但够不到任何 body"的 cutter 保持线框可见——用户需要找到它。cutter 在部件/装配体中如何呈现与随行,另见 docs/content/parts.md。
Automatic Booleans:自动布尔
行为:新建的 Extrude 或 Revolve 若与既有 body 重叠,会立即被布尔进这些 body——在面上画一个 pocket 直接切入所依附的实体,无需第二步。工具的 redo panel 提供Boolean下拉(None/Difference/Union/Intersect,见BOOLEAN_TOOL_OPERATIONS,operators/modifiers.py)用于选择或取消运算;Extrude/Revolve 工具设置栏中的Auto Boolean开关(scene.sketcher.use_auto_boolean,定义于 model/group_sketcher.py,默认True,UI 挂接在 workspacetools/extrude.py 与 workspacetools/revolve.py)关闭自动检测,让新实体保持独立——此时仍可在 redo panel 手动指定运算。
检测机制(BooleanFromToolMixin.finish_booleans(),operators/modifiers.py + utilities/boolean_targets.py)由两类信号驱动:
- 来源优先(Provenance):画在网格面上的草图,其 workplane 空物体记录了源 body(
sketch_source_body()读取face_anchor写入的slvs_wp_source)。该 body 无条件成为首个目标——即"push/pull 你所画的实体"。 - 空间重叠:
overlapping_bodies()(L196)先对候选做廉价的世界空间 AABB 预筛(_world_bounds_map直接读bound_box,不建网格),再用BVHTree.overlap做精确相交测试;候选集由candidate_bodies()排除 cutter 自身、隐藏对象及会闭合依赖环的对象。几何通过 depsgraph 实例单次批量读取(_world_geometry_map),因此对"节点修改器从 Curves 对象产出网格"的草图同样有效。
默认运算的推断(default_operation(),utilities/boolean_targets.py):有来源 body 且offset > 0(向外推)时默认Union(加材料),向实体内推(offset < 0)或无来源时默认Difference。这只是默认值,用户覆盖始终生效:redo 时每次重新检测目标,但逐项的 enabled 开关和手动指定的 operation 会被保留;拉得更长的拉伸若新够到某 body,也会自动补上该目标。
性能上,整轮检测只取一次 depsgraph 求值(finish_booleans()中注释解释了为何不能在写第一个修改器后再取新 depsgraph),重叠测试用的 numpy 批量读取(_mesh_arrays)取代逐元素 RNA 访问,据源码注释这是此前每次鼠标移动时最昂贵的开销。相关行为由 testing/test_boolean_targets.py 覆盖,其中包含use_auto_boolean开/关两种路径的测试。
小结:参数与源码索引
| 关注点 | 文档/源码位置 |
|---|---|
工具操作与快捷键(Ctrl+Shift+E/R/D/B) | docs/content/tools.md、keymaps.py |
| 五个操作符与共享基类 | operators/modifiers.py |
| 拉伸节点组(含开放壁面补丁) | utilities/extrude_nodes.py |
| 旋转节点组(边界环归一化、端帽、朝向修正) | utilities/revolve_nodes.py |
| 线性/圆形阵列节点组 | utilities/array_nodes.py、utilities/circular_array_nodes.py |
| 布尔节点组与求解器定义 | utilities/boolean_nodes.py |
| 自动布尔目标检测与默认运算 | utilities/boolean_targets.py |
| 新布尔默认求解器偏好(当前默认 Manifold) | base/preferences.py |
| Auto Boolean 场景开关 | model/group_sketcher.py |
| 部件中的 cutter 显示规则 | utilities/part.py、docs/content/parts.md |
- 桌面应用
- 3D建模
【免费下载链接】CAD_Sketcher
Constraint-based geometry sketcher for blender
相关推荐
ONNX修改器:基于Netron与Flask的可视化模型编辑工具
ONNX修改器:基于Netron与Flask的可视化模型编辑工具 基础介绍与编程语言: ONNX Modifier是一个高效且直观的开源项目,专门设计用于修改O
AI 应用深度学习**深度探索:QtNodes——构建强大的节点编辑器**
深度探索:QtNodes——构建强大的节点编辑器 一、项目简介 在众多图形编程和数据流可视化工具中,有一个项目凭借其强大而灵活的功能脱颖而出——QtNodes。
UI组件数据可视化如何 4 步装好 PT 插件:从浏览器安装到批量下种的完整指南
如何 4 步装好 PT 插件:从浏览器安装到批量下种的完整指南 PT 插件 PT Plugin Plus(PT 助手 Plus)是一款面向 PT 站点的浏览器扩
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考