void Foam::cellShapeControlMesh::barycentricCoords ( const Foam::point& pt, barycentric& bary, Cell_handle& ch ) const { // Use the previous cell handle as a hint on where to start searching // Giving a hint causes strange errors... ch = locate(toPoint(pt)); if (dimension() > 2 && !is_infinite(ch)) { oldCellHandle_ = ch; tetPointRef tet ( topoint(ch->vertex(0)->point()), topoint(ch->vertex(1)->point()), topoint(ch->vertex(2)->point()), topoint(ch->vertex(3)->point()) ); bary = tet.pointToBarycentric(pt); } }
AlphaSimplex3D:: AlphaSimplex3D(const Delaunay3D::Edge& e) { Cell_handle c = e.first; Parent::add(c->vertex(e.second)); Parent::add(c->vertex(e.third)); }
double operator() (const AdvancingFront& adv, Cell_handle& c, const int& index) const { // bound == 0 is better than bound < infinity // as it avoids the distance computations if(bound == 0){ return adv.smallest_radius_delaunay_sphere (c, index); } // If perimeter > bound, return infinity so that facet is not used double d = 0; d = sqrt(squared_distance(c->vertex((index+1)%4)->point(), c->vertex((index+2)%4)->point())); if(d>bound) return adv.infinity(); d += sqrt(squared_distance(c->vertex((index+2)%4)->point(), c->vertex((index+3)%4)->point())); if(d>bound) return adv.infinity(); d += sqrt(squared_distance(c->vertex((index+1)%4)->point(), c->vertex((index+3)%4)->point())); if(d>bound) return adv.infinity(); // Otherwise, return usual priority value: smallest radius of // delaunay sphere return adv.smallest_radius_delaunay_sphere (c, index); }
double cell_volume(const Cell_handle& c) { Tetrahedron t = Tetrahedron(c->vertex(0)->point(), c->vertex(1)->point(), c->vertex(2)->point(), c->vertex(3)->point()); return ( CGAL::to_double(CGAL::abs(t.volume()) ) ); }
void Foam::cellShapeControlMesh::writeTriangulation() { OFstream str ( "refinementTriangulation_" + name(Pstream::myProcNo()) + ".obj" ); label count = 0; Info<< "Write refinementTriangulation" << endl; for ( CellSizeDelaunay::Finite_edges_iterator e = finite_edges_begin(); e != finite_edges_end(); ++e ) { Cell_handle c = e->first; Vertex_handle vA = c->vertex(e->second); Vertex_handle vB = c->vertex(e->third); // Don't write far edges if (vA->farPoint() || vB->farPoint()) { continue; } // Don't write unowned edges if (vA->referred() && vB->referred()) { continue; } pointFromPoint p1 = topoint(vA->point()); pointFromPoint p2 = topoint(vB->point()); meshTools::writeOBJ(str, p1, p2, count); } if (is_valid()) { Info<< " Triangulation is valid" << endl; } else { FatalErrorIn ( "Foam::triangulatedMesh::writeRefinementTriangulation()" ) << "Triangulation is not valid" << abort(FatalError); } }
// Calculates void volume and spheres area (only for own four spheres) of regular triangulation cell. // Doesn't consider extraneous atoms from neighbor cells! True volume can be obtained only for cluster of cells comprising one connected void. // S. Sastry, D.S. Corti, P.G. Debenedetti, F.H. Stillinger, "Statistical geometry of particle packings. I. Algorithm for exact determination of connectivity, // volume, and surface areas of void space in monodisperse and polydisperse sphere packings", Phys. Rev. E, V.56, N5, p. 5524, 1997. doi:10.1103/PhysRevE.56.5524 double cell_void_volume(Cell_handle c, double &out_surf, Array_double_4 &out_atom_surf) { const Point *pts[4] = { &((c->vertex(0)->point()).point()), &((c->vertex(1)->point()).point()), &((c->vertex(2)->point()).point()), &((c->vertex(3)->point()).point()) }; const Point &V = c->weighted_circumcenter().point(); // Voronoi vertex V double volume = 0.0, surface = 0.0; Array_double_4 per_atom_surface; std::fill(per_atom_surface.begin(), per_atom_surface.end(), 0.0); // General idea: consider 24 subsimplexes with three orthogonal edges // by constructing normals from weighted circumcenter to the faces and then to the edges for (int i = 0; i < 4; i++) { // iteration over cell facets const Point *p_vertex_i = pts[i]; pts[i] = &V; // replace vertex(i) in array by weighted circumcenter of cell CGAL::Sign sV = orientation(*pts[0], *pts[1], *pts[2], *pts[3]); // do V and vertex(i) on the same side of facet? const int a[3] = { (i+1)%4, (i+2)%4, (i+3)%4 }; // indicies of atoms of face opposite to cell vertex i const Point E = K::Plane_3(*pts[a[0]], *pts[a[1]], *pts[a[2]]).projection(V); // projection of Voronoi vertex to facet const double z0 = sqrt((V-E).squared_length()); // length of normal to facet for (int j = 0; j < 3; j++) { // iteration over facet edges const int e[2] = { a[(j+1)%3], a[(j+2)%3] }; // indices of two edge atoms const Weighted_point WA[2] = { c->vertex(e[0])->point(), c->vertex(e[1])->point() }; const Point A[2] = { WA[0].point(), WA[1].point() }; const CGAL::Sign sE = coplanar_orientation(A[0], A[1], *pts[a[j]], E); const Point B = K::Line_3(A[0], A[1]).projection(E); // projection of E to edge const double y0 = sqrt((E-B).squared_length()); for (int k = 0; k < 2; k++) { // iteration over edge ends (two atoms) CGAL::Sign sB = CGAL::sign((B - A[k])*(A[(k+1)%2] - A[k])); // negative if point B is in other direction than opposite A[(k+1)%2] looking from A[k] CGAL::Sign sF = sV * sE * sB; // final subsimplex sign double x0 = sqrt((B-A[k]).squared_length()); double area; volume += sF * subsimplex_void_volume(x0, y0, z0, WA[k].weight(), &area); // sum signed volume contribution from subsimplexes surface += sF * area; per_atom_surface[e[k]] += sF * area; } } pts[i] = p_vertex_i; // place vertex(i) back to array } out_atom_surf = per_atom_surface; out_surf = surface; return volume; }
void draw_facets(Delaunay &Tr,std::vector<Facet> &facets,PointColor pcolors,CGAL::Geomview_stream &gv) { if(! gv_on) return; CGAL::Color colors[] = {CGAL::BLUE,CGAL::GREEN,CGAL::YELLOW,CGAL::DEEPBLUE, CGAL::PURPLE,CGAL::VIOLET,CGAL::ORANGE,CGAL::RED}; if(pcolors.size() == 0) return draw_facets(Tr,facets,gv); // draw with color interpolation for (std::vector<Facet>::iterator it = facets.begin();it != facets.end();it++) { Facet f = *it; Cell_handle c = f.first; int j = f.second; CGAL::Color vcolors[3]; int k = 0; for(int i = 0;i < 4;i++) if(i != j) vcolors[k++] = pcolors[c->vertex(i)->info()]; int r = (vcolors[0].red() + vcolors[1].red() + vcolors[2].red()) / 3; int g = (vcolors[0].green() + vcolors[1].green() + vcolors[2].green()) / 3; int b = (vcolors[0].blue() + vcolors[1].blue() + vcolors[2].blue()) / 3; gv << CGAL::Color(r,g,b); // std::cout << "RGB " << r << " " << g << " " << b <<std::endl; Triangle t = Tr.triangle(f); gv << t; } }
// Returns whether the facet facet is attached to the cell // that is used to represent facet bool triangle_attached_to(const DT_3& dt, const Facet& facet) { Cell_handle cell = facet.first; int index = facet.second; if(dt.is_infinite(cell)) { return false; } Vertex_handle v1 = cell->vertex((index+1)%4); Vertex_handle v2 = cell->vertex((index+2)%4); Vertex_handle v3 = cell->vertex((index+3)%4); Vertex_handle w = cell->vertex(facet.second); return CGAL::side_of_bounded_sphere(v1->point(),v2->point(),v3->point(),w->point())==CGAL::ON_BOUNDED_SIDE; }
// Returns whether the facet facet is attached to the cell // that is used to represent facet bool edge_attached_to(const DT_3& dt, const Edge& edge, const Facet& facet) { if(dt.is_infinite(facet)) { return false; } Vertex_handle v1 = edge.first->vertex(edge.second); Vertex_handle v2 = edge.first->vertex(edge.third); Cell_handle cell = facet.first; int i1 = facet.second; int i2 = cell->index(v1); int i3 = cell->index(v2); CGAL_assertion(i1!=i2); CGAL_assertion(i1!=i3); CGAL_assertion(i2!=i3); int j = 0; while(j==i1 || j==i2 || j==i3) { j++; } // j is the index of the third point of the facet Vertex_handle w = cell->vertex(j); return CGAL::side_of_bounded_sphere(v1->point(),v2->point(),w->point())==CGAL::ON_BOUNDED_SIDE; }
void draw_line_tetra(Delaunay &Tr,std::vector<Cell_handle> &cells,CGAL::Geomview_stream &gv) { if(! gv_on) return; CGAL::Color colors[] = {CGAL::BLUE,CGAL::GREEN,CGAL::YELLOW,CGAL::DEEPBLUE, CGAL::PURPLE,CGAL::VIOLET,CGAL::ORANGE,CGAL::RED}; int k = 0; for (std::vector<Cell_handle>::iterator it = cells.begin();it != cells.end();it++,k++) { Cell_handle c = *it; gv << colors[k % 8]; Segment segs[4]; for(int i = 0;i < 4;i++) { for(int j = i + 1;j < 4;j++) { Segment s = Segment(c->vertex(i)->point(),c->vertex(j)->point()); gv << s; } } } }
AlphaSimplex3D:: AlphaSimplex3D(const Delaunay3D::Facet& f) { Cell_handle c = f.first; for (int i = 0; i < 4; ++i) if (i != f.second) Parent::add(c->vertex(i)); }
Vertex_list fromCell(const Cell_handle& ch) { Vertex_list the_list; for (auto i = 0; i < 4; i++) { the_list.push_back(ch->vertex(i)); } return the_list; }
void print(const Cell_handle c, const std::string& name) const { std::cerr << name << ":[" ; for ( int i=0; i<4 ; ++i ) std::cerr << "[" << c->vertex(i)->point() << "]"; std::cerr << "] "; }
Point circumcenter(const Facet& f) { Cell_handle c = f.first; int id = f.second; Point p[3]; for(int i = 0; i < 3; i ++) p[i] = c->vertex((id + (i+1))%4)->point(); return cc_tr_3(p[0], p[1], p[2]); }
bool is_p_inside_cell(const Point& p, const Cell_handle& c, bool& is_degenerate) { is_degenerate = false; for(int id = 0; id < 4; id ++) { Tetrahedron t1 (c->vertex((id+1)%4)->point(), c->vertex((id+2)%4)->point(), c->vertex((id+3)%4)->point(), p); Tetrahedron t2 (c->vertex((id+1)%4)->point(), c->vertex((id+2)%4)->point(), c->vertex((id+3)%4)->point(), c->vertex(id)->point()); if( CGAL::is_negative(t1.volume()*t2.volume()) ) return false; if( ! CGAL::is_positive(t1.volume()*t2.volume()) ) is_degenerate = true; } return true; }
static void printCell(const Cell_handle &cell, const Vertex_handle &infinity) { DEBUG_START; unsigned infinityIndex = cell->index(infinity); for (unsigned i = 0; i < NUM_CELL_VERTICES; ++i) if (i != infinityIndex) std::cout << cell->vertex(i)->info() << " "; std::cout << std::endl; DEBUG_END; }
AlphaSimplex3D:: AlphaSimplex3D(const Delaunay3D::Edge& e, const SimplexSet& simplices, const Delaunay3D& Dt, Facet_circulator facet_bg) { Cell_handle c = e.first; Parent::add(c->vertex(e.second)); Parent::add(c->vertex(e.third)); Facet_circulator cur = facet_bg; while (Dt.is_infinite(*cur)) ++cur; SimplexSet::const_iterator cur_iter = simplices.find(AlphaSimplex3D(*cur)); RealValue min = cur_iter->alpha(); const VertexSet& vertices = static_cast<const Parent*>(this)->vertices(); VertexSet::const_iterator v = vertices.begin(); const DPoint& p1 = (*v++)->point(); const DPoint& p2 = (*v)->point(); attached_ = false; if (facet_bg != 0) do { VertexSet::const_iterator v = vertices.begin(); int i0 = (*cur).first->index(*v++); int i1 = (*cur).first->index(*v); int i = 6 - i0 - i1 - (*cur).second; if (Dt.is_infinite(cur->first->vertex(i))) { ++cur; continue; } DPoint p3 = (*cur).first->vertex(i)->point(); cur_iter = simplices.find(AlphaSimplex3D(*cur)); if (CGAL::side_of_bounded_sphere(p1, p2, p3) == CGAL::ON_BOUNDED_SIDE) attached_ = true; RealValue val = cur_iter->alpha(); if (val < min) min = val; ++cur; } while (cur != facet_bg); if (attached_) alpha_ = min; else alpha_ = CGAL::squared_radius(p1, p2); }
/** * Computes the circumcenters of the given cell * @param cell The cell to compute the circumcenters for * @param result Stores the circumcenter * @return TRUE always */ bool computeCircumcenter( const Cell_handle & cell, Point & result ) { // The four vertices of the cell (tetrahedron) const Point & p = cell->vertex( 0 )->point(); const Point & q = cell->vertex( 1 )->point(); const Point & r = cell->vertex( 2 )->point(); const Point & s = cell->vertex( 3 )->point(); // Translate p to the origin to simplify the expression double qpx = q.x() - p.x(); double qpy = q.y() - p.y(); double qpz = q.z() - p.z(); double qp2 = square( qpx ) + square( qpy ) + square( qpz ); double rpx = r.x() - p.x(); double rpy = r.y() - p.y(); double rpz = r.z() - p.z(); double rp2 = square( rpx ) + square( rpy ) + square( rpz ); double spx = s.x() - p.x(); double spy = s.y() - p.y(); double spz = s.z() - p.z(); double sp2 = square( spx ) + square( spy ) + square( spz ); double determinant = CGAL::determinant( qpx, qpy, qpz, rpx, rpy, rpz, spx, spy, spz ); if ( determinant == 0 ) { result = CGAL::ORIGIN + ( ( p - CGAL::ORIGIN ) * 0.25 + ( q - CGAL::ORIGIN ) * 0.25 + ( r - CGAL::ORIGIN ) * 0.25 + ( s - CGAL::ORIGIN ) * 0.25 ); return true; } double numX = CGAL::determinant( qpy, qpz, qp2, rpy, rpz, rp2, spy, spz, sp2 ); double numY = CGAL::determinant( qpx, qpz, qp2, rpx, rpz, rp2, spx, spz, sp2 ); double numZ = CGAL::determinant( qpx, qpy, qp2, rpx, rpy, rp2, spx, spy, sp2 ); double inverse = 1.0 / ( 2.0 * determinant ); result = Point( p.x() + numX * inverse, p.y() - numY * inverse, p.z() + numZ * inverse ); return true; }
AlphaSimplex3D:: AlphaSimplex3D(const Delaunay3D::Facet& f, const SimplexSet& simplices, const Delaunay3D& Dt) { Cell_handle c = f.first; for (int i = 0; i < 4; ++i) if (i != f.second) Parent::add(c->vertex(i)); Cell_handle o = c->neighbor(f.second); int oi = o->index(c); VertexSet::const_iterator v = static_cast<const Parent*>(this)->vertices().begin(); const DPoint& p1 = (*v++)->point(); const DPoint& p2 = (*v++)->point(); const DPoint& p3 = (*v)->point(); attached_ = false; if (!Dt.is_infinite(c->vertex(f.second)) && CGAL::side_of_bounded_sphere(p1, p2, p3, c->vertex(f.second)->point()) == CGAL::ON_BOUNDED_SIDE) attached_ = true; else if (!Dt.is_infinite(o->vertex(oi)) && CGAL::side_of_bounded_sphere(p1, p2, p3, o->vertex(oi)->point()) == CGAL::ON_BOUNDED_SIDE) attached_ = true; else alpha_ = CGAL::squared_radius(p1, p2, p3); if (attached_) { if (Dt.is_infinite(c)) alpha_ = simplices.find(AlphaSimplex3D(*o))->alpha(); else if (Dt.is_infinite(o)) alpha_ = simplices.find(AlphaSimplex3D(*c))->alpha(); else alpha_ = std::min(simplices.find(AlphaSimplex3D(*c))->alpha(), simplices.find(AlphaSimplex3D(*o))->alpha()); } }
/** * Adds a new tetrahedron to the hierarchy * @param cell The cell associated with the tetrahedron */ void UniformGrid::add( Cell_handle cell ) { Point3D v0( cell->vertex( 0 )->point().x(), cell->vertex( 0 )->point().y(), cell->vertex( 0 )->point().z() ); Point3D v1( cell->vertex( 1 )->point().x(), cell->vertex( 1 )->point().y(), cell->vertex( 1 )->point().z() ); Point3D v2( cell->vertex( 2 )->point().x(), cell->vertex( 2 )->point().y(), cell->vertex( 2 )->point().z() ); Point3D v3( cell->vertex( 3 )->point().x(), cell->vertex( 3 )->point().y(), cell->vertex( 3 )->point().z() ); tetrahedrons.push_back( new Tetrahedron( v0, v1, v2, v3, cell ) ); xmin = MIN( xmin, MIN( MIN( v0.x, v1.x ), MIN( v2.x, v3.x ) ) ); ymin = MIN( ymin, MIN( MIN( v0.y, v1.y ), MIN( v2.y, v3.y ) ) ); zmin = MIN( zmin, MIN( MIN( v0.z, v1.z ), MIN( v2.z, v3.z ) ) ); xmax = MAX( xmax, MAX( MAX( v0.x, v1.x ), MAX( v2.x, v3.x ) ) ); ymax = MAX( ymax, MAX( MAX( v0.y, v1.y ), MAX( v2.y, v3.y ) ) ); zmax = MAX( zmax, MAX( MAX( v0.z, v1.z ), MAX( v2.z, v3.z ) ) ); }
bool DualPolyhedron_3::lift(Cell_handle cell, unsigned iNearest) { DEBUG_START; Vector_3 xOld = cell->info().point - CGAL::Origin(); std::cout << "Old tangient point: " << xOld << std::endl; const auto activeGroup = calculateActiveGroup(cell, iNearest, items); if (activeGroup.empty()) { DEBUG_END; return false; } Vector_3 xNew = leastSquaresPoint(activeGroup, items); std::cout << "New tangient point: " << xNew << std::endl; ASSERT(cell->has_vertex(infinite_vertex())); unsigned infinityIndex = cell->index(infinite_vertex()); std::vector<Vertex_handle> vertices; Vertex_handle dominator; double alphaMax = 0.; double alphaMax2 = 0.; for (unsigned i = 0; i < NUM_CELL_VERTICES; ++i) { if (i == infinityIndex) continue; vertices.push_back(cell->vertex(i)); Vertex_handle vertex = mirror_vertex(cell, i); Plane_3 plane = ::dual(vertex->point()); double alpha; bool succeeded; std::tie(succeeded, alpha) = calculateAlpha(xOld, xNew, plane); if (!succeeded) return false; std::cout << "Alpha #" << i << ": " << alpha << std::endl; ASSERT(alpha <= 1. && "Wrongly computed alpha"); if (alpha > alphaMax) { alphaMax2 = alphaMax; alphaMax = alpha; dominator = vertex; } } std::cout << "Maximal alpha: " << alphaMax << std::endl; std::cout << "Maximal alpha 2nd: " << alphaMax2 << std::endl; ASSERT(alphaMax < 1. && "What to do then?"); if (alphaMax > 0.) { std::cout << "Full move is impossible, performing partial move" << std::endl; partiallyMove(xOld, xNew, vertices, dominator, alphaMax, alphaMax2); } else { std::cout << "Performing full move" << std::endl; if (!fullyMove(vertices, xNew, activeGroup)) { DEBUG_END; return false; } } DEBUG_END; return true; }
// Constructs regular triangulation of weighted points. // Builds graph of cells with voids. Finds connected components of this graph (voids as clusters of cells). // For each component calculates total void volume and area. // Excludes roughs on the surface (components, connected to the infinite cell). bool regular_triangulation_voids(const Wpi_container& points, Voids_result &res) { typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Cell_handle> CellGraph; typedef typename boost::graph_traits<CellGraph>::vertex_descriptor GVertex; typedef typename boost::graph_traits<CellGraph>::vertex_iterator GVertex_iterator; Rt T; // regular triangulation CellGraph G; // Voronoi subgraph where cells_sqr_r > 0 and edges_sqr_r > 0 (cells and edges with voids) GVertex_iterator vi, vi_end; res.out_log << "Number of input points for RT : " << points.size() << std::endl; T.insert(points.begin(), points.end()); // insert all points in a row (this is faster than one insert() at a time). T.infinite_vertex()->info().atom_id = -1; // set special atom_id for dummy infinite triangulation vertex assert(T.dimension() == 3); /*res.out_log << "Is valid : " << T.is_valid() << std::endl;*/ res.out_log << "Number of vertices in RT : " << T.number_of_vertices() << std::endl; res.out_log << "Number of cells : " << T.number_of_cells() << std::endl; res.out_log << "Number of finite cells : " << T.number_of_finite_cells() << std::endl; /*res.out_log << "Inf. vert. atom id : " << T.infinite_vertex()->info().atom_id << std::endl;*/ // Add finite cells having sqr_r > 0 as graph verticies Finite_cells_iterator cit, cit_end = T.finite_cells_end(); for (cit = T.finite_cells_begin(); cit != cit_end; cit++) { if (cit->weighted_circumcenter().weight() > 0) // cached weighted circumcenter computation cit->id() = add_vertex(Cell_handle(cit), G); // store corresponding graph vertex descriptor in cell's id() } // store one of infinite cells in graph: it will represent all of them. It should be the last vertex in graph with maximal vertex_descriptor. const GVertex inf_graph_v = add_vertex(T.infinite_cell(), G); res.out_log << "inf_graph_v descriptor : " << inf_graph_v << std::endl; // Add graph edges connecting cells with void for (boost::tie(vi, vi_end) = vertices(G); vi != vi_end; ++vi) { const GVertex v1 = *vi; // current graph vertex if (v1 != inf_graph_v) { // check edges only from finite cells const Cell_handle c1 = G[v1]; // current cell for (int i = 0; i < 4; i++) { // for each of four neighbor cells const Cell_handle c2 = c1->neighbor(i); bool ends_in_void = false; // do both corresponding Voronoi verticies lie in void space? bool on_same_side = false; // do both ends lie on the same side of corresponding cell facet? const Weighted_point &p1 = c1->vertex((i+1)&3)->point(); // weighted points of common facet between c and c2 cells const Weighted_point &p2 = c1->vertex((i+2)&3)->point(); const Weighted_point &p3 = c1->vertex((i+3)&3)->point(); const Point &wcc = c1->weighted_circumcenter().point(); GVertex v2; // neighbor vertex in graph to construct edge to if (c2->id() != -1) { // neighbor cell is finite and in graph: id()=vertex_descriptor v2 = c2->id(); ends_in_void = true; on_same_side = (orientation(p1.point(), p2.point(), p3.point(), wcc) == orientation(p1.point(), p2.point(), p3.point(), c2->weighted_circumcenter().point())); } else if (T.is_infinite(c2)) { v2 = inf_graph_v; // use our infinite graph vertex as destination if neighbor cell is infinite ends_in_void = true; on_same_side = (orientation(p1.point(), p2.point(), p3.point(), wcc) != orientation(p1.point(), p2.point(), p3.point(), c1->vertex(i)->point().point())); // same side with infinite vertex = different sides with cell vertex } typename K::Compute_squared_radius_smallest_orthogonal_sphere_3 r_mouth; // facet bottleneck squared radius (facet closed if < 0) if (ends_in_void && (v2 > v1) && (on_same_side || r_mouth(p1, p2, p3) > 0)) add_edge(v1, v2, G); // use ordering v2>v1 to rule out most of the parallel edges: rely on v_inf>v1 for all finite vertices } } } res.out_log << "Number of vertices in G : " << num_vertices(G) << std::endl; res.out_log << "Number of edges in G : " << num_edges(G) << std::endl; // Find connected components on this Voronoi subnetwork std::vector<int> component(num_vertices(G)); int num_components = boost::connected_components(G, &component[0]); const int inf_comp_id = component[inf_graph_v]; // component of infinite vertex res.out_log << "Number of connected components : " << num_components << std::endl; res.out_log << "Component id of infinite vertex : " << inf_comp_id << std::endl; // Reserve storage for results res.voids.resize(num_components); std::fill(res.atom_surf.begin(), res.atom_surf.end(), 0.0L); // Calculate total volume of each finite component for (boost::tie(vi, vi_end) = vertices(G); vi != vi_end; ++vi) { const int comp_id = component[*vi]; if (comp_id != inf_comp_id) { // skip infinite component Cell_handle cell = G[*vi]; // current cell double Vc, Sc; // cell volume and surface Array_double_4 Sa; // per atom surface in cell Vc = cell_void_volume(cell, Sc, Sa); // void volume of cell and its surface area res.voids[comp_id].volume += Vc; // add to volume of component res.voids[comp_id].surface += Sc; for (int k = 0; k < 4; k++) { // process four cell atoms int atom_id = cell->vertex(k)->info().atom_id; res.atom_surf[atom_id] += Sa[k]; // atom exposed surface res.voids[comp_id].atoms.insert(atom_id); // add to set of cavity atoms } } } // Remove skipped infinite component with zero values res.voids.erase(res.voids.begin() + inf_comp_id); // Sort cavities by volume std::sort(res.voids.begin(), res.voids.end(), void_greater); return true; }