.. _program_listing_file_src_polyhedron.hpp: Program Listing for File polyhedron.hpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/polyhedron.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 2019,2020 Greg Tucker brille is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. brille 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with brille. If not, see . */ #ifndef BRILLE_POLYHEDRON_H_ #define BRILLE_POLYHEDRON_H_ #include #include #include #include "array_latvec.hpp" // defines bArray #include "debug.hpp" #include "utilities.hpp" #include "approx.hpp" namespace brille { template static std::vector unique(const std::vector& x){ std::vector out; out.push_back(x[0]); for (auto & v: x) if (std::find(out.begin(), out.end(), v) == out.end()) out.push_back(v); return out; } template static bool is_not_dangling(const std::vector& adjacents, const std::vector& face){ bool not_dangling{true}; for (auto & x: face) not_dangling &= adjacents[x] > 2u; return not_dangling; } template std::vector bare_winding_angles(const bArray& vecs, const size_t i, const bArray& n){ if (vecs.ndim() !=2 || vecs.size(1) != 3) throw std::runtime_error("Finding a winding angle requires the cross product, which is only defined in 3D"); // vecs should be normalized already std::vector angles(vecs.size(0), 0.); T dotij, y_len, angij; bArray x(1u,3u), y(1u,3u); // ensure all have memory allocated T crsij[3]; // to hold the cross product for (size_t j=0; j // std::vector winding_angles(const LQVec& vecs, const size_t i, const LQVec& n){ // // vecs should be normalized already // std::vector angles(vecs.size()); // T dotij, y_len, angij; // LQVec crsij, x, y; // for (size_t j=0; j static T triangle_area_squared(const T a, const T b, const T c){ T s = (a+b+c)/2; return s*(s-a)*(s-b)*(s-c); } template int face_has_area(const bArray& points){ // first verify that all points are coplanar // pick the first three points to define a plane, then ensure all points are in it if (points.size(0)<3) return -2; // can't be a face auto p0 = points.view(0); T s=0; bArray a,b; for (size_t i=1; i bArray point_inside_plane( const bArray& n, const bArray& p, const bArray& x ){ return dot(n, x-p).is(brille::cmp::le, 0.); // true if x is closer to the origin } template std::vector intersection( const bArray& ni, const bArray& pi, const bArray& nj, const bArray& pj, const bArray& nk, const bArray& pk, bArray& at ){ using namespace brille; size_t num[6]{ni.size(0), pi.size(0), nj.size(0), pj.size(0), nk.size(0), pk.size(0)}; size_t npt=0; for (int i=0; i<6; ++i) if (num[i]>npt) npt=num[i]; for (int i=0; i<6; ++i) if (!ok_size(num[i],npt)) throw std::runtime_error("All normals and points must be singular or equal sized"); // output storage to indicate if an intersection exists std::vector out(npt, false); // find the scaled intersection points // cross, multiply, and dot scale-up singleton inputs auto tmp = cross(nj,nk)*dot(pi,ni) + cross(nk,ni)*dot(pj,nj) + cross(ni,nj)*dot(pk,nk); T detM, mat[9]; const T *ptri=ni.ptr(0,0), *ptrj=nj.ptr(0,0), *ptrk=nk.ptr(0,0); bool ui{ni.size(0) == npt}, uj{nj.size(0) == npt}, uk{nk.size(0) == npt}; for (ind_t a=0; a 1e-10){ at.set(a, tmp.view(a)/detM); out[a] = true; } } return out; } template std::vector intersection( const bArray& n, const bArray& p, const bArray& ni, const bArray& pi, const bArray& nj, const bArray& pj, const bArray& nk, const bArray& pk, bArray& at){ std::vector ok = intersection(ni, pi, nj, pj, nk, pk, at); for (size_t i=0; i bool one_intersection(L... args){ // return intersection(args...).getvalue(0); // } template bArray one_intersection( const bArray& n, const bArray& p, const bArray& ni, const bArray& pi, const bArray& nj, const bArray& pj, const bArray& nk, const bArray& pk) { bArray at(1u,3u); if (intersection(n,p,ni,pi,nj,pj,nk,pk,at)[0]) return at; else return bArray(); } template std::vector reverse(const std::vector& x){ std::vector r; for (size_t i = x.size(); i--;) r.push_back(x[i]); return r; } template std::vector> reverse_each(const std::vector>& x){ std::vector> r; std::transform(x.begin(), x.end(), std::back_inserter(r), [](const std::vector& y){return reverse(y);}); // for (auto i: x) r.push_back(reverse(i)); return r; } class Polyhedron{ public: using ind_t = brille::ind_t; using shape_t = typename bArray::shape_t; protected: bArray vertices; bArray points; bArray normals; std::vector> faces_per_vertex; std::vector> vertices_per_face; public: // empty initializer explicit Polyhedron(): vertices(bArray()), points(bArray()), normals(bArray()), faces_per_vertex(std::vector>()), vertices_per_face(std::vector>()) {} Polyhedron(const bArray& v): vertices(v){ this->keep_unique_vertices(); if (vertices.size(0) > 3){ this->find_convex_hull(); this->find_all_faces_per_vertex(); this->polygon_vertices_per_face(); this->purge_central_polygon_vertices(); this->sort_polygons(); this->purge_extra_vertices(); } } Polyhedron(const bArray& v, const bArray& p): vertices(v), points(p), normals(p/norm(p)) { this->keep_unique_vertices(); this->find_all_faces_per_vertex(); this->polygon_vertices_per_face(); // this->sort_polygons(); this->purge_central_polygon_vertices(); this->sort_polygons(); this->purge_extra_vertices(); } // initialize from vertices, points, and all relational information Polyhedron(const bArray& v, const bArray& p, const std::vector>& fpv, const std::vector>& vpf): vertices(v), points(p), normals(p/norm(p)), faces_per_vertex(fpv), vertices_per_face(vpf){ this->sort_polygons(); } // initalize from vertices, points, normals, and three-plane intersection information Polyhedron(const bArray& v, const bArray& p, const bArray& n): vertices(v), points(p), normals(n) { verbose_update("Construct a polyhedron from vertices:\n",vertices.to_string()); verbose_update("and planes (points, normals):\n", normals.to_string(), points.to_string()); this->keep_unique_vertices(); this->find_all_faces_per_vertex(); this->polygon_vertices_per_face(); // this->sort_polygons(); this->purge_central_polygon_vertices(); this->sort_polygons(); this->purge_extra_vertices(); } // initialize from vertices, points, normals, and all relational information Polyhedron(const bArray& v, const bArray& p, const bArray& n, const std::vector>& fpv, const std::vector>& vpf): vertices(v), points(p), normals(n), faces_per_vertex(fpv), vertices_per_face(vpf){ this->special_keep_unique_vertices(); // for + below, which doesn't check for vertex uniqueness } // initialize from vertices, and vertices_per_face Polyhedron(const bArray& v, const std::vector>& vpf): vertices(v), points(0,3), normals(0,3), vertices_per_face(vpf) { this->find_face_points_and_normals(); this->sort_polygons(); this->find_all_faces_per_vertex(); // do we really need this? } // initialize from vertices, point, normals, and vertices_per_face (which needs sorting) Polyhedron( const bArray& v, const bArray& p, const bArray& n, const std::vector>& vpf ): vertices(v), points(p), normals(n), vertices_per_face(vpf){ this->sort_polygons(); this->find_all_faces_per_vertex(); verbose_update("Finished constructing Polyhedron with vertices\n",vertices.to_string(),"points\n",points.to_string(),"normals\n",normals.to_string()); } // copy constructor Polyhedron(const Polyhedron& other): vertices(other.get_vertices()), points(other.get_points()), normals(other.get_normals()), faces_per_vertex(other.get_faces_per_vertex()), vertices_per_face(other.get_vertices_per_face()) {} // assignment from another CentredPolyhedron Polyhedron& operator=(const Polyhedron& other){ this->vertices = other.get_vertices(); this->points = other.get_points(); this->normals = other.get_normals(); this->faces_per_vertex = other.get_faces_per_vertex(); this->vertices_per_face = other.get_vertices_per_face(); return *this; } Polyhedron mirror(void) const { return Polyhedron(-1*this->vertices, -1*this->points, -1*this->normals, this->faces_per_vertex, reverse_each(this->vertices_per_face)); } template Polyhedron rotate(const std::array rot) const { bArray newv(vertices.size(0),3u); bArray newp(points.size(0), 3u); bArray newn(normals.size(0), 3u); for (size_t i=0; i(newv.ptr(i), rot.data(), vertices.ptr(i)); for (size_t i=0; i(newp.ptr(i), rot.data(), points.ptr(i)); for (size_t i=0; i(newn.ptr(i), rot.data(), normals.ptr(i)); return Polyhedron(newv, newp, newn, this->faces_per_vertex, this->vertices_per_face); } template Polyhedron translate(const bArray& vec) const { if (vec.numel() != 3) throw std::runtime_error("Translating a Polyhedron requires a single three-vector"); // protect against + with an empty Array: if (0==vertices.size(0)||0==points.size(0)) return Polyhedron(); return Polyhedron(vertices+vec, points+vec, normals, this->faces_per_vertex, this->vertices_per_face); } Polyhedron operator+(const Polyhedron& other) const { auto ndim = this->vertices.ndim(); size_t d = this->vertices.size(ndim-1); if (other.vertices.ndim() != ndim || other.vertices.size(ndim-1) != d) throw std::runtime_error("Only equal dimensionality polyhedra can be combined."); // combine all vertices, points, and normals; adjusting the fpv and vpf indexing int tvn = static_cast(this->vertices.size(0)); int tfn = static_cast(this->points.size(0)); size_t ovn = other.vertices.size(0); size_t ofn = other.points.size(0); bArray v(tvn+ovn, d); bArray p(tfn+ofn, d), n(tfn+ofn, d); for (size_t i=0; i(tvn); ++i) v.set(i, this->vertices.view(i)); for (size_t i=0; i(tfn); ++i) p.set(i, this->points.view(i)); for (size_t i=0; i(tfn); ++i) n.set(i, this->normals.view(i)); for (size_t i=0; i> fpv(this->faces_per_vertex), vpf(this->vertices_per_face); fpv.resize(tvn+ovn); vpf.resize(tfn+ofn); for (size_t i=0; i get_vertices(void) const { return vertices; } bArray get_points(void) const { return points; } bArray get_normals(void) const { return normals; } std::vector> get_faces_per_vertex(void) const { return faces_per_vertex; } std::vector> get_vertices_per_face(void) const {return vertices_per_face; } // bArray get_half_edges(void) const{ // for each face find the point halfway between each set of neighbouring vertices // Convex polyhedra always have edges neighbouring two faces, so we will // only ever find (∑pᵢ)>>1 half-edge points, where pᵢ are the number of // vertices (or edges) on the iᵗʰ face. size_t nfv = 0; for (auto f: vertices_per_face) nfv += f.size(); // we don't care about point order, but we only want to have one copy // of each half-edge point. At the expense of memory, we can keep track // of which pairs we've already visited: std::vector unvisited(nfv*nfv, true); bArray hep(nfv>>1, 3u); size_t found=0, a,b; for (auto f: vertices_per_face) for (size_t i=0; i>1){ std::string msg = "Found " + std::to_string(found) + " half edge"; msg += " points but expected to find " + std::to_string(nfv>>1); if (found < nfv>>1) msg += ". Is the polyhedron open? "; throw std::runtime_error(msg); } return hep; } std::string string_repr(void) const { size_t nv = vertices.size(0), nf=points.size(0); std::string repr = "Polyhedron with "; repr += std::to_string(nv) + " " + (1==nv?"vertex":"vertices") + " and "; repr += std::to_string(nf) + " " + (1==nf?"facet":"facets"); debug_exec(repr += "; volume " +std::to_string(this->get_volume());) return repr; } double get_volume(void) const { /* per, e.g., http://wwwf.imperial.ac.uk/~rn/centroid.pdf For a polyhedron with N triangular faces, each with ordered vertices (aᵢ, bᵢ, cᵢ), one can define nᵢ = (bᵢ-aᵢ)×(cᵢ-aᵢ) for each face and then find that the volume of the polyhedron is V = 1/6 ∑ᵢ₌₁ᴺ aᵢ⋅ nᵢ In our case here the polyhedron faces are likely not triangular, but we can subdivide each n-polygon face into n-2 triangles relatively easily. Furthermore, we can ensure that the vertex order is correct by comparing the triangle-normal to our already-stored facet normals. */ double volume{0.}, subvol; double n[3]; for (size_t f=0; fvertices.view(vertices_per_face[f][0]); for (size_t i=1; ivertices.view(vertices_per_face[f][ i ]) - a; auto ca = this->vertices.view(vertices_per_face[f][i+1]) - a; brille::utils::vector_cross(n, ba.ptr(0), ca.ptr(0)); subvol = brille::utils::vector_dot(a.ptr(0), n); // if (brille::utils::vector_dot(n, normals.data(f)) < 0) subvol *= -1.0; volume += subvol; } } return volume/6.0; // not-forgetting the factor of 1/6 } bArray get_centroid(void) const { // also following http://wwwf.imperial.ac.uk/~rn/centroid.pdf bArray centroid({1u,3u}, 0.); double n[3]; for (auto verts: vertices_per_face){ auto a = vertices.view(verts[0]); for (size_t i=1; iget_volume()); } double get_circumsphere_radius(void) const { auto centroid2vertices = vertices - this->get_centroid(); auto c2v_lengths = norm(centroid2vertices); auto longest_c2v = c2v_lengths.max(0); double csphrad = longest_c2v[0]; return csphrad; // return norm(vertices - this->get_centroid()).max(0)[0]; } protected: void keep_unique_vertices(void){ std::vector flg(vertices.size(0), true); for (size_t i=1; ivertices = this->vertices.extract(flg); } void special_keep_unique_vertices(){ std::vector flg(vertices.size(0), true); for (size_t i=1; i map; int count{0}; for (auto f: flg) map.push_back( f ? count++ : -1); // find the equivalent vertices for the non-unique ones: for (size_t i=0; i=0 && vertices.match(i,j)) map[i] = map[j]; for (auto & face: vertices_per_face){ std::vector one; for (auto & f: face) one.push_back(map[f]); face = one; } // keep only the unique vertices this->vertices = this->vertices.extract(flg); } } void find_convex_hull(void){ /* Find the set of planes which contain all vertices. The cross product between two vectors connecting three points defines a plane normal. If the plane passing through the three points partitions space into point-free and not-point-free then it is one of the planes of the convex-hull. */ debug_update_if(vertices.size(0)<3, "find_convex_hull:: Not enough vertices for brille::utils::binomial_coefficient"); unsigned long long bc = brille::utils::binomial_coefficient(vertices.size(0), 3u); if (bc > static_cast(std::numeric_limits::max())) throw std::runtime_error("Too many vertices to count all possible normals with a `size_t` integer"); bArray n(static_cast(bc), 3u); bArray p(static_cast(bc), 3u); bArray ab(2u, 3u); size_t count = 0; // The same algorithm without temporary nijk, vi, vj, vk arrays (utilizing // vertices.extract, p.extract, and n.extract instead) does not work. // Either the compiler optimizes something important away or there is a bug // in repeatedly extracting the same array from an Array. In either // case, vectors which should not have been zero ended up as such. for (size_t i=0; i> fpv(vertices.size(0)); // for (size_t i=0; i(j)); // } std::vector> fpv; for (size_t i=0; i onplane = dot(normals, vertices.view(i)-points).find(brille::cmp::eq,0.0); std::vector vind; for (auto i: onplane) vind.push_back(static_cast(i)); fpv.push_back(vind); } verbose_update("Found faces per vertex array\n",fpv); this->faces_per_vertex = fpv; } void polygon_vertices_per_face(void) { bool flag; // We have 3+ faces per vertex, so we can now find the vertices per face std::vector> vpf(this->points.size(0)); for (size_t i=0; i(i))==vpf[facet].end()) vpf[facet].push_back(i); flag = true; for (auto vertex: vpf[facet]) if (static_cast(vertex)==i) flag = false; if (flag) vpf[facet].push_back(static_cast(i)); } } verbose_update("Found vertices per face array\n", vpf); // additionally, we only want to keep faces which describe polygons std::vector is_polygon(vpf.size(), true); for (size_t i=0; i 0; verbose_update("Face is polygon:",is_polygon); this->points = this->points.extract(is_polygon); this->normals = this->normals.extract(is_polygon); // we should modify faces_per_vertex here, to ensure its indexing is correct size_t count = 0, max=vpf.size(); std::vector map; for (size_t i=0; i> reduced_fpv(faces_per_vertex.size()); for (size_t i=0; i(map[facet])); this->faces_per_vertex = reduced_fpv; verbose_update("Faces per vertex reduced to\n", faces_per_vertex); // plus cut-down the vertices_per_face vector std::vector> polygon_vpf; for (size_t i=0; i2) polygon_vpf.push_back(i); this->vertices_per_face = polygon_vpf; verbose_update("Vertices per (polygon) face\n", vertices_per_face); } void purge_central_polygon_vertices(void){ /* We often build polyhedra from convex hulls of points and it is not uncommon for such point sets to include central face polygon points. Such points are not needed to describe the polyhedra and they inhibit sort_polygons from working, as the normalisation of face vertices causes a division by zero. */ // Go through all faces an remove central vertices std::vector is_centre; verbose_update("Starting vertices_per_face\n",vertices_per_face); for (size_t j=0; j(facet_verts.size(0)); facet_verts -= facet_centre; auto is_centre = norm(facet_verts).is(brille::cmp::eq,0.).to_std(); // std::vector needed for erase verbose_update("Face ",j," has central vertices", is_centre); for (size_t i=0; iactual_vertex_purge(); } void actual_vertex_purge(void){ // go through all faces again and determine whether a vertex is present std::vector keep(vertices.size(0), false); for (size_t i=0; i(i)) != v.end()){ keep[i] = true; break; } } size_t total = std::count(keep.begin(), keep.end(), true); if (total < vertices.size(0)){ verbose_update("Keeping ", total, " of ", vertices.size(0), " vertices"); // Remap the vertices_per_face array size_t count{0}; std::vector map; for (auto tf : keep) map.push_back(tf ? count++: total); for (auto& fv: vertices_per_face) for (auto& v: fv) if (map[v] 0; ) if (!keep[i]) faces_per_vertex.erase(faces_per_vertex.begin()+i); vertices = vertices.extract(keep); } } void sort_polygons(void){ std::vector> sorted_vpp; std::vector facet, perm, used; std::vector angles; double min_angle; size_t min_idx; auto all_normals = this->get_normals(); for (size_t j=0; jpoints.size(0); ++j){ facet = this->vertices_per_face[j]; verbose_update("Sorting face ",j," which has vertices",facet); auto facet_normal = all_normals.view(j); auto facet_verts = vertices.extract(facet); auto facet_centre = facet_verts.sum(0)/static_cast(facet.size()); facet_verts -= facet_centre; // these are now on-face vectors to each vertex // if a point happens to be at the face centre dividing by the norm is a problem. facet_verts = facet_verts/norm(facet_verts); // and now just their directions; verbose_update("With on-plane vectors\n",facet_verts.to_string()); perm.resize(facet.size()); perm[0] = 0; // always start with whichever vertex is first used.clear(); used.push_back(0); for (size_t i=1; i= facet.size()){ std::string msg = "Error finding minimum winding angle polygon vertex\n"; for (size_t d=0; dvertices.view(facet[d]).to_string(); msg += "Facet centre " + facet_centre.to_string(); msg += "Facet face vertices\n" + facet_verts.to_string(); msg += "Winding angles [ "; for (auto ang: angles) msg += std::to_string(ang) + " "; msg += "]"; throw std::runtime_error(msg); } perm[i] = min_idx; used.push_back(static_cast(min_idx)); } verbose_update("Producing sorting permutation",perm); std::vector sorted_face_v; for (size_t i=0; ivertices_per_face = sorted_vpp; } void purge_extra_vertices(void){ /* If we used our convex hull algorithm to determine our polygon, it might have extraneous vertices within its convex polygonal faces */ /* This method should be used only after find_all_faces_per_vertex, polygon_vertices_per_face, and sort_polygons have all been run as it assumes that the vertices per face are in increasing-winding-order. */ for (size_t n=0; n3 && iactual_vertex_purge(); } void find_face_points_and_normals(void){ // if the Polyhedron is defined from its vertices and vertices_per_face, // then we require the input to be correct, and calculate points and normals this->points.resize(vertices_per_face.size()); this->normals.resize(vertices_per_face.size()); size_t count = 0; for (auto face: vertices_per_face){ // auto centre = vertices.extract(face).sum(0); // this->points.set(count, centre/static_cast(face.size())); // this->normals.set(count, cross(vertices.view(face[1])-vertices.view(face[0]), vertices.view(face[2])-vertices.view(face[1]))); auto fv = vertices.extract(face); auto centre = fv.sum(0)/static_cast(face.size()); this->points.set(count, centre); auto v01 = fv.view(1) - fv.view(0); auto v12 = fv.view(2) - fv.view(1); auto nrm = cross(v01, v12)/norm(v01)/norm(v12); this->normals.set(count, nrm); ++count; } } public: Polyhedron centre(void) const { auto centroid = this->get_centroid(); return Polyhedron(vertices - centroid, points - centroid, normals, faces_per_vertex, vertices_per_face); } std::vector contains(const std::vector>& x) const { return this->contains(bArray::from_std(x)); } std::vector contains(const bArray& x) const { if (x.ndim()!=2 || x.size(x.ndim()-1)!=3) throw std::runtime_error("x must contain 3-vectors"); std::vector out; for (size_t i=0; istring_repr()," with ",other.string_repr()); auto itrsct = Polyhedron::bisect(*this, other.normals, other.points); // If two polyhedra intersect one another, their intersection is not null. return !brille::approx::scalar(itrsct.get_volume(), 0.); } Polyhedron intersection(const Polyhedron& other) const { // auto centroid = this->get_centroid(); // Polyhedron centred(vertices - centroid, points - centroid, normals, faces_per_vertex, vertices_per_face); // Polyhedron ipoly = Polyhedron::bisect(centred, other.normals, other.points-centroid); // return Polyhedron(ipoly.vertices + centroid, ipoly.points + centroid, ipoly.normals, ipoly.faces_per_vertex, ipoly.vertices_per_face); return Polyhedron::bisect(*this, other.normals, other.points); } template Polyhedron divide(const bArray&n, const bArray& p){ bArray centroid = this->get_centroid(); Polyhedron centred(vertices-centroid, points-centroid, normals, faces_per_vertex, vertices_per_face); Polyhedron divided = Polyhedron::bisect(centred, n, p-centroid); return divided.translate(centroid); } template static Polyhedron bisect(const Polyhedron& pin, const bArray& n, const bArray& p) { assert(n.ndim()==2 && p.ndim()==2 && n.size(1)==3 && p.size(1)==(3) && n.size(0)==p.size(0)); Polyhedron pout(pin); std::vector vertex_map; // copy the current vertices, normals, and relational information auto pv = pout.get_vertices(); auto pn = pout.get_normals(); auto pp = pout.get_points(); auto fpv = pout.get_faces_per_vertex(); auto vpf = pout.get_vertices_per_face(); // move the output polyhedron to be centred on the origin -- which requires we move all cutting planes as well // auto origin = sum(pv)/static_cast(pv.size(0)); verbose_update("Cut a ",pout.string_repr()," by ",n.size(0)," planes"); for (size_t i=0; i del_vertices; for (size_t j=0; j(j)); verbose_update("Vertices beyond the cut ", del_vertices); // and the list of facets which need to be cut or removed std::vector cut; for (size_t f=0; f(f)); } verbose_update("Facets ",cut," are cut by the plane"); // find the new intersection points of two neighbouring facets and the cutting plane std::vector new_vector; int last_face = static_cast(pn.size(0)); // the to-be-index of the new facet (normal) unsigned new_face_vertex_count{0}, new_vertex_count{0}; for (size_t j=0; j(pv.size(0)); verbose_update("Add intersection point ",at.to_string(0));//," to existing points\n",pv.to_string()); pv.append(0, at); // plus add the face index to the faces_per_vertex list for this new vertex fpv.push_back({cut[j], cut[k], last_face}); // track how many new vertices we add ++new_vertex_count; } else { // find the matching index that already is in the list: lv = static_cast(norm(pv-at).first(brille::cmp::eq,0.)); verbose_update("Reusing existing intersection point ",pv.to_string(lv)," for found intersection ",at.to_string(0)); } // add the new vertex to the list for each existing facet -- if its not already present if (std::find(vpf[cut[j]].begin(), vpf[cut[j]].end(), lv)==vpf[cut[j]].end()) vpf[cut[j]].push_back(lv); if (std::find(vpf[cut[k]].begin(), vpf[cut[k]].end(), lv)==vpf[cut[k]].end()) vpf[cut[k]].push_back(lv); // and the yet-to-be-created facet new_vector.push_back(lv); // keeping track of how many points ++new_face_vertex_count; } } } debug_update_if(new_vector.size()!=new_face_vertex_count, "New vertices is wrong size!"); if (new_vector.size()){ // add the normal and point for the new face // pn = cat(0, pn, ni); // pp = cat(0, pp, pi); pn.append(0,ni); pp.append(0,pi); // extend the vertices per face vector vpf.push_back(new_vector); } // extend the keep std::vector to cover the new points for (size_t z=0; z nnv; for (auto x: face) if (keep[x]) nnv.push_back(vertex_map[x]); if (nnv.empty()) nnv.push_back(0); // avoid having an unallocated face. Removed later face = unique(nnv); // remove duplicates (where do they come from?) } // we need to calculate the face centres, but only for true faces (more than 2 vertices) for (size_t j=0; j2) { auto cen = pv.extract(vpf[j]); // store the centroid as the on-plane point // We need to be extra careful whenever we use Array.set since we may be sharing data with other Array(s)!!! pp = pp.decouple(); // decouple only copies memory if it is shared // if (cen.size(0)) // this check isn't necessary since vpf[j].size()!=0 pp.set(j, cen.sum(0)/static_cast(cen.size(0)) ); } // remove any faces without three vertices std::vector has3v; for (size_t j=0; j2); pp = pp.extract(has3v); pn = pn.extract(has3v); // and remove their empty vertex lists vpf.erase(std::remove_if(vpf.begin(), vpf.end(), [](std::vector i){return unique(i).size()<3;}), vpf.end()); // remove any faces that are not connected on all sides bool check_again{true}; while (check_again){ // find the number of adjacent faces for each vertes std::vector adjacent_face_no(pv.size(0), 0u); for (size_t j=0; j(j))!=face.end()) adjacent_face_no[j]++; } // for each face now check if all vertices are adjacent to 3+ faces // storing the result for reducing pp & pn std::vector ok; for (auto & face: vpf) ok.push_back(is_not_dangling(adjacent_face_no, face)); // and going through a second time to erase extraneous faces: vpf.erase( std::remove_if(vpf.begin(), vpf.end(), [adjacent_face_no](std::vector& face){ return !is_not_dangling(adjacent_face_no, face); } ), vpf.end()); check_again = std::find(ok.begin(), ok.end(), false) != ok.end(); if (check_again){ pp = pp.extract(ok); pn = pn.extract(ok); } } // remove any vertices not on a face std::vector kv(pv.size(0), false); for (auto & face: vpf) for (auto & x: face) kv[x] = true; if (std::count(kv.begin(), kv.end(), false)){ std::vector kv_map; int kv_count{0}; for (auto && j : kv) kv_map.push_back(j ? kv_count++ : -1); for (auto & face: vpf){ std::vector nnv; for (auto & x: face) nnv.push_back(kv_map[x]); face = nnv; } pv = pv.extract(kv); } // with less than four points, normals, or vertices it can't be a Polyhedron if (pv.size(0)<4 || pp.size(0)<4 || pn.size(0)<4){ verbose_update("Null intersection"); return Polyhedron(); } // use the Polyhedron intializer to sort out fpv -- vpf should be correct pout = Polyhedron(pv, pp, pn, vpf); verbose_update("New ",pout.string_repr()); // copy the updated vertices, normals, and relational information pv=pout.get_vertices(); pn=pout.get_normals(); pp=pout.get_points(); fpv = pout.get_faces_per_vertex(); vpf = pout.get_vertices_per_face(); } } return pout; } bArray rand_rejection(const size_t n, const unsigned int seed=0) const { // initialize the random number generator with an optional non-zero seed: std::default_random_engine generator(seed>0 ? seed : std::chrono::system_clock::now().time_since_epoch().count()); // construct the uniform distribution spanning [0,1] std::uniform_real_distribution distribution(0.,1.); // find the minimum bounding corner and the vector to the maximum bounding corner bArray vmin = vertices.min(0), vdif = vertices.max(0) - vmin; // initialize the output points array bArray p(n,3); // generate random points until we have `n` which are inside the polyhedron for (ind_t i=0; icontains(p.view(i))[0] ) ++i; } return p; } }; template Polyhedron polyhedron_box(std::array& xmin, std::array& xmax){ std::vector> v{ {xmin[0], xmin[1], xmin[2]}, // 000 0 {xmin[0], xmax[1], xmin[2]}, // 010 1 {xmin[0], xmax[1], xmax[2]}, // 011 2 {xmin[0], xmin[1], xmax[2]}, // 001 3 {xmax[0], xmin[1], xmin[2]}, // 100 4 {xmax[0], xmax[1], xmin[2]}, // 110 5 {xmax[0], xmax[1], xmax[2]}, // 111 6 {xmax[0], xmin[1], xmax[2]} // 101 7 }; std::vector> vpf{{3,0,4,7},{3,2,1,0},{0,1,5,4},{3,7,6,2},{7,4,5,6},{2,6,5,1}}; return Polyhedron(bArray::from_std(v), vpf); } } // end namespace brille #endif // _POLYHEDRON_H_