2D拓撲的應用在電信網管和電力SCADA領域早已習以為常了,隨著OpenGL特別是WebGL技術的普及,3D方式的資料視覺化也慢慢從佛殿神堂步入了尋常百姓家,似乎和最近高檔會所被整改為普通茶館是一樣的節奏。
3D呈現固然比2D方式更直觀,但如果擺放圖元佈局卻比2D麻煩,畢竟增加了一個維度,手工佈局不如以前2D手工操作方便,因此3D的自動佈局功能比2D凸顯其重要性。最近玩了玩HT的彈力自動佈局外掛挺有意思,特別在平板上Touch方式拖拽三維空間圖元節點時,對我這種控制慾較強者很有滿足感。
彈力佈局也不是啥新鮮玩意兒了,傳統彈力佈局演算法都是採用通過CPU迭代運算的方式,對於海量資料特別是在純客戶端運算的方式肯定是不可行的,因此這些年也有很多采用GPU的方式進行平行計算的方式可極大提高效能,等OpenCL更成熟HT for Web提供了WebCL的解決方案我再來張開這個話題。今天的話題採用的還是CPU,只不過我把自動佈局的演算法拉到了Web Worker來運算,純屬為了好玩實際意義不大,畢竟Worker運算結果還得不斷序列化給GUI頁面層,不斷來回資料傳輸也挺耗效能,當然如果你讓Worker執行一段時間,只把最終結果push回Web層進行呈現還是有點實際意義的,畢竟不用Worker時js單執行緒執行,對這種計算密集型的演算法只會卡死介面無法進行其他業務操作。
以下是頁面部分的程式碼,通過new Worker('workderjs')構建Worker後臺執行物件,通過worker.addEventListener('message', ..)監聽後臺自動佈局後派發的圖元位置資訊進行更新,通過worker.postMessage(info)傳送介面拖拽圖元位置變化資訊。
function reload() { var info = { A: parseInt($("A").value), B: parseInt($("B").value) }; reloadModel(dataModel, info); worker.postMessage(info); } function init() { dataModel = new ht.DataModel(); g3d = new ht.graph3d.Graph3dView(dataModel); toolbar = new ht.widget.Toolbar(items); borderPane = new ht.widget.BorderPane(); borderPane.setTopView(toolbar); borderPane.setCenterView(g3d); g3d.mi(function(evt){ if(evt.kind === 'betweenMove'){ moveMap = {}; g3d.sm().each(function(data){ if(data instanceof ht.Node){ moveMap[data._id] = data.p3(); } }); worker.postMessage({moveMap: moveMap}); } }); worker = new Worker("worker.js"); worker.addEventListener('message', function(e) { var info = e.data; for(var id in info.result){ var data = dataModel.getDataById([id]); if(data && !g3d.isSelected(data)){ data.p3(info.result[id]); } } }); reload(); }
以下是後臺Work.js的程式碼,通過importScripts("ht.js")引入HT核心包,通過importScripts("ht-forcelayout.js")引入HT的彈力佈局外掛,通過importScripts("util.js")引入和頁面程式碼共享的一些通用函式,通過self.postMessage({result: result})傳送自動佈局運算結果推送到頁面,通過
self.addEventListener('message', ...)監聽頁面發過來的位置變化資訊,從而實現了前後臺的互通。
importScripts("ht.js"); importScripts("ht-forcelayout.js"); importScripts("util.js"); ht = self.ht; dataModel = new ht.DataModel(); forceLayout = new ht.layout.Force3dLayout(dataModel); forceLayout.onRelaxed = function(){ var result = {}; dataModel.each(function(data){ if(data instanceof ht.Node){ result[data._id] = data.p3(); } }); self.postMessage({result: result}); }; forceLayout.start(); self.addEventListener('message', function(e) { var info = e.data; if(info.moveMap){ dataModel.sm().cs(); for(var id in info.moveMap){ var data = dataModel.getDataById(id); if(data){ data.p3(info.moveMap[id]); dataModel.sm().as(data); } } } else{ reloadModel(dataModel, info); } }, false);
以下視訊為在Android平板上跑3D拓撲自動佈局的效果,這個例子純粹為了玩玩Web Workers,這樣折騰效能並不會提高,甚至因為來回序列化更費效能,Web Worker可以使用的場景並不太多,比較適合純數學運算的業務邏輯,同時還需要注意跑在Worker的程式碼是不能操作任何介面物件,例如window和document之類的物件。
下篇《3D拓撲自動佈局之Node.js篇》我們再將演算法移到Node.js端玩
http://v.youku.com/v_show/id_XNjc1MjYzODg4.html