bom-viewer
v0.1.0
Published
Mechanical digital-twin exploded-view Viewer as a framework-agnostic Web Component (<explode-viewer>) + SDK — Three.js.
Readme
ExplodeViewer
机械装配体数字孪生·爆炸图 Viewer SDK(仅前端)。
- 包名:
bom-viewer - 技术栈:纯原生 JavaScript(无框架)+ Three.js (WebGL2) + Web Components;Vite 仅用于打包/本地 demo,Vitest 用于测试
- 交付形态是浏览器原生自定义元素
<explode-viewer>;不依赖 React/Vue,也不用 TypeScript 写运行时逻辑 types/index.d.ts为手写类型声明(仅供编辑器补全);运行时是 JS
- 交付形态是浏览器原生自定义元素
- 设计依据:
- 《机械数字孪生爆炸图技术实现方案 v2.2》
- 《Mechanical Viewer SDK 接口规范 v0.3》
这是什么
输入一个零件编号路径(occurrencePath),定位其 occurrence,进入直接父总成并执行单层爆炸,
高亮目标零件。固定产品契约为 Single-Level Explosion + Isolated Drill-down:
explode(parent) = explode(parent.directChildren)查看子总成内部时不递归展开,而是显式 enterScope(child) 后再对该子总成执行一次单层 explode。
作为组件调用(Web Component)
对前端交付一个框架无关的自定义元素 <explode-viewer>。构建产两份产物(npm run build:lib):
| 产物 | three | 适用 |
|---|---|---|
| dist/explode-viewer.mjs | 外部化(peer) | 有构建工具的宿主(Vue/React/Vite/webpack),npm i three three-mesh-bvh 共用一份 three |
| dist/explode-viewer.standalone.mjs | 内联 | <script type="module"> / 无构建页面,开箱即用 |
两者都注册同一个 <explode-viewer> 元素。
<!-- 无构建:引 standalone,three 已内联 -->
<script type="module" src="https://cdn.example/explode-viewer.standalone.mjs"></script>
<explode-viewer
api-base="https://viewer.host/api"
asset-base="https://viewer.host/assets"
occurrence-path="SY215CPEBK_0/…/SY215CCM4KS_1_1_1_1_1-1">
</explode-viewer>// 有构建:import ESM 产物即自动注册;宿主需自备 three
import 'bom-viewer';
const v = document.querySelector('explode-viewer');
const r = await v.showPart('SY215CPEBK_0/…/SY215CCM4KS_1_1_1_1_1-1');
if (!r.ok) toast(r.error.code, r.error.message); // 边界返回结构化结果,不抛异常
v.addEventListener('viewer-error', (e) => log(e.detail)); // 异步失败也能收到属性:api-base(REST 后端)、asset-base(可选,缺省=api-base)、occurrence-path(改变即定位)。
方法:showPart(path)、explode(ratio?)、collapseAll()、focus(id?)、enterScope(id)、exitScope()。
事件:viewer-ready、viewer-select、viewer-error(detail = {code, message, …})。
输入边界错误契约
showPart(occurrencePath) 对可预期边界返回 {ok:false, error}(不抛);成功返回 {ok:true, targetNodeIds:[…]}。
同一父总成下同名零件的多个实例会全部命中并高亮(targetNodeIds 为数组);仅当匹配跨不同父总成时才判为多义。
| code | 含义 |
|---|---|
| INVALID_INPUT | 空/格式非法的路径 |
| NOT_FOUND(which:'device'/'part') | 整机根 / 叶子零件不存在(id 回显) |
| PATH_MISMATCH | 零件存在但中间总成链/位置对不上(回显 expected/candidates) |
| AMBIGUOUS | 匹配跨多个父总成(返回 candidates 供二选一) |
| LOAD_FAILED / TIMEOUT | 资产/网络失败(并派发 viewer-error 事件) |
数据由 ExplodeViewerBackend 提供;examples/component.html 是最小示例(?api=<backend>/api)。
工程边界
本工程只做前端 Viewer SDK:
- ✅
ViewerEngine引擎契约(Cloud / Local 后端可互换的统一接口) - ✅ 引擎无关领域核心(PartTree / ExplosionSolver-runtime / Scope-Camera,禁止
import three) - ✅ Three.js 本地渲染后端(
LocalViewerEngine)
不在本工程(由现有 SanyAssembly Python 仓库/后端提供,本工程通过 REST 契约消费):
- ❌ 离线资产管线(OCCT/Python:STEP→结构树/三角化/去重/材质/ExplosionMeta 预计算/打包)
- ❌ 后端 REST/API、鉴权、CDN、IoT 网关
- ❌ Cloud 渲染后端(WebRTC + UE/Unity)——Phase 1 仅接口桩
Phase 1 验收重点
occurrence/PartTree → 按 Scope 几何加载 → 单层爆炸 → Drill-down → 相机 Fit → Picking 的最小闭环, 并保证上层 React 代码对 Cloud/Local 后端互换零改动。
Phase 1 明确不做:IoT、Snapshot、剖切、测量、多选、完整 Streaming、水印溯源、Session Broker。
v2.3 离线增强契约
ExplosionRuntime可消费 collision solver 的waypoints,按折线路径插值,避免两段式拆出路径被直线截弯穿模;旧的direction × distance数据不变。- 本地引擎从 manifest 的
geometries[].lods读取 level 0 URI,仍兼容旧geometry/<id>.glb。 - 节点含离线
bestView.assembled/exploded时,focus(node)直接采用对应爆炸状态的相机;缺省时继续 bbox fit。 - 多级运行时屏幕误差自动选级仍是独立后续工作;当前版本只确保多级资产契约和 LOD0 加载闭环。
选中与聚焦视觉
- 有选中对象时,零件或总成的整个子树始终使用青蓝色呼吸发光,不显示包围框。Scope 处于爆炸态时,其他几何保持原始不透明材质;处于合拢态时,其他几何降为透明上下文。
- 没有选中对象、点击视口空白处或切换 Scope 时,选中特效完全清除并恢复所有原始材质。
- 树节点、三维拾取、搜索查看都会聚焦;“爆炸”会在动画完成后再聚焦,避免相机追丢目标。
- 聚焦保留离线
bestView的少遮挡观察方向,但按当前爆炸态的 Scope 真实包围盒取景,同时呈现当前模型全貌和选中对象所在位置。
规划(OpenSpec)
实现计划以 OpenSpec change 按模块组织,见 openspec/changes/。执行 openspec list 查看。
Wave 1(基础闭环,依赖顺序):
bootstrap-explode-viewer— 工程脚手架 +viewer-engine-contract+viewer-data-contractsexplode-viewer-domain-core—part-tree-store+explosion-runtime+scope-visibility-cameraexplode-viewer-local-engine—local-viewer-engine(Three.js 后端)
Wave 2+(后续):cloud-viewer-engine、viewer-interaction-ui、IoT / Streaming / 水印 / Session Broker。
