iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Modern Web

在Three.js探索CAD的奧秘系列 第 29

Day 29 : 三角網格布林

前言

三角網格布林(Boolean),是一個基本的網格編輯功能,包含聯集、差集、交集處理。

三角網格布林模組

網路上已經有現成的模組,是由 chandlerprall 所建立,專案中也有新增 es6 的版本供使用。
https://github.com/chandlerprall/ThreeCSG

但是直接使用該原始碼會有問題,同樣的筆者有進行內容的修正,使其可以在 Webpack 正常使用,修正後的檔案如下:
https://github.com/QQBoxy/threecad/blob/master/client/common/threeCSG.js

選擇物件

筆者希望選擇模型後能在模型的外面畫上一個透明的 BoundingBox ,並且在點選第二下後刪掉該 BoundingBox 用來表示不選了。

首先在 Editor 宣告一個 selectedMeshs 用來放置被選擇的網格模型。

this.selectedMeshs = [];

透過建立旗標的方式判斷網格是否為選擇的邊界盒,若邊界盒已經存在時再點選邊界盒則清除邊界盒與對應選擇的三角網格資訊。

var isSelectedBox = intersected.object.selectedBox;
if (isSelectedBox) {
    var i = 0;
    for (i = 0; i < self.selectedMeshs.length;i++) {
        var mesh = self.selectedMeshs[i];
        if (mesh.boxId === intersected.object.id) {
            self.viewer.scene.remove(intersected.object);
            self.selectedMeshs.splice(i, 1);
            break;
        }
    }
} else {
    // Do Something
}

接著若點選的是一般物件時就創造邊界盒,透過 computeBoundingBox 函數可以計算 geometry 的邊界盒,經過簡單的計算就能得到邊界盒的長寬高。

geometry.computeBoundingBox();
var box_x = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
var box_y = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
var box_z = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
var boxGeometry = new THREE.BoxGeometry(box_x + 0.1, box_y + 0.1, box_z + 0.1);
var boxMaterial = new THREE.MeshPhongMaterial({
    color: 0xffffff,
    flatShading: true, //單一法向量渲染
    transparent: true,
    opacity: 0.5
});

有了幾何與材質後就要建立三角網格,由於邊界盒創造後並不在對象模型的位置,因此將邊界盒移動到對象模型之邊界盒中心點,並且給予 selectedBox 旗標表示這個三角網格是個邊界盒。

var boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
boxMesh.position.copy(geometry.boundingBox.getCenter());
boxMesh.selectedBox = true;
boxMesh.skip = true;

最後在場景中加入邊界盒,並且也在選擇的三角網格清單紀錄 ID 對應清單。

self.viewer.scene.add(boxMesh);
self.viewer.meshs.push(boxMesh);
self.selectedMeshs.push({
    meshId: intersected.object.id,
    boxId: boxMesh.id
});

另一方面還要建立一個函數用來在做完布林後清除所有邊界盒。

Editor.prototype.clearBox = function () {
    var self = this;
    var i = 0;
    for (i = 0; i < self.viewer.meshs.length; i++) {
        var mesh = self.viewer.meshs[i];
        if (mesh.selectedBox) {
            self.viewer.scene.remove(mesh);
        }
    }
    self.selectedMeshs = [];
};

三角網格布林

三角網格布林在使用非常容易,首先建立一個 CSGBoolean.js 模組,用來管理不林相關函數的應用程式。

聯集

首先是幾何的聯集功能,首先要找回兩個要處理的 Mesh 資料,我們透過自訂一個簡單的函數來取得資料。

CSGBoolean.prototype.getMesh = function (id) {
    var self = this;
    var i = 0;
    for (i = 0; i < self.viewer.meshs.length; i++) {
        var mesh = self.viewer.meshs[i];
        if (mesh.id === id) {
            return mesh;
        }
    }
};

接著判斷選擇2個模型時,取得我們需要的兩筆Mesh資料,如果不是兩筆三角網格資料的話,就不允許進行布林操作。

CSGBoolean.prototype.union = function () {
    var self = this;
    if (self.editor.selectedMeshs.length === 2) {
        var meshA = self.getMesh(self.editor.selectedMeshs[0].meshId);
        var meshB = self.getMesh(self.editor.selectedMeshs[1].meshId);
        
        // Do Something
        
    } else {
        alert("Please select two object.");
    }
};

接著透過 ThreeBSP 建立物件A與物件B,然後使用 union 函數計算出聯集的結果,並且透過 toGeometry 函數可以建立新的 newGeometry,最後只要從場景刪掉舊的模型並建立新的模型,以及清除模型選擇清單即可,效果如下圖。

var selectA = new ThreeBSP(meshA.geometry);
var selectB = new ThreeBSP(meshB.geometry);

var union_bsp = selectA.union(selectB);
var newGeometry = union_bsp.toGeometry();

self.viewer.scene.remove(meshA);
self.viewer.scene.remove(meshB);

self.viewer.add(newGeometry);
self.editor.clearBox();

https://lh3.googleusercontent.com/GLOz2kZY0ApN6Dup0Yr4ECihjAd-XIqJ4o35xZGyR-A9OmVyO8B_fMo_JSc35d2MByTT2ka-VSoTO6KZtA=s0-tmp.gif
選擇方塊與偏移的球 SphereShiftBinary.STL 測試聯集效果

差集與交集

差集與交集的寫法與聯集相同,只有改變 subtractintersect 兩個語法的部分,結果如下圖。

//差集
CSGBoolean.prototype.subtract = function () {
    var self = this;
    if (self.editor.selectedMeshs.length === 2) {
        var meshA = self.getMesh(self.editor.selectedMeshs[0].meshId);
        var meshB = self.getMesh(self.editor.selectedMeshs[1].meshId);

        var selectA = new ThreeBSP(meshA.geometry);
        var selectB = new ThreeBSP(meshB.geometry);

        var subtract_bsp = selectA.subtract(selectB);
        var newGeometry = subtract_bsp.toGeometry();

        self.viewer.scene.remove(meshA);
        self.viewer.scene.remove(meshB);

        self.viewer.add(newGeometry);
        self.editor.clearBox();
    } else {
        alert("Please select two object.");
    }
};

//交集
CSGBoolean.prototype.intersect = function () {
    var self = this;
    if (self.editor.selectedMeshs.length === 2) {
        var meshA = self.getMesh(self.editor.selectedMeshs[0].meshId);
        var meshB = self.getMesh(self.editor.selectedMeshs[1].meshId);

        var selectA = new ThreeBSP(meshA.geometry);
        var selectB = new ThreeBSP(meshB.geometry);

        var intersect_bsp = selectA.intersect(selectB);
        var newGeometry = intersect_bsp.toGeometry();

        self.viewer.scene.remove(meshA);
        self.viewer.scene.remove(meshB);

        self.viewer.add(newGeometry);
        self.editor.clearBox();
    } else {
        alert("Please select two object.");
    }
};

https://lh3.googleusercontent.com/ip2mApZdB9mW0RbbGERhF5THSvBv-fcUaRXP3wBeRGiDbRM5Ra2KAokQGSzEvtBuC8hLruowzgTvcW5LHQ=s0-tmp.gif
先選方形再選球的差集效果

https://lh3.googleusercontent.com/rwhJc57mv-8piMCDsOuRZLEUYjtJrZGSntfROs7Pgev_KrLFbDvC325vo2An8FVBeKGmfswXSehx310mZg=s0-tmp.gif
先選球再選方形的差集效果

https://lh3.googleusercontent.com/fLLg5L1ILyfdmSiBS_TFLLaOaWdnpq6LFvm6KGdQNzkkm2hvIopicDA29cdH5hXcMkSfLE8mpvC1LVZ24A=s0-tmp.gif
交集效果

範例模型下載位置

https://github.com/QQBoxy/threecad/tree/master/models

線上範例

https://qqboxy.github.io/threecad/public/example23.html

範例原始碼

https://github.com/QQBoxy/threecad/blob/master/client/example23/

後記

鐵人賽也即將告一段落,趁著倒數第二天也稍微提一下布林方法,今天的布林範例只有實作選擇功能與布林,其實還需要一個鍵盤事件可以讓功能更加完整。由於筆者想盡量在我的文章呈現的是各種CAD函數在Web-based的實現,而不是再去介紹Three.js的移動範例,大家如果有興趣不妨自己實作看看鍵盤移動功能。


上一篇
Day 28 : 網格邊翻轉實作
下一篇
Day 30 : CAD的未來
系列文
在Three.js探索CAD的奧秘30

尚未有邦友留言

立即登入留言