上回提到邊翻轉的方法,而偵測邊線錯誤的方法其實也不只一種,本次要來試著實作看看。
首先修改 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://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
本次可以看到基本的邊翻轉修正功能已經完成,但由於筆者很想將範例簡化到最簡單的方式,所以有省略到許多修正步驟避免大家看不懂,因此此程式仍有一些缺點,未來還需要搭配其它演算法來改善,若鐵人賽完畢後還有時間再回頭來進行修改。