void TriangleMesh::buildAdjacency() { int nPruned = pruneInvalidFaces( m_edgeToFace ); int nFaces = numFaces(); // walk over all faces // and build an adjacency map: // edge -> adjacent face // if nothing was pruned, then it's already valid if( nPruned != 0 ) { m_edgeToFace.clear(); for( int f = 0; f < nFaces; ++f ) { Vector3i face = m_faces[ f ]; Vector2i e0 = face.xy; Vector2i e1 = face.yz; Vector2i e2 = face.zx(); m_edgeToFace[ e0 ] = f; m_edgeToFace[ e1 ] = f; m_edgeToFace[ e2 ] = f; } } // build face to face adjacency // for each face: // find 3 edges // flip edge: if the flipped edge has an adjacent face // add it as a neighbor of this face m_faceToFace.clear(); m_faceToFace.resize( nFaces ); for( int f = 0; f < nFaces; ++f ) { Vector3i face = m_faces[ f ]; // get 3 edge twins Vector2i e0t = face.yx(); Vector2i e1t = face.zy(); Vector2i e2t = face.xz(); if( m_edgeToFace.find( e0t ) != m_edgeToFace.end() ) { m_faceToFace[ f ].push_back( m_edgeToFace[ e0t ] ); } if( m_edgeToFace.find( e1t ) != m_edgeToFace.end() ) { m_faceToFace[ f ].push_back( m_edgeToFace[ e1t ] ); } if( m_edgeToFace.find( e2t ) != m_edgeToFace.end() ) { m_faceToFace[ f ].push_back( m_edgeToFace[ e2t ] ); } } // build edge to next edge adjacency // iterate over all faces m_edgeToPrevEdge.clear(); m_edgeToNextEdge.clear(); for( int f = 0; f < nFaces; ++f ) { Vector3i face = m_faces[ f ]; Vector2i e0 = face.xy; Vector2i e1 = face.yz; Vector2i e2 = face.zx(); m_edgeToPrevEdge[ e0 ] = e2; m_edgeToPrevEdge[ e1 ] = e0; m_edgeToPrevEdge[ e2 ] = e1; m_edgeToNextEdge[ e0 ] = e1; m_edgeToNextEdge[ e1 ] = e2; m_edgeToNextEdge[ e2 ] = e0; } // build vertex to outgoing edge adjacency int nVertices = numVertices(); m_vertexToOutgoingEdge.clear(); m_vertexToOutgoingEdge.resize( nVertices ); for( int f = 0; f < nFaces; ++f ) { Vector3i face = m_faces[ f ]; m_vertexToOutgoingEdge[ face[0] ] = face[ 1 ]; m_vertexToOutgoingEdge[ face[1] ] = face[ 2 ]; m_vertexToOutgoingEdge[ face[2] ] = face[ 0 ]; } // build vertex to vertex (one-ring neighborhoods) // for each vertex v // start with initial outgoing edge // next edge = edge->next->next->twin m_oneRingIsClosed.clear(); m_vertexToVertex.clear(); m_vertexToFace.clear(); m_oneRingIsClosed.resize( nVertices ); m_vertexToVertex.resize( nVertices ); m_vertexToFace.resize( nVertices ); for( int v = 0; v < nVertices; ++v ) { Vector2i initialOutgoingEdge{ v, m_vertexToOutgoingEdge[ v ] }; m_vertexToVertex[ v ].push_back( initialOutgoingEdge.y ); m_vertexToFace[ v ].push_back( m_edgeToFace[ initialOutgoingEdge ] ); Vector2i nextIncomingEdge = m_edgeToNextEdge[ m_edgeToNextEdge[ initialOutgoingEdge ] ]; Vector2i nextOutgoingEdge = nextIncomingEdge.yx(); while( !( isBoundaryEdge( nextIncomingEdge ) ) && nextOutgoingEdge != initialOutgoingEdge ) { m_vertexToVertex[ v ].push_back( nextIncomingEdge.x ); m_vertexToFace[ v ].push_back( m_edgeToFace[ nextOutgoingEdge ] ); nextIncomingEdge = m_edgeToNextEdge[ m_edgeToNextEdge[ nextOutgoingEdge ] ]; nextOutgoingEdge = nextIncomingEdge.yx(); } // if we looped around, great, we're done // otherwise, we hit a boundary, need to go the other way around! if( isBoundaryEdge( nextIncomingEdge ) ) { m_oneRingIsClosed[ v ] = false; // don't forget to push on the last vertex m_vertexToVertex[ v ].push_back( nextIncomingEdge.x ); // no face // check that the initial outgoing edge is not a boundary // (otherwise we're done) if( isBoundaryEdge( initialOutgoingEdge ) ) { continue; } // flip orientation: start from the initial outgoing edge // and go clockwise, pushing to the front Vector2i initialIncomingEdge = initialOutgoingEdge.yx(); nextOutgoingEdge = m_edgeToNextEdge[ initialIncomingEdge ]; nextIncomingEdge = nextOutgoingEdge.yx(); while( !( isBoundaryEdge( nextOutgoingEdge ) ) ) { m_vertexToVertex[ v ].push_front( nextOutgoingEdge.y ); m_vertexToFace[ v ].push_front( m_edgeToFace[ nextOutgoingEdge ] ); nextOutgoingEdge = m_edgeToNextEdge[ nextIncomingEdge ]; nextIncomingEdge = nextOutgoingEdge.yx(); } // don't forget the last vertex m_vertexToVertex[ v ].push_front( nextOutgoingEdge.y ); m_vertexToFace[ v ].push_front( m_edgeToFace[ nextOutgoingEdge ] ); } else { m_oneRingIsClosed[ v ] = true; } } }
int TriangleMesh::pruneInvalidFaces( std::map< Vector2i, int >& edgeToFace ) { // walk over all faces // build for each edge (v0,v1) // edgeToFace[ v0, v1 ] = face // if it already exists, we have a problem // and we will throw the face away int nFaces = numFaces(); edgeToFace.clear(); //edgeToFace.reserve( 3 * nFaces ); std::vector< Vector3i > validFaces; validFaces.reserve( nFaces ); //ProgressReporter pr( "Pruning invalid faces", nFaces ); int nPruned = 0; for( int f = 0; f < nFaces; ++f ) { Vector3i face = m_faces[ f ]; Vector2i e0 = face.xy; Vector2i e1 = face.yz; Vector2i e2 = face.zx(); if( edgeToFace.find( e0 ) == edgeToFace.end() && edgeToFace.find( e1 ) == edgeToFace.end() && edgeToFace.find( e2 ) == edgeToFace.end() ) { edgeToFace[ e0 ] = f; edgeToFace[ e1 ] = f; edgeToFace[ e2 ] = f; validFaces.push_back( face ); } else { ++nPruned; fprintf( stderr, "Found invalid face: (%d, %d, %d)\n", face.x, face.y, face.z ); if( edgeToFace.find( e0 ) != edgeToFace.end() ) { Vector3i existingFace = m_faces[ edgeToFace[ e0 ] ]; fprintf( stderr, "Existing face: (%d, %d, %d)\n", existingFace.x, existingFace.y, existingFace.z ); } if( edgeToFace.find( e1 ) != edgeToFace.end() ) { Vector3i existingFace = m_faces[ edgeToFace[ e1 ] ]; fprintf( stderr, "Existing face: (%d, %d, %d)\n", existingFace.x, existingFace.y, existingFace.z ); } if( edgeToFace.find( e2 ) != edgeToFace.end() ) { Vector3i existingFace = m_faces[ edgeToFace[ e2 ] ]; fprintf( stderr, "Existing face: (%d, %d, %d)\n", existingFace.x, existingFace.y, existingFace.z ); } } //pr.notifyAndPrintProgressString(); } if( nPruned > 0 ) { fprintf( stderr, "Pruned %d faces\n", nPruned ); m_faces = validFaces; } return nPruned; }