专栏首页.NET技术与企业级解决方案C#开发BIMFACE系列39 网页集成开发3:审图系统中三维模型比对

C#开发BIMFACE系列39 网页集成开发3:审图系统中三维模型比对

  在建筑施工图审查系统中,设计单位提交设计完成的模型/图纸,审查专家审查模型/图纸。审查过程中如果发现不符合规范的地方,则流程退回到设计单位,设计单位人员根据审查意见重新调整设计,调整完成后再次提交到审查专家。此时为了便于专家审查,需要知道当前轮次的模型/图纸与上一轮次的模型/图纸发生了哪些异动,针对异动情况进行审查即可。

先看个效果

效果如上图。左侧是当前审查轮次的模型,中间是上一轮次的模型,右侧是2个模型的对比产生的异动列表。

(1)点击“新增构建”中的构件,自动定位到当前轮次中新增的目标构件。异动构件以浅绿色表示。2个模型视角同步移动。

(2)点击“删除构建”中的构件,自动定位到上一轮次中的目标构件,本轮次中的构件被删除,所以不显示。异动构件以浅绿色表示。2个模型视角同步移动。

(3)点击“修改构建”中的构件,自动定位到当前轮次中修改的构件以及上一轮次对应的构件。异动构件以浅绿色表示。2个模型视角同步移动。

BIMFACE之前是没有三维模型联动对比的功能,在我和BIMFACE的技术支持团队的美丽小姐姐沟通后,他们把我的要求纳入了他们产品的需求,经过工程师们加班加点的辛苦付出,很快就实现了该功能。特此感谢BIMFACE团队的所有小伙伴,感谢你们对开发者的信任与接受,感谢你们的辛苦付出。 滴水之恩,当涌泉相报,奉献上个人的BIMFace C#版SDK开源项目。 开源地址:https://gitee.com/NAlps/BIMFace.SDK 作者:张传宁 QQ:905442693 微信:savionzhang 欢迎下载使用,交流、分享。

下面介绍BIMFACE模型对比功能的原理与实现。

  模型对比可以对两个文件/模型进行差异性分析,确定两个文件/模型之间构件的几何和属性差异,包括增加的构件、删除的构件和修改的构件。 模型对应可以用于进行文件/模型的版本对比。

特别说明:模型对比是在BIMFACE云端进行的,通常需要5~10分钟。当模型对比完成后,BIMFACE能通知对比结果。

前置条件

  • 您需要将修改前和修改后的模型上传到云端并转换成功以后才能发起模型对比;
  • 目前仅支持.rvt单文件的模型对比。

基本步骤

  1. 通过服务端API发起模型对比(对比前后模型文件的fileId);
  2. 等待云端对比任务执行;
  3. 对比完成后,在网页端通过调用JavaScript API实现差异模型的显示;
  4. 除了显示差异模型,还需要调用服务端API获取对比结果(包括新增、删除、修改的构件列表)。

对比流程

  模型文件经过云端转换后,生成了BIMFACE定义的数据包。因此,要对比两个模型文件,实际上需要对比两个文件的数据包。如下图所示,文件B是文件A修改后的版本,对比完成之后,其结果包括两个部分:

  • 几何差异;
  • 变更构件及属性。

BIMFACE提供了服务端API,用于发起对比,获取对比状态、获取对比结果。

调用服务器端的API获取对比结果后,前端需要使用JS来实现同步联动效果,以及点击异动构件后自动定位到构件所在的视角。

 1 // 同步新旧模型的平移和旋转操作
 2 function correspond() {
 3     latestViewer = latest.getViewer();
 4     var state, focus;
 5 
 6     prevViewer = prev.getViewer();
 7     view1Bind = function (data) {
 8         //用新模型的状态更新旧模型状态
 9         var latestState = latestViewer.getCurrentState();
10         prev.setState(latestState);
11         prev.getViewer().camera.up.copy(latestViewer.getViewer().camera.up);
12     }
13 
14     view2Bind = function (data) {
15         //用旧模型的状态更新新模型状态
16         var prevState = prev.getCurrentState();
17         latestViewer.setState(prevState);
18         latestViewer.getViewer().camera.up.copy(prev.getViewer().camera.up);
19     }
20 
21     //考虑到死循环的影响,不能同时监听render事件,因此以鼠标所在位置模型的监听为主
22     document.querySelector('#container').addEventListener('mousemove',
23         function (e) {
24             if (focus == undefined) {
25                 var width = document.querySelector('.latest').offsetWidth;
26                 if (e.clientX > width) {
27                     focus = 1;
28                     latestViewer.removeEventListener('Rendered', view1Bind);
29                     prev.addEventListener('Rendered', view2Bind);
30                 } else {
31                     focus = 0;
32                     prev.removeEventListener('Rendered', view2Bind);
33                     latestViewer.addEventListener('Rendered', view1Bind);
34                 }
35             }
36         });
37 
38     view1.addEventListener('mouseover',
39         function (e) {
40             if (focus == 0) {
41                 return;
42             }
43             focus = 0;
44             // 解绑与重新绑定事件,同步新旧模型的Rendered
45             prev.removeEventListener('Rendered', view2Bind);
46             latestViewer.addEventListener('Rendered', view1Bind);
47         });
48 
49     view2.addEventListener('mouseover',
50         function () {
51             if (focus == 1) {
52                 return;
53             }
54             focus = 1;
55             // 解绑与重新绑定事件,同步新旧模型的Rendered
56             latestViewer.removeEventListener('Rendered', view1Bind);
57             prev.addEventListener('Rendered', view2Bind);
58         });
59 
60     // 同步新旧模型的Hover事件
61     prev.addEventListener('ComponentsHoverChanged',
62         function (e) {
63             latestViewer.getViewer().modelManager.sceneState.setHoverId(e.objectId);
64         });
65 
66     latestViewer.addEventListener('ComponentsHoverChanged',
67         function (e) {
68             prev.getViewer().modelManager.sceneState.setHoverId(e.objectId);
69         });
70 
71     var ViewerEvent = Glodon.Bimface.Viewer.Viewer3D;
72     latestViewer.setCameraAnimation(false);
73     prev.setCameraAnimation(false);
74 }
 1 //创建异动列表与构件的click事件
 2 function createDom(result) {
 3         // 设置构件差异构件树的UI
 4         var newItems = result.newItems,
 5             deleteItems = result.deleteItems,
 6             changeItems = result.changeItems;
 7         var typeBoxs = document.querySelectorAll('.type-box');
 8         typeBoxs[0].innerHTML =
 9             `<div class="title"><i class="icon arrow"></i><i class="icon-type new"></i>新增构件(${newItems.length})</div>
10                                       <ul id="addElement" class="type-ul">${createDomNode(newItems)}</ul>`;
11 
12         // 删除构件列表
13         typeBoxs[1].innerHTML =
14             `<div class="title"><i class="icon arrow"></i><i class="icon-type remove"></i>删除构件(${deleteItems.length})</div>
15                                       <ul id="removeElement" class="type-ul">${createDomNode(deleteItems)}</ul>`;
16 
17         // 修改构件列表
18         typeBoxs[2].innerHTML =
19             `<div class="title"><i class="icon arrow"></i><i class="icon-type revise"></i>修改构件(${changeItems.length})</div>
20                                       <ul id="reviseElement" class="type-ul">${createDomNode(changeItems)}</ul>`;
21 
22         // 差异构件树列表
23         document.querySelector('.compare-content').addEventListener('click',
24             function (e) {
25                 var element = e.target;
26                 if (element.tagName == 'I' && element.hasClass('arrow')) {
27                     if (element.hasClass('close')) {
28                         element.removeClass('close');
29                         element.parentElement.nextElementSibling.removeClass('close');
30                     } else {
31                         element.addClass('close');
32                         element.parentElement.nextElementSibling.addClass('close');
33                     }
34                 } else if (element.tagName == 'SPAN' && element.getAttribute('type')) {
35                     var type = element.getAttribute('type'),
36                         id = element.parentElement.getAttribute('data-oid');
37                     if (type == 'NEW') {
38                         latestViewer.setSelectedComponentsById([id]);// 高亮选中构件
39                         latestViewer.zoomToSelectedComponents();  // 定位
40                         view1Bind();
41                     } else if (type == 'DELETE') {
42                         prev.setSelectedComponentsById([id]);// 高亮选中构件
43                         prev.zoomToSelectedComponents(); // 定位
44                         view2Bind();
45                     } else {
46                         latestViewer.setSelectedComponentsById([id]); // 高亮选中构件
47                         latestViewer.zoomToSelectedComponents();// 定位;
48                         view1Bind();
49                         prev.setSelectedComponentsById([id]);
50                     }
51                 }
52             });
53     }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C#开发BIMFACE系列31 服务端API之模型对比2:获取模型对比状态

      在上一篇《C#开发BIMFACE系列30 服务端API之模型对比1:发起模型对比》中发起了2个模型对比,由于模型对比是在BIMFACE云端进行的,通常需要5...

    张传宁老师
  • xBIM 实战01 在浏览器中加载IFC模型文件

      由于WexXplorer 加载的是 .wexBIM格式的文件或者文件流,所以需要在Web.config文件中添加如下配置

    张传宁老师
  • C#开发BIMFACE系列2 二次开发流程

      BIMFACE 平台是一个对外开放的平台,建筑行业的相关公司、软件公司或者有 BIM 业务需求的公司都可以注册成为开发者并使用其提供的强大功能。

    张传宁老师
  • 中台之上(十四):尝试构建轻量级架构设计工具

    上一篇介绍了通过构件模型支持组装式设计的思路,本节再讲讲将其应用于构建轻量级架构设计工具的思路。

    用户6900693
  • 身处枪林弹雨的我,简直被自己帅哭了

    VRPinea
  • 首席信息官们的数字化转型,到底是什么阻碍了?

    程序你好
  • 从商用到开源:DB2迁移至MySQL的最佳实践

    身处数据驱动快速变革的时代,数据库系统的选型和架构设计对于整个IT基础架构,甚至企业的发展都起到至关重要的作用。那么今天,如果您的企业需要搭建一套新的应用系统,...

    数据和云
  • Linux Redis自动化挖矿感染蠕虫分析及安全建议

    云鼎实验室
  • 年度必看AI论文:生成式非对抗网络(停止对抗,用爱学习)

    震惊!(本次使用已获UC震惊部授权) 就在昨天,人工智能领域一个开创性的成果出现了:关于生成对抗网络(GAN)的最新论文出炉。很多学者和业内人士,都用震惊二字描...

    量子位
  • “中国互联网大会”小程序上线啦!| 案例上新

    ? 今天,中国互联网协会联合腾讯推出的“中国互联网大会”小程序-云上中国互联网大会平台正式上线,用户在微信搜索“中国互联网大会”即可进入小程序查询相关内容。 ...

    腾讯文旅

扫码关注云+社区

领取腾讯云代金券