尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Pascal 3D 编辑器垂直模型(Vertical Model)架构详解:层级堆叠、墙体顶面夹持与板面支撑规则

Pascal 3D 编辑器垂直模型(Vertical Model)架构详解:层级堆叠、墙体顶面夹持与板面支撑规则 Pascal 3D 编辑器垂直模型Vertical Model架构详解层级堆叠、墙体顶面夹持与板面支撑规则【免费下载链接】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本文以 wiki/architecture/vertical-model.md 为骨架深入 Pascal 3D 编辑器的垂直几何模型层级高度如何存储与堆叠、普通墙体顶面如何被夹持到层高平面、板面如何通过厚度向下生长、支撑宿主support host如何被持久化以及一套夹持而非询问clamp, never ask的规则如何让整栋建筑在编辑时始终保持拓扑一致。读完本文你将掌握这套模型的全部存储字段、解析辅助函数、加载迁移逻辑与落地陷阱并能对照源码逐行验证。该模型适用于一切读写垂直几何的构件层级level、墙体wall、板面slab、天花板ceiling、楼梯stair、围栏fence、落地放置的家具物品floor-placed items。其核心源码集中在packages/core/src/services/storey.ts —— 层级堆叠、墙顶平面、天花板夹持边界packages/core/src/systems/wall/wall-top.ts —— 墙体顶面解析与最小墙高packages/core/src/systems/slab/slab-support.ts —— 板面支撑选举与墙体基座夹持packages/core/src/systems/stair/stair-rise.ts —— 楼梯总升高解析与直线段收敛packages/core/src/utils/vertical-scene-migration.ts —— 加载时的垂直模型迁移一句话不变量普通受平面约束plane-bound的墙体顶面被钉在层级平面上。绘制在地形或其它抬升支撑上的墙体则通过保留幽灵体高度ghost body height的方式维持其高度先物化该高度再从选举出的基座向上平移整面墙。可选的地形填充只向下延伸墙体底部永远不改变墙体作者设定的高度与顶面。这套不变量的核心是墙体顶面由所在层高平面决定而不是由墙体自己的高度字段决定——只要墙没有被显式写入height它的顶面就永远等于层高平面或被上层板面底面截短因此无论板面如何抬升墙体基座墙只会变矮、不会变高层顶永远不会出现缝隙。存储事实Stored truth每个字段的语义垂直模型的全部状态都是显式存储在场景节点字段中的。下表是这套模型的数据契约理解它就能理解后续所有解析逻辑字段含义缺省Absent时意味着level.height层高米层间楼板到楼板floor-to-floor。层的世界 Y 由getLevelElevations按level序号累加解析未迁移的遗留数据加载后不会出现迁移会写入消费方回退到DEFAULT_LEVEL_HEIGHT2.5level.baseElevation相对计算堆叠位置的附加偏移。它同时平移本层与同一建筑中所有更高层允许负值0schema 默认值wall.height显式墙身高度矮墙、女儿墙、或需要保持幽灵高度的抬升支撑草绘。落地承载的墙总是解析为顶 选举基座 height含基准面以下其它遗留下沉支撑保持其绝对顶面约束受平面约束普通基准面放置的默认形态顶面跟随getWallPlaneTop—— 即min(层高, 跨段上最低覆盖板面底面)ceiling.height显式自定义高度写入时被夹持到边界内跟随层高实时解析为getCeilingClampBoundmin(层高, 覆盖底面) − 0.01slab.elevation行走面顶面层局部坐标默认 0.05slab.thickness向下生长实体占据[elevation − thickness, elevation]默认 0.05slab.recessed下沉意图开放壳体底面为elevation边缘为recessedRimElevation。被排除在覆盖查询与墙面贴附之外实心板slab.recessedRimElevation抬升/下沉凹池的可选边缘锚点。相对预设保持此锚点不变而改变深度层平面0保持遗留水池slab.fillToTerrain在实心板固定底面之下添加跟随地形的周界基础。行走面与作者设定的结构厚度保持水平无地形基础supportSlabId墙体与所有落地放置种类的持久化支撑宿主。仅当重叠支撑在标高上不一致时在提交时写入ground哨兵值把平台下的裸地钉住。结构块总是钉住其放置时的宿主防止上方生成的房间板面反过来抬升平台支撑按查询即时选举墙用覆盖选举物品用足迹最大值wall.supportOffset可选层局部增量相对选举出的支撑。地形墙链用它让每一段保持在首点的构造平面上只存一个数字不存地形采样零偏移墙直接位于其选举的板面或雕刻地形源上fence.supportOffset可选层局部增量相对围栏的板面宿主或层平面。平移整条围栏而保持高度零偏移围栏直接位于宿主或层平面上wall.fillToTerrain从作者设定的基座向下延伸到地形左右面独立采样。墙身高度与顶面保持不变固定基座无地形填充stair.deckSlabId目标平台板升高实时跟随deck.elevation − 楼梯自身选举基座附着期间禁用开洞同步目标是一个层stair.totalRise显式自定义总升高优先于一切跟随由平台板或所在层推导syncStairRises将直梯段收敛到解析出的升高一个值得注意的细节supportSlabId只在重叠支撑标高不一致时才持久化写入——如果所有候选支撑高度一致就无需记录查询时会自然得到相同结果。而ground哨兵值的语义是由层基座地面承载在 packages/core/src/hooks/spatial-grid/support-host-id.ts 中定义。两条保护语义的 schema 规则含义承载字段不加 Zod 默认值。level.height、wall.height、ceiling.height、stair.totalRise均为.optional()且没有.default()——缺失本身就是数据。创建处显式写入值migrateNodes的输出是类型断言cast而非解析parse所以即便定义了默认值也不会在遗留数据加载时物化。store 会删除显式undefined的键。updateNode(id, { height: undefined })会移除该键见node-actions.ts中的mergeNodeUpdate。遗留受平面约束的墙可能仍然省略height墙体面板在启用地形填充前会解析并物化其当前墙身高。天花板与楼梯的跟随模式继续由字段存在性推导——没有持久化的模式枚举。这条规则的直接好处跟随模式follows mode不需要一个mode: follow字段只要height键不存在天花板/楼梯就处于跟随层高的活语义一旦写入height就变为显式覆盖。迁移与编辑器逻辑共享同一套判据。解析辅助函数Resolution helpers永远不要手写?? 2.5仓库提供了成套的解析函数所有消费方都应调用它们而不是各自回退到魔法常量。下表是权威清单辅助函数所在文件解析内容getStoredLevelHeight、getLevelElevations、getLevelAbove/Belowservices/storey.ts层高、感知偏移的逐建筑堆叠、相邻层getWallPlaneTopservices/storey.ts受平面约束墙的顶面层高被覆盖板面底面夹持跨段采样且边界包含的带重叠resolveWallTop、resolveWallEffectiveHeight、MIN_WALL_HEIGHTsystems/wall/wall-top.ts给定平面 选举基座后的墙顶 / 有效高度getWallBaseElevationForNodes、getWallEffectiveHeightForNodesspatial-grid manager带地形/支撑偏移的选举基座与墙身高供 UI 叠加层使用getCeilingClampBound、getCoveringSlabUndersideAtservices/storey.ts天花板夹持边界跨层覆盖查询上一层、非下沉板resolveCeilingHeightservices/level-height.ts天花板有效高度显式或跟随resolveStairTotalRise、syncStairRisessystems/stair/stair-rise.ts楼梯升高优先级 直梯段收敛computeWallSlabSupport、getSlabSupportForItem、getSupportCandidatesForFootprintsystems/slab/slab-support.ts spatial-grid manager支撑选举渲染多边形、宿主优先、可选maxElevation上限resolveSlabPlacementElevationsystems/slab/slab-placement.ts把实心板的作者顶/厚区间平移到捕获的基底平面上下沉板保持层相对getSlabBaseElevation、applySlabBaseElevationChange、applySlabThicknessChangenodes/slab/elevation-limit.ts分离整体底面放置与固定基座的厚度编辑resolveFenceLiftElevationnodes/fence/lift.ts围栏板面宿主标高 可选手动支撑偏移clampSlabElevationForWallsslab-support nodes/slab/elevation-limit.ts受平面约束墙体下的板顶夹持源码级关键点一层级堆叠如何解析getLevelElevations是整套模型的基石。它的实现storey.ts 第 65-102 行要点逐建筑堆叠先解析每个层所属建筑显式parentId优先其次才是建筑children数组中的成员关系按序号升序排序用累积变量cumulativeYByBuilding逐层累加baseY 上一层的 (baseY height) 本层的 baseElevation。因此baseElevation偏移会顺带抬升同建筑内所有更高层身份键 WeakMap 记忆化nodes是不可变 store 切片引用相同即场景未变。这是针对热调用路径的优化——注释明确记录了一个真实案例WallCutout每帧每墙调用一次曾导致每帧重建 1000 次的相同 Map。源码级关键点二墙体顶面三态解析wall-top.ts 的resolveWallTop是整个垂直模型反转的核心三种情况export function resolveWallTop(wall, storeyHeight, electedBase): number { if (wall.height null) return storeyHeight // ① 受平面约束顶 层高平面 if (wall.supportSlabId ground) return electedBase wall.height // ② 地面承载顶 基座 高度含基准面以下 return electedBase 0 ? electedBase wall.height : wall.height // ③ 遗留抬升承载基座 0 才跟随否则保持绝对顶 }① 无height→ 顶面钉在层高平面板面抬升基座只会让墙变矮②ground哨兵地形承载→ 高度始终是墙身高度即使基准面以下也成立因此雕刻地形不会拉伸墙体③ 显式height但非地面承载遗留下沉板场景→ 只有基座抬升0时才跟随保持遗留的绝对顶面约束。MIN_WALL_HEIGHT 0.5米同时约束墙高箭头的最低拖拽边界与板面标高夹持见下文夹持规则。夹持规则Clamp, never ask垂直模型把一致性实现为写入时夹持而非写入后询问/修复。四条核心规则受平面约束墙体下的板面板面标高夹持到层高 − MIN_WALL_HEIGHT0.5。否则墙身挤出平面减基座会塌缩到最小值以下到平面时甚至为零。实现见 slab-support.ts 的clampSlabElevationForWalls它把候选板面标高代入选举集合后再跑选举因此只有在提出标高时才赢得选举的板面也会被夹持而被兄弟板面挤出选举的不会。getSlabElevationUpperBound更进一步先用把该板抬到所有兄弟之上的探测标高试探若任何受平面约束墙会选举它拖拽上限就是层高 − 0.5否则无上界。天花板夹持写入时以及通过空间检测响应式向下夹持到min(层顶, 覆盖板面底面) − 0.01。0.01是 storey.ts 中的CEILING_CLAMP_MARGIN常量目的是让天花板表面永不与其上方实体重合。受平面约束墙顶夹持到覆盖板面底面——厚板或与层齐平的上层板会缩短其下的墙体Revit 的 attach-to-floor-bottom 自动行为显式高度的墙豁免。实现上getWallPlaneTop用墙的厚度带中心线 双面线、感知弧线通过wallOverlapsSlabFootprint对每块覆盖板面的存储多边形扣除孔洞做裁剪——刻意不做点采样因为自动板面多边形由墙中心线推导周界墙正好落在多边形边界上射线投射的点在多边形判定会随边方向翻转一面墙被夹持而邻居没有。边界接触在任何一侧都算覆盖。实心板控制器的非重叠契约立方体拖拽平移占据区间而保持thicknesschevron 与面板厚度控件固定底面、通过同时写thickness与elevation移动行走面面板标高平移整体而保持厚度下沉凹池单独存储边缘锚点使地面/边缘/深度控件与预设始终相对同一锚点。配套规则还有结构标高跟踪器移动参考基座而非构件尺寸——板跟踪器移动底面并保持thickness墙跟踪器写supportOffset并物化当前墙身高围栏跟踪器写supportOffset并保持height。而下沉板保留独立的边缘/深度控件。getRenderableSlabPolygon中的墙面贴附wall-face adoption只适用于接地板elevation − thickness ≤ 0.01且非下沉——悬浮平台保留其手绘多边形并被跳过作为接缝候选。指针决定的放置Pointer-decided placement堆叠表面决策必须来自真实相机射线而非平面命中因为网格事件相交的平面骑在幽灵体的标高上。getPointedSupportSurface返回最近的合格表面加上交点而支撑选举上限maxElevation与光标 XZ 都来自这一次计算指向平台板下方→ 选举地面指向平台顶面→ 选举平台。朝上的块体几何是板、围栏、柱、楼梯、物品与注册表驱动地面对象的共享放置表面墙体草绘还可额外包含朝上的墙、可堆叠物品与柱几何。这些节点顶命中会为本次放置冻结一个标量构造平面throw 期有效它们不是持久化承载边不跟随后续宿主编辑。命中后的持久化方式分三类板存为elevation墙与围栏存为supportOffset落地放置位置节点存为其规范 Y 偏移。三者还会钉住块体下方的板或地面防止稍后生成的板面反馈抬升已放置对象。普通板/地面命中会持久化其选举的支撑源保持正常的阶梯式基座行为。2D 平面图放置没有相机射线保持最大选举max-election。板面草绘的平面冻结墙与板草绘共享水平构造平面解析器slab-placement.ts 的resolveSlabPlacementElevation板冻结第一个吸附顶点的平面后续顶点都保持在该平面提交时把作者的垂直区间平移到捕获的基座绝不把厚度或单个顶点铺盖drape到地形上。可选的地形跟随是与固定底面分离的独立周界基础绝不改变板区间下沉板保留显式边缘锚点被锁定的构造平面打上fixed-plane标签这样世界 Y0 的平面在后续指针移动时不会与地形查询平面混淆。自动房间表面的垂直派生自动房间表面从包围墙体派生垂直位置storey.ts 相关逻辑每面边界墙的基座通过渲染器所用的同一套选举解析——包括其下的地面因此画在裸地上的房间也能获得地形输入即使没有墙携带显式supportSlabId地面取最高的墙基座并保持其既定的 0.05 m 行走面偏移天花板取最低的墙顶并落在其下 0.01 m。两面都保持水平混合标高的围合不再退回到备用放置选择最高基座是因为较低墙本来就向下延伸入地面不会在任何墙下开启天光现有autoFromWalls表面会与后续墙体支撑偏移变化以及雕刻协调——层的结构签名对每面墙解析后的层基座做哈希因此在已完成的房间下移动地面会重新派生其地面与天花板每笔刷一次在该笔刷自己的撤销步骤内。手动表面永不重写自动板被排除在这套墙基座选举之外防止派生地面递归抬升自己的墙、再抬升自己。继承地形Inheriting terrain通用接缝levelBaseElevationAt(nodes, levelId, x, z)是当节点下方没有任何建造物时它停靠的表面是什么的唯一答案——雕刻地形处返回地形高度其它地方返回0。它的实现terrain-support.ts通过terrainSupportLift(...) ?? 0组合而成terrainSupportLift回答这里有没有地形可空调用方可分支levelBaseElevationAt回答这里的世界地面有多高总量调用方无需知道地形是否存在。关键历史背景地形曾经是按种类per-kind选择加入的因为每个场地都手写terrainSupportLift(…) ?? 0而每个忘记询问的消费方都默默假设平面y 0。通过本函数解析基座任何种类都能跟随地面无需注册任何东西。需要区分平地与与层基座齐平的建造表面的调用方仍需terrainSupportLift的 null——两者在此都读0只有前者会铺盖山坡。三种让种类 Y 触达地面的方式按推荐优先级capabilities.floorPlaced—— 解析器getFloorPlacedElevation见 floor-placed-elevation.ts在每个足迹上于重叠板与层基座之间选举FloorElevationSystem每帧把结果写入注册网格。适用于一切站在表面上的东西。实现细节层级基座在节点自身 XZ采样一次不是每足迹、也不是足迹平均——这保证一排柱子各自在坡上正确而复合节点柜体组合、L 形桌保持刚性不剪切。ground哨兵在这里直接返回groundLift()。纯def.geometry构建器中的ctx.levelBaseAt(x, z)—— 适用于把垂直原点烘进网格的种类如围栏的内部抬升组。调用它同时登记该种类参与地形失效noteLevelBaseConsumer在首次构建时记录类型markTerrainSupportDependents在每次地形变化时弄脏这些节点以便原点重建。询问即注册——刻意如此声明式标志只会成为又一种每种类选择加入而过度近似为每个有构建器的种类会在每次笔刷涂抹时重建整个底层地板。def.floorplan缺此能力平面视图不绘制标高共享构建器必须将其视为可选而不能假设 2D 中地面是平的。直接调用levelBaseElevationAt—— 适用于渲染路径之外、已持有节点记录的解析器支撑选举、吸附引导、手柄放置。采样必须与渲染器相同的 XZ否则叠加层与网格会不一致。集体渲染器一个组件绘制多个节点如实例化网格三者皆不适用FloorElevationSystem写入节点注册的对象而这些种类的注册对象是选择代理而非实例。此类渲染器必须自行通过getFloorStackedPosition解析每个实例的 Y。加载迁移Load migration常驻加载路径垂直模型迁移在migrateVerticalSceneNodesvertical-scene-migration.ts中实现无限期驻留。原因是社区自动保存只在首次加载后编辑时才持久化迁移必须留在加载路径上。它纯净且服务器安全——编辑器加载器与托管场景权威都会调用它使协作比较或持久化操作前双方规范出相同字段。五步迁移写入每个遗留层的精确派生高度默认遗留楼层存 2.55 0.05 板 2.5 墙绝不吸附到预设。派生逻辑见 level-height.ts 的deriveLegacyLevelHeight取子构件中天花板显式高度与墙解析顶的最大值墙顶通过computeWallSlabSupport(...).elevation选举基座后由resolveWallTop解析。按建筑压缩level序号以零为锚非负 → 0,1,2…负 → −1,−2…——地下室保持地下室。每次加载都运行幂等。对墙顶对照派生平面分类|plane − top| 0.20严格小于→ 受平面约束移除height键否则显式缺失高度的矮墙物化 2.5。ε 由生产普查校准存在刻意的 0.20 短墙不能吸附。**天花板在 ε 内贴近边界以及所有autoFromWalls天花板**丢弃height→ 跟随模式楼梯丢弃遗留的盲目totalRise: 2.5。两者都受场景是遗留的某层缺height门控。板面thickness : elevation字节级相同的占据区间含退化零负标高水池变为recessed: true且elevation不变。此外迁移还修复一个特殊场景地形雕刻时代在平面场景 3D 草绘的墙被印上supportSlabId: ground该钉会短路板选举使墙脚埋在板内 z-fighting。迁移仅在埋入状态修复此钉板区间严格跨越钉住的基座容差 1e-3有雕刻地形的场景整体跳过——那里地面宿主是承重的且实时地形场对此纯通道不可见。陷阱清单Gotchas序号是语义的。level 0渲染为Basement Nlevel 0是地面层查询。永远不要脱离零锚点重新编号。边界几何。自动板多边形由墙中心线派生墙/顶夹持采样恰好落在多边形边缘上——始终使用边界包含的带重叠辅助函数wallOverlapsSlabFootprint、slabCoversPoint这些路径上绝不要用原始射线投射的点在多边形判定其答案会随边方向翻转。直梯从存储的段高构建而非解析出的升高——任何升高变化都必须走syncStairRises由StairOpeningSystem应用历史暂停在 store 更新后一个微任务执行等空间网格安定。曲线/螺旋楼梯直接读取解析升高无需同步。响应性是显式的。level.height变化弄脏该层的墙/楼梯/天花板/围栏板变化弄脏同层重叠支撑、下层墙/天花板与附着平台的楼梯。每次实时地形笔刷弄脏地面承载结构、fillToTerrain墙/板、每个在平地的floorPlaced节点以及每个构建器询问过ctx.levelBaseAt的种类——通过 spatial-grid-sync.ts 中的瞬态useLiveTerrain订阅场景图与撤销历史仅在笔刷提交时写一次。自动房间重派生按笔刷而非按笔刷内涂抹per stroke, not per dab。实时涂抹只发布到useLiveTerrain从不触碰场景 store因此重派生房间地面/天花板的结构签名差异在松手时只运行一次。拖拽中地面承载墙跟随笔刷而地面等待松手——刻意设计按涂抹重派生意味着每笔都写场景、地面在光标下抖动。宿主生命周期。删除板会在同一撤销提交中从幸存者剥离supportSlabId/deckSlabId仅被重塑掉的宿主静默回退板返回后自动恢复。克隆路径不同。clone-scene-graph.ts 重映射supportSlabId/deckSlabId编辑器剪贴板scene-clipboard.ts故意不重映射它重新选举房间放置重映射二者。新增克隆/实例化路径时两个字段都必须重映射。源码对照速查验证本文论断的最短路径层级堆叠与墙顶平面读 packages/core/src/services/storey.ts 的getLevelElevations、getWallPlaneTop、getCeilingClampBound墙顶三态与最小墙高读 packages/core/src/systems/wall/wall-top.ts 的resolveWallTop、MIN_WALL_HEIGHT支撑选举与夹持读 packages/core/src/systems/slab/slab-support.ts 的computeWallSlabSupport、clampSlabElevationForWalls以及 packages/nodes/src/slab/elevation-limit.ts 的底板/厚度/顶面/预设控制器楼梯升高读 packages/core/src/systems/stair/stair-rise.ts 的resolveStairTotalRise、syncStairRises地形接缝读 packages/core/src/lib/terrain-support.ts 的levelBaseElevationAt、noteLevelBaseConsumer以及 packages/nodes/src/fence/lift.ts 的resolveFenceLiftElevation加载迁移读 packages/core/src/utils/vertical-scene-migration.ts 的migrateVerticalSceneNodes。延迟决策Deferred by decision持续的房间身份Persistent Room identity、部分楼层导航、板参考面枚举、悬挂天花板、以及坡地场地基准site datum for sloped terrain在plans/中都有命名的门控gates均不阻塞本模型。平台板deck以目录房间/预设形式交付一步式夹层/阳台工具已被移除代码保留在 editor 的e30042db提交。换句话说垂直模型是一套已稳定落地、被所有垂直几何构件共享的底层契约未来扩展只需遵循同一套字段、辅助函数与夹持规则。【免费下载链接】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),仅供参考
返回列表