平面图内部标注线保真度修复实录:Pascal 编辑器施工尺寸基线与房间侧门宽标注的源码级剖析
平面图内部标注线保真度修复实录Pascal 编辑器施工尺寸基线与房间侧门宽标注的源码级剖析【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor导读本文以仓库根目录下的 design-qa.md 视觉对比文档为主线完整还原 Pascal 开源 3D 建筑编辑器editor93/editor中一次针对平面图内部标注线internal dimension lines保真度的质量修复过程源视觉稿中标注线塌缩到墙体上、文本变成悬浮数字以及封闭外围墙大门的房间侧宽度标注缺失。文章将逐层拆解问题的 P0 定位、offsetDistance偏移机制的根本原因、自动基线与渲染器的调用链、房间侧开门链的规划逻辑以及由 61 个通过的测试构成的回归保障。读完你既能复现这套以源视觉为基准的 QA 对比方法论也能理解施工尺寸标注从规划plan到几何geometry再到 SVG 渲染的完整数据流。一、问题背景平面图标注 QA 的对比目标与判定标准design-qa.md是一个典型的视觉对比 QA 记录它定义了本次修复的验收基线源视觉真值Source visual truth两张来自系统剪贴板的参考截图作为渲染结果的事实基准实现截图Implementation screenshot本地编辑器导出的平面图预览视口 1280 × 720期望状态Intended state内部标注的基线baseline、引出线witness lines、刻度线ticks和数值values清晰脱离墙体渲染且封闭外围墙enclosed perimeter walls上的门在房间内侧获得宽度标注。这份文档本身记录了 QA 过程中的一个关键阻塞P0本地编辑器预览停留在加载指示器上点击 2D 后 3D 仍处于选中态导致同一场景状态的浏览器渲染对比无法完成。这是仓库 QA 流程的典型形态——几何层可用测试断言像素层需要人工截图验收两者缺一不可。二、问题定位基线坐标被显式钉在引出点offsetDistance被覆盖design-qa.md的Comparison History给出了问题的最早形态Earlier P0: internal baseline coordinates were explicitly equal to witness coordinates, overridingoffsetDistanceand collapsing lines onto walls.也就是说内部标注的基线两端坐标被显式赋值为引出点witness坐标等于把偏移距离写死为 0最终标注线全部贴死在墙面上数值字符串读起来像一堆脱离上下文的悬浮文本。2.1 根源buildDimensionStringGeometry的偏移默认值语义几何组装的统一入口在 packages/nodes/src/shared/dimension-string.tsexport function buildDimensionStringGeometry(input: DimensionStringGeometryInput): FloorplanGeometry { return { kind: dimension-string, segments: input.segments.map((segment) ({ ... })), offsetNormal: input.offsetNormal, offsetDistance: input.offsetDistance ?? 0, // 未提供时默认 0 extensionStartGap: input.extensionStartGap, extensionOvershoot: input.extensionOvershoot ?? 0, ... } }这里语义非常关键dimensionStart/dimensionEnd是可选的——当调用方不显式提供基线坐标时渲染器会依据offsetNormal × offsetDistance自动推导基线位置反之如果调用方把基线坐标显式设为与 witness 相同那么无论offsetDistance配置成多少都被覆盖成 0。2.2 修复一保留省略的自动基线让渲染器应用配置偏移design-qa.md记录的修复方向是Fix: preserve omitted automatic baselines so the renderer applies the configured offset; add enclosed room-side opening chains for perimeter walls.对应到代码墙施工尺寸规划在 packages/nodes/src/wall/construction-dimensions.ts 的buildInteriorWallDimensions中产出PlannedConstructionDimension时只给 witness 起点/终点与偏移量不写死dimensionStart/dimensionEndplanned.push({ tier: interior, start: pointAt(start), end: pointAt(end), offsetNormal: normal, offsetDistance: standard.openingChainOffset, // 默认 0.55 }) planned.push({ tier: interior-overall, start: pointAt(spanStart), end: pointAt(spanEnd), offsetNormal: normal, offsetDistance: openingSpans.length 0 ? standard.wallSpanOffset : standard.openingChainOffset, })随后由renderPlannedConstructionDimensions同文件 L383-L416把偏移原样透传给buildDimensionStringGeometry。渲染端 SVG 组件在收到没有显式基线的dimension-string时会按偏移自动生成基线——这正是回归测试 packages/editor/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx 所断言的test(offsets automatic dimension-string lines when no explicit baseline is supplied, () { const automaticString { kind: dimension-string, segments: [{ start: [0, 0], end: [2, 0], text: 2m }], offsetNormal: [0, 1], offsetDistance: 0.55, ... } // 渲染后基线 y 坐标应为 0.55 expect(markup).toContain(data-floorplan-dimension-default-y10.55) expect(markup).toContain(data-floorplan-dimension-default-y20.55) })需要说明的是手动施工尺寸packages/nodes/src/construction-dimension/floorplan.ts 的buildLinearOrChord走的是另一条路径布局函数resolveConstructionDimensionLayout会解析出明确的基线因此那里offsetDistance: 0是正确语义——显式基线与自动基线两种模式必须区分对待这正是本次 BUG 修复的关键认知。三、修复二封闭外围墙房间侧开门链perimeter door room-side widthsdesign-qa.md记录的第二项缺口左侧较大的外围墙门在源状态下没有房间侧宽度标注。修复后Generated plans now include room-side opening chains for enclosed perimeter walls in all four orientations, including a left-side door.3.1 房间侧法向的判定enclosedRoomSideNormal在 construction-dimensions.ts 中enclosedRoomSideNormal负责确定房间在墙的哪一侧function enclosedRoomSideNormal(wall, walls): FloorplanPoint | null { const outward exteriorNormal(wall) if (!outward) return null ... const { frontClearance, backClearance } interiorDimensionClearances(wall, walls, tangent) const inward negate(outward) const inwardClearance dot(inward, front) 0 ? frontClearance : backClearance return inwardClearance null ? null : inward }核心逻辑取外墙朝外的法向翻转为朝内方向再结合interiorDimensionClearances沿两侧做射线求交得到最近的墙/弧线距离见同文件 L775-L800判定该方向确实被围合从而得到合法的房间侧法向。之后buildInteriorWallDimensions以normalOverride形式接收该法向沿房间内侧生成开门宽度链与整墙跨度标注。3.2 回归覆盖四个方向的 perimeter door 都要有房间侧宽度规划层测试 packages/nodes/src/wall/construction-dimensions.test.ts 用上/右/下/左四堵封闭外围墙分别挂载不同宽度的门逐一断言test(dimensions perimeter door widths on the room side in every wall orientation, () { const top wall({ id: wall_top, end: [6, 0] }) const right wall({ id: wall_right, start: [6, 0], end: [6, -6], frontSide: exterior, backSide: interior }) const bottom wall({ id: wall_bottom, start: [6, -6], end: [0, -6], frontSide: exterior, backSide: interior }) const left wall({ id: wall_left, start: [0, -6], end: [0, 0], frontSide: exterior, backSide: interior }) const cases [ { wall: top, normal: [0, -1], width: 1.2 }, { wall: right, normal: [-1, 0], width: 1.3 }, { wall: bottom, normal: [0, 1], width: 1.4 }, { wall: left, normal: [1, 0], width: 1.5 }, ] ... for (const { wall: host, normal, width } of cases) { const roomSideDimensions (plan.get(host.id) ?? []).filter( (entry) (entry.tier interior || entry.tier interior-overall) entry.offsetNormal[0] * normal[0] entry.offsetNormal[1] * normal[1] 0.99, // 方向须朝向房间内侧 ) expect(roomSideDimensions.length).toBeGreaterThan(0) expect(dimensionTexts(renderPlannedConstructionDimensions(roomSideDimensions, metric))) .toContain(${width}m) } })这个测试同时验证了三点标注链存在、方向确实朝向房间内侧点积 0.99、渲染出的文本包含对应门宽。仓库内还有对边为弧形墙时房间侧门/窗标注不丢失的变体用例同测试文件 L694 起说明弧形边界场景也被覆盖。四、标注偏移背后的绘制标准配置上述 0.55 m、1.05 m 等数值并非魔法数字而是出自统一的绘制标准配置 packages/nodes/src/shared/construction-dimension-standards.tsexport const DEFAULT_CONSTRUCTION_DIMENSION_STANDARD { datumPolicy: wall-face, // 基准策略中心线 / 墙面 / 结构面 / 完成面 intersectionReferencePolicy: single, terminator: architectural-tick, // 建筑刻度线 textPosition: above, // 文字在基线上方 imperialPrecision: 1/16, metricNotation: meters, openingChainOffset: 0.55, // 开门/窗链距墙面偏移m——本次修复的回归值 wallSpanOffset: 1.05, // 整墙跨度标注距墙面偏移m firstOpeningWidthOffset: 0.62, firstGeneralTierOffset: 0.55, tierSpacing: 0.62, // 多级标注带间距m extensionStartGap: 0.075, // 引出线起点离墙间隙 extensionOvershoot: 0.12, // 引出线超出基线长度 } satisfies ConstructionDimensionDrawingStandard类型定义同文件 L7-L21还包含datumPolicy: centerline | wall-face | structural-face | finish-face四种基准策略。datumPolicy会直接传导到墙施工尺寸的基准距离计算如wallDatumOffset中centerline返回 0、其余策略按getWallThickness(wall) / 2计算见 packages/nodes/src/construction-dimension/floorplan.ts理解这一层才能解释偏移从哪来、被谁消费。在层级规划中buildLevelWallConstructionDimensionPlanconstruction-dimensions.ts会把标注按tier分层opening-widths → openings → partitions → structure → jogs → overall → structural-overall内部墙再追加interior/interior-overall。测试 construction-dimensions.test.ts 验证了分区墙 门 窗的完整链条开门窗链偏移 0.55、整墙跨度偏移 1.05且逐段断言渲染基线相对 witness 的净偏移等于配置值。五、回归证据与验收清单design-qa.md记录的修复后证据与当前仓库测试状态一致验证项结果依据尺寸 / 墙体 / 平面图 / 注册表测试61 passed, 0 faileddesign-qa.md 记录Nodes 包构建通过design-qa.md 记录Editor 包类型检查被无关的resolveFloorplanExportViewport缺失导出阻塞floorplan-export.test.ts引用design-qa.md 记录Biome 检查通过design-qa.md 记录Git diff 空白检查通过design-qa.md 记录浏览器控制台警告/错误无design-qa.md 记录其中自动基线偏移 0.55 m由 SVG 渲染器回归测试断言floorplan-dimension-renderer.test.tsx四方向外围墙房间侧门宽由规划器回归测试断言construction-dimensions.test.ts。文档同时给出了浏览器渲染证据仍被阻塞的验收清单Implementation Checklist这是像素级验收的必经步骤恢复本地编辑器预览在 2D 中重新打开目标房间确认每条内部字符串都有可见的平行基线、引出线与刻度线确认左侧大门显示其房间侧宽度标注。后续抛光建议Follow-up Polish则强调内部标注线对比度contrast的重新评估必须等到修正后的几何在目标场景中可见之后再进行——即几何正确性是像素级评审的前置条件。六、方法论沉淀几何可测、像素须验从design-qa.md这份 QA 记录可以提炼出一套可复用的平面图标注保真度验收方法双证据体系几何层用测试断言偏移值、tier 顺序、方向点积、渲染文本像素层用截图对比源视觉真值 vs 实现截图明确的 P0 分级浏览器渲染证据缺失即 P0因为它直接阻塞屏幕空间线可见性、碰撞与门宽放置的最终验收显式/自动基线语义分离dimension-string几何中未提供基线坐标意味着渲染器按offsetDistance自动推导基线任何显式钉死基线的行为都会覆盖偏移——这是本次缺陷的根因也是后续新增标注类型时最容易踩的坑方向敏感的房间侧标注外围墙的门宽标注必须落在房间内侧由enclosedRoomSideNormalinteriorDimensionClearances共同判定并以点积断言保护。对希望深入源码的读者推荐按以下顺序追踪数据流绘制标准配置construction-dimension-standards.ts→ 墙标注规划与渲染construction-dimensions.ts→ 几何统一装配dimension-string.ts→ SVG 渲染器回归floorplan-dimension-renderer.test.tsx。结合本文与 design-qa.md 对照阅读即可完整复现发现问题 → 定位根因 → 修复 → 几何回归 → 像素验收的完整闭环。【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考