/*===========================================================================*\
* *
* OpenVolumeMesh *
* Copyright (C) 2011 by Computer Graphics Group, RWTH Aachen *
* www.openvolumemesh.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenVolumeMesh. *
* *
* OpenVolumeMesh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenVolumeMesh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenVolumeMesh. If not, *
* see . *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision$ *
* $Date$ *
* $LastChangedBy$ *
* *
\*===========================================================================*/
#include "TopologyKernel.hh"
namespace OpenVolumeMesh {
// Initialize constants
const VertexHandle TopologyKernel::InvalidVertexHandle = VertexHandle(-1);
const EdgeHandle TopologyKernel::InvalidEdgeHandle = EdgeHandle(-1);
const HalfEdgeHandle TopologyKernel::InvalidHalfEdgeHandle = HalfEdgeHandle(-1);
const FaceHandle TopologyKernel::InvalidFaceHandle = FaceHandle(-1);
const HalfFaceHandle TopologyKernel::InvalidHalfFaceHandle = HalfFaceHandle(-1);
const CellHandle TopologyKernel::InvalidCellHandle = CellHandle(-1);
TopologyKernel::TopologyKernel() :
n_vertices_(0u),
has_vertex_adjacencies_(false),
has_edge_adjacencies_(false),
has_face_adjacencies_(false) {
}
TopologyKernel::~TopologyKernel() {
}
//========================================================================================
/// Add edge
EdgeHandle TopologyKernel::add_edge(const VertexHandle& _fromVertex,
const VertexHandle& _toVertex) {
if((unsigned int)_fromVertex >= n_vertices() || (unsigned int)_toVertex >= n_vertices()) {
std::cerr << "Vertex handle is out of bounds!" << std::endl;
return InvalidEdgeHandle;
}
// Test if edge does not exist, yet
for(unsigned int i = 0; i < edges_.size(); ++i) {
if(edge(EdgeHandle(i)).from_vertex() == _fromVertex && edge(EdgeHandle(i)).to_vertex() == _toVertex) {
return EdgeHandle(i);
} else if(edge(EdgeHandle(i)).from_vertex() == _toVertex && edge(EdgeHandle(i)).to_vertex() == _fromVertex) {
return EdgeHandle(i);
}
}
// Create edge object
OpenVolumeMeshEdge e(_fromVertex, _toVertex);
// Store edge locally
edges_.push_back(e);
// Resize props
resize_eprops(n_edges());
// Get handle of recently created edge
return EdgeHandle((int)edges_.size()-1);
}
//========================================================================================
/// Add face via incident edges
FaceHandle TopologyKernel::add_face(const std::vector& _halfedges, bool _topologyCheck) {
// Test if all edges are valid
for(std::vector::const_iterator it = _halfedges.begin();
it != _halfedges.end(); ++it) {
if((unsigned int)*it >= edges_.size() * 2u) {
std::cerr << "Halfedge handle out of bounds!" << std::endl;
return InvalidFaceHandle;
}
}
// Perform topology check
if(_topologyCheck) {
/*
* Test if halfedges are connected
*
* The test works as follows:
* For every edge in the parameter vector
* put all incident vertices into a
* set of either "from"-vertices or "to"-vertices,
* respectively.
* If and only if all edges are connected,
* then both sets are identical.
*/
std::set fromVertices;
std::set toVertices;
for(std::vector::const_iterator it = _halfedges.begin();
it != _halfedges.end(); ++it) {
fromVertices.insert(halfedge(*it).from_vertex());
toVertices.insert(halfedge(*it).to_vertex());
}
for(std::set::const_iterator v_it = fromVertices.begin();
v_it != fromVertices.end(); ++v_it) {
if(toVertices.count(*v_it) != 1) {
std::cerr << "The specified halfedges are not connected!" << std::endl;
return InvalidFaceHandle;
}
}
// The halfedges are now guaranteed to be connected
}
// Create face
OpenVolumeMeshFace face(_halfedges);
faces_.push_back(face);
// Get added face's handle
FaceHandle fh(faces_.size() - 1);
// Resize props
resize_fprops(n_faces());
// Return handle of recently created face
return fh;
}
//========================================================================================
/// Add face via incident vertices
/// Define the _vertices in counter-clockwise order (from the "outside")
FaceHandle TopologyKernel::add_face(const std::vector& _vertices) {
// Test if all vertices exist
for(std::vector::const_iterator it = _vertices.begin();
it != _vertices.end(); ++it) {
if((unsigned int)*it >= n_vertices()) {
std::cerr << "Vertex handle out of bounds!" << std::endl;
return InvalidFaceHandle;
}
}
// Add edge for each pair of vertices
std::vector halfedges;
std::vector::const_iterator it = _vertices.begin();
for(; (it+1) != _vertices.end(); ++it) {
EdgeHandle e_idx = add_edge(*it, *(it+1));
// Swap halfedge if edge already existed and
// has been initially defined in reverse orientation
int swap = 0;
if(edge(e_idx).to_vertex() == *it) swap = 1;
halfedges.push_back(halfedge_handle(e_idx, swap));
}
EdgeHandle e_idx = add_edge(*it, *_vertices.begin());
int swap = 0;
if(edge(e_idx).to_vertex() == *it) swap = 1;
halfedges.push_back(halfedge_handle(e_idx, swap));
// Add face
#ifndef NDEBUG
return add_face(halfedges, true);
#else
return add_face(halfedges, false);
#endif
}
//========================================================================================
/// Add cell via incident halffaces
CellHandle TopologyKernel::add_cell(const std::vector& _halffaces, bool _topologyCheck) {
// Test if halffaces have valid indices
for(std::vector::const_iterator it = _halffaces.begin();
it != _halffaces.end(); ++it) {
if((unsigned int)*it >= faces_.size() * 2u) {
std::cerr << "HalfFace handle is out of bounds!" << std::endl;
return InvalidCellHandle;
}
}
// Perform topology check
if(_topologyCheck) {
/*
* Test if all halffaces are connected and form a two-manifold
* => Cell is closed
*
* This test is simple: The number of involved half-edges has to be
* exactly twice the number of involved edges.
*/
std::set incidentHalfedges;
std::set incidentEdges;
for(std::vector::const_iterator it = _halffaces.begin();
it != _halffaces.end(); ++it) {
OpenVolumeMeshFace hface = halfface(*it);
for(std::vector::const_iterator he_it = hface.halfedges().begin();
he_it != hface.halfedges().end(); ++he_it) {
incidentHalfedges.insert(*he_it);
incidentEdges.insert(edge_handle(*he_it));
}
}
if(incidentHalfedges.size() != (incidentEdges.size() * 2u)) {
std::cerr << "The specified halffaces are not connected!" << std::endl;
return InvalidCellHandle;
}
// The halffaces are now guaranteed to form a two-manifold
}
// Create new cell
OpenVolumeMeshCell cell(_halffaces);
cells_.push_back(cell);
// Resize props
resize_cprops(n_cells());
return CellHandle((int)cells_.size()-1);
}
//========================================================================================
/// Get edge with handle _edgeHandle
const OpenVolumeMeshEdge& TopologyKernel::edge(const EdgeHandle& _edgeHandle) const {
// Test if edge is valid
assert((unsigned int)_edgeHandle < edges_.size());
assert(_edgeHandle >= 0);
return edges_[_edgeHandle];
}
//========================================================================================
/// Get face with handle _faceHandle
const OpenVolumeMeshFace& TopologyKernel::face(const FaceHandle& _faceHandle) const {
// Test if face is valid
assert((unsigned int)_faceHandle < faces_.size());
assert(_faceHandle >= 0);
return faces_[_faceHandle];
}
//========================================================================================
/// Get cell with handle _cellHandle
const OpenVolumeMeshCell& TopologyKernel::cell(const CellHandle& _cellHandle) const {
// Test if cell is valid
assert((unsigned int)_cellHandle < cells_.size());
assert(_cellHandle >= 0);
return cells_[_cellHandle];
}
//========================================================================================
/// Get edge with handle _edgeHandle
OpenVolumeMeshEdge& TopologyKernel::edge(const EdgeHandle& _edgeHandle) {
// Test if edge is valid
assert((unsigned int)_edgeHandle < edges_.size());
assert(_edgeHandle >= 0);
return edges_[_edgeHandle];
}
//========================================================================================
/// Get face with handle _faceHandle
OpenVolumeMeshFace& TopologyKernel::face(const FaceHandle& _faceHandle) {
// Test if face is valid
assert((unsigned int)_faceHandle < faces_.size());
assert(_faceHandle >= 0);
return faces_[_faceHandle];
}
//========================================================================================
/// Get cell with handle _cellHandle
OpenVolumeMeshCell& TopologyKernel::cell(const CellHandle& _cellHandle) {
// Test if cell is valid
assert((unsigned int)_cellHandle < cells_.size());
assert(_cellHandle >= 0);
return cells_[_cellHandle];
}
//========================================================================================
/// Get edge that corresponds to halfedge with handle _halfEdgeHandle
const OpenVolumeMeshEdge TopologyKernel::halfedge(const HalfEdgeHandle& _halfEdgeHandle) const {
// Is handle in range?
assert((unsigned int)_halfEdgeHandle < (edges_.size() * 2));
assert(_halfEdgeHandle >= 0);
// In case the handle is even, just return the corresponding edge
/// Otherwise return the opposite halfedge via opposite()
if(_halfEdgeHandle % 2 == 0)
return edges_[(int)(_halfEdgeHandle / 2)];
else
return opposite_halfedge(edges_[(int)(_halfEdgeHandle / 2)]);
}
//========================================================================================
/// Get face that corresponds to halfface with handle _halfFaceHandle
const OpenVolumeMeshFace TopologyKernel::halfface(const HalfFaceHandle& _halfFaceHandle) const {
// Is handle in range?
assert((unsigned int)_halfFaceHandle < (faces_.size() * 2));
assert(_halfFaceHandle >= 0);
// In case the handle is not even, just return the corresponding face
// Otherwise return the opposite halfface via opposite()
if(_halfFaceHandle % 2 == 0)
return faces_[(int)(_halfFaceHandle / 2)];
else
return opposite_halfface(faces_[(int)(_halfFaceHandle / 2)]);
}
//========================================================================================
/// Get opposite halfedge that corresponds to halfedge with handle _halfEdgeHandle
const OpenVolumeMeshEdge TopologyKernel::opposite_halfedge(const HalfEdgeHandle& _halfEdgeHandle) const {
// Is handle in range?
assert(_halfEdgeHandle >= 0);
assert((unsigned int)_halfEdgeHandle < (edges_.size() * 2));
// In case the handle is not even, just return the corresponding edge
// Otherwise return the opposite halfedge via opposite()
if(_halfEdgeHandle % 2 != 0)
return edges_[(int)(_halfEdgeHandle / 2)];
else
return opposite_halfedge(edges_[(int)(_halfEdgeHandle / 2)]);
}
//========================================================================================
/// Get opposite halfface that corresponds to halfface with handle _halfFaceHandle
const OpenVolumeMeshFace TopologyKernel::opposite_halfface(const HalfFaceHandle& _halfFaceHandle) const {
// Is handle in range?
assert(_halfFaceHandle >= 0);
assert((unsigned int)_halfFaceHandle < (faces_.size() * 2));
// In case the handle is not even, just return the corresponding face
// Otherwise return the opposite via the first face's opposite() function
if(_halfFaceHandle % 2 != 0)
return faces_[(int)(_halfFaceHandle / 2)];
else
return opposite_halfface(faces_[(int)(_halfFaceHandle / 2)]);
}
//========================================================================================
const HalfEdgeHandle TopologyKernel::halfedge(const VertexHandle& _vh1, const VertexHandle& _vh2) const {
assert(_vh1.idx() < (int)n_vertices());
assert(_vh2.idx() < (int)n_vertices());
for(VertexOHalfEdgeIter voh_it = voh_iter(_vh1); voh_it.valid(); ++voh_it) {
if(halfedge(*voh_it).to_vertex() == _vh2) {
return *voh_it;
}
}
return InvalidHalfEdgeHandle;
}
//========================================================================================
const HalfEdgeHandle TopologyKernel::next_halfedge_in_halfface(const HalfEdgeHandle& _heh, const HalfFaceHandle& _hfh) const {
assert((unsigned int)_hfh < faces_.size() * 2u);
assert((unsigned int)_heh < edges_.size() * 2u);
std::vector hes = halfface(_hfh).halfedges();
for(std::vector::const_iterator it = hes.begin();
it != hes.end(); ++it) {
if(*it == _heh) {
if((it + 1) != hes.end()) return *(it + 1);
else return *hes.begin();
}
}
return InvalidHalfEdgeHandle;
}
//========================================================================================
const HalfEdgeHandle TopologyKernel::prev_halfedge_in_halfface(const HalfEdgeHandle& _heh, const HalfFaceHandle& _hfh) const {
assert((unsigned int)_hfh < faces_.size() * 2u);
assert((unsigned int)_heh < edges_.size() * 2u);
std::vector hes = halfface(_hfh).halfedges();
for(std::vector::const_iterator it = hes.begin();
it != hes.end(); ++it) {
if(*it == _heh) {
if(it != hes.begin()) return *(it - 1);
else return *(hes.end() - 1);
}
}
return InvalidHalfEdgeHandle;
}
//========================================================================================
void TopologyKernel::update_adjacencies() {
update_vertex_adjacencies();
update_edge_adjacencies();
update_face_adjacencies();
}
//========================================================================================
void TopologyKernel::update_vertex_adjacencies() {
// Clear adjacencies
outgoing_hes_per_vertex_.clear();
outgoing_hes_per_vertex_.resize(n_vertices());
// Store outgoing halfedges per vertex
unsigned int n_vertices = edges_.size();
for(unsigned int i = 0; i < n_vertices; ++i) {
VertexHandle from = edges_[i].from_vertex();
if((unsigned int)from >= outgoing_hes_per_vertex_.size()) {
std::cerr << "update_adjacencies(): Vertex handle is out of bounds!" << std::endl;
return;
}
outgoing_hes_per_vertex_[from].push_back(halfedge_handle(EdgeHandle(i), 0));
VertexHandle to = edges_[i].to_vertex();
if((unsigned int)to >= outgoing_hes_per_vertex_.size()) {
std::cerr << "update_adjacencies(): Vertex handle is out of bounds!" << std::endl;
return;
}
// Store opposite halfedge handle
outgoing_hes_per_vertex_[to].push_back(halfedge_handle(EdgeHandle(i), 1));
}
has_vertex_adjacencies_ = true;
}
//========================================================================================
void TopologyKernel::update_edge_adjacencies() {
// Clear
incident_hfs_per_he_.clear();
incident_hfs_per_he_.resize(edges_.size() * 2u);
// Store incident halffaces per halfedge
unsigned int n_faces = faces_.size();
for(unsigned int i = 0; i < n_faces; ++i) {
std::vector halfedges = faces_[i].halfedges();
// Go over all halfedges
for(std::vector::const_iterator he_it = halfedges.begin();
he_it != halfedges.end(); ++he_it) {
incident_hfs_per_he_[*he_it].push_back(halfface_handle(FaceHandle(i), 0));
incident_hfs_per_he_[opposite_halfedge_handle(*he_it)].push_back(
halfface_handle(FaceHandle(i), 1));
}
}
/* Put halffaces in clockwise order via the
* same cell property which now exists.
* Note, this only works for manifold configurations though.
* Proceed as follows: Pick one starting halfface. Assuming
* that all halfface normals point into the incident cell,
* we find the adjacent halfface within the incident cell
* along the considered halfedge. We set the found halfface
* to be the one to be processed next. If we reach an outside
* region, we try to go back from the starting halfface in reverse
* order. If the complex is properly connected (the pairwise
* intersection of two adjacent 3-dimensional cells is always
* a 2-dimensional entity, namely a facet), such an ordering
* always exists and will be found. If not, a correct order
* can not be given and, as a result, the related iterators
* will address the related entities in an arbitrary fashion.
*/
unsigned int n_edges = edges_.size();
for(unsigned int i = 0; i < n_edges; ++i) {
for(unsigned char s = 0; s <= 1; s++) {
HalfEdgeHandle cur_he = halfedge_handle(i, s);
std::vector new_halffaces;
HalfFaceHandle start_hf = InvalidHalfFaceHandle;
HalfFaceHandle cur_hf = InvalidHalfFaceHandle;
// Start with one incident halfface and go
// into the first direction
if(incident_hfs_per_he_[cur_he].size() != 0) {
// Get start halfface
cur_hf = *incident_hfs_per_he_[cur_he].begin();
start_hf = cur_hf;
while(cur_hf != InvalidHalfFaceHandle) {
// Add halfface
new_halffaces.push_back(cur_hf);
// Go to next halfface
cur_hf = adjacent_halfface_in_cell(cur_hf, cur_he);
if(cur_hf != InvalidHalfFaceHandle)
cur_hf = opposite_halfface_handle(cur_hf);
// End when we're through
if(cur_hf == start_hf) break;
}
// First direction has terminated
// If new_halffaces has the same size as old (unordered)
// vector of incident halffaces, we are done here
// If not, try the other way round
if(new_halffaces.size() != incident_hfs_per_he_[cur_he].size()) {
// Get opposite of start halfface
cur_hf = start_hf;
while(cur_hf != InvalidHalfFaceHandle) {
cur_hf = opposite_halfface_handle(cur_hf);
cur_hf = adjacent_halfface_in_cell(cur_hf, cur_he);
if(cur_hf == start_hf) break;
if(cur_hf != InvalidHalfFaceHandle)
new_halffaces.insert(new_halffaces.begin(), cur_hf);
else break;
}
}
// Everything worked just fine, set the new ordered vector
if(new_halffaces.size() == incident_hfs_per_he_[cur_he].size()) {
incident_hfs_per_he_[cur_he] = new_halffaces;
}
}
}
}
has_edge_adjacencies_ = true;
}
//========================================================================================
void TopologyKernel::update_face_adjacencies() {
// Clear
incident_cell_per_hf_.clear();
incident_cell_per_hf_.resize(faces_.size() * 2u, InvalidCellHandle);
unsigned int n_cells = cells_.size();
for(unsigned int i = 0; i < n_cells; ++i) {
std::vector halffaces = cells_[i].halffaces();
// Go over all halffaces
for(std::vector::const_iterator hf_it = halffaces.begin();
hf_it != halffaces.end(); ++hf_it) {
if(incident_cell_per_hf_[*hf_it] == InvalidCellHandle) {
incident_cell_per_hf_[*hf_it] = CellHandle(i);
} else {
std::cerr << "Detected non-three-manifold configuration!" << std::endl;
std::cerr << "Connectivity probably won't work." << std::endl;
continue;
}
}
}
// Compute boundary faces
// Clear
boundary_faces_.clear();
// Get boundary faces
unsigned int n_faces = faces_.size();
for(unsigned int i = 0; i < n_faces; ++i) {
if(incident_cell_per_hf_[halfface_handle(FaceHandle(i), 0)] == InvalidCellHandle ||
incident_cell_per_hf_[halfface_handle(FaceHandle(i), 1)] == InvalidCellHandle) {
// If at least one of two halffaces does not have an
// incident cell it is a boundary face
boundary_faces_.push_back(FaceHandle(i));
}
}
has_face_adjacencies_ = true;
}
//========================================================================================
HalfFaceHandle
TopologyKernel::adjacent_halfface_in_cell(const HalfFaceHandle& _halfFaceHandle, const HalfEdgeHandle& _halfEdgeHandle) const {
if((unsigned int)_halfFaceHandle >= incident_cell_per_hf_.size() || _halfFaceHandle < 0) {
return InvalidHalfFaceHandle;
}
if(incident_cell_per_hf_[_halfFaceHandle] == InvalidCellHandle) {
// Specified halfface is on the outside of the complex
return InvalidHalfFaceHandle;
}
OpenVolumeMeshCell c = cell(incident_cell_per_hf_[_halfFaceHandle]);
// Make sure that _halfFaceHandle is incident to _halfEdgeHandle
bool skipped = false;
bool found = false;
HalfFaceHandle idx = InvalidHalfFaceHandle;
for(std::vector::const_iterator hf_it = c.halffaces().begin();
hf_it != c.halffaces().end(); ++hf_it) {
if(*hf_it == _halfFaceHandle) {
skipped = true;
continue;
}
OpenVolumeMeshFace hf = halfface(*hf_it);
for(std::vector::const_iterator he_it = hf.halfedges().begin();
he_it != hf.halfedges().end(); ++he_it) {
if(edge_handle(*he_it) == edge_handle(_halfEdgeHandle)) {
found = true;
idx = *hf_it;
}
if(skipped && found) break;
}
if(skipped && found) break;
}
return ((skipped && found) ? idx : InvalidHalfFaceHandle);
}
//========================================================================================
CellHandle TopologyKernel::incident_cell(const HalfFaceHandle& _halfFaceHandle) const {
if((unsigned int)_halfFaceHandle >= incident_cell_per_hf_.size() || _halfFaceHandle.idx() < 0) {
return InvalidCellHandle;
}
return incident_cell_per_hf_[_halfFaceHandle];
}
} // Namespace OpenVolumeMesh