基於 WebGL 的 CSG 構造實體幾何書架
CSG 構造實體幾何這個概念在工業水利水電施工上、遊戲上已經有很多人使用了,最簡單的實體表示叫作體元,通常是形狀簡單的物體,如立方體、圓柱體、稜柱、稜錐、球體、圓錐等。根據每個軟體包的不同這些體元也有所不同,在一些軟體包中可以使用彎曲的物體進行 CSG 處理,在另外一些軟體包中則不支援這些功能。構造物體就是將體元根據集合論的布林邏輯組合在一起,這些運算包括:並集、交集以及補集。我們一般可以用 CSG 來將簡單的模型合在一起生成複雜的模型,這樣在構造模型的時候會省很多力。
HT 中的 ht.CSGNode 圖元型別就是參考 CSG 封裝的一個函式,ht.CSGNode 繼承於 ht.Node,當 style 的 shape3d 屬性為空時顯示為六面體效果,CSGNode如果通過 setHost 吸附到 宿主 CSGNode 或 CSGShape 後,宿主 CSGNode 或 CSGShape 可與吸附的CSGNode圖元進行CSG的組合建模。詳情請參考 HT for Web 建模手冊 CSGNode 章節。這裡我用 CSG 的概念寫了一個例子,讓大家能更好地理解這個概念。
本例 Demo 地址: http://hightopo.com/guide/guide/plugin/modeling/examples/example_bookshelf.html
先來看下效果圖:
從上面效果圖可以看到,我們將介面分為三個部分,這三個部分先是右邊部分上下分割,然後將整個介面左右分割,HT 用封裝好的 ht.widget.SplitView 進行介面的分割,然後將分割元件新增進底層 div 中:
dm = new ht.DataModel(); // 資料模型
treeView = new ht.widget.TreeView(dm); // 樹元件
gv1 = new ht.graph3d.Graph3dView(dm); // 3D 元件
gv2 = new ht.graph3d.Graph3dView(dm);
splitView = new ht.widget.SplitView(gv1, gv2, 'v', 0.6); // 分割元件
mainSplit = new ht.widget.SplitView(treeView, splitView, 'h', 0.27);
view = mainSplit.getView();
view.className = 'main';
document.body.appendChild(view);
window.addEventListener('resize', function (e) {
mainSplit.invalidate();
}, false);
上面程式碼是一種非常常見的在 HTML 中新增 HT 元件的方法,詳情可參考 HT for Web 入門手冊元件章節。這種方法進行新增 HT 元件有一個需要注意的點,因為 HT 一般都以設定 position 為 absolute 的絕對定位方式,必須設定 left、right、top、bottom 等等基礎 css 樣式,像這樣:
.main {
margin: 0px;
padding: 0px;
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
}
所以為了最外層元件載入填充滿視窗的方便性,HT 的所有元件都有 addToDOM 函式,其思想邏輯如下,其中 iv 是 invalidate 的縮寫:
addToDOM = function(){
var self = this,
view = self.getView(),
style = view.style;
document.body.appendChild(view);
style.left = '0';
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize', function () { self.iv(); }, false);
}
以後我們在程式碼中就可以直接呼叫 addToDOM 函式,而不用寫一大堆程式碼了,上面程式碼用 addToDOM 取代之後的程式碼如下,而且不用描繪 css 樣式:
dm = new ht.DataModel(); // 資料模型
treeView = new ht.widget.TreeView(dm); // 樹元件
gv1 = new ht.graph3d.Graph3dView(dm); // 3D 元件
gv2 = new ht.graph3d.Graph3dView(dm);
splitView = new ht.widget.SplitView(gv1, gv2, 'v', 0.6); // 分割元件
mainSplit = new ht.widget.SplitView(treeView, splitView, 'h', 0.27);
mainSplit.addToDOM();
介面分配好之後我們就要對其新增內容了,介面的左邊部分是 HT 封裝的樹元件,我在之前的文章寫到過,樹元件是一個非常方便的繪製樹形關係的元件,開發人員能夠輕鬆地從資料模型 DataModel 中獲取資料和節點之間的關係放到樹上,只需要在樹元件宣告的過程中,將對應的資料模型 DataModel 放進樹元件的引數即可,當然我們還擴充套件了很多跟樹元件有關的函式,非常方便實用,這裡我們只用了 expandAll 函式,將所有物件展開:
treeView = new ht.widget.TreeView(dm); // 樹元件
treeView.expandAll();
右邊部分上下分為兩部分,都是 3D 場景,就是設定顯示有點不同,其他完全相同,上面的 3D 場景過載了 getVisibleFunc 函式,如果元素的 showMe 屬性為 true,則可視;如果節點為 ht.CSGNode 型別並且節點的 getHost 函式的引數為空,則不可視;其他情況均可視:
gv1.setVisibleFunc(function(data){
if(data.showMe){
return true;
}
if(data instanceof ht.CSGNode && data.getHost()){
return false;
}
return true;
});
我們先向 3D 場景中新增元素物件,我們先解釋中間的書架,對兩邊的書架有缺的再進行補充。首先我們新增了一個 ht.CSGNode 節點 shelf,作為書架的主節點,其他的節點都是依附於這個節點的,對這個節點設定了位置、大小、名稱以及六個面的顏色,然後新增進資料模型 DataModel:
var shelf = new ht.CSGNode();
shelf.s3(500, 400, 120);
shelf.p3(0, 200, 0);
shelf.setName('shelf1');
shelf.s({
'all.color': '#E5BB77'
});
dm.add(shelf);
接著向這個 shelf 中新增 10 個節點,做書架的格子效果,並設定依附關係和父子關係新增進資料模型中:
for(var i=0; i<2; i++){
for(var j=0; j<5; j++){
var clipNode = new ht.CSGNode();
clipNode.setHost(shelf);
clipNode.s3(80, 100, 120);
clipNode.p3(-200+j*100, 340-i*120, 20);
clipNode.setName('substract-'+i+'-'+j);
clipNode.s('batch', 'tt');
clipNode.setParent(shelf);
dm.add(clipNode);
}
}
為了讓書架變得更美觀一點,我們在書架的上下左右都加上了 ht.CSGNode,最後為了更加具象化,我們還新增了一本書,實現方式也差不多,都非常簡單:
var book = new ht.Node();
book.setName('CSS3: The Missing Manual');
book.s3(60, 80, 8);
book.p3(-100, 210, 20);
book.r3(-Math.PI/6, Math.PI/5, 0);
book.setIcon('book');
book.s({
'front.image': 'book',
'back.color': 'white',
'left.color': 'white',
'all.color': 'gray'
});
book.setHost(shelf);
book.setParent(shelf);
dm.add(book);
接著左邊的書架也是類似的構建方法,有一點不同的是,這邊有一個 ht.CSGBox 型別,繼承於 ht.CSGNode,其除具備父類 CSGNode 的挖空等功能外,還可對六個面進行旋轉展開關閉的操作,這裡我們的節點只設定了前面的能夠旋轉展開,並且設定了一系列的樣式:
clipNode = new ht.CSGBox();
clipNode.setName('CSGBox-Expand-Left');
clipNode.s3(100, 100, 120);
clipNode.p3(0, 65, 0.1);
clipNode.setHost(shelf);
clipNode.showMe = true;
clipNode.s({
'all.visible': false, // 6面均不可見
'front.visible': true, // 前面可見
'front.toggleable': true, // 允許前面雙擊展開
'front.reverse.flip': true, // 前面的反面顯示正面的內容
'front.transparent': true, // 前面透明
'front.end': Math.PI * 0.7, // 前面展開狀態的結束旋轉弧度
'front.color': 'rgba(0, 50, 50, 0.7)' // 前面顏色
});
可能你們還想知道下面的地球是怎麼做到的?還記得之前的文章寫到過 HT 中設定了 shape3d 屬性,設定這個屬性實際上就是在操作 setShape3dModel(name, model) 和 getShape3dModel(name),可以通過這個屬性設定為 box|sphere|cylinder|cone|torus|star|rect|roundRect|triangle|rightTriangle|parallelogram|trapezoid 等等模型,這些模型也都是 HT 封裝好的,要使用時直接設定 shape3d 為其中的一個值即可,如這個例子中用到 “shape3d: sphere” 就是設定為球體。我們簡單地用一張地圖圖片包裹在這個球體的外側,當然,這張地圖圖片是先通過 ht.Default.setImage 註冊過的,然後通過 shape3d.image 將圖片附到這個節點上:
earth = new ht.Node();
earth.setName('earth');
earth.s3(70, 70, 70);
earth.p3(0, 50, 0);
earth.s({
'shape3d': 'sphere',
'shape3d.image': 'earth'
});
earth.setHost(shelf);
earth.setParent(shelf);
dm.add(earth);
右邊的書架,同樣也是有一個主節點,其他節點依附於它,但是我們看到這邊換了一個新的節點型別 ht.DoorWindow,ht.DoorWindow 繼承於 ht.CSGNode,其除具備父類 CSGNode 的挖空等功能外,還可進行整體的旋轉展開關閉的操作, 常用於作為門或窗的業務物件,吸附於 CSGNode 或 CSGShape 的 host 作為牆面的圖元。這個節點型別就是 ht.CSGNode 的延展,相對來說就是區分了實際應用而新增了不同的 style 引數,更多的屬性請到 HT for Web 建模手冊 DoorWindow 章節 檢視然後新增到節點中玩玩:
photos = new ht.DoorWindow();
photos.setName('DoorWindow-Photos');
photos.setIcon('ben12');
photos.s3(110, 100, 130);
photos.p3(5, 180, 0);
photos.setHost(shelf);
photos.showMe = true;
photos.s({
'bottom.uv': [1,1, 1,0, 0,0, 0,1],
'bottom.uv.scale': [1, 1],
'left.uv.scale': [3, 3],
'top.uv.scale': [2, 2],
'dw.s3': [0.8, 0.9, 0.05],
'dw.t3': [0, -5, 0],
'dw.axis': 'v',
'dw.toggleable': false,
'front.image': 'ben1',
'back.image': 'ben2',
'all.color': '#F8CE8B'
});
photos.setParent(shelf);
dm.add(photos);
最後,我們將左側的地球 earth 和右側的照片 photo 旋轉起來:
var angle = 0;
setInterval(function(){
angle += Math.PI/40;
earth.r3(0, angle, 0);
photos.s('dw.angle', angle);
}, 50);
我們看到,其實雖然 HT 封裝了很多不同的 CSG 節點型別,但是實際應用都差不多,而且內容也沒有差特別多,差別都是在 style 引數上,但是真的在實際開發中,這種區分就會很大程度上加快開發速度,畢竟名稱一目瞭然,就知道要運用哪些 style 屬性了。
相關文章
- 基於SpringCloud的Microservices架構實戰案例-架構拆解SpringGCCloudROS架構
- B站基於Iceberg的湖倉一體架構實踐架構
- OnZoom 基於Apache Hudi的流批一體架構實踐OOMApache架構
- WebGL:使用著色器進行幾何造型Web
- 基於零信任架構的IDaaS實現架構
- 基於SPA架構的GraphQL工程實踐架構
- 基於 RocketMQ 的基金數字化陪伴體系的架構實踐MQ架構
- 觸寶科技基於Apache Hudi的流批一體架構實踐Apache架構
- HyperWorks基於幾何投影的網格變形
- 《架構基礎 從需求到架構》讀書架構
- 基於Maven的SSM總體架構設計(一)MavenSSM架構
- WebGL場景的兩種地面構造方法Web構造方法
- 基於Hadoop的大資料平臺實施——整體架構設計Hadoop大資料架構
- 阿里架構師,講述基於微服務的軟體架構模式(附資料)阿里架構微服務模式
- 介紹基於事件的架構事件架構
- 基於 dubbo 的分散式架構分散式架構
- 基於 Istio 的灰度釋出架構方案實踐之路架構
- IT架構的基礎實施架構
- 關於軟體架構和業務架構的思考架構
- 基於 RocketMQ 的 MQTT 服務架構在小米的實踐MQQT架構
- Android架構系列-基於MVP建立適合自己的架構Android架構MVP
- 基於 WebGL 的 3D 電信機架之資料繫結Web3D
- 基於Gin的IM聊天架構——HiChat架構
- AR的平面檢測和利用SceneKit構建幾何體
- 基於SpringCloud分散式架構SpringGCCloud分散式架構
- 百度造車,勝算幾何?
- three.js基礎之幾何體Curve、GeometryJS
- 同程旅行基於 RocketMQ 高可用架構實踐MQ架構
- webgl值得重視的基礎構建Web
- 輕鬆構建基於 Serverless 架構的小程式Server架構
- 異構幾何問題
- 基於大中臺架構的電商業務中臺最佳實踐之一:業務中臺總體架構介紹架構
- 基於函式計算的 BFF 架構函式架構
- 基於Vue的組織架構樹元件Vue架構元件
- 基於SpringCloud的微服務架構設計SpringGCCloud微服務架構
- webgl內建函式--幾何函式與矩陣函式Web函式矩陣
- 袋鼠雲:基於Flink構建實時計算平臺的總體架構和關鍵技術點架構
- 基於Drone實現CI/CD【0到1架構系列】架構