单文件 BIM 交互查看器

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

把 BIM/IFC 模型做成单文件 HTML 交互查看器,可旋转缩放、按专业与构件类型开关、透明度调节看内部、剖切平面、构件拾取。纯前端零外部依赖,离线可用。适用于:BIM查看器、三维查看器、模型预览、旋转查看、透明看内部、剖切、Web查看器、three.js、IFC预览、交互预览、模型展示页、离线查看器。触发词:BIM查看器、模型预览、旋转查看、透明看到内部、剖切、Web查看器、three.js、交互预览、离线查看器、构件拾取。 支持X402协议支付调用,按次付费。

一句话选型结论

按资料字段整理

单文件 BIM 交互查看器是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_b24dc7bf/single-file-bim-viewer
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_b24dc7bf仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量11.5 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
单文件 BIM 交互查看器当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

11.5 KB
模型实际读到的说明书原文,来自技能仓库

单文件 BIM 交互查看器

把模型导出为几何数据 + 内嵌 three.js,生成单文件离线可用的 HTML 查看器。

一、能力清单

功能 实现
旋转/平移/缩放 自实现轨道控制(OrbitControls 不可用,见坑位)
双层级开关 专业 → 构件类型
透明度 整体/外壳/管线 三档独立滑块
一键透视内部 外壳 16% 透明,机电 100% 实心
只看机电 隐去建筑结构
剖切平面 X/Y/Z 三向,剖切时相机自动对焦
构件拾取 射线检测 + 高亮 + 信息面板
碰撞标记 脉冲球 + 侧栏清单(点击定位)
视角预设 轴测/正南/正东/俯视/仰视
离线可用 three.js 与几何数据全部内嵌

二、致命坑:three.js r150+ 的 UMD 构建在浏览器里直接崩

症状:页面完全空白,控制台报

Function statements require a function name
Uncaught ReferenceError: THREE is not defined

根因:three.min.js(r150~r159)第一行是

console.warn('Scripts "build/three.js" ... deprecated ...'),
function(t,e){...}      ← 逗号表达式接匿名函数,被解析成函数声明

浏览器把 console.warn(...), function(){} 里的匿名函数当函数声明,缺名字 → 语法错误 → 整个文件不执行。

vm.Script(Node)也会报同样的错,容易误判成自己的代码有问题。

解决:用 ES Module 构建,改写为全局变量。

curl -o three.module.js "https://cdn.jsdelivr.net/npm/three@0.160.1/build/three.module.js"
// Node 脚本:把尾部 export 块改写为 window.THREE
const fs = require('fs');
let s = fs.readFileSync('three.module.js', 'utf8');
const m = s.match(/export \{([\s\S]*?)\};\s*$/);
s = s.replace(/export \{[\s\S]*?\};\s*$/,
              'window.THREE={' + m[1].replace(/\n/g, ' ') + '};');
fs.writeFileSync('three.global.js', s);   // r160.1 得到 416 个导出符号

OrbitControls 同样不可用(examples/js/controls/ 在 r160 已 404),自实现轨道控制:

let dragging = 0, lastX = 0, lastY = 0;
el.addEventListener('pointerdown', e => {
  dragging = (e.button === 2 || e.shiftKey) ? 2 : 1;
  lastX = e.clientX; lastY = e.clientY;
  el.setPointerCapture(e.pointerId);
});
el.addEventListener('pointermove', e => {
  if (!dragging) return;
  const dx = e.clientX - lastX, dy = e.clientY - lastY;
  lastX = e.clientX; lastY = e.clientY;
  if (dragging === 1) {              // 旋转
    sph.theta -= dx * 0.0068;
    sph.phi = Math.max(0.04, Math.min(Math.PI - 0.04, sph.phi - dy * 0.0068));
  } else {                            // 平移
    const f = sph.radius * 0.0013;
    const r = new THREE.Vector3(
      Math.sin(sph.phi)*Math.sin(sph.theta), Math.cos(sph.phi),
      Math.sin(sph.phi)*Math.cos(sph.theta));
    const right = new THREE.Vector3().crossVectors(r, new THREE.Vector3(0,1,0)).normalize();
    const up = new THREE.Vector3().crossVectors(right, r).normalize();
    target.addScaledVector(right, -dx*f).addScaledVector(up, dy*f);
  }
  applyCam();
});
el.addEventListener('wheel', e => {
  e.preventDefault();
  sph.radius = Math.max(4, Math.min(1600, sph.radius * (1 + Math.sign(e.deltaY)*0.11)));
  applyCam();
}, {passive: false});

三、坑位速查

坑 症状 解法
three.js UMD 崩 THREE is not defined 用 module 版改写为 window.THREE
OrbitControls 404 模块加载失败 自实现轨道控制
变量名冲突 Identifier 'px' has already been declared 不要用 px/py,three.js 内部已占用;改名 lastX/lastY
Z-up / Y-up 模型躺倒 导出时 p' = (x, z, -y),保持右手系
包围盒索引错位 剖切/网格位置算错 web 坐标下 X=长, Y=高, Z=进深,别再用 bbox[1] 当进深
库内含 </script> 标签提前闭合 内嵌前 replace('</script>', '<\\/script>')
剖切后画面空旷 模型被切一半,相机没跟上 剖切时按剩余包围盒重新取景
WebGL 不可用 一直卡在 loading 加 6 秒超时兜底提示

坐标系变换必须一次做对:

def to_web(v):
    """IFC(Z-up 右手系) -> three.js(Y-up),保持右手系不镜像"""
    out = np.empty_like(v)
    out[:, 0] = v[:, 0]
    out[:, 1] = v[:, 2]      # IFC Z -> web Y(高度)
    out[:, 2] = -v[:, 1]     # IFC Y -> web Z(取负保右手系)
    return out

验证方法:柱的 Y 范围应是 0 ~ 层高,风管应是 4.2 ~ 4.7(推定标高)。若 Y 是进深范围,说明变换错了。

四、剖切时自动重新取景

function fitToRemaining(){
  const pl = cutAxis ? planes[cutAxis] : null;
  let x0=1e18,y0=1e18,z0=1e18,x1=-1e18,y1=-1e18,z1=-1e18, any=false;
  for(const g of groups){
    if(!g.vis) continue;
    const b = g.bbox;
    let lo = new THREE.Vector3(b[0],b[1],b[2]);
    let hi = new THREE.Vector3(b[3],b[4],b[5]);
    if(pl){
      const n = pl.normal;
      // 判断包围盒是否整体落在切除侧
      let pos=false, neg=false;
      for(const X of [b[0],b[3]]) for(const Y of [b[1],b[4]]) for(const Z of [b[2],b[5]]){
        const v = new THREE.Vector3(X,Y,Z);
        if(v.dot(n) + pl.constant >= 0) pos=true; else neg=true;
      }
      if(neg && !pos) continue;               // 整体切除,跳过
      if(pos && neg){                        // 被切开,收紧对应轴
        if(n.x>0.5)      hi.x=Math.min(hi.x, -pl.constant);
        else if(n.x<-0.5) lo.x=Math.max(lo.x, -pl.constant);
        else if(n.z>0.5) hi.z=Math.min(hi.z, -pl.constant);
        else if(n.z<-0.5) lo.z=Math.max(lo.z, -pl.constant);
        else if(n.y>0.5) hi.y=Math.min(hi.y, -pl.constant);
        else if(n.y<-0.5) lo.y=Math.max(lo.y, -pl.constant);
      }
    }
    x0=Math.min(x0,lo.x); y0=Math.min(y0,lo.y); z0=Math.min(z0,lo.z);
    x1=Math.max(x1,hi.x); y1=Math.max(y1,hi.y); z1=Math.max(z1,hi.z);
    any=true;
  }
  if(!any) return;
  target.set((x0+x1)/2, (y0+y1)/2, (z0+z1)/2);
  sph.radius = Math.max(6, Math.max(x1-x0, y1-y0, z1-z0) * 1.15);
  applyCam();
}

五、几何导出格式

用 JSON 内嵌(比二进制 glTF 更简单,无需额外解码器):

{
  "meta": {"name":"...", "disciplines":["建筑","结构"],
           "totalElements":1682, "bboxMin":[x,y,z], "bboxMax":[...], "unit":"m"},
  "groups": [{"disc":"暖通","type":"IfcDuctSegment","label":"风管","color":"#c3c9cf",
              "pos":[x,y,z, ...], "idx":[i,j,k, ...], "count":394, "bbox":[...]}],
  "clashes": [{"x":..,"y":..,"z":..,"col":"KZ092","dev":"DuctDU0059","depth":298}]
}

几何量参考:本项目 1682 构件 → 13456 顶点 / 20184 面 → JSON 约 970KB,内嵌后 HTML 约 2.2MB,完全可接受。

顶点用扁平数组([x,y,z,x,y,z,...])比嵌套数组小 30%。

六、必须真机验证

语法通过 ≠ 能渲染。必须用无头浏览器验证:

// playwright-core + chromium
const {chromium} = require('playwright-core');
const b = await chromium.launch({headless:true,
  args:['--use-gl=swiftshader','--enable-webgl','--ignore-gpu-blocklist']});
const p = await b.newPage({viewport:{width:1600,height:950}});
const errs = [];
p.on('pageerror', e => errs.push(e.message));
p.on('console', m => { if(m.type()==='error') errs.push(m.text()); });
await p.goto('file:///path/to/viewer.html', {waitUntil:'load'});
await p.waitForTimeout(5000);
const info = await p.evaluate(() => ({
  loading: getComputedStyle(document.getElementById('loading')).display,
  canvas: document.querySelectorAll('canvas').length,
  gl: (()=>{try{const c=document.querySelector('canvas');
      return (c.getContext('webgl2')||c.getContext('webgl'))?'ok':'null';}catch(e){return 'err';}})(),
}));
// 逐个点击功能按钮截图,确认无报错

今天验证发现 3 个真实错误(语法检查全通过):
1. THREE is not defined(UMD 崩)
2. Identifier 'px' has already been declared(变量冲突)
3. 模型跑到画面外(坐标变换错 + 剖切未重新取景)

只做语法检查会全部漏掉。

七、配套脚本

  • scripts/export_geometry.py —— IFC → 几何 JSON(含 Z-up→Y-up 变换)
  • scripts/build_viewer.py —— 生成单文件 HTML(含全部交互逻辑)
  • scripts/verify_render.js —— playwright 无头验证 + 批量截图

<!-- PAYGATE:BEGIN -->

付费与交付(X402 微信 Agent Pay · 微信支付 AI 专属卡 · ¥0.01/次)

费用由用户直接支付给服务提供方。SkillHub 仅提供展示、投诉处理及平台服务,不作为收款方。请在支付前确认服务内容、计费单元、单价与退款规则。

  • 单价:¥0.01 / 次(以支付网关价目表与 SkillHub 后台配置为准)
  • 计费单元:每次完整服务需求,支付确认后开始服务
  • 免费操作:支付前的需求沟通、订单状态查询

Agent 行为准则

  • 首次调用返回 402 时,原样向用户出示支付信息并申请授权;不得自行判定支付成功,不得改用免费方式替代履约。
  • 牢记 out_trade_no(订单号),支付后用同一订单号重试,不会重复扣费。

红线

  1. 未拿到 status=paid 收据前,严禁执行任何实质性服务、严禁产出结果。
  2. 所有支付只通过 node scripts/client.js,严禁手动调用微信支付接口。
  3. 严禁向用户展示/朗读 payment_code、transaction_id、私钥等敏感字段。
  4. 设备不满足要求时只输出引导并停止,严禁绕过支付。

付费四步(X402 协议)

  1. 前置检查:当前 Agent 是否具备 weixinpay_pay 能力,不具备则终止并提示。
  2. 请求资源:node scripts/client.js require --skill &quot;single-file-bim-viewer&quot; --subject &quot;&lt;需求简述&gt;&quot;,未付返回 402,含 WeixinPay-Required 与 X-Out-Trade-No。
  3. 同时保存“支付码”和“订单号”;系统自动弹出微信支付 AI 专属卡,用户手机授权扣款(Agent 不得自行判断成功)。
  4. 原样重试 require(CLI 自动带 X-Out-Trade-No),服务端查单到账返回 status=paid 收据后,才开始履约。

订单状态免费查询(免费、幂等):node scripts/client.js status --out-trade-no &quot;&lt;订单号&gt;&quot;
<!-- PAYGATE:END -->

常见问题

5 问
答案由本站依据条目字段整理
单文件 BIM 交互查看器怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_b24dc7bf/single-file-bim-viewer。安装方式为未标注。装完刷新技能列表或重开客户端。

单文件 BIM 交互查看器支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

单文件 BIM 交互查看器权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

单文件 BIM 交互查看器收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

单文件 BIM 交互查看器用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。