751 lines
22 KiB
C++
751 lines
22 KiB
C++
|
/**
|
||
|
@file MeshAlgAdjacency.cpp
|
||
|
|
||
|
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||
|
@created 2003-09-14
|
||
|
@edited 2010-04-26
|
||
|
|
||
|
Copyright 2000-2012, Morgan McGuire.
|
||
|
All rights reserved.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "G3D/Table.h"
|
||
|
#include "G3D/MeshAlg.h"
|
||
|
#include "G3D/Set.h"
|
||
|
#include "G3D/Stopwatch.h"
|
||
|
#include "G3D/SmallArray.h"
|
||
|
#include "G3D/AreaMemoryManager.h"
|
||
|
|
||
|
namespace G3D {
|
||
|
|
||
|
/** Two-level table mapping index 0 -> index 1 -> list of face indices */
|
||
|
class MeshEdgeTable {
|
||
|
public:
|
||
|
|
||
|
/** We expect 2 faces per edge. */
|
||
|
typedef SmallArray<int, 2> FaceIndexArray;
|
||
|
|
||
|
class Edge {
|
||
|
public:
|
||
|
int i1;
|
||
|
|
||
|
FaceIndexArray faceIndexArray;
|
||
|
};
|
||
|
|
||
|
/** We expect at most 6 edges per vertex; that matches a typical regular grid mesh */
|
||
|
typedef SmallArray<Edge, 6> EdgeArray;
|
||
|
|
||
|
typedef Array< EdgeArray > ET;
|
||
|
|
||
|
private:
|
||
|
|
||
|
ET table;
|
||
|
|
||
|
public:
|
||
|
|
||
|
MeshEdgeTable() {
|
||
|
AreaMemoryManager::Ref mm = AreaMemoryManager::create();
|
||
|
table.clearAndSetMemoryManager(mm);
|
||
|
}
|
||
|
|
||
|
void clear() {
|
||
|
table.clear();
|
||
|
}
|
||
|
|
||
|
void resize(int maxV) {
|
||
|
table.resize(maxV);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Inserts the faceIndex into the edge's face list.
|
||
|
The index may be a negative number indicating a backface.
|
||
|
|
||
|
\param v0 Vertex index 0
|
||
|
\param v1 Vertex index 1
|
||
|
*/
|
||
|
void insert(int v0, int v1, int faceIndex) {
|
||
|
|
||
|
debugAssert(v0 <= v1);
|
||
|
EdgeArray& edgeArray = table[v0];
|
||
|
for (int i = 0; i < edgeArray.size(); ++i) {
|
||
|
if (edgeArray[i].i1 == v1) {
|
||
|
edgeArray[i].faceIndexArray.push(faceIndex);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Edge& p = edgeArray.next();
|
||
|
p.i1 = v1;
|
||
|
p.faceIndexArray.push(faceIndex);
|
||
|
}
|
||
|
|
||
|
class Iterator {
|
||
|
friend class MeshEdgeTable;
|
||
|
private:
|
||
|
|
||
|
int m_i0;
|
||
|
/** Pair index */
|
||
|
int m_p;
|
||
|
ET& m_array;
|
||
|
EdgeArray* m_edgeArray;
|
||
|
bool m_end;
|
||
|
|
||
|
public:
|
||
|
|
||
|
int i0() const {
|
||
|
return m_i0;
|
||
|
}
|
||
|
|
||
|
int i1() const {
|
||
|
return (*m_edgeArray)[m_p].i1;
|
||
|
}
|
||
|
|
||
|
FaceIndexArray& faceIndex() {
|
||
|
return (*m_edgeArray)[m_p].faceIndexArray;
|
||
|
}
|
||
|
|
||
|
Iterator& operator++() {
|
||
|
if ((m_i0 >= 0) && (m_p < m_edgeArray->size() - 1)) {
|
||
|
++m_p;
|
||
|
} else {
|
||
|
// Skip over elements with no face array
|
||
|
do {
|
||
|
++m_i0;
|
||
|
if (m_i0 == m_array.size()) {
|
||
|
m_end = true;
|
||
|
return *this;
|
||
|
} else {
|
||
|
m_edgeArray = &m_array[m_i0];
|
||
|
m_p = 0;
|
||
|
}
|
||
|
} while (m_edgeArray->size() == 0);
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
bool isValid() const {
|
||
|
return ! m_end;
|
||
|
}
|
||
|
|
||
|
/** @deprecated Use isValid */
|
||
|
bool hasMore() const {
|
||
|
return ! m_end;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
Iterator(ET& a) : m_i0(-1), m_p(-1), m_array(a), m_edgeArray(NULL), m_end(false) {
|
||
|
++(*this);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
Iterator begin() {
|
||
|
return Iterator(table);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
Assigns the edge index into the next unassigned edge
|
||
|
index. The edge index may be negative, indicating
|
||
|
a reverse edge.
|
||
|
*/
|
||
|
static void assignEdgeIndex(MeshAlg::Face& face, int e) {
|
||
|
for (int i = 0; i < 3; ++i) {
|
||
|
if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
|
||
|
face.edgeIndex[i] = e;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
debugAssertM(false, "Face has already been assigned 3 edges");
|
||
|
}
|
||
|
|
||
|
|
||
|
void MeshAlg::computeAdjacency(
|
||
|
const Array<Vector3>& vertexGeometry,
|
||
|
const Array<int>& indexArray,
|
||
|
Array<Face>& faceArray,
|
||
|
Array<Edge>& edgeArray,
|
||
|
Array< Array<int> >& adjacentFaceArray) {
|
||
|
|
||
|
Array<Vertex> vertexArray;
|
||
|
|
||
|
computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
|
||
|
|
||
|
// Convert the vertexArray into adjacentFaceArray
|
||
|
adjacentFaceArray.clear();
|
||
|
adjacentFaceArray.resize(vertexArray.size());
|
||
|
for (int v = 0; v < adjacentFaceArray.size(); ++v) {
|
||
|
const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
|
||
|
Array<int>& dst = adjacentFaceArray[v];
|
||
|
dst.resize(src.size());
|
||
|
for (int f = 0; f < dst.size(); ++f) {
|
||
|
dst[f] = src[f];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void MeshAlg::computeAdjacency(
|
||
|
const Array<Vector3>& vertexGeometry,
|
||
|
const Array<int>& indexArray,
|
||
|
Array<Face>& faceArray,
|
||
|
Array<Edge>& edgeArray,
|
||
|
Array<Vertex>& vertexArray) {
|
||
|
|
||
|
MeshEdgeTable edgeTable;
|
||
|
|
||
|
edgeArray.clear();
|
||
|
vertexArray.clear();
|
||
|
faceArray.clear();
|
||
|
|
||
|
// Face normals
|
||
|
Array<Vector3> faceNormal;
|
||
|
faceNormal.resize(indexArray.size() / 3);
|
||
|
faceArray.resize(faceNormal.size());
|
||
|
|
||
|
// This array has the same size as the vertex array
|
||
|
vertexArray.resize(vertexGeometry.size());
|
||
|
|
||
|
edgeTable.resize(vertexArray.size());
|
||
|
|
||
|
// Iterate through the triangle list
|
||
|
for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
|
||
|
|
||
|
Vector3 vertex[3];
|
||
|
MeshAlg::Face& face = faceArray[f];
|
||
|
|
||
|
// Construct the face
|
||
|
for (int j = 0; j < 3; ++j) {
|
||
|
int v = indexArray[q + j];
|
||
|
face.vertexIndex[j] = v;
|
||
|
face.edgeIndex[j] = Face::NONE;
|
||
|
|
||
|
// Store back pointers in the vertices
|
||
|
vertexArray[v].faceIndex.append(f);
|
||
|
|
||
|
// We'll need these vertices to find the face normal
|
||
|
vertex[j] = vertexGeometry[v];
|
||
|
}
|
||
|
|
||
|
// Compute the face normal
|
||
|
const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
|
||
|
faceNormal[f] = N.directionOrZero();
|
||
|
|
||
|
static const int nextIndex[] = {1, 2, 0};
|
||
|
|
||
|
// Add each edge to the edge table.
|
||
|
for (int j = 0; j < 3; ++j) {
|
||
|
const int i0 = indexArray[q + j];
|
||
|
const int i1 = indexArray[q + nextIndex[j]];
|
||
|
|
||
|
if (i0 < i1) {
|
||
|
// The edge was directed in the same manner as in the face
|
||
|
edgeTable.insert(i0, i1, f);
|
||
|
} else {
|
||
|
// The edge was directed in the opposite manner as in the face
|
||
|
edgeTable.insert(i1, i0, ~f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For each edge in the edge table, create an edge in the edge array.
|
||
|
// Collapse every 2 edges from adjacent faces.
|
||
|
|
||
|
MeshEdgeTable::Iterator cur = edgeTable.begin();
|
||
|
|
||
|
Array<Edge> tempEdgeArray;
|
||
|
while (cur.isValid()) {
|
||
|
MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
|
||
|
|
||
|
// Process this edge
|
||
|
while (faceIndexArray.size() > 0) {
|
||
|
|
||
|
// Remove the last index
|
||
|
int f0 = faceIndexArray.pop();
|
||
|
|
||
|
// Find the normal to that face
|
||
|
const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
|
||
|
|
||
|
bool found = false;
|
||
|
|
||
|
// We try to find the matching face with the closest
|
||
|
// normal. This ensures that we don't introduce a lot
|
||
|
// of artificial ridges into flat parts of a mesh.
|
||
|
float ndotn = -2;
|
||
|
int f1 = -1, i1 = -1;
|
||
|
|
||
|
// Try to find the face with the matching edge
|
||
|
for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
|
||
|
int f = faceIndexArray[i];
|
||
|
|
||
|
if ((f >= 0) != (f0 >= 0)) {
|
||
|
// This face contains the oppositely oriented edge
|
||
|
// and has not been assigned too many edges
|
||
|
|
||
|
const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
|
||
|
float d = n1.dot(n0);
|
||
|
|
||
|
if (found) {
|
||
|
// We previously found a good face; see if this
|
||
|
// one is better.
|
||
|
if (d > ndotn) {
|
||
|
// This face is better.
|
||
|
ndotn = d;
|
||
|
f1 = f;
|
||
|
i1 = i;
|
||
|
}
|
||
|
} else {
|
||
|
// This is the first face we've found
|
||
|
found = true;
|
||
|
ndotn = d;
|
||
|
f1 = f;
|
||
|
i1 = i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create the new edge
|
||
|
int e = tempEdgeArray.size();
|
||
|
Edge& edge = tempEdgeArray.next();
|
||
|
|
||
|
edge.vertexIndex[0] = cur.i0();
|
||
|
edge.vertexIndex[1] = cur.i1();
|
||
|
|
||
|
if (f0 >= 0) {
|
||
|
edge.faceIndex[0] = f0;
|
||
|
edge.faceIndex[1] = Face::NONE;
|
||
|
assignEdgeIndex(faceArray[f0], e);
|
||
|
} else {
|
||
|
// The face indices above are two's complemented.
|
||
|
// this code restores them to regular indices.
|
||
|
debugAssert((~f0) >= 0);
|
||
|
edge.faceIndex[1] = ~f0;
|
||
|
edge.faceIndex[0] = Face::NONE;
|
||
|
|
||
|
// The edge index *does* need to be inverted, however.
|
||
|
assignEdgeIndex(faceArray[~f0], ~e);
|
||
|
}
|
||
|
|
||
|
if (found) {
|
||
|
// We found a matching face; remove both
|
||
|
// faces from the active list.
|
||
|
faceIndexArray.fastRemove(i1);
|
||
|
|
||
|
if (f1 >= 0) {
|
||
|
edge.faceIndex[0] = f1;
|
||
|
assignEdgeIndex(faceArray[f1], e);
|
||
|
} else {
|
||
|
edge.faceIndex[1] = ~f1;
|
||
|
assignEdgeIndex(faceArray[~f1], ~e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++cur;
|
||
|
}
|
||
|
|
||
|
edgeTable.clear();
|
||
|
|
||
|
// Move boundary edges to the end of the list and then
|
||
|
// clean up the face references into them
|
||
|
{
|
||
|
// Map old edge indices to new edge indices
|
||
|
Array<int> newIndex;
|
||
|
newIndex.resize(tempEdgeArray.size());
|
||
|
|
||
|
// Index of the start and end of the edge array
|
||
|
int i = 0;
|
||
|
int j = tempEdgeArray.size() - 1;
|
||
|
|
||
|
edgeArray.resize(tempEdgeArray.size());
|
||
|
for (int e = 0; e < tempEdgeArray.size(); ++e) {
|
||
|
if (tempEdgeArray[e].boundary()) {
|
||
|
newIndex[e] = j;
|
||
|
--j;
|
||
|
} else {
|
||
|
newIndex[e] = i;
|
||
|
++i;
|
||
|
}
|
||
|
edgeArray[newIndex[e]] = tempEdgeArray[e];
|
||
|
}
|
||
|
|
||
|
debugAssertM(i == j + 1, "Counting from front and back of array did not match");
|
||
|
|
||
|
// Fix the faces
|
||
|
for (int f = 0; f < faceArray.size(); ++f) {
|
||
|
Face& face = faceArray[f];
|
||
|
for (int q = 0; q < 3; ++q) {
|
||
|
int e = face.edgeIndex[q];
|
||
|
if (e < 0) {
|
||
|
// Backwards edge; twiddle before and after conversion
|
||
|
face.edgeIndex[q] = ~newIndex[~e];
|
||
|
} else {
|
||
|
// Regular edge; remap the index
|
||
|
face.edgeIndex[q] = newIndex[e];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now order the edge indices inside the faces correctly.
|
||
|
for (int f = 0; f < faceArray.size(); ++f) {
|
||
|
Face& face = faceArray[f];
|
||
|
int e0 = face.edgeIndex[0];
|
||
|
int e1 = face.edgeIndex[1];
|
||
|
int e2 = face.edgeIndex[2];
|
||
|
|
||
|
// e0 will always remain first. The only
|
||
|
// question is whether e1 and e2 should be swapped.
|
||
|
|
||
|
// See if e1 begins at the vertex where e1 ends.
|
||
|
const int e0End = (e0 < 0) ?
|
||
|
edgeArray[~e0].vertexIndex[0] :
|
||
|
edgeArray[e0].vertexIndex[1];
|
||
|
|
||
|
const int e1Begin = (e1 < 0) ?
|
||
|
edgeArray[~e1].vertexIndex[1] :
|
||
|
edgeArray[e1].vertexIndex[0];
|
||
|
|
||
|
if (e0End != e1Begin) {
|
||
|
// We must swap e1 and e2
|
||
|
face.edgeIndex[1] = e2;
|
||
|
face.edgeIndex[2] = e1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fill out the edge adjacency information in the vertex array
|
||
|
for (int e = 0; e < edgeArray.size(); ++e) {
|
||
|
const Edge& edge = edgeArray[e];
|
||
|
vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
|
||
|
vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void MeshAlg::weldBoundaryEdges(
|
||
|
Array<Face>& faceArray,
|
||
|
Array<Edge>& edgeArray,
|
||
|
Array<Vertex>& vertexArray) {
|
||
|
|
||
|
// Copy over the original edge array
|
||
|
Array<Edge> oldEdgeArray = edgeArray;
|
||
|
|
||
|
// newEdgeIndex[e] is the new index of the old edge with index e
|
||
|
// Note that newEdgeIndex[e] might be negative, indicating that
|
||
|
// the edge switched direction between the arrays.
|
||
|
Array<int> newEdgeIndex;
|
||
|
newEdgeIndex.resize(edgeArray.size());
|
||
|
edgeArray.resize(0);
|
||
|
|
||
|
// boundaryEdgeIndices[v_low] is an array of the indices of
|
||
|
// all boundary edges whose lower vertex is v_low.
|
||
|
Table<int, Array<int> > boundaryEdgeIndices;
|
||
|
|
||
|
// Copy over non-boundary edges to the new array
|
||
|
for (int e = 0; e < oldEdgeArray.size(); ++e) {
|
||
|
if (oldEdgeArray[e].boundary()) {
|
||
|
|
||
|
// Add to the boundary table
|
||
|
const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
|
||
|
if (! boundaryEdgeIndices.containsKey(v_low)) {
|
||
|
boundaryEdgeIndices.set(v_low, Array<int>());
|
||
|
}
|
||
|
boundaryEdgeIndices[v_low].append(e);
|
||
|
|
||
|
// We'll fill out newEdgeIndex[e] later, when we find pairs
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// Copy the edge to the new array
|
||
|
newEdgeIndex[e] = edgeArray.size();
|
||
|
edgeArray.append(oldEdgeArray[e]);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove all edges from the table that have pairs.
|
||
|
Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
|
||
|
Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
|
||
|
while (cur != end) {
|
||
|
Array<int>& boundaryEdge = cur->value;
|
||
|
|
||
|
for (int i = 0; i < boundaryEdge.size(); ++i) {
|
||
|
int ei = boundaryEdge[i];
|
||
|
const Edge& edgei = oldEdgeArray[ei];
|
||
|
|
||
|
for (int j = i + 1; j < boundaryEdge.size(); ++j) {
|
||
|
int ej = boundaryEdge[j];
|
||
|
const Edge& edgej = oldEdgeArray[ej];
|
||
|
|
||
|
// See if edge ei is the reverse (match) of edge ej.
|
||
|
|
||
|
// True if the edges match
|
||
|
bool match = false;
|
||
|
|
||
|
// True if edgej's vertex indices are reversed from
|
||
|
// edgei's (usually true).
|
||
|
bool reversej = false;
|
||
|
|
||
|
int u = edgei.vertexIndex[0];
|
||
|
int v = edgei.vertexIndex[1];
|
||
|
|
||
|
if (edgei.faceIndex[0] != Face::NONE) {
|
||
|
// verts|faces
|
||
|
// edgei = [u v A /]
|
||
|
|
||
|
if (edgej.faceIndex[0] != Face::NONE) {
|
||
|
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
|
||
|
// This is the most common of the four cases
|
||
|
|
||
|
// edgej = [v u B /]
|
||
|
match = true;
|
||
|
reversej = true;
|
||
|
}
|
||
|
} else {
|
||
|
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
|
||
|
// edgej = [u v / B]
|
||
|
match = true;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// edgei = [u v / A]
|
||
|
if (edgej.faceIndex[0] != Face::NONE) {
|
||
|
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
|
||
|
// edgej = [u v B /]
|
||
|
match = true;
|
||
|
}
|
||
|
} else {
|
||
|
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
|
||
|
// edgej = [v u / B]
|
||
|
match = true;
|
||
|
reversej = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (match) {
|
||
|
// ei and ej can be paired as a single edge
|
||
|
int e = edgeArray.size();
|
||
|
Edge& edge = edgeArray.next();
|
||
|
|
||
|
// Follow the direction of edgei.
|
||
|
edge = edgei;
|
||
|
newEdgeIndex[ei] = e;
|
||
|
|
||
|
// Insert the face index for edgej.
|
||
|
int fj = edgej.faceIndex[0];
|
||
|
if (fj == Face::NONE) {
|
||
|
fj = edgej.faceIndex[1];
|
||
|
}
|
||
|
|
||
|
if (edge.faceIndex[0] == Face::NONE) {
|
||
|
edge.faceIndex[0] = fj;
|
||
|
} else {
|
||
|
edge.faceIndex[1] = fj;
|
||
|
}
|
||
|
|
||
|
if (reversej) {
|
||
|
// The new edge is backwards of the old edge for ej
|
||
|
newEdgeIndex[ej] = ~e;
|
||
|
} else {
|
||
|
newEdgeIndex[ej] = e;
|
||
|
}
|
||
|
|
||
|
// Remove both ei and ej from being candidates for future pairing.
|
||
|
// Remove ej first since it comes later in the list (removing
|
||
|
// ei would decrease the index of ej to j - 1).
|
||
|
boundaryEdge.fastRemove(j);
|
||
|
boundaryEdge.fastRemove(i);
|
||
|
|
||
|
// Re-process element i, which is now a new edge index
|
||
|
--i;
|
||
|
|
||
|
// Jump out of the j for-loop
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
++cur;
|
||
|
}
|
||
|
|
||
|
// Anything remaining in the table is a real boundary edge; just copy it to
|
||
|
// the end of the array.
|
||
|
cur = boundaryEdgeIndices.begin();
|
||
|
end = boundaryEdgeIndices.end();
|
||
|
while (cur != end) {
|
||
|
Array<int>& boundaryEdge = cur->value;
|
||
|
|
||
|
for (int b = 0; b < boundaryEdge.size(); ++b) {
|
||
|
const int e = boundaryEdge[b];
|
||
|
|
||
|
newEdgeIndex[e] = edgeArray.size();
|
||
|
edgeArray.append(oldEdgeArray[e]);
|
||
|
}
|
||
|
|
||
|
++cur;
|
||
|
}
|
||
|
|
||
|
// Finally, fix up edge indices in the face and vertex arrays
|
||
|
for (int f = 0; f < faceArray.size(); ++f) {
|
||
|
Face& face = faceArray[f];
|
||
|
for (int i = 0; i < 3; ++i) {
|
||
|
int e = face.edgeIndex[i];
|
||
|
|
||
|
if (e < 0) {
|
||
|
face.edgeIndex[i] = ~newEdgeIndex[~e];
|
||
|
} else {
|
||
|
face.edgeIndex[i] = newEdgeIndex[e];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (int v = 0; v < vertexArray.size(); ++v) {
|
||
|
Vertex& vertex = vertexArray[v];
|
||
|
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
|
||
|
int e = vertex.edgeIndex[i];
|
||
|
|
||
|
if (e < 0) {
|
||
|
vertex.edgeIndex[i] = ~newEdgeIndex[~e];
|
||
|
} else {
|
||
|
vertex.edgeIndex[i] = newEdgeIndex[e];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void MeshAlg::weldAdjacency(
|
||
|
const Array<Vector3>& originalGeometry,
|
||
|
Array<Face>& faceArray,
|
||
|
Array<Edge>& edgeArray,
|
||
|
Array<Vertex>& vertexArray,
|
||
|
float radius) {
|
||
|
|
||
|
// Num vertices
|
||
|
const int n = originalGeometry.size();
|
||
|
|
||
|
// canonical[v] = first occurance of any vertex near oldVertexArray[v]
|
||
|
Array<int> canonical;
|
||
|
canonical.resize(n);
|
||
|
|
||
|
Array<int> toNew, toOld;
|
||
|
// Throw away the new vertex array
|
||
|
Array<Vector3> dummy;
|
||
|
computeWeld(originalGeometry, dummy, toNew, toOld, radius);
|
||
|
|
||
|
for (int v = 0; v < canonical.size(); ++v) {
|
||
|
// Round-trip through the toNew/toOld process. This will give
|
||
|
// us the original vertex.
|
||
|
canonical[v] = toOld[toNew[v]];
|
||
|
}
|
||
|
|
||
|
// Destroy vertexArray (we reconstruct it below)
|
||
|
vertexArray.clear();
|
||
|
vertexArray.resize(n);
|
||
|
|
||
|
bool hasBoundaryEdges = false;
|
||
|
|
||
|
// Fix edge vertex indices
|
||
|
for (int e = 0; e < edgeArray.size(); ++e) {
|
||
|
Edge& edge = edgeArray[e];
|
||
|
|
||
|
const int v0 = canonical[edge.vertexIndex[0]];
|
||
|
const int v1 = canonical[edge.vertexIndex[1]];
|
||
|
|
||
|
edge.vertexIndex[0] = v0;
|
||
|
edge.vertexIndex[1] = v1;
|
||
|
|
||
|
vertexArray[v0].edgeIndex.append(e);
|
||
|
vertexArray[v1].edgeIndex.append(~e);
|
||
|
|
||
|
hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
|
||
|
}
|
||
|
|
||
|
// Fix face vertex indices
|
||
|
for (int f = 0; f < faceArray.size(); ++f) {
|
||
|
Face& face = faceArray[f];
|
||
|
for (int i = 0; i < 3; ++i) {
|
||
|
const int v = canonical[face.vertexIndex[i]];
|
||
|
|
||
|
face.vertexIndex[i] = v;
|
||
|
|
||
|
// Add the back pointer
|
||
|
vertexArray[v].faceIndex.append(f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hasBoundaryEdges) {
|
||
|
// As a result of the welding, some of the boundary edges at
|
||
|
// the end of the array may now have mates and no longer be
|
||
|
// boundaries. Try to pair these up.
|
||
|
|
||
|
weldBoundaryEdges(faceArray, edgeArray, vertexArray);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void MeshAlg::debugCheckConsistency(
|
||
|
const Array<Face>& faceArray,
|
||
|
const Array<Edge>& edgeArray,
|
||
|
const Array<Vertex>& vertexArray) {
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
for (int v = 0; v < vertexArray.size(); ++v) {
|
||
|
const MeshAlg::Vertex& vertex = vertexArray[v];
|
||
|
|
||
|
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
|
||
|
const int e = vertex.edgeIndex[i];
|
||
|
debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < vertex.faceIndex.size(); ++i) {
|
||
|
const int f = vertex.faceIndex[i];
|
||
|
debugAssert(faceArray[f].containsVertex(v));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
for (int e = 0; e < edgeArray.size(); ++e) {
|
||
|
const MeshAlg::Edge& edge = edgeArray[e];
|
||
|
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
|
||
|
faceArray[edge.faceIndex[i]].containsEdge(e));
|
||
|
|
||
|
debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Every face's edge must be on that face
|
||
|
for (int f = 0; f < faceArray.size(); ++f) {
|
||
|
const MeshAlg::Face& face = faceArray[f];
|
||
|
for (int i = 0; i < 3; ++i) {
|
||
|
int e = face.edgeIndex[i];
|
||
|
int ei = (e >= 0) ? e : ~e;
|
||
|
debugAssert(edgeArray[ei].inFace(f));
|
||
|
|
||
|
// Make sure the edge is oriented appropriately
|
||
|
if (e >= 0) {
|
||
|
debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
|
||
|
} else {
|
||
|
debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
|
||
|
}
|
||
|
|
||
|
debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
(void)faceArray;
|
||
|
(void)edgeArray;
|
||
|
(void)vertexArray;
|
||
|
#endif // _DEBUG
|
||
|
}
|
||
|
|
||
|
} // G3D namespace
|