基於HTML5 WebGL的工業化3D電子圍欄

来源:https://www.cnblogs.com/xhload3d/archive/2019/10/09/11639320.html
-Advertisement-
Play Games

現代工業化的推進在極大加速現代化進程的同時也帶來的相應的安全隱患,在傳統的可視化監控領域,一般都是基於 Web SCADA 的前端技術來實現 2D 可視化監控,本系統採用 Hightopo 的 HT for Web 產品來構造輕量化的 3D 可視化場景,該 3D 場景從正面展示了一個現代化工廠的現實... ...


前言

現代工業化的推進在極大加速現代化進程的同時也帶來的相應的安全隱患,在傳統的可視化監控領域,一般都是基於 Web SCADA 的前端技術來實現 2D 可視化監控,本系統採用 Hightopo 的 HT for Web 產品來構造輕量化的 3D 可視化場景,該 3D 場景從正面展示了一個現代化工廠的現實場景,包括工廠工人的實時位置、電子圍欄的範圍、現場的安全情況等等,幫助我們直觀的瞭解當前工廠人員的安全狀況。

本篇文章通過對工廠可視化場景的搭建和模型的載入,人物實時定位代碼的實現、電子圍欄和軌跡圖的實現進行闡述,幫助我們瞭解如何通過使用HT實現一個簡單的3D電子圍欄可視化。

以下是項目地址:基於HTML5 WebGL的工業化3D電子圍欄軌跡圖

效果預覽

工廠人員實時定位效果及電子圍欄效果

軌跡圖效果圖

軌跡圖

代碼實現

人物模型及場景

項目中使用的人物模型是通過 3dMax 建模生成的,該建模工具可以導出 obj 與 mtl 文件,在 HT 中可以通過解析 obj 與 mtl 文件來生成 3d 場景中的攝像頭模型。

項目中場景通過 HT 的 3d 編輯器進行搭建,場景中的模型有些是通過 HT 建模,有些通過 3dMax 建模,之後導入 HT 中。

繪製電子圍欄

場景中的電子圍欄並不是使用3dMax搭建的模型,HT提供了多種基礎形體類型供用戶建模使用,不同於傳統的3D建模方式,HT的建模核心都是基於API的介面方式, 通過預定義的圖元類型和參數介面,進行設置達到三維模型的構建。根據形狀,我將電子圍欄分成圓柱、長方體和底部為多邊形的棱柱。

以下是我繪製電子圍欄的相關偽代碼:

  1 G.makeShapes = function (data, typeName, color, lastColor, g3dDm) {
  2     //data是包含電子圍欄圖形信息的json對象數組
  3     let shapes = data;
  4     for (let i = 0; i < shapes.length; i++) {
  5         let shape = shapes[i];
  6         let type = Number(shape['type']);
  7         let x = Number(shape['x']);
  8         let y = Number(shape['y']);
  9         let z = Number(shape['z']);
 10         let width = Number(shape['width']);
 11         let height = Number(shape['height']);
 12         let tall = Number(shape['tall']);
 13         let radius = Number(shape['radius']);
 14         let vertexX = shape['vertexX'];
 15         let vertexY = shape['vertexY'];
 16         let nodePoints = [];
 17         let p3 = [];
 18         let s3 = [];
 19         let centerX = 0;
 20         let centerY = 0;
 21         let centerZ = 0;
 22         let node = new ht.Node();
 23         node.setTag(typeName + i);
 24         switch (type) {
 25             //第一種形狀:圓柱
 26             case 1:
 27                 p3 = [-x, tall / 2, -y];
 28                 s3 = [radius, tall, radius];
 29                 //定義電子圍欄樣式
 30                 node.s({
 31                     "shape3d": "cylinder",
 32                     "shape3d.color": color,
 33                     "shape3d.transparent": true,
 34                     "shape3d.reverse.color": color,
 35                     "shape3d.top.color": color,
 36                     "shape3d.top.visible": false,
 37                     "shape3d.bottom.color": color,
 38                     "shape3d.from.color": color,
 39                     "shape3d.to.color": color
 40                 });
 41                 node.p3(p3);    //設置三維坐標
 42                 node.s3(s3);    //設置形狀信息
 43                 break;
 44             //第二種形狀:長方體
 45             case 2:
 46                 centerX = x - width / 2;
 47                 centerY = y - height / 2;
 48                 centerZ = z + tall / 2;
 49                 p3 = [-Number(centerX) - width, Number(centerZ), -Number(centerY) - height];
 50                 s3 = [width, tall, height];
 51                 node.s({
 52                     "all.color": color,
 53                     "all.reverse.color": color,
 54                     "top.visible": false,
 55                     "all.transparent": true
 56                 });
 57                 node.p3(p3);
 58                 node.s3(s3);
 59                 break;
 60             //第三種形狀:底部為不規則形狀的等高體
 61             case 3:
 62                 let segments = [];
 63                 for (let i = 0; i < vertexX.length; i++) {
 64                     let x = -vertexX[i];
 65                     let y = -vertexY[i];
 66                     let newPoint = { x: x, y: y };
 67                     nodePoints.push(newPoint);
 68                     //1: moveTo,占用1個點信息,代表一個新路徑的起點
 69                     if (i === 0) {
 70                         segments.push(1);
 71                     }
 72                     else {
 73                         //2: lineTo,占用1個點信息,代表從上次最後點連接到該點
 74                         segments.push(2);
 75                         if (i === vertexX.length - 1) {
 76                             //5: closePath,不占用點信息,代表本次路徑繪製結束,並閉合到路徑的起始點
 77                             segments.push(5);
 78                         }
 79                     }
 80                 }
 81                 node = new ht.Shape();
 82                 node.setTag(typeName + i);
 83                 node.s({
 84                     'shape.background': lastColor,
 85                     'shape.border.width': 10,
 86                     'shape.border.color': lastColor,
 87                     'all.color': lastColor,
 88                     "all.transparent": true,
 89                     'all.opacity': 0.3,
 90                 });
 91                 p3 = [nodePoints[0]['x'], tall / 2, nodePoints[0]['y']];
 92                 node.p3(p3);
 93                 node.setTall(tall);
 94                 node.setThickness(5);
 95                 node.setPoints(nodePoints); //node設置點集位置信息
 96                 node.setSegments(segments); //node設置點集連接規則
 97                 break;
 98         }
 99         g3dDm.add(node);
100     }
101 }

 

 

考慮到電子圍欄在某些情況下可能會影響到對人物位置的觀察,設置了隱藏電子圍欄的功能。在HT中用戶可以自定義設置標簽Tag作為模型唯一的標識,我將所有的電子圍欄模型的標簽首碼都統一併且保存在fenceName中,需要隱藏的時候則遍歷所有標簽名稱首碼為fenceName的模型,並且根據模型種類的不同設置不同的隱藏方式。

以下是相關偽代碼:

 1 g3dDm.each((data) => {
 2     if (data.getTag() && data.getTag().substring(0, 4) === fenceName) {
 3         if (data.s('all.opacity') === '0') {
 4             data.s('all.opacity', '0.3');
 5         }
 6         else {
 7             data.s('shape3d.visible', true);
 8             data.s('all.visible', true);
 9             data.s("2d.visible", true);
10             data.s("3d.visible", true);
11         }
12     }
13 });

人物模型實時定位

因為項目使用的是http協議獲取數據,因此使用定時器定時刷新人物數據信息,HT有設置節點位置的setPosition3d方法,因此不做過多介紹,但是人物節點的位置的刷新還包括人物的朝向,因此每次人物移動都需要和上次位置進行比對,計算出偏移的角度。

相關偽代碼如下:

 1 // 刷新數據的人物結點與原來的人物節點標簽相同,則存在做位置更新
 2 if (realInfoData.tagId === tag.getTag()) {
 3     //計算位置朝向偏移參數
 4     let angleNumber = Math.atan2(((-p3[2]) - (-tag.p3()[2])), ((-p3[0]) - (-tag.p3()[0])));
 5     //如果在原地就不轉向,判斷人物在平面位置是否發生變化
 6     if (p3[0] !== tag.p3()[0] || p3[2] !== tag.p3()[2]) {
 7         if (angleNumber > 0) {
 8             angleNumber = Math.PI - angleNumber;
 9         } else {
10             angleNumber = -Math.PI - angleNumber;
11         }
12         //設置人物朝向
13         tag.setRotation3d(0, angleNumber + Math.PI / 2, 0);
14     }
15     //設置人物位置
16     tag.p3(p3);
17 }

人物觸發警報

當人物觸發警報時,有2種方式同時提醒系統使用者。一是人物頭上的面板顏色發生改變,並且顯示報警信息。

相關代碼如下:

 1 switch(obj.alarmType){
 2     case null:
 3         if(panel){//無警報
 4             panel.a('alarmContent','');
 5             panel.a('bg','rgba(6,13,36,0.80)');
 6         }
 7         break;
 8     case '0':
 9         panel.a('alarmContent','進入圍欄');
10         panel.a('bg','rgb(212,0,0)');
11         break;
12     case '1':
13         panel.a('alarmContent','SOS');
14         panel.a('bg','rgb(212,0,0)');
15         break;
16     case '2':
17         panel.a('alarmContent',''); //離開圍欄
18         panel.a('bg','rgba(6,13,36,0.80)');   
19         break;
20     case '3':
21         panel.a('alarmContent','長時間未動');
22         panel.a('bg','rgb(212,0,0)');
23         break;
24 }

 

二是頁面的右側面板會增加警報信息。

相關代碼如下:

1 data.a('text', info);
2 list.dm().add(data);

軌跡圖軌跡實現原理

在發生警報後,需要根據人物的軌跡圖回溯發生警報的來龍去脈。如果使用根據點集每走一步就繪製一個canvas腳步節點的方式去重現軌跡,很容易造成節點繪製過多,頁面卡頓的情況,因此我使用一整條管道的方式代替一個人物的所有腳步節點,使用管道的好處是,每個人物的軌跡圖從開始到結束只有一個管道的圖元信息,因此對頁面的渲染更加友好和流暢。

生成管道軌跡的代碼如下:

 1 //生成軌跡
 2 this.ployLines[i] = new ht.Polyline();
 3 this.ployLines[i].setParent(node);
 4 this.points[i] = [];
 5 this.points[i].push({ x: p3[0], y: p3[2], e: p3[1] -50 });
 6 this.ployLines[i].setPoints(this.points[i]);
 7 this.ployLines[i].s({
 8     'shape.border.color': 'red'
 9 });
10 g3dDm.add(this.ployLines[i]);

 

人物前進一步,則往管道的點集中推進一個點的坐標,同時繪製新的管道部分。同理,人物後退一步,則管道的點集中推出當前最後一個點的坐標,同時管道失去最後兩點連接的部分。另外我通過使用定時器,對軌跡圖的前進和後退分別做了快進和快退的處理。以下為軌跡圖的運行效果:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關於CSS的書寫規範和順序,是大部分前端er都必須要攻剋的一門關卡,如果沒有按照良好的CSS書寫規範來寫CSS代碼,會影響代碼的閱讀體驗。這裡總結了一個CSS書寫規範、CSS書寫順序供大家參考,這些是參考了國外一些文章以及我的個人經驗總結出來,我想對寫CSS的前端用戶來說是值得學習的。 CSS書寫順 ...
  • vue對比jquery vue:mvvm 數據驅動影響視圖 適用於複雜數據jquery:mvc 視圖塞入數據 適用於複雜視圖動效 (其實都是js 的封裝,以及對html 的擴展) 相關指令 v-text 等同大鬍子效果 但是會轉換為字元串 v-html 綁定html屬性 v-if三兄弟 只會渲染判斷 ...
  • 1. Promise 的含義 Promise 是非同步編程的一種解決方案,比傳統的解決方案 回調函數和事件更合理、更強大。 1.1 什麼是Promise 簡單來說就是一個容器,裡面保存著某個未來才會結束的事件(也就是非同步操作)的結果。從語法上來講,Promise是一個對象,從它可以獲取非同步操作的消息, ...
  • //js layui.use(['laydate', 'layer', 'element', 'form','laypage'], function() { form = layui.form, form.on('switch(used)', function(data){ var s... ...
  • 一.安裝依賴 ​ or` 二.使用 三.官方文檔 "點我官方文檔" "點我中文官方文檔" ...
  • <script type="text/javascript"> String.prototype.reverse = function(){ this.split("").reverse().join("") } //在二進位中迴文數最低位會是1 那麼代表這個數會是奇數 var num = 11 w... ...
  • 4、數字 4.1 不同進位表示法 ES6中新增了不同進位的書寫格式,在後臺傳參的時候要註意這一點。 4.2 精確到指定位數的小數 將數字四捨五入到指定的小數位數。使用 Math.round() 和模板字面量將數字四捨五入為指定的小數位數。 省略第二個參數 decimals ,數字將被四捨五入到一個整 ...
  • 真實生活中的對象、屬性和方法 在真實生活中,汽車是一個對象。 汽車有諸如車重和顏色等屬性,也有諸如啟動和停止的方法: car.name = porsche car.model = 911 car.length = 4499mm car.color = white car.start() car.dr ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...