iT邦幫忙

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

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

Day 18 : 模型所見即得的設計

前言

所見即得的設計模型,是提供CAD設計者建立 操作手感 的一種方法,設計者可以透過所見即得的視覺感受,來決定要將模型調整到什麼樣的狀態。在技術上就是透過滑鼠拖動的同時即時渲染模型,並在放開滑鼠後再做完其它的運算以結束模型的修改。

所見即得調整模型

無論是哪一種模型變形演算法,都需要清楚劃分出 開始設計設計中設計結束 這三種狀態,以下將透過簡單的網格變形功能來說明。首先對此範例來說 開始設計 在程式中就是 mouseDown 的事件,按下滑鼠時必須要完成的事情有: 當前要更新的模型對象、取得變形前的滑鼠位置、取得網格面變形前的狀態、網格頂點變形前的座標資訊,程式宣告如下:

this.currentObject = null;
this.preFace = new THREE.Face3();
this.preMouse = new THREE.Vector2();
this.preVertices = [];

因此就需要將 開始設計 的事件劃分出來,先將網格變形前的資訊暫存起來,由於程式碼與先前範例類似就不再重複說明。

Editor.prototype.mouseDown = function (viewer) {
    var self = this;
    var intersected = viewer.intersects[0];
    self.enable = true;
    self.viewer = viewer;
    self.preFace.copy(intersected.face);
    self.currentObject = viewer.meshs.find((mesh) => (intersected.object.id === mesh.id));
    self.preMouse.copy(self.viewer.mouse);

    var point = [self.preFace.a, self.preFace.b, self.preFace.c];
    self.preVertices = [];
    var vid = 0;
    for (var v = 0; v < point.length; v++) {
        vid = point[v];
        var vertex = new THREE.Vector3();
        vertex.copy(self.currentObject.geometry.vertices[vid]);
        self.preVertices.push(vertex);
    }
};

接下來需要建立第二個步驟 設計中 的事件,首先在 EditorViewer 模組中都添加一個 action 函數並且綁定在一起,並且在 animate 函數中增加呼叫它。

Viewer.prototype.animate = function () {
    var self = this;

    //...

    self.action();
};

Editor 中的 action 函數為了完成所見即得的功能,需要即時的更新顯示網格點座標,但是要注意到不需要立刻重新計算網格向量,因為我們只是要暫時的顯示而已。在這裡我們是使用滑鼠拖動的距離作為變形強度係數,達成拖拉時會有即時變形的效果。

Editor.prototype.action = function () {
    var self = this;
    if(!self.enable) return;

    var distance = self.preMouse.distanceTo(self.viewer.mouse) * 10;

    var geometry = self.currentObject.geometry;
    var topology = self.currentObject.topology;

    var normal = self.preFace.normal;
    var point = [self.preFace.a, self.preFace.b, self.preFace.c];
    
    var vid = 0;
    for (var v = 0; v < point.length; v++) {
        vid = point[v];
        
        var x = self.preVertices[v].x + normal.x * distance;
        var y = self.preVertices[v].y + normal.y * distance;
        var z = self.preVertices[v].z + normal.z * distance;
        
        geometry.vertices[vid] = new THREE.Vector3(x, y, z);
        topology.vertex[vid].vector3 = new THREE.Vector3(x, y, z);
    }
    //更新模型資訊
    geometry.verticesNeedUpdate = true;
    geometry.elementsNeedUpdate = true;
};

最後的步驟 設計結束 就需要建立在 mouseUp 事件中,網格面的法向量重新計算的部分就可以在最後再完成,如此一來就可以節省大量的網格即時計算時間,結果如下圖。

Editor.prototype.mouseUp = function (viewer) {
    var self = this;
    if(!self.enable) return;
    self.enable = false;

    var geometry = self.currentObject.geometry;
    var topology = self.currentObject.topology;
    var point = [self.preFace.a, self.preFace.b, self.preFace.c];

    var faceIDs = [];

    var vid = 0;
    for (var v = 0; v < point.length; v++) {
        vid = point[v];
        faceIDs.push(...topology.vertex[vid].faceIDs);
    }

    //過濾重複的面
    faceIDs = faceIDs.filter(function (face, index, arr) {
        return arr.indexOf(face) === index;
    });

    //重新計算面的法向量
    for (var i = 0; i < faceIDs.length; i++) {
        var id = faceIDs[i];
        var cb = new THREE.Vector3();
        var ab = new THREE.Vector3();
        var vA = geometry.vertices[geometry.faces[id].a];
        var vB = geometry.vertices[geometry.faces[id].b];
        var vC = geometry.vertices[geometry.faces[id].c];
        cb.subVectors(vC, vB);
        ab.subVectors(vA, vB);
        cb.cross(ab);
        geometry.faces[id].normal.copy(cb.normalize());
    }
    geometry.normalsNeedUpdate = true;
    self.preVertices = [];
};

https://i.imgur.com/vY89xGf.gif
所見即得拖拉變形效果

範例模型下載位置

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

線上範例

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

範例原始碼

https://github.com/QQBoxy/threecad/blob/master/client/example14/Editor.js

後記

感謝網友私底下的建議,因為目前為止都只有在前端運作,所以本次直接在 github 放了範例,讓大家可以所見即玩一下看效果,而不只是看看gif圖片動來動去囉。有興趣的朋友也不妨將clone整個專案到自己電腦玩玩看,如果有建議也歡迎發Repo給我,謝謝。


上一篇
Day 17 : 修正面的法向量
下一篇
Day 19 : 點選網格頂點
系列文
在Three.js探索CAD的奧秘30

尚未有邦友留言

立即登入留言