iT邦幫忙

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

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

Day 28 : 網格邊翻轉實作

前言

上回提到邊翻轉的方法,而偵測邊線錯誤的方法其實也不只一種,本次要來試著實作看看。

邊翻轉前

首先修改 Viewer 模組的 add 函數,增加一個引數 topology 並將拓樸資訊改為判斷不存在時才建立新的,避免進行邊翻轉後拓樸資訊被刷新。

if (topology) {
    mesh.topology = topology;
} else {
    mesh.topology = new TOPOLOGY.createFromGeometry(geometry);
}

為了加速計算的時間,事先紀錄細分時有增加的邊線,因為這些邊線有可能是凌亂的,在 Refinement 宣告一個變數用來存放這些邊線的ID。

this.newEdgeIDs = [];

存入先前細分時有新增的邊線,但不包含原始被一分為二的邊線。

self.newEdgeIDs.push(edgeCD);
//...
self.newEdgeIDs.push(edgeDE);

要特別注意的是,因為用來移除舊拓樸資訊的 remove 函數會將拓樸資訊的最後一筆往前挪動並改為舊的ID,因此在移除之前直接把最後一筆刪除,並且存入舊的ID。

self.newEdgeIDs.pop();
self.newEdgeIDs.push(edge.ID);

還要記得將拓樸資訊放入引數直接提供新的 geometry 去套用。

self.viewer.add(newGeometry, topology);

進行邊翻轉

開始新增一個 flipedge 函數用來計算邊翻轉,並宣告需要用到的變數、幾何、拓樸、長度等資訊。

Refinement.prototype.flipedge = function () {
    var self = this;
    var vertexIdA = 0;
    var vertexIdB = 0;
    var vertexIdC = 0;
    var vertexIdD = 0;

    var edgeAB = 0;
    var edgeCD = 0;
    var edgeAC = 0;
    var edgeBC = 0;
    var edgeAD = 0;
    var edgeBD = 0;

    var faceABC = 0;
    var faceABD = 0;
    var faceACD = 0;
    var faceBCD = 0;

    var geometry = self.viewer.meshs[0].geometry;
    var topology = self.viewer.meshs[0].topology;
    
    var len = self.newEdgeIDs.length;
    
    // Do Something...
};

建立一個迴圈用來針對每個邊進行翻轉,並且宣告基本需要的變數資訊。

var i = 0;
for (i = 0; i < len; i++) {
    var id = self.newEdgeIDs[i];
    var edge = topology.edge[id];
    var vertexIDs = edge.vertexIDs;
    var faceIDs = edge.faceIDs;
    
    // Do Something...
}

宣告要翻轉的兩個目標面以及每個相關頂點的資訊。

faceABC = faceIDs[0];
faceABD = faceIDs[1];

vertexIdA = vertexIDs[0];
vertexIdB = vertexIDs[1];
vertexIdC = topology.face[faceABC].vertexIDs.find(
    (element, index, array) => (vertexIDs.indexOf(element) === -1)
);
vertexIdD = topology.face[faceABD].vertexIDs.find(
    (element, index, array) => (vertexIDs.indexOf(element) === -1)
);

因為本次的邊線問題是因細分而來,而價數計算是在面對原本就整齊的網格時效果並不夠顯著,所以本文採用簡單的計算邊線長度比較來判斷是否邊翻轉。

var distanceAB = topology.vertex[vertexIdA].vector3.distanceTo(topology.vertex[vertexIdB].vector3);
var distanceCD = topology.vertex[vertexIdC].vector3.distanceTo(topology.vertex[vertexIdD].vector3);
var isNeedFlip = (distanceAB > distanceCD);

當判斷需要進行翻轉時,就尋找邊線以及創造新的拓樸邊,並創造新的拓樸面。

if (isNeedFlip) {
    edgeAB = topology.edgeIDWithVertices(vertexIdA, vertexIdB);
    edgeCD = topology.create('edge').ID; //創造新的邊線
    edgeAC = topology.edgeIDWithVertices(vertexIdA, vertexIdC);
    edgeBC = topology.edgeIDWithVertices(vertexIdB, vertexIdC);
    edgeAD = topology.edgeIDWithVertices(vertexIdA, vertexIdD);
    edgeBD = topology.edgeIDWithVertices(vertexIdB, vertexIdD);

    //創造新的面
    faceACD = topology.create('face').ID;
    faceBCD = topology.create('face').ID;
    
    // Do Something...
}

然後根據前面所宣告的頂點、邊線、面去定義新的三角形拓樸關係。

topology.addTriangleData(vertexIdA, vertexIdD, vertexIdC, edgeAD, edgeCD, edgeAC, faceACD);
topology.addTriangleData(vertexIdB, vertexIdC, vertexIdD, edgeBC, edgeCD, edgeBD, faceBCD);

接著刪除舊的拓樸面與拓樸邊線。

topology.remove(topology.face[faceABC]);
topology.remove(topology.face[faceABD]);
topology.remove(topology.edge[edgeAB]);

最後在迴圈外面建立新的 geometry 並將拓樸資訊放回去,就完成邊翻轉的動作。

var newGeometry = topology.convertToGeometry();
self.viewer.scene.remove(self.viewer.meshs[0]);
self.viewer.meshs.shift();
self.viewer.add(newGeometry, topology);

https://lh3.googleusercontent.com/SK5PrEHkLSHR7iPXzkAMnvC5SvSiUrVWyB-h3qbIGyqEnKquCg4j5p526BnNC2T2TezkvDy8lN7K2eK5cA=s0-tmp.gif
邊翻轉效果

範例模型下載位置

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

線上範例

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

範例原始碼

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

後記

本次可以看到基本的邊翻轉修正功能已經完成,但由於筆者很想將範例簡化到最簡單的方式,所以有省略到許多修正步驟避免大家看不懂,因此此程式仍有一些缺點,未來還需要搭配其它演算法來改善,若鐵人賽完畢後還有時間再回頭來進行修改。


上一篇
Day 27 : 三角網格邊翻轉
下一篇
Day 29 : 三角網格布林
系列文
在Three.js探索CAD的奧秘30

尚未有邦友留言

立即登入留言