/*===========================================================================*\
* *
* 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$ *
* *
\*===========================================================================*/
#ifndef NDEBUG
#include
#endif
#include
#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),
v_bottom_up_(true),
e_bottom_up_(true),
f_bottom_up_(true),
deferred_deletion(true),
fast_deletion(true)
{
}
TopologyKernel::~TopologyKernel() {
}
//========================================================================================
VertexHandle TopologyKernel::add_vertex() {
++n_vertices_;
vertex_deleted_.push_back(false);
// Create item for vertex bottom-up incidences
if(v_bottom_up_) {
outgoing_hes_per_vertex_.resize(n_vertices_);
}
// Resize vertex props
resize_vprops(n_vertices_);
// Return 0-indexed handle
return VertexHandle((int)(n_vertices_ - 1));
}
//========================================================================================
/// Add edge
EdgeHandle TopologyKernel::add_edge(const VertexHandle& _fromVertex,
const VertexHandle& _toVertex,
bool _allowDuplicates) {
// If the conditions are not fulfilled, assert will fail (instead
// of returning an invalid handle)
assert(_fromVertex.is_valid() && (size_t)_fromVertex.idx() < n_vertices());
assert(_toVertex.is_valid() && (size_t)_toVertex.idx() < n_vertices());
// Test if edge does not exist, yet
if(!_allowDuplicates) {
if(v_bottom_up_) {
assert((size_t)_fromVertex.idx() < outgoing_hes_per_vertex_.size());
std::vector& ohes = outgoing_hes_per_vertex_[_fromVertex.idx()];
for(std::vector::const_iterator he_it = ohes.begin(),
he_end = ohes.end(); he_it != he_end; ++he_it) {
if(halfedge(*he_it).to_vertex() == _toVertex) {
return edge_handle(*he_it);
}
}
} else {
for(size_t 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);
edge_deleted_.push_back(false);
// Resize props
resize_eprops(n_edges());
EdgeHandle eh((int)edges_.size()-1);
// Update vertex bottom-up incidences
if(v_bottom_up_) {
assert((size_t)_fromVertex.idx() < outgoing_hes_per_vertex_.size());
assert((size_t)_toVertex.idx() < outgoing_hes_per_vertex_.size());
outgoing_hes_per_vertex_[_fromVertex.idx()].push_back(halfedge_handle(eh, 0));
outgoing_hes_per_vertex_[_toVertex.idx()].push_back(halfedge_handle(eh, 1));
}
// Create item for edge bottom-up incidences
if(e_bottom_up_) {
incident_hfs_per_he_.resize(n_halfedges());
}
// Get handle of recently created edge
return eh;
}
//========================================================================================
/// Add face via incident edges
FaceHandle TopologyKernel::add_face(const std::vector& _halfedges, bool _topologyCheck) {
#ifndef NDEBUG
// Assert that halfedges are valid
for(std::vector::const_iterator it = _halfedges.begin(),
end = _halfedges.end(); it != end; ++it)
assert(it->is_valid() && (size_t)it->idx() < edges_.size() * 2u);
#endif
// 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(),
end = _halfedges.end(); it != end; ++it) {
fromVertices.insert(halfedge(*it).from_vertex());
toVertices.insert(halfedge(*it).to_vertex());
}
for(std::set::const_iterator v_it = fromVertices.begin(),
v_end = fromVertices.end(); v_it != v_end; ++v_it) {
if(toVertices.count(*v_it) != 1) {
// The situation here is different, the caller has requested a
// topology check and expects an invalid handle if the half-edges
// are not connected. Give him a message in debug mode.
#ifndef NDEBUG
std::cerr << "add_face(): The specified halfedges are not connected!" << std::endl;
#endif
return InvalidFaceHandle;
}
}
// The halfedges are now guaranteed to be connected
}
// Create face
OpenVolumeMeshFace face(_halfedges);
faces_.push_back(face);
face_deleted_.push_back(false);
// Get added face's handle
FaceHandle fh(faces_.size() - 1);
// Resize props
resize_fprops(n_faces());
// Update edge bottom-up incidences
if(e_bottom_up_) {
for(std::vector::const_iterator it = _halfedges.begin(),
end = _halfedges.end(); it != end; ++it) {
assert((size_t)it->idx() < incident_hfs_per_he_.size());
assert((size_t)opposite_halfedge_handle(*it).idx() < incident_hfs_per_he_.size());
incident_hfs_per_he_[it->idx()].push_back(halfface_handle(fh, 0));
incident_hfs_per_he_[opposite_halfedge_handle(*it).idx()].push_back(halfface_handle(fh, 1));
}
}
// Create item for face bottom-up incidences
if(f_bottom_up_) {
incident_cell_per_hf_.resize(n_halffaces(), InvalidCellHandle);
}
// 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) {
#ifndef NDEBUG
// Assert that all vertices have valid indices
for(std::vector::const_iterator it = _vertices.begin(),
end = _vertices.end(); it != end; ++it)
assert(it->is_valid() && (size_t)it->idx() < n_vertices());
#endif
// Add edge for each pair of vertices
std::vector halfedges;
std::vector::const_iterator it = _vertices.begin();
std::vector::const_iterator end = _vertices.end();
for(; (it+1) != 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
}
//========================================================================================
void TopologyKernel::reorder_incident_halffaces(const EdgeHandle& _eh) {
/* 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.
*/
for(unsigned char s = 0; s <= 1; s++) {
HalfEdgeHandle cur_he = halfedge_handle(_eh, s);
std::vector new_halffaces;
HalfFaceHandle start_hf = InvalidHalfFaceHandle;
HalfFaceHandle cur_hf = InvalidHalfFaceHandle;
// Start with one incident halfface and go into the first direction
assert((size_t)cur_he.idx() < incident_hfs_per_he_.size());
if(incident_hfs_per_he_[cur_he.idx()].size() != 0) {
// Get start halfface
cur_hf = *incident_hfs_per_he_[cur_he.idx()].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;
// if one of the faces of the cell was already incident to another cell we need this check
// to prevent running into an infinite loop.
if(std::find(new_halffaces.begin(), new_halffaces.end(), cur_hf) != new_halffaces.end()) 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.idx()].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;
// if one of the faces of the cell was already incident to another cell we need this check
// to prevent running into an infinite loop.
if(std::find(new_halffaces.begin(), new_halffaces.end(), cur_hf) != new_halffaces.end()) break;
}
}
// Everything worked just fine, set the new ordered vector
if(new_halffaces.size() == incident_hfs_per_he_[cur_he.idx()].size()) {
incident_hfs_per_he_[cur_he.idx()] = new_halffaces;
}
}
}
}
//========================================================================================
/// Add cell via incident halffaces
CellHandle TopologyKernel::add_cell(const std::vector& _halffaces, bool _topologyCheck) {
#ifndef NDEBUG
// Assert that halffaces have valid indices
for(std::vector::const_iterator it = _halffaces.begin(),
end = _halffaces.end(); it != end; ++it)
assert(it->is_valid() && ((size_t)it->idx() < faces_.size() * 2u));
#endif
// 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(),
end = _halffaces.end(); it != end; ++it) {
OpenVolumeMeshFace hface = halfface(*it);
for(std::vector::const_iterator he_it = hface.halfedges().begin(),
he_end = hface.halfedges().end(); he_it != he_end; ++he_it) {
incidentHalfedges.insert(*he_it);
incidentEdges.insert(edge_handle(*he_it));
}
}
if(incidentHalfedges.size() != (incidentEdges.size() * 2u)) {
#ifndef NDEBUG
std::cerr << "add_cell(): The specified half-faces are not connected!" << std::endl;
#endif
return InvalidCellHandle;
}
// The halffaces are now guaranteed to form a two-manifold
}
// Create new cell
OpenVolumeMeshCell cell(_halffaces);
cells_.push_back(cell);
cell_deleted_.push_back(false);
// Resize props
resize_cprops(n_cells());
CellHandle ch((int)cells_.size()-1);
// Update face bottom-up incidences
if(f_bottom_up_) {
std::set cell_edges;
for(std::vector::const_iterator it = _halffaces.begin(),
end = _halffaces.end(); it != end; ++it) {
assert((size_t)it->idx() < incident_cell_per_hf_.size());
#ifndef NDEBUG
if(_topologyCheck) {
if(incident_cell_per_hf_[it->idx()] != InvalidCellHandle) {
// Shouldn't this situation be dealt with before adding the
// cell and return InvalidCellHandle in this case?
// Mike: Not if the user intends to add non-manifold
// configurations. Although, in this case, he should be
// warned about it.
std::cerr << "add_cell(): One of the specified half-faces is already incident to another cell!" << std::endl;
}
}
#endif
// Overwrite incident cell for current half-face
incident_cell_per_hf_[it->idx()] = ch;
// Collect all edges of cell
const std::vector hes = halfface(*it).halfedges();
for(std::vector::const_iterator he_it = hes.begin(),
he_end = hes.end(); he_it != he_end; ++he_it) {
cell_edges.insert(edge_handle(*he_it));
}
}
if(e_bottom_up_) {
// Try to reorder all half-faces w.r.t.
// their incident half-edges such that all
// half-faces are in cyclic order around
// a half-edge
for(std::set::const_iterator e_it = cell_edges.begin(),
e_end = cell_edges.end(); e_it != e_end; ++e_it) {
reorder_incident_halffaces(*e_it);
}
}
}
return ch;
}
//========================================================================================
/// Set the vertices of an edge
void TopologyKernel::set_edge(const EdgeHandle& _eh, const VertexHandle& _fromVertex, const VertexHandle& _toVertex) {
Edge& e = edge(_eh);
// Update bottom-up entries
if(has_vertex_bottom_up_incidences()) {
const VertexHandle& fv = e.from_vertex();
const VertexHandle& tv = e.to_vertex();
const HalfEdgeHandle heh0 = halfedge_handle(_eh, 0);
const HalfEdgeHandle heh1 = halfedge_handle(_eh, 1);
std::vector::iterator h_end =
std::remove(outgoing_hes_per_vertex_[fv.idx()].begin(), outgoing_hes_per_vertex_[fv.idx()].end(), heh0);
outgoing_hes_per_vertex_[fv.idx()].resize(h_end - outgoing_hes_per_vertex_[fv.idx()].begin());
h_end = std::remove(outgoing_hes_per_vertex_[tv.idx()].begin(), outgoing_hes_per_vertex_[tv.idx()].end(), heh1);
outgoing_hes_per_vertex_[tv.idx()].resize(h_end - outgoing_hes_per_vertex_[tv.idx()].begin());
outgoing_hes_per_vertex_[_fromVertex.idx()].push_back(heh0);
outgoing_hes_per_vertex_[_toVertex.idx()].push_back(heh1);
}
e.set_from_vertex(_fromVertex);
e.set_to_vertex(_toVertex);
}
//========================================================================================
/// Set the half-edges of a face
void TopologyKernel::set_face(const FaceHandle& _fh, const std::vector& _hes) {
Face& f = face(_fh);
if(has_edge_bottom_up_incidences()) {
const HalfFaceHandle hf0 = halfface_handle(_fh, 0);
const HalfFaceHandle hf1 = halfface_handle(_fh, 1);
const std::vector& hes = f.halfedges();
for(std::vector::const_iterator he_it = hes.begin(),
he_end = hes.end(); he_it != he_end; ++he_it) {
std::vector::iterator h_end =
std::remove(incident_hfs_per_he_[he_it->idx()].begin(),
incident_hfs_per_he_[he_it->idx()].end(), hf0);
incident_hfs_per_he_[he_it->idx()].resize(h_end - incident_hfs_per_he_[he_it->idx()].begin());
h_end = std::remove(incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].begin(),
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].end(), hf1);
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].resize(h_end - incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].begin());
}
for(std::vector::const_iterator he_it = _hes.begin(),
he_end = _hes.end(); he_it != he_end; ++he_it) {
incident_hfs_per_he_[he_it->idx()].push_back(hf0);
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].push_back(hf1);
}
// TODO: Reorder incident half-faces
}
f.set_halfedges(_hes);
}
//========================================================================================
/// Set the half-faces of a cell
void TopologyKernel::set_cell(const CellHandle& _ch, const std::vector& _hfs) {
Cell& c = cell(_ch);
if(has_face_bottom_up_incidences()) {
const std::vector& hfs = c.halffaces();
for(std::vector::const_iterator hf_it = hfs.begin(),
hf_end = hfs.end(); hf_it != hf_end; ++hf_it) {
incident_cell_per_hf_[*hf_it] = InvalidCellHandle;
}
for(std::vector::const_iterator hf_it = _hfs.begin(),
hf_end = _hfs.end(); hf_it != hf_end; ++hf_it) {
incident_cell_per_hf_[*hf_it] = _ch;
}
}
c.set_halffaces(_hfs);
}
//========================================================================================
/**
* \brief Delete vertex from mesh
*
* Get all incident higher-dimensional entities and delete the complete
* subtree of the mesh incident to vertex _h.
* In this function all incident entities are gathered
* and deleted using the delete_*_core functions
* that do the actual deletion including the update
* of the bottom-up incidences, etc.
*
* @param _h The handle to the vertex to be deleted
*/
VertexIter TopologyKernel::delete_vertex(const VertexHandle& _h) {
std::vector vs;
vs.push_back(_h);
std::set incidentEdges_s;
get_incident_edges(vs, incidentEdges_s);
std::set incidentFaces_s;
get_incident_faces(incidentEdges_s, incidentFaces_s);
std::set incidentCells_s;
get_incident_cells(incidentFaces_s, incidentCells_s);
// Delete cells
for(std::set::const_reverse_iterator c_it = incidentCells_s.rbegin(),
c_end = incidentCells_s.rend(); c_it != c_end; ++c_it) {
delete_cell_core(*c_it);
}
// Delete faces
for(std::set::const_reverse_iterator f_it = incidentFaces_s.rbegin(),
f_end = incidentFaces_s.rend(); f_it != f_end; ++f_it) {
delete_face_core(*f_it);
}
// Delete edges
for(std::set::const_reverse_iterator e_it = incidentEdges_s.rbegin(),
e_end = incidentEdges_s.rend(); e_it != e_end; ++e_it) {
delete_edge_core(*e_it);
}
// Delete vertex
return delete_vertex_core(_h);
}
//========================================================================================
/**
* \brief Delete edge from mesh
*
* Get all incident higher-dimensional entities and delete the complete
* subtree of the mesh incident to edge _h.
* In this function all incident entities are gathered
* and deleted using the delete_*_core functions
* that do the actual deletion including the update
* of the bottom-up incidences, etc.
*
* @param _h The handle to the edge to be deleted
*/
EdgeIter TopologyKernel::delete_edge(const EdgeHandle& _h) {
std::vector es;
es.push_back(_h);
std::set incidentFaces_s;
get_incident_faces(es, incidentFaces_s);
std::set incidentCells_s;
get_incident_cells(incidentFaces_s, incidentCells_s);
// Delete cells
for(std::set::const_reverse_iterator c_it = incidentCells_s.rbegin(),
c_end = incidentCells_s.rend(); c_it != c_end; ++c_it) {
delete_cell_core(*c_it);
}
// Delete faces
for(std::set::const_reverse_iterator f_it = incidentFaces_s.rbegin(),
f_end = incidentFaces_s.rend(); f_it != f_end; ++f_it) {
delete_face_core(*f_it);
}
// Delete edge
return delete_edge_core(_h);
}
//========================================================================================
/**
* \brief Delete face from mesh
*
* Get all incident higher-dimensional entities and delete the complete
* subtree of the mesh incident to face _h.
* In this function all incident entities are gathered
* and deleted using the delete_*_core functions
* that do the actual deletion including the update
* of the bottom-up incidences, etc.
*
* @param _h The handle to the face to be deleted
*/
FaceIter TopologyKernel::delete_face(const FaceHandle& _h) {
std::vector fs;
fs.push_back(_h);
std::set incidentCells_s;
get_incident_cells(fs, incidentCells_s);
// Delete cells
for(std::set::const_reverse_iterator c_it = incidentCells_s.rbegin(),
c_end = incidentCells_s.rend(); c_it != c_end; ++c_it) {
delete_cell_core(*c_it);
}
// Delete face
return delete_face_core(_h);
}
//========================================================================================
/**
* \brief Delete cell from mesh
*
* Since there's no higher dimensional incident
* entity to a cell, we can safely delete it from the
* mesh.
*
* @param _h The handle to the cell to be deleted
*/
CellIter TopologyKernel::delete_cell(const CellHandle& _h) {
return delete_cell_core(_h);
}
/**
* \brief Delete all entities that are marked as deleted
*/
void TopologyKernel::collect_garbage()
{
if (!deferred_deletion_enabled())
return; // nothing todo
deferred_deletion = false;
for (unsigned int i = n_cells(); i > 0; --i)
if (is_deleted(CellHandle(i-1)))
{
cell_deleted_[i-1] = false;
delete_cell_core(CellHandle(i-1));
}
for (unsigned int i = n_faces(); i > 0; --i)
if (is_deleted(FaceHandle(i-1)))
{
face_deleted_[i-1] = false;
delete_face_core(FaceHandle(i-1));
}
for (unsigned int i = n_edges(); i > 0; --i)
if (is_deleted(EdgeHandle(i-1)))
{
edge_deleted_[i-1] = false;
delete_edge_core(EdgeHandle(i-1));
}
for (unsigned int i = n_vertices(); i > 0; --i)
if (is_deleted(VertexHandle(i-1)))
{
vertex_deleted_[i-1] = false;
delete_vertex_core(VertexHandle(i-1));
}
deferred_deletion = true;
}
//========================================================================================
template
void TopologyKernel::get_incident_edges(const ContainerT& _vs,
std::set& _es) const {
_es.clear();
if(v_bottom_up_) {
for(typename ContainerT::const_iterator v_it = _vs.begin(),
v_end = _vs.end(); v_it != v_end; ++v_it) {
const std::vector& inc_hes = outgoing_hes_per_vertex_[v_it->idx()];
for(std::vector::const_iterator he_it = inc_hes.begin(),
he_end = inc_hes.end(); he_it != he_end; ++he_it) {
_es.insert(edge_handle(*he_it));
}
}
} else {
for(typename ContainerT::const_iterator v_it = _vs.begin(),
v_end = _vs.end(); v_it != v_end; ++v_it) {
for(EdgeIter e_it = edges_begin(), e_end = edges_end(); e_it != e_end; ++e_it) {
const Edge& e = edge(*e_it);
if(e.from_vertex() == *v_it || e.to_vertex() == *v_it) {
_es.insert(*e_it);
}
}
}
}
}
//========================================================================================
template
void TopologyKernel::get_incident_faces(const ContainerT& _es,
std::set& _fs) const {
_fs.clear();
if(e_bottom_up_) {
for(typename ContainerT::const_iterator e_it = _es.begin(),
e_end = _es.end(); e_it != e_end; ++e_it) {
for(HalfEdgeHalfFaceIter hehf_it = hehf_iter(halfedge_handle(*e_it, 0));
hehf_it.valid(); ++hehf_it) {
const FaceHandle fh = face_handle(*hehf_it);
if(_fs.count(fh) == 0) {
_fs.insert(fh);
}
}
}
} else {
for(typename ContainerT::const_iterator e_it = _es.begin(),
e_end = _es.end(); e_it != e_end; ++e_it) {
for(FaceIter f_it = faces_begin(),
f_end = faces_end(); f_it != f_end; ++f_it) {
const std::vector& hes = face(*f_it).halfedges();
for(std::vector::const_iterator he_it = hes.begin(),
he_end = hes.end(); he_it != he_end; ++he_it) {
if(edge_handle(*he_it) == *e_it) {
_fs.insert(*f_it);
break;
}
}
}
}
}
}
//========================================================================================
template
void TopologyKernel::get_incident_cells(const ContainerT& _fs,
std::set& _cs) const {
_cs.clear();
if(f_bottom_up_) {
for(typename ContainerT::const_iterator f_it = _fs.begin(),
f_end = _fs.end(); f_it != f_end; ++f_it) {
const HalfFaceHandle hfh0 = halfface_handle(*f_it, 0);
const HalfFaceHandle hfh1 = halfface_handle(*f_it, 1);
const CellHandle c0 = incident_cell(hfh0);
const CellHandle c1 = incident_cell(hfh1);
if(c0.is_valid()) _cs.insert(c0);
if(c1.is_valid()) _cs.insert(c1);
}
} else {
for(typename ContainerT::const_iterator f_it = _fs.begin(),
f_end = _fs.end(); f_it != f_end; ++f_it) {
for(CellIter c_it = cells_begin(), c_end = cells_end();
c_it != c_end; ++c_it) {
const std::vector& hfs = cell(*c_it).halffaces();
for(std::vector::const_iterator hf_it = hfs.begin(),
hf_end = hfs.end(); hf_it != hf_end; ++hf_it) {
if(face_handle(*hf_it) == *f_it) {
_cs.insert(*c_it);
break;
}
}
}
}
}
}
//========================================================================================
/**
* \brief Delete vertex from mesh
*
* After performing this operation, all vertices
* following vertex _h in the array will be accessible
* through their old handle decreased by one.
* This function directly fixes the vertex links
* in all edges. These steps are performed:
*
* 1) Decrease all vertex handles > _h in incident edges
* 2) Delete entry in bottom-up list: V -> HE
* 3) Delete vertex itself (not necessary here since
* a vertex is only represented by a number)
* 4) Delete property entry
*
* @param _h A vertex's handle
*/
VertexIter TopologyKernel::delete_vertex_core(const VertexHandle& _h) {
VertexHandle h = _h;
assert(h.is_valid() && (size_t)h.idx() < n_vertices());
if (fast_deletion_enabled() && !deferred_deletion_enabled()) // for fast deletion swap handle with last not deleted vertex
{
VertexHandle last_undeleted_vertex = VertexHandle(n_vertices()-1);
assert(!vertex_deleted_[last_undeleted_vertex.idx()]);
swap_vertices(h, last_undeleted_vertex);
h = last_undeleted_vertex;
}
if (deferred_deletion_enabled())
{
vertex_deleted_[h.idx()] = true;
// deleted_vertices_.push_back(h);
// Iterator to next element in vertex list
// return (vertices_begin() + h.idx()+1);
return VertexIter(this, VertexHandle(h.idx()+1));
}
else
{
// 1)
if(v_bottom_up_) {
// Decrease all vertex handles >= _h in all edge definitions
for(int i = h.idx(), end = n_vertices(); i < end; ++i) {
const std::vector& hes = outgoing_hes_per_vertex_[i];
for(std::vector::const_iterator he_it = hes.begin(),
he_end = hes.end(); he_it != he_end; ++he_it) {
Edge& e = edge(edge_handle(*he_it));
if(e.from_vertex().idx() == i) {
e.set_from_vertex(VertexHandle(i-1));
}
if(e.to_vertex().idx() == i) {
e.set_to_vertex(VertexHandle(i-1));
}
}
}
} else {
// Iterate over all edges
for(EdgeIter e_it = edges_begin(), e_end = edges_end();
e_it != e_end; ++e_it) {
// Decrease all vertex handles in edge definitions that are greater than _h
if(edge(*e_it).from_vertex() > h) {
edge(*e_it).set_from_vertex(VertexHandle(edge(*e_it).from_vertex().idx() - 1));
}
if(edge(*e_it).to_vertex() > h) {
edge(*e_it).set_to_vertex(VertexHandle(edge(*e_it).to_vertex().idx() - 1));
}
}
}
// 2)
if(v_bottom_up_) {
assert((size_t)h.idx() < outgoing_hes_per_vertex_.size());
outgoing_hes_per_vertex_.erase(outgoing_hes_per_vertex_.begin() + h.idx());
}
// 3)
--n_vertices_;
vertex_deleted_.erase(vertex_deleted_.begin() + h.idx());
// 4)
vertex_deleted(h);
// Iterator to next element in vertex list
// return (vertices_begin() + h.idx());
return VertexIter(this, h);
}
}
//========================================================================================
/**
* \brief Delete edge from mesh
*
* After performing this operation, all edges
* following edge _h in the array will be accessible
* through their old handle decreased by one.
* This function directly fixes the edge links
* in all faces. These steps are performed:
*
* 1) Delete bottom-up links from incident vertices
* 2) Decrease all half-edge handles > _h in incident faces
* 3) Delete entry in bottom-up list: HE -> HF
* 4) Decrease all half-edge handles > 2*_h.idx() in
* vertex bottom-up list
* 5) Delete edge itself
* 6) Delete property entry
*
* @param _h An edge's handle
*/
EdgeIter TopologyKernel::delete_edge_core(const EdgeHandle& _h) {
EdgeHandle h = _h;
assert(h.is_valid() && (size_t)h.idx() < edges_.size());
if (fast_deletion_enabled() && !deferred_deletion_enabled()) // for fast deletion swap handle with last one
{
EdgeHandle last_edge = EdgeHandle(edges_.size()-1);
assert(!edge_deleted_[last_edge.idx()]);
swap_edges(h, last_edge);
h = last_edge;
}
// 1)
if(v_bottom_up_) {
VertexHandle v0 = edge(h).from_vertex();
VertexHandle v1 = edge(h).to_vertex();
assert(v0.is_valid() && (size_t)v0.idx() < outgoing_hes_per_vertex_.size());
assert(v1.is_valid() && (size_t)v1.idx() < outgoing_hes_per_vertex_.size());
outgoing_hes_per_vertex_[v0.idx()].erase(
std::remove(outgoing_hes_per_vertex_[v0.idx()].begin(),
outgoing_hes_per_vertex_[v0.idx()].end(),
halfedge_handle(h, 0)),
outgoing_hes_per_vertex_[v0.idx()].end());
outgoing_hes_per_vertex_[v1.idx()].erase(
std::remove(outgoing_hes_per_vertex_[v1.idx()].begin(),
outgoing_hes_per_vertex_[v1.idx()].end(),
halfedge_handle(h, 1)),
outgoing_hes_per_vertex_[v1.idx()].end());
}
if (deferred_deletion_enabled())
{
edge_deleted_[h.idx()] = true;
// deleted_edges_.push_back(h);
// Return iterator to next element in list
// return (edges_begin() + h.idx()+1);
return EdgeIter(this, EdgeHandle(h.idx()+1));
}
else
{
if (!fast_deletion_enabled())
{
// 2)
if(e_bottom_up_) {
assert((size_t)halfedge_handle(h, 0).idx() < incident_hfs_per_he_.size());
// Decrease all half-edge handles > he and
// delete all half-edge handles == he in face definitions
// Get all faces that need updates
std::set update_faces;
for(std::vector >::const_iterator iit =
(incident_hfs_per_he_.begin() + halfedge_handle(h, 0).idx()),
iit_end = incident_hfs_per_he_.end(); iit != iit_end; ++iit) {
for(std::vector::const_iterator it = iit->begin(),
end = iit->end(); it != end; ++it) {
update_faces.insert(face_handle(*it));
}
}
// Update respective handles
HEHandleCorrection cor(halfedge_handle(h, 1));
for(std::set::iterator f_it = update_faces.begin(),
f_end = update_faces.end(); f_it != f_end; ++f_it) {
std::vector hes = face(*f_it).halfedges();
// Delete current half-edge from face's half-edge list
hes.erase(std::remove(hes.begin(), hes.end(), halfedge_handle(h, 0)), hes.end());
hes.erase(std::remove(hes.begin(), hes.end(), halfedge_handle(h, 1)), hes.end());
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector::iterator it = hes.begin(), end = hes.end();
it != end; ++it) {
cor.correctValue(*it);
}
#else
std::for_each(hes.begin(), hes.end(),
fun::bind(&HEHandleCorrection::correctValue, &cor, fun::placeholders::_1));
#endif
face(*f_it).set_halfedges(hes);
}
} else {
// Iterate over all faces
for(FaceIter f_it = faces_begin(), f_end = faces_end();
f_it != f_end; ++f_it) {
// Get face's half-edges
std::vector hes = face(*f_it).halfedges();
// Delete current half-edge from face's half-edge list
hes.erase(std::remove(hes.begin(), hes.end(), halfedge_handle(h, 0)), hes.end());
hes.erase(std::remove(hes.begin(), hes.end(), halfedge_handle(h, 1)), hes.end());
// Decrease all half-edge handles greater than _h in face
HEHandleCorrection cor(halfedge_handle(h, 1));
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector::iterator it = hes.begin(), end = hes.end();
it != end; ++it) {
cor.correctValue(*it);
}
#else
std::for_each(hes.begin(), hes.end(),
fun::bind(&HEHandleCorrection::correctValue, &cor, fun::placeholders::_1));
#endif
face(*f_it).set_halfedges(hes);
}
}
}
// 3)
if(e_bottom_up_) {
assert((size_t)halfedge_handle(h, 1).idx() < incident_hfs_per_he_.size());
incident_hfs_per_he_.erase(incident_hfs_per_he_.begin() + halfedge_handle(h, 1).idx());
incident_hfs_per_he_.erase(incident_hfs_per_he_.begin() + halfedge_handle(h, 0).idx());
}
if (!fast_deletion_enabled())
{
// 4)
if(v_bottom_up_) {
HEHandleCorrection cor(halfedge_handle(h, 1));
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector >::iterator it = outgoing_hes_per_vertex_.begin(),
end = outgoing_hes_per_vertex_.end(); it != end; ++it) {
cor.correctVecValue(*it);
}
#else
std::for_each(outgoing_hes_per_vertex_.begin(),
outgoing_hes_per_vertex_.end(),
fun::bind(&HEHandleCorrection::correctVecValue, &cor, fun::placeholders::_1));
#endif
}
}
// 5)
edges_.erase(edges_.begin() + h.idx());
edge_deleted_.erase(edge_deleted_.begin() + h.idx());
// 6)
edge_deleted(h);
// Return iterator to next element in list
// return (edges_begin() + h.idx());
return EdgeIter(this, h);
}
}
//========================================================================================
/**
* \brief Delete face from mesh
*
* After performing this operation, all faces
* following face _h in the array will be accessible
* through their old handle decreased by one.
* This function directly fixes the face links
* in all cells. These steps are performed:
*
* 1) Delete bottom-up links from incident edges
* 2) Decrease all half-face handles > _h in incident cells
* 3) Delete entry in bottom-up list: HF -> C
* 4) Decrease all half-face handles > 2*_h.idx() in
* half-edge bottom-up list
* 5) Delete face itself
* 6) Delete property entry
*
* @param _h An face's handle
*/
FaceIter TopologyKernel::delete_face_core(const FaceHandle& _h) {
FaceHandle h = _h;
assert(h.is_valid() && (size_t)h.idx() < faces_.size());
if (fast_deletion_enabled() && !deferred_deletion_enabled()) // for fast deletion swap handle with last one
{
FaceHandle last_face = FaceHandle(faces_.size()-1);
assert(!face_deleted_[last_face.idx()]);
swap_faces(h, last_face);
h = last_face;
}
// 1)
if(e_bottom_up_) {
const std::vector& hes = face(h).halfedges();
for(std::vector::const_iterator he_it = hes.begin(),
he_end = hes.end(); he_it != he_end; ++he_it) {
assert((size_t)std::max(he_it->idx(), opposite_halfedge_handle(*he_it).idx()) < incident_hfs_per_he_.size());
incident_hfs_per_he_[he_it->idx()].erase(
std::remove(incident_hfs_per_he_[he_it->idx()].begin(),
incident_hfs_per_he_[he_it->idx()].end(),
halfface_handle(h, 0)), incident_hfs_per_he_[he_it->idx()].end());
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].erase(
std::remove(incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].begin(),
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].end(),
halfface_handle(h, 1)), incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].end());
}
}
if (deferred_deletion_enabled())
{
face_deleted_[h.idx()] = true;
// deleted_faces_.push_back(h);
// Return iterator to next element in list
// return (faces_begin() + h.idx()+1);
return FaceIter(this, FaceHandle(h.idx()+1));
}
else
{
if (!fast_deletion_enabled())
{
// 2)
if(f_bottom_up_) {
// Decrease all half-face handles > _h in all cells
// and delete all half-face handles == _h
std::set update_cells;
for(std::vector::const_iterator c_it = (incident_cell_per_hf_.begin() + halfface_handle(h, 0).idx()),
c_end = incident_cell_per_hf_.end(); c_it != c_end; ++c_it) {
if(!c_it->is_valid()) continue;
update_cells.insert(*c_it);
}
for(std::set::const_iterator c_it = update_cells.begin(),
c_end = update_cells.end(); c_it != c_end; ++c_it) {
std::vector hfs = cell(*c_it).halffaces();
// Delete current half-faces from cell's half-face list
hfs.erase(std::remove(hfs.begin(), hfs.end(), halfface_handle(h, 0)), hfs.end());
hfs.erase(std::remove(hfs.begin(), hfs.end(), halfface_handle(h, 1)), hfs.end());
HFHandleCorrection cor(halfface_handle(h, 1));
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector::iterator it = hfs.begin(),
end = hfs.end(); it != end; ++it) {
cor.correctValue(*it);
}
#else
std::for_each(hfs.begin(), hfs.end(),
fun::bind(&HFHandleCorrection::correctValue, &cor, fun::placeholders::_1));
#endif
cell(*c_it).set_halffaces(hfs);
}
} else {
// Iterate over all cells
for(CellIter c_it = cells_begin(), c_end = cells_end(); c_it != c_end; ++c_it) {
std::vector hfs = cell(*c_it).halffaces();
// Delete current half-faces from cell's half-face list
hfs.erase(std::remove(hfs.begin(), hfs.end(), halfface_handle(h, 0)), hfs.end());
hfs.erase(std::remove(hfs.begin(), hfs.end(), halfface_handle(h, 1)), hfs.end());
HFHandleCorrection cor(halfface_handle(h, 1));
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector::iterator it = hfs.begin(),
end = hfs.end(); it != end; ++it) {
cor.correctValue(*it);
}
#else
std::for_each(hfs.begin(), hfs.end(),
fun::bind(&HFHandleCorrection::correctValue, &cor, fun::placeholders::_1));
#endif
cell(*c_it).set_halffaces(hfs);
}
}
}
// 3)
if(f_bottom_up_) {
assert((size_t)halfface_handle(h, 1).idx() < incident_cell_per_hf_.size());
incident_cell_per_hf_.erase(incident_cell_per_hf_.begin() + halfface_handle(h, 1).idx());
incident_cell_per_hf_.erase(incident_cell_per_hf_.begin() + halfface_handle(h, 0).idx());
}
if (!fast_deletion_enabled())
{
// 4)
if(e_bottom_up_) {
HFHandleCorrection cor(halfface_handle(h, 1));
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector >::iterator it = incident_hfs_per_he_.begin(), end = incident_hfs_per_he_.end(); it != end; ++it) {
cor.correctVecValue(*it);
}
#else
std::for_each(incident_hfs_per_he_.begin(),
incident_hfs_per_he_.end(),
fun::bind(&HFHandleCorrection::correctVecValue, &cor, fun::placeholders::_1));
#endif
}
}
// 5)
faces_.erase(faces_.begin() + h.idx());
face_deleted_.erase(face_deleted_.begin() + h.idx());
// 6)
face_deleted(h);
// Return iterator to next element in list
// return (faces_begin() + h.idx());
return FaceIter(this, h);
}
}
//========================================================================================
/**
* \brief Delete cell from mesh
*
* After performing this operation, all cells
* following cell _h in the array will be accessible
* through their old handle decreased by one.
* These steps are performed:
*
* 1) Delete links in BU: HF -> C
* 2) Decrease all entries > c in BU: HF -> C
* 3) Delete cell from storage array
* 4) Delete property item
*
* @param _h A cell handle
*/
CellIter TopologyKernel::delete_cell_core(const CellHandle& _h) {
CellHandle h = _h;
assert(h.is_valid() && (size_t)h.idx() < cells_.size());
if (fast_deletion_enabled() && !deferred_deletion_enabled()) // for fast deletion swap handle with last not deleted cell
{
CellHandle last_undeleted_cell = CellHandle(cells_.size()-1);
assert(!cell_deleted_[last_undeleted_cell.idx()]);
swap_cells(h, last_undeleted_cell);
h = last_undeleted_cell;
}
// 1)
if(f_bottom_up_) {
const std::vector& hfs = cell(h).halffaces();
for(std::vector::const_iterator hf_it = hfs.begin(),
hf_end = hfs.end(); hf_it != hf_end; ++hf_it) {
assert((size_t)hf_it->idx() < incident_cell_per_hf_.size());
if (incident_cell_per_hf_[hf_it->idx()] == h)
incident_cell_per_hf_[hf_it->idx()] = InvalidCellHandle;
}
}
if (deferred_deletion_enabled())
{
cell_deleted_[h.idx()] = true;
// deleted_cells_.push_back(h);
// deleted_cells_set.insert(h);
// return (cells_begin() + h.idx()+1);
return CellIter(this, CellHandle(h.idx()+1));
}
else
{
// 2)
if (!fast_deletion_enabled())
{
if(f_bottom_up_) {
CHandleCorrection cor(h);
#if defined(__clang_major__) && (__clang_major__ >= 5)
for(std::vector::iterator it = incident_cell_per_hf_.begin(),
end = incident_cell_per_hf_.end(); it != end; ++it) {
cor.correctValue(*it);
}
#else
std::for_each(incident_cell_per_hf_.begin(),
incident_cell_per_hf_.end(),
fun::bind(&CHandleCorrection::correctValue, &cor, fun::placeholders::_1));
#endif
}
}
// 3)
cells_.erase(cells_.begin() + h.idx());
cell_deleted_.erase(cell_deleted_.begin() + h.idx());
// 4)
cell_deleted(h);
// return handle to original position
// return (cells_begin() + h.idx()+1);
return CellIter(this, h);
}
}
void TopologyKernel::swap_cells(CellHandle _h1, CellHandle _h2)
{
assert(_h1.idx() >= 0 && _h1.idx() < (int)cells_.size());
assert(_h2.idx() >= 0 && _h2.idx() < (int)cells_.size());
if (_h1 == _h2)
return;
int id1 = _h1.idx();
int id2 = _h2.idx();
Cell c1 = cells_[id1];
Cell c2 = cells_[id2];
// correct pointers to those cells
std::vector hfhs1 = c1.halffaces();
for (unsigned int i = 0; i < hfhs1.size(); ++i)
{
HalfFaceHandle hfh = hfhs1[i];
if (incident_cell_per_hf_[hfh.idx()] == id1)
incident_cell_per_hf_[hfh.idx()] = id2;
}
std::vector hfhs2 = c2.halffaces();
for (unsigned int i = 0; i < hfhs2.size(); ++i)
{
HalfFaceHandle hfh = hfhs2[i];
if (incident_cell_per_hf_[hfh.idx()] == id2)
incident_cell_per_hf_[hfh.idx()] = id1;
}
// swap vector entries
std::swap(cells_[id1], cells_[id2]);
bool tmp = cell_deleted_[id1];
cell_deleted_[id1] = cell_deleted_[id2];
cell_deleted_[id2] = tmp;
swap_cell_properties(_h1, _h2);
}
void TopologyKernel::swap_faces(FaceHandle _h1, FaceHandle _h2)
{
assert(_h1.idx() >= 0 && _h1.idx() < (int)faces_.size());
assert(_h2.idx() >= 0 && _h2.idx() < (int)faces_.size());
if (_h1 == _h2)
return;
std::vector ids;
ids.push_back(_h1.idx());
ids.push_back(_h2.idx());
unsigned int id1 = _h1.idx();
unsigned int id2 = _h2.idx();
// correct pointers to those faces
// correct cells that contain a swapped faces
if (has_face_bottom_up_incidences())
{
std::set processed_cells; // to ensure ids are only swapped once (in the case that the two swapped faces belong to a common cell)
for (unsigned int i = 0; i < 2; ++i) // For both swapped faces
{
unsigned int id = ids[i];
for (unsigned int j = 0; j < 2; ++j) // for both halffaces
{
HalfFaceHandle hfh = HalfFaceHandle(2*id+j);
CellHandle ch = incident_cell_per_hf_[hfh];
if (!ch.is_valid())
continue;
if (processed_cells.find(ch.idx()) == processed_cells.end())
{
Cell& c = cells_[ch.idx()];
// replace old halffaces with new halffaces where the ids are swapped
std::vector new_halffaces;
for (unsigned int k = 0; k < c.halffaces().size(); ++k)
if (c.halffaces()[k].idx()/2 == (int)id1) // if halfface belongs to swapped face
new_halffaces.push_back(HalfFaceHandle(2 * id2 + (c.halffaces()[k].idx() % 2)));
else if (c.halffaces()[k].idx()/2 == (int)id2) // if halfface belongs to swapped face
new_halffaces.push_back(HalfFaceHandle(2 * id1 + (c.halffaces()[k].idx() % 2)));
else
new_halffaces.push_back(c.halffaces()[k]);
c.set_halffaces(new_halffaces);
processed_cells.insert(ch.idx());
}
}
}
}
else
{
// serach for all cells that contain a swapped face
for (unsigned int i = 0; i < cells_.size(); ++i)
{
Cell& c = cells_[i];
// check if c contains a swapped face
bool contains_swapped_face = false;
for (unsigned int k = 0; k < c.halffaces().size(); ++k)
{
if (c.halffaces()[k].idx()/2 == (int)id1)
contains_swapped_face = true;
if (c.halffaces()[k].idx()/2 == (int)id2)
contains_swapped_face = true;
if (contains_swapped_face)
break;
}
if (contains_swapped_face)
{
// replace old halffaces with new halffaces where the ids are swapped
std::vector new_halffaces;
for (unsigned int k = 0; k < c.halffaces().size(); ++k)
if (c.halffaces()[k].idx()/2 == (int)id1) // if halfface belongs to swapped face
new_halffaces.push_back(HalfFaceHandle(2 * id2 + (c.halffaces()[k].idx() % 2)));
else if (c.halffaces()[k].idx()/2 == (int)id2) // if halfface belongs to swapped face
new_halffaces.push_back(HalfFaceHandle(2 * id1 + (c.halffaces()[k].idx() % 2)));
else
new_halffaces.push_back(c.halffaces()[k]);
c.set_halffaces(new_halffaces);
}
}
}
// correct bottom up indices
if (has_edge_bottom_up_incidences())
{
std::set processed_halfedges; // to ensure ids are only swapped once (in the case that a halfedge is incident to both swapped faces)
for (unsigned int i = 0; i < 2; ++i) // For both swapped faces
{
unsigned int id = ids[i];
for (unsigned int j = 0; j < 2; ++j) // for both halffaces
{
HalfFaceHandle hfh = HalfFaceHandle(2*id+j);
Face hf = halfface(hfh);
for (unsigned int k = 0; k < hf.halfedges().size(); ++k)
{
HalfEdgeHandle heh = hf.halfedges()[k];
if (processed_halfedges.find(heh.idx()) != processed_halfedges.end())
continue;
std::vector& incident_halffaces = incident_hfs_per_he_[heh.idx()];
for (unsigned int l = 0; l < incident_halffaces.size(); ++l)
{
HalfFaceHandle& hfh2 = incident_halffaces[l];
if (hfh2.idx()/2 == (int)id1) // if halfface belongs to swapped face
hfh2 = HalfFaceHandle(2 * id2 + (hfh2.idx() % 2));
else if (hfh2.idx()/2 == (int)id2) // if halfface belongs to swapped face
hfh2 = HalfFaceHandle(2 * id1 + (hfh2.idx() % 2));
}
processed_halfedges.insert(heh);
}
}
}
}
// swap vector entries
std::swap(faces_[ids[0]], faces_[ids[1]]);
bool tmp = face_deleted_[ids[0]];
face_deleted_[ids[0]] = face_deleted_[ids[1]];
face_deleted_[ids[1]] = tmp;
std::swap(incident_cell_per_hf_[2*ids[0]+0], incident_cell_per_hf_[2*ids[1]+0]);
std::swap(incident_cell_per_hf_[2*ids[0]+1], incident_cell_per_hf_[2*ids[1]+1]);
swap_face_properties(_h1, _h2);
swap_halfface_properties(halfface_handle(_h1, 0), halfface_handle(_h2, 0));
swap_halfface_properties(halfface_handle(_h1, 1), halfface_handle(_h2, 1));
}
void TopologyKernel::swap_edges(EdgeHandle _h1, EdgeHandle _h2)
{
assert(_h1.idx() >= 0 && _h1.idx() < (int)edges_.size());
assert(_h2.idx() >= 0 && _h2.idx() < (int)edges_.size());
if (_h1 == _h2)
return;
std::vector ids;
ids.push_back(_h1.idx());
ids.push_back(_h2.idx());
// correct pointers to those edges
if (has_edge_bottom_up_incidences())
{
std::set processed_faces; // to ensure ids are only swapped once (in the case that the two swapped edges belong to a common face)
for (unsigned int i = 0; i < 2; ++i) // For both swapped edges
{
HalfEdgeHandle heh = HalfEdgeHandle(2*ids[i]);
std::vector& incident_halffaces = incident_hfs_per_he_[heh.idx()];
for (unsigned int j = 0; j < incident_halffaces.size(); ++j) // for each incident halfface
{
HalfFaceHandle hfh = incident_halffaces[j];
unsigned int f_id = hfh.idx() / 2;
if (processed_faces.find(f_id) == processed_faces.end())
{
Face& f = faces_[f_id];
// replace old incident halfedges with new incident halfedges where the ids are swapped
std::vector new_halfedges;
for (unsigned int k = 0; k < f.halfedges().size(); ++k)
{
HalfEdgeHandle heh2 = f.halfedges()[k];
if (heh2.idx() / 2 == (int)ids[0])
new_halfedges.push_back(HalfEdgeHandle(2*ids[1] + (heh2.idx() % 2)));
else if (heh2.idx() / 2 == (int)ids[1])
new_halfedges.push_back(HalfEdgeHandle(2*ids[0] + (heh2.idx() % 2)));
else
new_halfedges.push_back(heh2);
}
f.set_halfedges(new_halfedges);
processed_faces.insert(f_id);
}
}
}
}
else
{
// search for all faces that contain one of the swapped edges
for (unsigned int i = 0; i < faces_.size(); ++i)
{
Face& f = faces_[i];
// check if f contains a swapped edge
bool contains_swapped_edge = false;
for (unsigned int k = 0; k < f.halfedges().size(); ++k)
{
if (f.halfedges()[k].idx()/2 == (int)ids[0])
contains_swapped_edge = true;
if (f.halfedges()[k].idx()/2 == (int)ids[1])
contains_swapped_edge = true;
if (contains_swapped_edge)
break;
}
if (contains_swapped_edge)
{
// replace old incident halfedges with new incident halfedges where the ids are swapped
std::vector new_halfedges;
for (unsigned int k = 0; k < f.halfedges().size(); ++k)
{
HalfEdgeHandle heh2 = f.halfedges()[k];
if (heh2.idx() / 2 == (int)ids[0])
new_halfedges.push_back(HalfEdgeHandle(2*ids[1] + (heh2.idx() % 2)));
else if (heh2.idx() / 2 == (int)ids[1])
new_halfedges.push_back(HalfEdgeHandle(2*ids[0] + (heh2.idx() % 2)));
else
new_halfedges.push_back(heh2);
}
f.set_halfedges(new_halfedges);
}
}
}
// correct bottom up incidences
if (has_vertex_bottom_up_incidences())
{
std::set processed_vertices;
for (unsigned int i = 0; i < 2; ++i) // For both swapped edges
{
Edge e = edge(EdgeHandle(ids[i]));
std::vector vhs;
vhs.push_back(e.from_vertex());
vhs.push_back(e.to_vertex());
for (unsigned int j = 0; j < 2; ++j) // for both incident vertices
{
if (processed_vertices.find(vhs[j].idx()) != processed_vertices.end())
continue;
std::vector& outgoing_hes = outgoing_hes_per_vertex_[vhs[j].idx()];
for (unsigned int k = 0; k < outgoing_hes.size(); ++k)
{
HalfEdgeHandle& heh = outgoing_hes[k];
if (heh.idx() / 2 == (int)ids[0])
heh = HalfEdgeHandle(2 * ids[1] + (heh.idx() % 2));
else if (heh.idx() / 2 == (int)ids[1])
heh = HalfEdgeHandle(2 * ids[0] + (heh.idx() % 2));
}
processed_vertices.insert(vhs[j]);
}
}
}
// swap vector entries
std::swap(edges_[ids[0]], edges_[ids[1]]);
bool tmp = edge_deleted_[ids[0]];
edge_deleted_[ids[0]] = edge_deleted_[ids[1]];
edge_deleted_[ids[1]] = tmp;
std::swap(incident_hfs_per_he_[2*ids[0]+0], incident_hfs_per_he_[2*ids[1]+0]);
std::swap(incident_hfs_per_he_[2*ids[0]+1], incident_hfs_per_he_[2*ids[1]+1]);
swap_edge_properties(_h1, _h2);
swap_halfedge_properties(halfedge_handle(_h1, 0), halfedge_handle(_h2, 0));
swap_halfedge_properties(halfedge_handle(_h1, 1), halfedge_handle(_h2, 1));
}
void TopologyKernel::swap_vertices(VertexHandle _h1, VertexHandle _h2)
{
assert(_h1.idx() >= 0 && _h1.idx() < (int)n_vertices_);
assert(_h2.idx() >= 0 && _h2.idx() < (int)n_vertices_);
if (_h1 == _h2)
return;
std::vector ids;
ids.push_back(_h1.idx());
ids.push_back(_h2.idx());
// correct pointers to those vertices
if (has_vertex_bottom_up_incidences())
{
for (unsigned int i = 0; i < 2; ++i) // For both swapped vertices
{
std::set processed_edges; // to ensure ids are only swapped once (in the case that the two swapped vertices are connected by an edge)
std::vector& outgoing_hes = outgoing_hes_per_vertex_[ids[i]];
for (unsigned int k = 0; k < outgoing_hes.size(); ++k) // for each outgoing halfedge
{
unsigned int e_id = outgoing_hes[k].idx() / 2;
if (processed_edges.find(e_id) == processed_edges.end())
{
Edge& e = edges_[e_id];
if (e.from_vertex() == (int)ids[0])
e.set_from_vertex(VertexHandle(ids[1]));
else if (e.from_vertex() == (int)ids[1])
e.set_from_vertex(VertexHandle(ids[0]));
if (e.to_vertex() == (int)ids[0])
e.set_to_vertex(VertexHandle(ids[1]));
else if (e.to_vertex() == (int)ids[1])
e.set_to_vertex(VertexHandle(ids[0]));
processed_edges.insert(e_id);
}
}
}
}
else
{
// search for all edges containing a swapped vertex
for (unsigned int i = 0; i < edges_.size(); ++i)
{
Edge& e = edges_[i];
if (e.from_vertex() == (int)ids[0])
e.set_from_vertex(VertexHandle(ids[1]));
else if (e.from_vertex() == (int)ids[1])
e.set_from_vertex(VertexHandle(ids[0]));
if (e.to_vertex() == (int)ids[0])
e.set_to_vertex(VertexHandle(ids[1]));
else if (e.to_vertex() == (int)ids[1])
e.set_to_vertex(VertexHandle(ids[0]));
}
}
// swap vector entries
bool tmp = vertex_deleted_[ids[0]];
vertex_deleted_[ids[0]] = vertex_deleted_[ids[1]];
vertex_deleted_[ids[1]] = tmp;
std::swap(outgoing_hes_per_vertex_[ids[0]], outgoing_hes_per_vertex_[ids[1]]);
swap_vertex_properties(_h1, _h2);
}
//========================================================================================
void TopologyKernel::delete_multiple_vertices(const std::vector& _tag) {
assert(_tag.size() == n_vertices());
std::vector newIndices(n_vertices(), -1);
int curIdx = 0;
std::vector::iterator idx_it = newIndices.begin();
for(std::vector::const_iterator t_it = _tag.begin(),
t_end = _tag.end(); t_it != t_end; ++t_it, ++idx_it) {
if(!(*t_it)) {
// Not marked as deleted
*idx_it = curIdx;
++curIdx;
} else {
--n_vertices_;
}
}
// Delete properties accordingly
delete_multiple_vertex_props(_tag);
EdgeCorrector corrector(newIndices);
std::for_each(edges_.begin(), edges_.end(), corrector);
}
//========================================================================================
void TopologyKernel::delete_multiple_edges(const std::vector& _tag) {
assert(_tag.size() == n_edges());
std::vector newIndices(n_edges(), -1);
int curIdx = 0;
std::vector newEdges;
std::vector::iterator idx_it = newIndices.begin();
std::vector::const_iterator e_it = edges_.begin();
for(std::vector::const_iterator t_it = _tag.begin(),
t_end = _tag.end(); t_it != t_end; ++t_it, ++idx_it, ++e_it) {
if(!(*t_it)) {
// Not marked as deleted
newEdges.push_back(*e_it);
*idx_it = curIdx;
++curIdx;
}
}
// Swap edges
edges_.swap(newEdges);
// Delete properties accordingly
delete_multiple_edge_props(_tag);
FaceCorrector corrector(newIndices);
std::for_each(faces_.begin(), faces_.end(), corrector);
}
//========================================================================================
void TopologyKernel::delete_multiple_faces(const std::vector& _tag) {
assert(_tag.size() == n_faces());
std::vector newIndices(n_faces(), -1);
int curIdx = 0;
std::vector newFaces;
std::vector::iterator idx_it = newIndices.begin();
std::vector::const_iterator f_it = faces_.begin();
for(std::vector::const_iterator t_it = _tag.begin(),
t_end = _tag.end(); t_it != t_end; ++t_it, ++idx_it, ++f_it) {
if(!(*t_it)) {
// Not marked as deleted
newFaces.push_back(*f_it);
*idx_it = curIdx;
++curIdx;
}
}
// Swap faces
faces_.swap(newFaces);
// Delete properties accordingly
delete_multiple_face_props(_tag);
CellCorrector corrector(newIndices);
std::for_each(cells_.begin(), cells_.end(), corrector);
}
//========================================================================================
void TopologyKernel::delete_multiple_cells(const std::vector& _tag) {
assert(_tag.size() == n_cells());
std::vector newCells;
std::vector::const_iterator c_it = cells_.begin();
for(std::vector::const_iterator t_it = _tag.begin(),
t_end = _tag.end(); t_it != t_end; ++t_it, ++c_it) {
if(!(*t_it)) {
// Not marked as deleted
newCells.push_back(*c_it);
}
}
// Swap cells
cells_.swap(newCells);
// Delete properties accordingly
delete_multiple_cell_props(_tag);
}
//========================================================================================
CellIter TopologyKernel::delete_cell_range(const CellIter& _first, const CellIter& _last) {
assert(_first >= cells_begin());
assert(_last <= cells_end());
std::vector::iterator it = cells_.erase(cells_.begin() + _first->idx(), cells_.begin() + _last->idx());
// Re-compute face bottom-up incidences if necessary
if(f_bottom_up_) {
f_bottom_up_ = false;
enable_face_bottom_up_incidences(true);
}
return CellIter(this, CellHandle(it - cells_.begin()));
}
void TopologyKernel::enable_deferred_deletion(bool _enable)
{
if (deferred_deletion && !_enable)
collect_garbage();
deferred_deletion = _enable;
}
//========================================================================================
/// Get edge with handle _edgeHandle
const OpenVolumeMeshEdge& TopologyKernel::edge(const EdgeHandle& _edgeHandle) const {
// Test if edge is valid
assert(_edgeHandle.is_valid() && (size_t)_edgeHandle.idx() < edges_.size());
return edges_[_edgeHandle.idx()];
}
//========================================================================================
/// Get face with handle _faceHandle
const OpenVolumeMeshFace& TopologyKernel::face(const FaceHandle& _faceHandle) const {
// Test if face is valid
assert(_faceHandle.is_valid() && (size_t)_faceHandle.idx() < faces_.size());
return faces_[_faceHandle.idx()];
}
//========================================================================================
/// Get cell with handle _cellHandle
const OpenVolumeMeshCell& TopologyKernel::cell(const CellHandle& _cellHandle) const {
// Test if cell is valid
assert(_cellHandle.is_valid() && (size_t)_cellHandle.idx() < cells_.size());
return cells_[_cellHandle.idx()];
}
//========================================================================================
/// Get edge with handle _edgeHandle
OpenVolumeMeshEdge& TopologyKernel::edge(const EdgeHandle& _edgeHandle) {
// Test if edge is valid
assert(_edgeHandle.is_valid() && (size_t)_edgeHandle.idx() < edges_.size());
return edges_[_edgeHandle.idx()];
}
//========================================================================================
/// Get face with handle _faceHandle
OpenVolumeMeshFace& TopologyKernel::face(const FaceHandle& _faceHandle) {
// Test if face is valid
assert((size_t)_faceHandle.idx() < faces_.size());
assert(_faceHandle.idx() >= 0);
return faces_[_faceHandle.idx()];
}
//========================================================================================
/// Get cell with handle _cellHandle
OpenVolumeMeshCell& TopologyKernel::cell(const CellHandle& _cellHandle) {
// Test if cell is valid
assert((size_t)_cellHandle.idx() < cells_.size());
assert(_cellHandle.idx() >= 0);
return cells_[_cellHandle.idx()];
}
//========================================================================================
/// Get edge that corresponds to halfedge with handle _halfEdgeHandle
OpenVolumeMeshEdge TopologyKernel::halfedge(const HalfEdgeHandle& _halfEdgeHandle) const {
// Is handle in range?
assert((size_t)_halfEdgeHandle.idx() < (edges_.size() * 2));
assert(_halfEdgeHandle.idx() >= 0);
// In case the handle is even, just return the corresponding edge
/// Otherwise return the opposite halfedge via opposite()
if(_halfEdgeHandle.idx() % 2 == 0)
return edges_[(int)(_halfEdgeHandle.idx() / 2)];
else
return opposite_halfedge(edges_[(int)(_halfEdgeHandle.idx() / 2)]);
}
//========================================================================================
/// Get face that corresponds to halfface with handle _halfFaceHandle
OpenVolumeMeshFace TopologyKernel::halfface(const HalfFaceHandle& _halfFaceHandle) const {
// Is handle in range?
assert((size_t)_halfFaceHandle.idx() < (faces_.size() * 2));
assert(_halfFaceHandle.idx() >= 0);
// In case the handle is not even, just return the corresponding face
// Otherwise return the opposite halfface via opposite()
if(_halfFaceHandle.idx() % 2 == 0)
return faces_[(int)(_halfFaceHandle.idx() / 2)];
else
return opposite_halfface(faces_[(int)(_halfFaceHandle.idx() / 2)]);
}
//========================================================================================
/// Get opposite halfedge that corresponds to halfedge with handle _halfEdgeHandle
OpenVolumeMeshEdge TopologyKernel::opposite_halfedge(const HalfEdgeHandle& _halfEdgeHandle) const {
// Is handle in range?
assert(_halfEdgeHandle.idx() >= 0);
assert((size_t)_halfEdgeHandle.idx() < (edges_.size() * 2));
// In case the handle is not even, just return the corresponding edge
// Otherwise return the opposite halfedge via opposite()
if(_halfEdgeHandle.idx() % 2 != 0)
return edges_[(int)(_halfEdgeHandle.idx() / 2)];
else
return opposite_halfedge(edges_[(int)(_halfEdgeHandle.idx() / 2)]);
}
//========================================================================================
/// Get opposite halfface that corresponds to halfface with handle _halfFaceHandle
OpenVolumeMeshFace TopologyKernel::opposite_halfface(const HalfFaceHandle& _halfFaceHandle) const {
// Is handle in range?
assert(_halfFaceHandle.idx() >= 0);
assert((size_t)_halfFaceHandle.idx() < (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.idx() % 2 != 0)
return faces_[(int)(_halfFaceHandle.idx() / 2)];
else
return opposite_halfface(faces_[(int)(_halfFaceHandle.idx() / 2)]);
}
//========================================================================================
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;
}
//========================================================================================
HalfFaceHandle TopologyKernel::halfface(const std::vector& _vs) const {
assert(_vs.size() > 2);
VertexHandle v0 = _vs[0], v1 = _vs[1], v2 = _vs[2];
assert(v0.is_valid() && v1.is_valid() && v2.is_valid());
HalfEdgeHandle he0 = halfedge(v0, v1);
if(!he0.is_valid()) return InvalidHalfFaceHandle;
HalfEdgeHandle he1 = halfedge(v1, v2);
if(!he1.is_valid()) return InvalidHalfFaceHandle;
std::vector hes;
hes.push_back(he0);
hes.push_back(he1);
return halfface(hes);
}
HalfFaceHandle TopologyKernel::halfface_extensive(const std::vector& _vs) const
{
//TODO: schöner machen
assert(_vs.size() > 2);
VertexHandle v0 = _vs[0];
VertexHandle v1 = _vs[1];
assert(v0.is_valid() && v1.is_valid());
HalfEdgeHandle he0 = halfedge(v0, v1);
if(!he0.is_valid()) return InvalidHalfFaceHandle;
for(HalfEdgeHalfFaceIter hehf_it = hehf_iter(he0); hehf_it.valid(); ++hehf_it)
{
std::vector hes = halfface(*hehf_it).halfedges();
if (hes.size() != _vs.size())
continue;
int offset = 0;
for (unsigned int i = 0; i < hes.size(); ++i)
if (hes[i] == he0)
offset = i;
bool all_vertices_found = true;
for (unsigned int i = 0; i < hes.size(); ++i)
{
HalfEdgeHandle heh = hes[(i+offset)%hes.size()];
if (halfedge(heh).from_vertex() != _vs[i])
{
all_vertices_found = false;
break;
}
}
if (all_vertices_found)
return *hehf_it;
}
return InvalidHalfFaceHandle;
}
//========================================================================================
HalfFaceHandle TopologyKernel::halfface(const std::vector& _hes) const {
assert(_hes.size() >= 2);
HalfEdgeHandle he0 = _hes[0], he1 = _hes[1];
assert(he0.is_valid() && he1.is_valid());
for(HalfEdgeHalfFaceIter hehf_it = hehf_iter(he0); hehf_it.valid(); ++hehf_it) {
std::vector hes = halfface(*hehf_it).halfedges();
if(std::find(hes.begin(), hes.end(), he1) != hes.end()) {
return *hehf_it;
}
}
return InvalidHalfFaceHandle;
}
//========================================================================================
HalfEdgeHandle TopologyKernel::next_halfedge_in_halfface(const HalfEdgeHandle& _heh, const HalfFaceHandle& _hfh) const {
assert(_heh.is_valid() && (size_t)_heh.idx() < edges_.size() * 2u);
assert(_hfh.is_valid() && (size_t)_hfh.idx() < faces_.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;
}
//========================================================================================
HalfEdgeHandle TopologyKernel::prev_halfedge_in_halfface(const HalfEdgeHandle& _heh, const HalfFaceHandle& _hfh) const {
assert(_heh.is_valid() && (size_t)_heh.idx() < edges_.size() * 2u);
assert(_hfh.is_valid() && (size_t)_hfh.idx() < faces_.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;
}
//========================================================================================
HalfFaceHandle
TopologyKernel::adjacent_halfface_in_cell(const HalfFaceHandle& _halfFaceHandle, const HalfEdgeHandle& _halfEdgeHandle) const {
assert(_halfFaceHandle.is_valid() && (size_t)_halfFaceHandle.idx() < faces_.size() * 2u);
assert(_halfEdgeHandle.is_valid() && (size_t)_halfEdgeHandle.idx() < edges_.size() * 2u);
assert(has_face_bottom_up_incidences());
assert((size_t)_halfFaceHandle.idx() < incident_cell_per_hf_.size());
if(incident_cell_per_hf_[_halfFaceHandle.idx()] == InvalidCellHandle) {
// Specified halfface is on the outside of the complex
return InvalidHalfFaceHandle;
}
OpenVolumeMeshCell c = cell(incident_cell_per_hf_[_halfFaceHandle.idx()]);
// 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(!has_face_bottom_up_incidences()) {
return InvalidCellHandle;
}
if((size_t)_halfFaceHandle.idx() >= incident_cell_per_hf_.size() || _halfFaceHandle.idx() < 0) {
return InvalidCellHandle;
}
return incident_cell_per_hf_[_halfFaceHandle.idx()];
}
//========================================================================================
void TopologyKernel::compute_vertex_bottom_up_incidences() {
// Clear incidences
outgoing_hes_per_vertex_.clear();
outgoing_hes_per_vertex_.resize(n_vertices());
// Store outgoing halfedges per vertex
size_t n_edges = edges_.size();
for(size_t i = 0; i < n_edges; ++i) {
if (edge_deleted_[i])
continue;
VertexHandle from = edges_[i].from_vertex();
// If this condition is not fulfilled, it is out of caller's control and
// definitely our bug, therefore an assert
assert((size_t)from.idx() < outgoing_hes_per_vertex_.size());
outgoing_hes_per_vertex_[from.idx()].push_back(halfedge_handle(EdgeHandle(i), 0));
VertexHandle to = edges_[i].to_vertex();
assert((size_t)to.idx() < outgoing_hes_per_vertex_.size());
// Store opposite halfedge handle
outgoing_hes_per_vertex_[to.idx()].push_back(halfedge_handle(EdgeHandle(i), 1));
}
}
//========================================================================================
void TopologyKernel::compute_edge_bottom_up_incidences() {
// Clear
incident_hfs_per_he_.clear();
incident_hfs_per_he_.resize(edges_.size() * 2u);
// Store incident halffaces per halfedge
size_t n_faces = faces_.size();
for(size_t i = 0; i < n_faces; ++i) {
if (face_deleted_[i])
continue;
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->idx()].push_back(halfface_handle(FaceHandle(i), 0));
incident_hfs_per_he_[opposite_halfedge_handle(*he_it).idx()].push_back(
halfface_handle(FaceHandle(i), 1));
}
}
}
//========================================================================================
void TopologyKernel::compute_face_bottom_up_incidences() {
// Clear
incident_cell_per_hf_.clear();
incident_cell_per_hf_.resize(faces_.size() * 2u, InvalidCellHandle);
size_t n_cells = cells_.size();
for(size_t i = 0; i < n_cells; ++i) {
if (cell_deleted_[i])
continue;
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->idx()] == InvalidCellHandle) {
incident_cell_per_hf_[hf_it->idx()] = CellHandle(i);
} else {
#ifndef NDEBUG
std::cerr << "compute_face_bottom_up_incidences(): Detected non-three-manifold configuration!" << std::endl;
std::cerr << "Connectivity probably won't work." << std::endl;
#endif
continue;
}
}
}
}
} // Namespace OpenVolumeMesh
| | |