ARTICLE · 1244618
AI拼积木 LDraw-Nova 技术说明书从设计意图到可验证 LDraw 模型的完整原理
LDraw-Nova 技术说明书
从设计意图到可验证 LDraw 模型的完整原理
版本 1.0 | 2026-10-24 | 基于本仓库 ldraw_tools 源码与 docs/agent 文档整理
0. 一句话理解
LDraw-Nova 不是“让 AI 直接写积木坐标”的工具,而是一套面向智能体(Agent)的模型编译与验证系统:
把人的设计意图,先写成分层的 JSON plan,再由 plan 或 Python 生成器编译成 LDraw MPD,最后用语法、几何、连接、BOM、渲染五类证据逐层收口。
它真正解决的不是“会不会画积木”,而是三个更难的问题:
可建造性:零件是否真实存在、是否能装上、是否互相穿模。 可复现性:最终模型是否能从 plan/生成器重新生成,而不是靠手改 MPD。 可审美迭代:几何正确之后,如何通过多角度渲染把“合法但难看”改成“比例、层次、焦点都成立”。
1. 为什么是“编译器”,不是“画图器”
1.1 设计意图的三层落地
design-brief.md | ||
*.plan.json | ||
*.mpd |
一个典型的生成链路是:
自然语言需求↓ 设计判断design-brief.md↓ 结构化model.plan.json generate.py↓ build ↓ 逐条生成 placement└──────────┬────────────┘↓model.mpd↓validate / inspect / render / bom / compare-bom
plan.json 和 generate.py 不是二选一。两者都常用:
plan 直接描述:适合结构清楚、坐标可枚举的模型。 生成器写 plan:适合参数化、重复结构、条件分支、按网格铺装、按高度阶梯计算。 两者都遵守同一个 schema,最终都走同一个 build管线。
1.2 为什么不让模型直接写 LDraw
LDraw 的 type 1 记录长这样:
1 4 0 -8 0 1 0 0 0 1 0 0 0 1 3003.dat
一个零件需要:颜色、三维平移、9 个旋转矩阵系数、文件名。再乘上几百上千个零件,手写坐标几乎必然出错。
LDraw-Nova 的判断是:
LLM 生成“产生几何的代码”通常比直接生成“几何数字”更可靠。
于是它把难点转移到 Python/JSON 层,把 LDraw 只当成最终的序列化目标。
2. 系统全貌:五层架构
LDraw-Nova 的五层结构
知识、数据、引擎、产物、证据,缺一层都不构成完整 Skill
知识层
instructions / docs
数据层parts / categories / db引擎层builder / validation产物层plan / mpd / glb证据层render / checks / bom
工作循环
读文档 → 选真实零件 → 写 design brief → 写 plan/生成器 → build → validate/inspect → 打开渲染图 → 改设计 → 重建
关键:几何通过 ≠ 外观正确;渲染成功 ≠ 物理可建;BOM 一致 ≠ 强度足够。
输入
主题、限制、颗粒预算
参考、配色、构图
转换intent → plan → placementspyldraw3 序列化 MPD输出与证据MPD + BOM + reportshome/front/top 等渲染图
2.1 知识层
instructions.md:Agent 的主工作流。 docs/agent/*.md:几何、验证、视觉设计、模块化、参考检索、车辆/飞船/Technic 等专题。 docs/ldraw-specs.pdf:LDraw 语言的最终权威。
知识层回答“应该怎么做”和“哪些结论不能声称”。
2.2 数据层
LDRAW_DIR) | .datLDConfig.ldr 颜色 |
data/categories/ | |
data/models-annotated/ | |
data/ldraw-info.db | |
data/offLibShadow/ | |
ldraw_tools/data/rectangular-parts.json |
2.3 引擎层
核心模块:
builder.py | |
validation.py | |
geometry.py | |
collision.py | |
connectivity.py | |
external.py | |
document.py | .dat/.ldr 边界、依赖闭包、提取 |
catalog.py |
2.4 产物层
最小可交付集:
output//design-brief.mdgenerate.py# 可选,但强烈建议.plan.json.mpdbuild.jsonvalidation.jsonbom.jsonreview/home.pngfront.pngtop.pngvisual-review.md
2.5 证据层
3. LDraw 物理模型:所有几何错误的根源
3.1 坐标系
LDraw 使用右手坐标系,Y 轴负方向向上。
坐标与堆叠:负 Y 向上
−Y(向上)
+X
砖:body 24
板:body 8
砖:body 24
上件起点 = 下件起点 − 上件 body
凸点进入上件插座:AABB 合法重叠 4 LDU,不能当穿模。
3.2 变换公式
对 type 1 记录:
p_world = M @ p_local + tM_world = M_parent @ M_childt_world = M_parent @ t_child + t_parent
其中 M 是 3×3 旋转矩阵。装配 profile 要求:
Mᵀ M ≈ Idet(M) > 0
即只允许刚性旋转,不允许缩放、剪切、镜像。
3.3 站立堆叠公式
普通直立砖/板:
y_upper = y_lower - height_of_upper_body
例子:
砖在 y=0,板放上去 →y=-8板在 y=-8,砖放上去 →y=-32
不要把凸点 4 LDU 再加进堆叠步长。凸点进入上件内部,是连接结构,不是额外高度。
3.4 半格与多格中心
一个 2×2 砖占 4 个 stud,中心在两个 stud 的中间。若按单格中心摆,就会整体偏半个 stud,和邻居发生 10 LDU 重叠。
这就是为什么手写网格生成器必须区分:
单格件(1×1)的 gx是整数。2×2 件中心是 gx + 0.5。2×4 / 4×2 件中心是格块几何中心。
很多“看起来检查通过、渲染却错位”的问题,根因都是半格对齐。
3.5 颜色
16:继承父级当前颜色,不是固定灰。 24:边线颜色,只能用于 type 2/5 的边,不能放在实体 placement。 0x2RRGGBB:直接 RGB,合法但不建议用于生成模型。 色号来自安装库的 LDConfig.ldr,不是 BrickLink 或 LEGO Element ID。
3.6 MPD 与物理边界
MPD 由多个 0 FILE 块组成,第一个块是主模型。
.ldr:装配子模型,参与装配层级。 .dat:零件定义,是 物理件边界;内部几何用于展开,但 BOM 只算这个物理件本身。
这条区分非常重要:
子装配里的 20 个零件是 20 个物理 placement;嵌入的
.dat零件内部即使有 200 个 polygon,在装配 BOM 里仍然只算 1 个物理件。
4. plan.json:系统的中间表示
4.1 顶层结构
{”version”: 1,”author”: ”liangdabiao”,”license”: ”optional”,”includes”: [”other.plan.json”],”assets”: [”assets/clock.mpd”],”sections”: []}
约束:
version必须为 1。sections至少 1 个,最多 200 个。 第一个 section 是 MPD 主模型。 section 名必须以 .ldr结尾,全局大小写不敏感唯一。每个 section 最多 1000 步,每步最多 1000 个 placement。 坐标绝对值不超过 1,000,000 LDU。
4.2 section
{”name”: ”house-wall.ldr”,”description”: ”Front wall with a door opening”,”author”: ”optional override”,”license”: ”optional override”,”anchors”: {”base”: {”at”: [0, 8, 0]},”top”: {”at”: [0, -160, 0]}},”steps”: [[{”id”: ”brick-1”, ”ref”: ”3001.dat”, ”colour”: 15,”at”: [0, -24, 0], ”purpose”: ”first course”}]]}
steps 是“步”的数组,不是扁平的 placement 列表。每一“步”代表同时安装的一组零件,build 会在步骤之间写 0 STEP。
4.3 placement 的五种定位方式
每个 placement 必须且只能使用一种:
at | yaw 或 matrix | |
on | ||
attach | ||
snap |
{”id”:”seat”, ”ref”:”3022.dat”, ”colour”:71,”at”:[0,-8,0], ”yaw”:90, ”purpose”:”rotated seat”}
yaw 是绕当前 section 的 +Y 轴右手旋转。yaw 与 matrix 互斥。
{”id”:”panel”, ”ref”:”3001.dat”, ”colour”:15,”at”:[0,-40,0],”matrix”:[[0,0,1],[0,1,0],[-1,0,0]]}
矩阵必须是 proper rotation。任意刚性朝向可以,但不能缩放/镜像。
{”id”:”plate”, ”ref”:”3022.dat”, ”colour”:15,”on”:”brick-1”, ”offset_studs”:[0.5,0.5]}
builder 的过程:
position = support.position+ support.matrix * Vector(offset_x*20, -part_height, offset_z*20)
然后检查上件和下件是否至少有一个 stud 对齐点。偏移以支撑件的局部轴为准。
on 不是“万能吸附”:只适用于 curated 的直立矩形砖/板 profile。斜面、轮胎、夹子、横向 SNOT、子模型引用都不支持。
{”id”:”upper”, ”ref”:”floor.ldr”, ”colour”:19,”attach”:{”to”:”lower”, ”anchor”:”next”, ”using”:”base”}}
数学解:
T_moving = T_support × A_support × T_offset × inverse(A_moving)
它对齐的是作者声明的模块接口,不是自动连接器检测。锚点必须保持实际 stud/socket 可接触。
{”id”:”cap”, ”ref”:”cap.ldr”, ”colour”:1,”snap”:{”to”:”base”, ”near”:[0,-8,0]}}
builder 会调用连接器求解器:
找 moving/fixed 的连接器特征; 生成候选姿态; 检查整个 moving 子树的碰撞; 检查选定端点是否真的形成接触; 选择未 blocked 的候选。
near 只是搜索起点,最终姿态由求解器决定。
{”id”:”post”, ”ref”:”3005.dat”, ”colour”:308,”at”:[-100,-24,-100],”repeat”:{”count”:8,”step”:[40,0,0]}}
生成 post-0 … post-7。step 是当前 section 轴的平移,不会跟随 yaw 旋转。repeat 只能和 at 一起用。
4.4 includes / assets
includes:加载其他 plan,路径相对声明文件;循环、超过 200 个文件、重复 section 名都会失败。 assets:嵌入已提取的 MPD 定义,保留原 header、作者、许可证与几何;命名空间必须唯一。 每个 section 可以覆盖 author/license;根 plan 的 license 不会错误地传播给未授权的 include。
4.5 为什么 plan 要有 purpose
purpose 会写成 MPD 注释:
0 // brick-1: first course
它不是装饰。调试大模型时,inspect / 渲染步骤 / 结构审查都需要靠它快速定位“这块到底是什么”。
5. build:把 plan 编译成 MPD
build 的八个阶段
任何一步失败,最终 MPD 都不会被写入
1 load_plan
includes / assets / headers
2 schemaJSON Schema 2020-123 resolve@parts / @colours4 dependency orderchildren before parents5 placement solveat / on / attach / snap6 serializeMPD + CRLF7 validatesyntax + assembly8 geometrycollision + contacts
写入门槛
serialize 后先重新解析并校验;任何 error 都不写。无 error 才做几何分析,最后还是无 error 才 atomic_write。
报告
checks_passed / written / diagnostics / geometry
退出码
0 = 通过;1 = 校验失败;2 = 输入/依赖/执行失败
5.1 load_plan
load_plan() 做四件事:
递归加载 includes,路径相对声明文件,检测循环,最多 200 个文件。每个 section 继承所属 plan 的 author/license(若自己没有)。 收集 assets为绝对路径。合并 sections,保留根 plan 的非 include 字段,再做一次完整 schema 检查。
5.2 schema 与符号解析
resolve_plan() 只修改build 的副本,不改磁盘上的可编辑 plan:
{”ref”:”@plants.PlantBush”, ”colour”:”@colours.Dark_Green”}
会解析成真实 .dat 和色号。磁盘上仍保留符号,便于人读和跨库迁移。
5.3 依赖排序
dependency_order() 先构建被引用的子模块,再构建父模块。原因:
snap和 attach需要子模块的物理件与锚点已经存在;子模块的 header、步骤和颜色必须先准备好; 但 第一个声明的 section 永远是 MPD root,排序不改变根。
5.4 Placement 求解
按 placement 模式逐一算最终 position / matrix:
at:直接使用。 on:堆叠公式 + stud 对齐检查。 attach:4×4 变换合成。 snap:连接器候选 + 碰撞 + 端点接触验证。
每个 placement 先写注释,再写 type 1。
5.5 序列化
serialize_mpd() 保证:
第一个 0 FILE是主模型;所有子模块作为 submodels 只写一次; UTF-8 无 BOM; 统一 CRLF; 文件末尾有换行。
5.6 写入门槛
build 不是“写出后再检查”,而是:
serialize → 重新解析 → validate_text → 若 error 停止→ geometry analyze → 若 error 停止→ atomic_write
所以失败构建不会留下半成品 MPD。--force 只控制覆盖已有文件,不会绕过校验。
6. 校验器:它到底检查什么
6.1 两种 profile
assembly | ||
syntax |
最终交付模型必须过 assembly。
6.2 assembly 检查清单
0 FILE;主块第一;section 名唯一 | |
.ldr.dat 是嵌入零件定义 | |
!LDRAW_ORG 分类 | |
6.3 为什么同一零件摆两次会报错
重复判定是世界变换级别:
(normalized ref, rounded position, rounded matrix)
不是“同一个 id”。所以:
同一个子模型被实例化两次、但位置不同 → 合法。 同一个零件在两个子模型里恰好落到同一世界坐标 → error。 同一位置、不同颜色 → 仍算重复,因为几何重叠。
6.4 错误 / 警告 / 退出码
error:阻止 build 写入, checks_passed=false。warning:不阻止写入,但必须阅读并解释。 --strict:把 warning 也当成失败。
退出码:
0:命令完成且选中检查通过。 1:校验失败、覆盖率失败、BOM 不一致、无 snap 候选等。 2:输入错误、依赖缺失、I/O、转换、预算、执行失败。
不要只看退出码。还要读:
checks_passedcompletephysical_validitydiagnostics*_truncatedcontacts_checked
7. 几何引擎:它为什么既强大又必须被怀疑
一次碰撞检查的判定顺序
从便宜到昂贵,从宽到严;最后仍需要人工材料审查
1 解析物理叶子
.dat 是边界,内部展开
2 AABB 粗筛
按 X 排序,扫描候选
3 15 轴 SAT
有向包围盒分离
4 矩形 body 检查
36 个砖/板真实高度
5 连接器接触
stud / socket / pin / axle
状态不是二值
rectangular_body_overlap / oriented_bounds_separated
stud_zone_overlap_review_connections / review_oriented_bounds / review_aabb_only
只有第一种是确定性 body 穿模;其余是候选或需要材料审查。
7.1 展开与预算
几何分析会递归展开所有被引用库几何。默认最多 100,000 个物理 occurrence;超过则报错,建议缩小 section 或提高预算。
7.2 physical_context 与 .dat 边界
physical_context() 会把嵌入 .dat 当作物理件边界:
装配遍历停在 .dat;BOM 只计这个零件; 但它的内部几何仍会被展开用于碰撞/接触。
7.3 AABB → OBB → Body
AABB:最快,但大量假阳性。 Oriented bounds SAT:用 15 根轴检查两个有向盒是否分离。 Curated rectangular body:对 36 个普通砖/板,用真实 body 高度(不含凸点)做 SAT。
第三种才会产生致命错误:
assembly.body_overlap
它意味着两个矩形砖/板 body 真的互相插入,超过 1e-5 LDU。
7.4 连接图
接触由 pyldraw3 + LDCad shadow metadata 推断:
confirmed:双方连接器证据都匹配。 optimistic:至少存在候选接触,但证据较弱或单边。 多组件警告 assembly.disconnected_evidence不自动等于悬空:可能是植物、装饰、独立人物; 可能是连接器元数据缺失; 可能是真的没接上。
必须具体检查被报告的零件。
7.5 500 件阈值
contacts="auto":
≤ 500 个物理件:计算接触。 500:显式跳过,并报
coverage.contacts_skipped。
大模型要主动选择:
validate model.mpd --geometry --contacts all
或按模块检查:
inspect model.mpd --section wall.ldr --contacts all
7.6 工具不能证明什么
不能证明所有材料不相交(AABB/SAT 不是实体布尔运算)。 不能证明凸点/插孔合法(连接器是推断)。 不能证明强度、稳定性、抗跌落。 不能证明零件在该颜色下真实生产/可购买。 physical_validity永远是 not_proven。
8. 连接器与 Snap:自动对位的边界
8.1 连接器元数据从哪里来
零件文件内联的 LDCad/pyldraw3 连接器记录。 data/offLibShadow/影子库。 Technic 专用 registry(仅结构件)。
覆盖度有三种:complete / partial / none。完整覆盖不等于每个物理连接都建模了。
8.2 一次 snap 的内部步骤
选中 moving leaf↓确定 moving_depth(默认只动叶子,0 动最外层子装配)↓收集 moving 子树与 stationary targets↓生成连接器候选姿态↓应用刚体 delta 到整个 moving 子树↓检查与静止件的 body 碰撞↓重算接触,验证选定端点真的接触↓按 blocked / review / contact / distance 排序
8.3 候选排序
优先级:
无 body collision; 未解决 overlap 更少; 接触更可信(confirmed 优先); 位移更短。
8.4 apply snap
apply_snap() 会:
校验报告指纹是否属于当前模型 revision; 沿 placement path 克隆祖先 section; 只改目标 placement; 不改变同一子模型的其他实例。
这保证“修一个窗户”不会把整排窗户都移动。
8.5 Snap 的边界
不枚举所有铰链角度/滑动位置。 保留自由 roll。 连接器是接口描述,不是实体体积。 接触不能抵消 body 碰撞。 不证明插入路径、强度、稳定性。
9. 渲染、BOM 与外部工具
9.1 LeoCAD 适配器
render 并不是直接把 MPD 丢给 LeoCAD:
解析 MPD,识别嵌入 .dat。为 LeoCAD 建立临时 mini library 与重写后的模型。 用唯一临时文件名渲染,避免旧图冒充成功。 导出 LeoCAD BOM。 把 BOM 里的临时名恢复成原始嵌入名。 原 MPD 与安装库保持不变。
这解决了 LeoCAD 会漏掉嵌入 DAT 零件的问题。
9.2 视图与相机
支持:
home front back left right top bottom
对 supported assembly,相机会按整个 bounds 自动取景:
FOV 30°; 包围球半径; 1.15 倍 margin; 根据视图方向放置相机。
默认输出:1000×800、AA 4、full shading、无高亮、无步骤淡化。
9.3 渲染成功不等于视觉审查
渲染工具只返回:
{”visual_review”:”required: open and inspect the images”}
必须真的打开图片,检查:
缩略图剪影是否能认出主题; 主焦点是否被遮挡; 比例是否成立; 颜色是否组织成角色; 侧面/背面是否穿帮; 有没有悬空、错位、异常接缝。
9.4 BOM 与 compare-bom
Python BOM 与 LeoCAD BOM 必须按:
(reference, colour, quantity)
完全一致。
render model.mpd --outdir reviewcompare-bom model.mpd --csv review/leocad-bom.csv
不一致不是“渲染问题”,而是物理件统计或适配器问题,必须解释并修复。
9.5 GLB
prepare-glb.sh --file model.mpd model.glb
GLB 是派生导出,MPD 仍是权威源。转换成功不增加几何/物理验证。
10. 零件与参考检索:先看真实几何,再看描述
10.1 真相优先级
安装库的真实 .dat 几何 / 连接器> catalog 的尺寸快照与符号> 零件名称/描述
名称只能提示,不能证明轴向、原点或堆叠高度。
10.2 三种检索方式
search parts | |
catalog parts | |
discover search |
discover 在有 Jev 时可语义重排;没有 API key / 服务不可用时,必须显式使用 --engine fts。CLI不会静默切换。
10.3 part 是最重要的单件工具
ldraw-agent part 3040b --limit 40
返回:
真实 bounds(含凸点/装饰); 原点相对尺寸; stud 位置; 连接器; 元数据覆盖; 矩形 profile(如果属于 36 个受控件)。
注意:bounds 高度不是堆叠高度。砖的 bounds 是 28,body 是 24。
10.4 study / extract
study:盘点层级、物理数量、复用、可达性、BOM、源问题。 extract:复制依赖闭包,重命名命名空间,保留作者/许可证,生成 manifest。
extract 的产物是“可审查副本”,不是物理认证。
10.5 part-board
对 1–12 个候选零件生成真实渲染卡片:
ldraw-agent part-board 6064b 3470 30176 --colour 74 --outdir output/board
卡片独立取景,不能凭图片大小判断真实尺寸;要对照 measured bounds。
11. 视觉设计:几何正确之后才开始
11.1 设计 brief 必须回答的七个问题
Identity:这是什么地方/物体?要传达什么气氛? Silhouette:三个主要体量是什么?高度差在哪里? Focal hierarchy:第一焦点、第二焦点、第三焦点分别是什么? Palette roles:大面积、中间面、小 accent 各是什么颜色? Depth:挑檐、退台、凹窗、基座提供了什么阴影? Detail vocabulary:重复的细节母题是什么? Quiet surfaces:哪些面故意留空?
11.2 构图规则
一个主焦点,两个支持特征,其余是小 accent。 大面颜色不超过 3 种。 细节集中在 1–2 个区域,不要全模型均匀撒。 植物/装饰不能遮挡入口、桥、主立面等识别点。 对称模型要有明确的轴线;非对称模型要有明确的视觉平衡。
11.3 三个尺度的审查
模块检查不能替代整场景检查。
11.4 为什么“检查全绿但很丑”很常见
因为几何检查只看:
是否穿模; 是否重复; 是否有连接器证据; 是否合法。
它不看:
比例是否像; 焦点是否被挡; 颜色是否平衡; 细节是否有节奏; 剪影是否可读。
所以实际流程必须是:
自检/校验 → 渲染 → 开图 → 改设计 → 再校验
而不是:
自检/校验 → 交付
11.5 收敛标准
工程上建议最多 2–3 轮“检查—修复”:
第 1 轮:修所有 error。 第 2 轮:修明显视觉硬伤(遮挡、比例、悬空)。 第 3 轮:只确认,不再追求完美。
超过这个范围通常是审美偏好,不是结构错误,应让用户决定。
12. 标准工作流:从零做一个模型
12.1 环境
Windows 当前工作区:
export LDRAW_DIR=”D:/LDraw/lib/ldraw”./.venv/Scripts/python.exe -m ldraw_tools.cli doctor
仓库原始 POSIX 形式:
./ldraw-agent doctor
如果 ldraw-agent 报 “Run ./setup.sh first”,优先用 .venv/Scripts/python.exe -m ldraw_tools.cli,不要重复跑 setup。
12.2 第 0 步:确认范围
写下:
主题; 尺度(底板 stud 数); 颗粒预算; 必须出现的 3 个特征; 明确不要出现的东西; 主视角。
12.3 第 1 步:设计 brief
不要先摆零件。先定:
总平面; 体量关系; 焦点; 配色; 材质/零件族; 模块划分。
12.4 第 2 步:查真实零件
对每个不熟悉的零件:
ldraw-agent part CODE --limit 40
记录:
真实 bounds; 原点; body 高度; 是否有凸点; 连接器; 是否需要旋转。
12.5 第 3 步:写 plan 或生成器
简单模型:直接 plan。 重复/参数化:Python 生成 plan。 大场景:按模块拆 section,先做单模块。
12.6 第 4 步:build
ldraw-agent build output//model.plan.json \--output output//model.mpd \--detail summary \--report output//build.json
12.7 第 5 步:validate / inspect
ldraw-agent validate output//model.mpd --geometry \--contacts all --report output//validation.jsonldraw-agent inspect output//model.mpd \--detail summary --report output//inspection.json
大模型必须显式决定 contacts all 或按 section 检查。
12.8 第 6 步:render + 开图
ldraw-agent render output//model.mpd \--outdir output//review \--views home front top right
打开所有图,记录:
看到了什么; 哪些是硬伤; 哪些只是偏好; 下一轮只改什么。
12.9 第 7 步:BOM / GLB / 交付
ldraw-agent bom output//model.mpd \--report output//bom.jsonldraw-agent compare-bom output//model.mpd \--csv output//review/leocad-bom.csv
12.10 第 8 步:复现性检查
改完最后一处后:
重跑生成器。 重建 MPD。 重跑校验、BOM、渲染。 确认生成器输出与交付 MPD 一致(哈希或逐字节)。
不能出现“MPD 已经修好,但生成器还是旧版”的状态。
13. 故障排查手册
syntax.field_count | ||
number.nonfinite | ||
assembly.nonrigid | ||
matrix.singular | ||
model.unknown_part | part/search 查真实文件名 | |
model.unknown_colour | colours 查 | |
colour.edge_on_surface | ||
colour.unresolved_current | ||
assembly.duplicate | ||
mpd.unreachable | ||
mpd.library_shadow | ||
assembly.library_internal | .dat | |
assembly.body_overlap | instances、sources、penetration | |
assembly.disconnected_evidence | ||
coverage.contacts_skipped | --contacts all 或按 section | |
coverage.collision_review | ||
13.1 “检查全绿但外观错”的排查顺序
先看俯视:总平面、遮挡、体量关系。 再看正视:立面比例、门窗、屋顶。 再看 3/4:整体剪影、阴影、焦点。 最后看侧/背:穿帮面、围栏遮挡、结构厚度。 只改“观众能一眼看出”的问题,不改洁癖问题。
13.2 常见几何陷阱
多格件半格中心:2×2 件不能放在单格中心。 旋转后尺寸交换:yaw 90 时 X/Z 尺寸互换。 凸点不算堆叠高度:砖 body 24,不是 bounds 28。 斜面件 AABB 高估实体:坡面下方是空气,不能按整盒判碰撞。 楔形/倒角件接触面不是盒底:必须读零件源文件。 手写 2×4 铺装:必须按 4×2 格块切分,不能按 20 LDU 步进。 共享子模型实例:改一个实例要通过 clone 祖先,不能直接改共享定义。
13.3 性能与稳定性
大模型先按 section 检查,不要一上来 --contacts all。超过 500 件,auto 会跳过接触;这是显式覆盖缺口,不是通过。 渲染前检查僵尸 LeoCAD 进程。 大场景用 --detail summary,需要时再分页--limit/--offset。不要反复重跑同一耗时任务;先看报告定位。
14. 交付清单
一个合格交付应包含:
design-brief.md:范围、构图、焦点、配色。*.plan.json:可编辑的结构化源。generate.py:如果模型是参数化生成。*.mpd:最终可打开模型。build.json:构建报告。validation.json:几何/连接报告。bom.json:物料清单。review/*.png:至少 home/front/top,复杂模型加侧/背。visual-review.md:看了哪些图、发现什么、改了什么。明确写出: physical_validity未证明、未做的检查、已知审美限制。
14.1 不能声称的事
不能因为 checks_passed=true就说“物理可建造”。不能因为渲染成功就说“视觉审查完成”。 不能因为 BOM 一致就说“零件一定买得到”。 不能因为连接器 confirmed 就说“强度足够”。 不能因为模型好看就说“结构稳定”。
15. 本工作区的四个实战案例
output/japanese-garden | |||
output/lingnan-garden | |||
output/suzhou-garden | |||
output/european-garden |
共同结论:
先定总平面,再摆零件,比边摆边想稳定得多。 几何检查通过后必须开图;四套模型的首版都出现过“合法但不对”的问题。 多格件中心、旋转尺寸、堆叠高度是最高频的三类 bug。 用户确认前不要自行连续重构;首版可看就先交付,再按点名方向修。
16. 维护与扩展:怎么改这套 Skill
16.1 新增一个“受控矩形件” profile
如果某个普通砖/板需要支持 on 的严格堆叠、或需要 body 碰撞检查:
在 ldraw_tools/data/rectangular-parts.json增加真实 body 尺寸:json "3001": {"description":"Brick 2 x 4","x_studs":4,"z_studs":2,"height":24.0,"studs":true}用 part CODE核对原点、bounds、stud 位置。补测试:正常堆叠、半格偏移、旋转后 X/Z 交换、错误偏移必须失败。 不要用 bounds 高度代替 body 高度。
16.2 新增分类符号
data/categories/ 是静态元数据。新增符号只是让 plan 可以用可读名引用零件:
必须仍然在安装库中解析真实 .dat;分类尺寸只是搜索提示,不能替代 part的实测;不要让分类文件成为“官方零件存在性”的证明。
16.3 新增 CLI 子命令
ldraw_tools/cli.py 的模式很固定:
parser()注册子命令与参数。 run(args)返回 (report_dict, exit_code)。所有报告必须是 JSON 可序列化。 退出码遵守 0/1/2 约定。 写文件必须使用 atomic_write(),并且只在检查通过后写。增加对应测试和 docs/agent/tooling.md条目。
16.4 新增渲染/导出适配器
渲染适配器必须保证:
不修改源 MPD 和安装库; 使用临时目录 + 唯一文件名; 子进程设置 timeout; 失败时清理临时产物; 嵌入 DAT 的命名在 BOM 中恢复; 返回的图片路径和 BOM 路径可验证。
16.5 升级 pyldraw3 / 影子库
重点回归:
物理件边界与嵌入 DAT; 旋转矩阵容差; 连接器 feature ID / occupancy; 方形插孔与圆凸点的适配; BOM 与 LeoCAD 的一致性; snap 候选与碰撞状态。
升级后如果连接器覆盖变化,必须重新跑 connectivity 和 snap 回归,不能只看语法测试。
16.6 不应修改的资源
安装的 LDraw 库; data/models-annotated/的源模型; data/ldraw-info.db; data/offLibShadow/的原始影子文件; 他人的作者信息与许可证。
所有新工作放 output/,所有修复留在 plan/生成器,不手改最终 MPD。
附录 D:源码阅读最短路线
如果要把这套 Skill 真正读懂,按这个顺序读,不要从 700 行的 CLI 开始:
instructions.md:先理解工作流和交付标准。 docs/agent/ldraw-reference.md:坐标系、type 1、MPD、颜色。 ldraw_tools/data/plan.schema.json:先看数据契约。 ldraw_tools/builder.py第 23–60 行:includes / schema / 继承。 builder.py第 157–248 行:placement 求解与最终写入门槛。 validation.py第 54–230 行:语法、矩阵、重复、可达性。 geometry.py第 65–190 行:碰撞、接触、组件。 collision.py:AABB / OBB / body 的三级判定。 connectivity.py第 111–244 行:snap 候选与 apply。 external.py第 216–247 行:渲染与 BOM。 document.py第 22–109 行:MPD 命名空间与物理边界。 docs/agent/visual-design.md:最后再学审美,不要反过来。
读完这条线,再看 cli.py 只是命令分发,不会迷失在参数里。
附录 A:核心命令速查
# 环境ldraw-agent doctor# 零件 truthldraw-agent part 3001 --limit 40ldraw-agent catalog parts 'plant bush' --category plants --limit 8 --measureldraw-agent colours blue# 参考ldraw-agent search parts 'brick 2 x 4'ldraw-agent study data/models-annotated/10270-1.mpd --report output/study.jsonldraw-agent extract data/models-annotated/10270-1.mpd \--section '10270 - Pendulum_Clock.ldr' --namespace ref-clock \--output output/assets/clock.mpd# 生成ldraw-agent build output/model.plan.json --output output/model.mpd \--detail summary --report output/build.json# 验证ldraw-agent validate output/model.mpd --geometry --contacts all \--report output/validation.jsonldraw-agent inspect output/model.mpd --detail summary \--report output/inspection.json# 视觉ldraw-agent render output/model.mpd --outdir output/review \--views home front top right# BOMldraw-agent bom output/model.mpd --report output/bom.jsonldraw-agent compare-bom output/model.mpd --csv output/review/leocad-bom.csv# 连接ldraw-agent connectors output/model.mpd --occurrence 1ldraw-agent snap output/model.mpd --moving 1 --fixed 0 --limit 5
Windows 当前工作区把 ldraw-agent 替换为:
./.venv/Scripts/python.exe -m ldraw_tools.cli
附录 B:术语表
physical_validity | not_proven,表示未做真实物理认证 |
附录 C:交付前自检
所有零件来自真实库;不认识的件用 part查过。堆叠高度用 body,不用 bounds。 多格件使用正确中心。 build无 error。validate --geometry无 error。大模型显式处理 contacts 覆盖。 打开过所有关键渲染图。 Python/LeoCAD BOM 一致。 生成器可以重建同一 MPD。 没有把“通过检查”说成“物理证明”。















































































