// Begin License: // Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). // All rights reserved. // // This file is part of the Carve CSG Library (http://carve-csg.com/) // // This file may be used under the terms of the GNU General Public // License version 2.0 as published by the Free Software Foundation // and appearing in the file LICENSE.GPL2 included in the packaging of // this file. // // This file is provided "AS IS" with NO WARRANTY OF ANY KIND, // INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE. // End: #if defined(HAVE_CONFIG_H) # include #endif #include #include #include #include namespace { inline double CALC_X(const carve::geom::plane<3> &p, double y, double z) { return -(p.d + p.N.y * y + p.N.z * z) / p.N.x; } inline double CALC_Y(const carve::geom::plane<3> &p, double x, double z) { return -(p.d + p.N.x * x + p.N.z * z) / p.N.y; } inline double CALC_Z(const carve::geom::plane<3> &p, double x, double y) { return -(p.d + p.N.x * x + p.N.y * y) / p.N.z; } carve::geom::vector<2> _project_1(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.z, v.y); } carve::geom::vector<2> _project_2(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.x, v.z); } carve::geom::vector<2> _project_3(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.y, v.x); } carve::geom::vector<2> _project_4(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.y, v.z); } carve::geom::vector<2> _project_5(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.z, v.x); } carve::geom::vector<2> _project_6(const carve::geom::vector<3> &v) { return carve::geom::VECTOR(v.x, v.y); } carve::geom::vector<3> _unproject_1(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(CALC_X(plane, p.y, p.x), p.y, p.x); } carve::geom::vector<3> _unproject_2(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(p.x, CALC_Y(plane, p.x, p.y), p.y); } carve::geom::vector<3> _unproject_3(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(p.y, p.x, CALC_Z(plane, p.y, p.x)); } carve::geom::vector<3> _unproject_4(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(CALC_X(plane, p.x, p.y), p.x, p.y); } carve::geom::vector<3> _unproject_5(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(p.y, CALC_Y(plane, p.y, p.x), p.x); } carve::geom::vector<3> _unproject_6(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { return carve::geom::VECTOR(p.x, p.y, CALC_Z(plane, p.x, p.y)); } static carve::geom::vector<2> (*project_tab[2][3])(const carve::geom::vector<3> &) = { { &_project_1, &_project_2, &_project_3 }, { &_project_4, &_project_5, &_project_6 } }; static carve::geom::vector<3> (*unproject_tab[2][3])(const carve::geom::vector<2> &, const carve::geom3d::Plane &) = { { &_unproject_1, &_unproject_2, &_unproject_3 }, { &_unproject_4, &_unproject_5, &_unproject_6 } }; } namespace carve { namespace mesh { template typename Face::project_t Face::getProjector(bool positive_facing, int axis) const { return NULL; } template<> Face<3>::project_t Face<3>::getProjector(bool positive_facing, int axis) const { return project_tab[positive_facing ? 1 : 0][axis]; } template typename Face::unproject_t Face::getUnprojector(bool positive_facing, int axis) const { return NULL; } template<> Face<3>::unproject_t Face<3>::getUnprojector(bool positive_facing, int axis) const { return unproject_tab[positive_facing ? 1 : 0][axis]; } template bool Face::containsPoint(const vector_t &p) const { if (!carve::math::ZERO(carve::geom::distance(plane, p))) return false; // return pointInPolySimple(vertices, projector(), (this->*project)(p)); std::vector > verts; getProjectedVertices(verts); return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT; } template bool Face::containsPointInProjection(const vector_t &p) const { std::vector > verts; getProjectedVertices(verts); return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT; } template bool Face::simpleLineSegmentIntersection( const carve::geom::linesegment &line, vector_t &intersection) const { if (!line.OK()) return false; carve::mesh::MeshSet<3>::vertex_t::vector_t p; carve::IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane, line, p); if (intersects == carve::INTERSECT_NONE || intersects == carve::INTERSECT_BAD) { return false; } std::vector > verts; getProjectedVertices(verts); if (carve::geom2d::pointInPolySimple(verts, project(p))) { intersection = p; return true; } return false; } template IntersectionClass Face::lineSegmentIntersection(const carve::geom::linesegment &line, vector_t &intersection) const { if (!line.OK()) return INTERSECT_NONE; vector_t p; IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane, line, p); if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) { return intersects; } std::vector > verts; getProjectedVertices(verts); carve::geom2d::PolyInclusionInfo pi = carve::geom2d::pointInPoly(verts, project(p)); switch (pi.iclass) { case POINT_VERTEX: intersection = p; return INTERSECT_VERTEX; case POINT_EDGE: intersection = p; return INTERSECT_EDGE; case POINT_IN: intersection = p; return INTERSECT_FACE; case POINT_OUT: return INTERSECT_NONE; default: break; } return INTERSECT_NONE; } template Face *Face::closeLoop(typename Face::edge_t *start) { edge_t *e = start; std::vector loop_edges; do { CARVE_ASSERT(e->rev == NULL); loop_edges.push_back(e); e = e->perimNext(); } while (e != start); const size_t N = loop_edges.size(); for (size_t i = 0; i < N; ++i) { loop_edges[i]->rev = new edge_t(loop_edges[i]->v2(), NULL); } for (size_t i = 0; i < N; ++i) { edge_t *e1 = loop_edges[i]->rev; edge_t *e2 = loop_edges[(i+1)%N]->rev; e1->prev = e2; e2->next = e1; } Face *f = new Face(start->rev); CARVE_ASSERT(f->n_edges == N); return f; } namespace detail { bool FaceStitcher::EdgeOrderData::Cmp::operator()(const EdgeOrderData &a, const EdgeOrderData &b) const { int v = carve::geom3d::compareAngles(edge_dir, base_dir, a.face_dir, b.face_dir); double da = carve::geom3d::antiClockwiseAngle(base_dir, a.face_dir, edge_dir); double db = carve::geom3d::antiClockwiseAngle(base_dir, b.face_dir, edge_dir); int v0 = v; v = 0; if (da < db) v = -1; if (db < da) v = +1; if (v0 != v) { std::cerr << "v0= " << v0 << " v= " << v << " da= " << da << " db= " << db << " " << edge_dir << " " << base_dir << " " << a.face_dir << b.face_dir << std::endl; } if (v < 0) return true; if (v == 0) { if (a.is_reversed && !b.is_reversed) return true; if (a.is_reversed == b.is_reversed) { return a.group_id < b.group_id; } } return false; } void FaceStitcher::matchSimpleEdges() { // join faces that share an edge, where no other faces are incident. for (edge_map_t::iterator i = edges.begin(); i != edges.end(); ++i) { const vpair_t &ev = (*i).first; edge_map_t::iterator j = edges.find(vpair_t(ev.second, ev.first)); if (j == edges.end()) { for (edgelist_t::iterator k = (*i).second.begin(); k != (*i).second.end(); ++k) { is_open[ (*k)->face->id] = true; } } else if ((*i).second.size() != 1 || (*j).second.size() != 1) { std::swap(complex_edges[(*i).first], (*i).second); } else { // simple edge. edge_t *a = (*i).second.front(); edge_t *b = (*j).second.front(); if (a < b) { // every simple edge pair is encountered twice. only merge once. a->rev = b; b->rev = a; face_groups.merge_sets(a->face->id, b->face->id); } } } } size_t FaceStitcher::faceGroupID(const Face<3> *face) { return face_groups.find_set_head(face->id); } size_t FaceStitcher::faceGroupID(const Edge<3> *edge) { return face_groups.find_set_head(edge->face->id); } void FaceStitcher::orderForwardAndReverseEdges(std::vector *> > &efwd, std::vector *> > &erev, std::vector > &result) { const size_t Nfwd = efwd.size(); const size_t Nrev = erev.size(); const size_t N = efwd[0].size(); result.resize(N); for (size_t i = 0; i < N; ++i) { Edge<3> *base = efwd[0][i]; result[i].reserve(Nfwd + Nrev); for (size_t j = 0; j < Nfwd; ++j) { result[i].push_back(EdgeOrderData(efwd[j][i], j, false)); CARVE_ASSERT(efwd[0][i]->v1() == efwd[j][i]->v1()); CARVE_ASSERT(efwd[0][i]->v2() == efwd[j][i]->v2()); } for (size_t j = 0; j < Nrev; ++j) { result[i].push_back(EdgeOrderData(erev[j][i], j, true)); CARVE_ASSERT(erev[0][i]->v1() == erev[j][i]->v1()); CARVE_ASSERT(erev[0][i]->v2() == erev[j][i]->v2()); } std::sort(result[i].begin(), result[i].end(), EdgeOrderData::Cmp(base->v2()->v - base->v1()->v, result[i][0].face_dir)); } } void FaceStitcher::edgeIncidentGroups(const vpair_t &e, const edge_map_t &all_edges, std::pair, std::set > &groups) { groups.first.clear(); groups.second.clear(); edge_map_t::const_iterator i; i = all_edges.find(e); if (i != all_edges.end()) { for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { groups.first.insert(faceGroupID(*j)); } } i = all_edges.find(vpair_t(e.second, e.first)); if (i != all_edges.end()) { for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { groups.second.insert(faceGroupID(*j)); } } } void FaceStitcher::buildEdgeGraph(const edge_map_t &all_edges) { for (edge_map_t::const_iterator i = all_edges.begin(); i != all_edges.end(); ++i) { edge_graph[(*i).first.first].insert((*i).first.second); } } void FaceStitcher::extractPath(std::vector &path) { path.clear(); edge_graph_t::iterator iter = edge_graph.begin(); const vertex_t *init = (*iter).first; const vertex_t *next = *(*iter).second.begin(); const vertex_t *prev = NULL; const vertex_t *vert = init; while ((*iter).second.size() == 2) { prev = *std::find_if((*iter).second.begin(), (*iter).second.end(), std::bind2nd(std::not_equal_to(), next)); next = vert; vert = prev; iter = edge_graph.find(vert); CARVE_ASSERT(iter != edge_graph.end()); if (vert == init) break; } init = vert; std::vector efwd; std::vector erev; edge_map_t::iterator edgeiter; edgeiter = complex_edges.find(vpair_t(vert, next)); std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(efwd)); edgeiter = complex_edges.find(vpair_t(next, vert)); std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(erev)); path.push_back(vert); prev = vert; vert = next; path.push_back(vert); iter = edge_graph.find(vert); CARVE_ASSERT(iter != edge_graph.end()); while (vert != init && (*iter).second.size() == 2) { next = *std::find_if((*iter).second.begin(), (*iter).second.end(), std::bind2nd(std::not_equal_to(), prev)); edgeiter = complex_edges.find(vpair_t(vert, next)); if ((*edgeiter).second.size() != efwd.size()) goto done; for (size_t i = 0; i < efwd.size(); ++i) { Edge<3> *e_next = efwd[i]->perimNext(); if (e_next->v2() != next) goto done; efwd[i] = e_next; } edgeiter = complex_edges.find(vpair_t(next, vert)); if ((*edgeiter).second.size() != erev.size()) goto done; for (size_t i = 0; i < erev.size(); ++i) { Edge<3> *e_prev = erev[i]->perimPrev(); if (e_prev->v1() != next) goto done; erev[i] = e_prev; } prev = vert; vert = next; path.push_back(vert); iter = edge_graph.find(vert); CARVE_ASSERT(iter != edge_graph.end()); } done:; } void FaceStitcher::removePath(const std::vector &path) { for (size_t i = 1; i < path.size() - 1; ++i) { edge_graph.erase(path[i]); } edge_graph[path[0]].erase(path[1]); if (edge_graph[path[0]].size() == 0) { edge_graph.erase(path[0]); } edge_graph[path[path.size()-1]].erase(path[path.size()-2]); if (edge_graph[path[path.size()-1]].size() == 0) { edge_graph.erase(path[path.size()-1]); } } void FaceStitcher::reorder(std::vector &ordering, size_t grp) { if (!ordering[0].is_reversed && ordering[0].group_id == grp) return; for (size_t i = 1; i < ordering.size(); ++i) { if (!ordering[i].is_reversed && ordering[i].group_id == grp) { std::vector temp; temp.reserve(ordering.size()); std::copy(ordering.begin() + i, ordering.end(), std::back_inserter(temp)); std::copy(ordering.begin(), ordering.begin() + i, std::back_inserter(temp)); std::copy(temp.begin(), temp.end(), ordering.begin()); return; } } } struct lt_second { template bool operator()(const pair_t &a, const pair_t &b) const { return a.second < b.second; } }; void FaceStitcher::fuseEdges(std::vector *> &fwd, std::vector *> &rev) { for (size_t i = 0; i < fwd.size(); ++i) { fwd[i]->rev = rev[i]; rev[i]->rev = fwd[i]; face_groups.merge_sets(fwd[i]->face->id, rev[i]->face->id); } } void FaceStitcher::joinGroups(std::vector *> > &efwd, std::vector *> > &erev, size_t fwd_grp, size_t rev_grp) { fuseEdges(efwd[fwd_grp], erev[rev_grp]); } void FaceStitcher::matchOrderedEdges(const std::vector >::iterator begin, const std::vector >::iterator end, std::vector *> > &efwd, std::vector *> > &erev) { typedef std::unordered_map, size_t> pair_counts_t; for (;;) { pair_counts_t pair_counts; for (std::vector >::iterator i = begin; i != end; ++i) { std::vector &e = *i; for (size_t j = 0; j < e.size(); ++j) { if (!e[j].is_reversed && e[(j+1)%e.size()].is_reversed) { pair_counts[std::make_pair(e[j].group_id, e[(j+1)%e.size()].group_id)]++; } } } if (!pair_counts.size()) break; std::vector > > counts; counts.reserve(pair_counts.size()); for (pair_counts_t::iterator iter = pair_counts.begin(); iter != pair_counts.end(); ++iter) { counts.push_back(std::make_pair((*iter).second, (*iter).first)); } std::make_heap(counts.begin(), counts.end()); std::set rem_fwd, rem_rev; while (counts.size()) { std::pair join = counts.front().second; std::pop_heap(counts.begin(), counts.end()); counts.pop_back(); if (rem_fwd.find(join.first) != rem_fwd.end()) continue; if (rem_rev.find(join.second) != rem_rev.end()) continue; size_t g1 = join.first; size_t g2 = join.second; joinGroups(efwd, erev, g1, g2); for (std::vector >::iterator i = begin; i != end; ++i) { (*i).erase(std::remove_if((*i).begin(), (*i).end(), EdgeOrderData::TestGroups(g1, g2)), (*i).end()); } rem_fwd.insert(g1); rem_rev.insert(g2); } } } void FaceStitcher::resolveOpenEdges() { // Remove open regions of mesh. Doing this may make additional // edges simple (for example, removing a fin from the edge of // a cube), and may also expose more open mesh regions. In the // latter case, the process must be repeated to deal with the // newly uncovered regions. std::unordered_set open_groups; for (size_t i = 0; i < is_open.size(); ++i) { if (is_open[i]) open_groups.insert(face_groups.find_set_head(i)); } while (!open_groups.empty()) { std::list edge_0, edge_1; for (edge_map_t::iterator i = complex_edges.begin(); i != complex_edges.end(); ++i) { bool was_modified = false; for(edgelist_t::iterator j = (*i).second.begin(); j != (*i).second.end(); ) { if (open_groups.find(faceGroupID(*j)) != open_groups.end()) { j = (*i).second.erase(j); was_modified = true; } else { ++j; } } if (was_modified) { if ((*i).second.empty()) { edge_0.push_back((*i).first); } else if ((*i).second.size() == 1) { edge_1.push_back((*i).first); } } } for (std::list::iterator i = edge_1.begin(); i != edge_1.end(); ++i) { vpair_t e1 = *i; edge_map_t::iterator e1i = complex_edges.find(e1); if (e1i == complex_edges.end()) continue; vpair_t e2 = vpair_t(e1.second, e1.first); edge_map_t::iterator e2i = complex_edges.find(e2); CARVE_ASSERT(e2i != complex_edges.end()); // each complex edge should have a mate. if ((*e2i).second.size() == 1) { // merge newly simple edges, delete both from complex_edges. edge_t *a = (*e1i).second.front(); edge_t *b = (*e2i).second.front(); a->rev = b; b->rev = a; face_groups.merge_sets(a->face->id, b->face->id); complex_edges.erase(e1i); complex_edges.erase(e2i); } } open_groups.clear(); for (std::list::iterator i = edge_0.begin(); i != edge_0.end(); ++i) { vpair_t e1 = *i; edge_map_t::iterator e1i = complex_edges.find(e1); vpair_t e2 = vpair_t(e1.second, e1.first); edge_map_t::iterator e2i = complex_edges.find(e2); if (e2i == complex_edges.end()) { // This could occur, for example, when two faces share // an edge in the same direction, but are both not // touching anything else. Both get removed by the open // group removal code, leaving an edge map with zero // edges. The edge in the opposite direction does not // exist, because there's no face that adjoins either of // the two open faces. continue; } for (edgelist_t::iterator j = (*e2i).second.begin(); j != (*e2i).second.end(); ++j) { open_groups.insert(faceGroupID(*j)); } complex_edges.erase(e1i); complex_edges.erase(e2i); } } } void FaceStitcher::extractConnectedEdges(std::vector::iterator begin, std::vector::iterator end, std::vector *> > &efwd, std::vector *> > &erev) { const size_t N = std::distance(begin, end) - 1; std::vector::iterator e1, e2; e1 = e2 = begin; ++e2; vpair_t start_f = vpair_t(*e1, *e2); vpair_t start_r = vpair_t(*e2, *e1); const size_t Nfwd = complex_edges[start_f].size(); const size_t Nrev = complex_edges[start_r].size(); size_t j; edgelist_t::iterator ji; efwd.clear(); efwd.resize(Nfwd); erev.clear(); erev.resize(Nrev); for (j = 0, ji = complex_edges[start_f].begin(); ji != complex_edges[start_f].end(); ++j, ++ji) { efwd[j].reserve(N); efwd[j].push_back(*ji); } for (j = 0, ji = complex_edges[start_r].begin(); ji != complex_edges[start_r].end(); ++j, ++ji) { erev[j].reserve(N); erev[j].push_back(*ji); } std::vector *> temp_f, temp_r; temp_f.resize(Nfwd); temp_r.resize(Nrev); for (j = 1; j < N; ++j) { ++e1; ++e2; vpair_t ef = vpair_t(*e1, *e2); vpair_t er = vpair_t(*e2, *e1); if (complex_edges[ef].size() != Nfwd || complex_edges[ef].size() != Nrev) break; for (size_t k = 0; k < Nfwd; ++k) { Edge<3> *e_next = efwd[k].back()->perimNext(); CARVE_ASSERT(e_next == NULL || e_next->rev == NULL); if (e_next == NULL || e_next->v2() != *e2) goto done; CARVE_ASSERT(e_next->v1() == *e1); CARVE_ASSERT(std::find(complex_edges[ef].begin(), complex_edges[ef].end(), e_next) != complex_edges[ef].end()); temp_f[k] = e_next; } for (size_t k = 0; k < Nrev; ++k) { Edge<3> *e_next = erev[k].back()->perimPrev(); if (e_next == NULL || e_next->v1() != *e2) goto done; CARVE_ASSERT(e_next->v2() == *e1); CARVE_ASSERT(std::find(complex_edges[er].begin(), complex_edges[er].end(), e_next) != complex_edges[er].end()); temp_r[k] = e_next; } for (size_t k = 0; k < Nfwd; ++k) { efwd[k].push_back(temp_f[k]); } for (size_t k = 0; k < Nrev; ++k) { erev[k].push_back(temp_r[k]); } } done:; } void FaceStitcher::construct() { matchSimpleEdges(); if (!complex_edges.size()) return; resolveOpenEdges(); if (!complex_edges.size()) return; buildEdgeGraph(complex_edges); std::list > paths; while (edge_graph.size()) { paths.push_back(std::vector()); extractPath(paths.back()); removePath(paths.back()); }; for (std::list >::iterator path = paths.begin(); path != paths.end(); ++path) { for (size_t i = 0; i < (*path).size() - 1;) { std::vector *> > efwd, erev; extractConnectedEdges((*path).begin() + i, (*path).end(), efwd, erev); std::vector > orderings; orderForwardAndReverseEdges(efwd, erev, orderings); matchOrderedEdges(orderings.begin(), orderings.end(), efwd, erev); i += efwd[0].size(); } } } } } // construct a MeshSet from a Polyhedron, maintaining on the // connectivity information in the Polyhedron. mesh::MeshSet<3> *meshFromPolyhedron(const poly::Polyhedron *poly, int manifold_id) { typedef mesh::Vertex<3> vertex_t; typedef mesh::Vertex<3>::vector_t vector_t; typedef mesh::Edge<3> edge_t; typedef mesh::Face<3> face_t; typedef mesh::Mesh<3> mesh_t; typedef mesh::MeshSet<3> meshset_t; std::vector vertex_storage; vertex_storage.reserve(poly->vertices.size()); for (size_t i = 0; i < poly->vertices.size(); ++i) { vertex_storage.push_back(vertex_t(poly->vertices[i].v)); } std::vector > faces; faces.resize(poly->manifold_is_closed.size()); std::unordered_map, std::list > vertex_to_edge; std::vector vert_ptrs; for (size_t i = 0; i < poly->faces.size(); ++i) { const poly::Polyhedron::face_t &src = poly->faces[i]; if (manifold_id != -1 && src.manifold_id != manifold_id) continue; vert_ptrs.clear(); vert_ptrs.reserve(src.nVertices()); for (size_t j = 0; j < src.nVertices(); ++j) { size_t vi = poly->vertexToIndex_fast(src.vertex(j)); vert_ptrs.push_back(&vertex_storage[vi]); } face_t *face = new face_t(vert_ptrs.begin(), vert_ptrs.end()); face->id = src.manifold_id; faces[src.manifold_id].push_back(face); edge_t *edge = face->edge; do { vertex_to_edge[std::make_pair(size_t(edge->v1() - &vertex_storage[0]), size_t(edge->v2() - &vertex_storage[0]))].push_back(edge); edge = edge->next; } while (edge != face->edge); } // copy connectivity from Polyhedron. for (size_t i = 0; i < poly->edges.size(); ++i) { const poly::Polyhedron::edge_t &src = poly->edges[i]; size_t v1i = poly->vertexToIndex_fast(src.v1); size_t v2i = poly->vertexToIndex_fast(src.v2); std::list &efwd = vertex_to_edge[std::make_pair(v1i, v2i)]; std::list &erev = vertex_to_edge[std::make_pair(v2i, v1i)]; const std::vector &facepairs = poly->connectivity.edge_to_face[i]; for (size_t j = 0; j < facepairs.size(); j += 2) { const poly::Polyhedron::face_t *fa, *fb; fa = facepairs[j]; fb = facepairs[j+1]; if (!fa || !fb) continue; CARVE_ASSERT(fa->manifold_id == fb->manifold_id); if (manifold_id != -1 && fa->manifold_id != manifold_id) continue; std::list::iterator efwdi, erevi; for (efwdi = efwd.begin(); efwdi != efwd.end() && (*efwdi)->face->id != (size_t)fa->manifold_id; ++efwdi); for (erevi = erev.begin(); erevi != erev.end() && (*erevi)->face->id != (size_t)fa->manifold_id; ++erevi); CARVE_ASSERT(efwdi != efwd.end() && erevi != erev.end()); (*efwdi)->rev = (*erevi); (*erevi)->rev = (*efwdi); } } std::vector meshes; meshes.reserve(faces.size()); for (size_t i = 0; i < faces.size(); ++i) { if (faces[i].size()) { meshes.push_back(new mesh_t(faces[i])); } } return new meshset_t(vertex_storage, meshes); } static void copyMeshFaces(const mesh::Mesh<3> *mesh, size_t manifold_id, const mesh::Vertex<3> *Vbase, poly::Polyhedron *poly, std::unordered_map, std::list *> > &edges, std::unordered_map *, size_t> &face_map) { std::vector vert_ptr; for (size_t f = 0; f < mesh->faces.size(); ++f) { mesh::Face<3> *src = mesh->faces[f]; vert_ptr.clear(); vert_ptr.reserve(src->nVertices()); mesh::Edge<3> *e = src->edge; do { vert_ptr.push_back(&poly->vertices[e->vert - Vbase]); edges[std::make_pair(e->v1() - Vbase, e->v2() - Vbase)].push_back(e); e = e->next; } while (e != src->edge); face_map[src] = poly->faces.size();; poly->faces.push_back(poly::Polyhedron::face_t(vert_ptr)); poly->faces.back().manifold_id = manifold_id; poly->faces.back().owner = poly; } } // construct a Polyhedron from a MeshSet poly::Polyhedron *polyhedronFromMesh(const mesh::MeshSet<3> *mesh, int manifold_id) { typedef poly::Polyhedron poly_t; typedef poly::Polyhedron::vertex_t vertex_t; typedef poly::Polyhedron::edge_t edge_t; typedef poly::Polyhedron::face_t face_t; poly::Polyhedron *poly = new poly::Polyhedron(); const mesh::Vertex<3> *Vbase = &mesh->vertex_storage[0]; poly->vertices.reserve(mesh->vertex_storage.size()); for (size_t i = 0; i < mesh->vertex_storage.size(); ++i) { poly->vertices.push_back(vertex_t(mesh->vertex_storage[i].v)); poly->vertices.back().owner = poly; } size_t n_faces = 0; if (manifold_id == -1) { poly->manifold_is_closed.resize(mesh->meshes.size()); poly->manifold_is_negative.resize(mesh->meshes.size()); for (size_t m = 0; m < mesh->meshes.size(); ++m) { n_faces += mesh->meshes[m]->faces.size(); poly->manifold_is_closed[m] = mesh->meshes[m]->isClosed(); poly->manifold_is_negative[m] = mesh->meshes[m]->isNegative(); } } else { poly->manifold_is_closed.resize(1); poly->manifold_is_negative.resize(1); n_faces = mesh->meshes[manifold_id]->faces.size(); poly->manifold_is_closed[manifold_id] = mesh->meshes[manifold_id]->isClosed(); poly->manifold_is_negative[manifold_id] = mesh->meshes[manifold_id]->isNegative(); } std::unordered_map, std::list *> > edges; std::unordered_map *, size_t> face_map; poly->faces.reserve(n_faces); if (manifold_id == -1) { for (size_t m = 0; m < mesh->meshes.size(); ++m) { copyMeshFaces(mesh->meshes[m], m, Vbase, poly, edges, face_map); } } else { copyMeshFaces(mesh->meshes[manifold_id], 0, Vbase, poly, edges, face_map); } size_t n_edges = 0; for (std::unordered_map, std::list *> >::iterator i = edges.begin(); i != edges.end(); ++i) { if ((*i).first.first < (*i).first.second || edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { n_edges++; } } poly->edges.reserve(n_edges); for (std::unordered_map, std::list *> >::iterator i = edges.begin(); i != edges.end(); ++i) { if ((*i).first.first < (*i).first.second || edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { poly->edges.push_back(edge_t(&poly->vertices[(*i).first.first], &poly->vertices[(*i).first.second], poly)); } } poly->initVertexConnectivity(); // build edge entries for face. for (size_t f = 0; f < poly->faces.size(); ++f) { face_t &face = poly->faces[f]; size_t N = face.nVertices(); for (size_t v = 0; v < N; ++v) { size_t v1i = poly->vertexToIndex_fast(face.vertex(v)); size_t v2i = poly->vertexToIndex_fast(face.vertex((v+1)%N)); std::vector found_edge; std::set_intersection(poly->connectivity.vertex_to_edge[v1i].begin(), poly->connectivity.vertex_to_edge[v1i].end(), poly->connectivity.vertex_to_edge[v2i].begin(), poly->connectivity.vertex_to_edge[v2i].end(), std::back_inserter(found_edge)); CARVE_ASSERT(found_edge.size() == 1); face.edge(v) = found_edge[0]; } } poly->connectivity.edge_to_face.resize(poly->edges.size()); for (size_t i = 0; i < poly->edges.size(); ++i) { size_t v1i = poly->vertexToIndex_fast(poly->edges[i].v1); size_t v2i = poly->vertexToIndex_fast(poly->edges[i].v2); std::list *> &efwd = edges[std::make_pair(v1i, v2i)]; std::list *> &erev = edges[std::make_pair(v1i, v2i)]; for (std::list *>::iterator j = efwd.begin(); j != efwd.end(); ++j) { mesh::Edge<3> *edge = *j; if (face_map.find(edge->face) != face_map.end()) { poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]); if (edge->rev == NULL) { poly->connectivity.edge_to_face[i].push_back(NULL); } else { poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->rev->face]]); } } } for (std::list *>::iterator j = erev.begin(); j != erev.end(); ++j) { mesh::Edge<3> *edge = *j; if (face_map.find(edge->face) != face_map.end()) { if (edge->rev == NULL) { poly->connectivity.edge_to_face[i].push_back(NULL); poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]); } } } } poly->initSpatialIndex(); // XXX: at this point, manifold_is_negative is not set up. This // info should be computed/stored in Mesh instances. return poly; } } // explicit instantiation for 2D case. // XXX: do not compile because of a missing definition for fitPlane in the 2d case. // template class carve::mesh::Vertex<2>; // template class carve::mesh::Edge<2>; // template class carve::mesh::Face<2>; // template class carve::mesh::Mesh<2>; // template class carve::mesh::MeshSet<2>; // explicit instantiation for 3D case. template class carve::mesh::Vertex<3>; template class carve::mesh::Edge<3>; template class carve::mesh::Face<3>; template class carve::mesh::Mesh<3>; template class carve::mesh::MeshSet<3>; carve::PointClass carve::mesh::classifyPoint( const carve::mesh::MeshSet<3> *meshset, const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *face_rtree, const carve::geom::vector<3> &v, bool even_odd, const carve::mesh::Mesh<3> *mesh, const carve::mesh::Face<3> **hit_face) { if (hit_face) *hit_face = NULL; #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{containsVertex " << v << "}" << std::endl; #endif if (!face_rtree->bbox.containsPoint(v)) { #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{final:OUT(aabb short circuit)}" << std::endl; #endif // XXX: if the top level manifolds are negative, this should be POINT_IN. // for the moment, this only works for a single manifold. if (meshset->meshes.size() == 1 && meshset->meshes[0]->isNegative()) { return POINT_IN; } return POINT_OUT; } std::vector *> near_faces; face_rtree->search(v, std::back_inserter(near_faces)); for (size_t i = 0; i < near_faces.size(); i++) { if (mesh != NULL && mesh != near_faces[i]->mesh) continue; // XXX: Do allow the tested vertex to be ON an open // manifold. This was here originally because of the // possibility of an open manifold contained within a closed // manifold. // if (!near_faces[i]->mesh->isClosed()) continue; if (near_faces[i]->containsPoint(v)) { #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{final:ON(hits face " << near_faces[i] << ")}" << std::endl; #endif if (hit_face) *hit_face = near_faces[i]; return POINT_ON; } } double ray_len = face_rtree->bbox.extent.length() * 2; std::vector *, carve::geom::vector<3> > > manifold_intersections; for (;;) { double a1 = random() / double(RAND_MAX) * M_TWOPI; double a2 = random() / double(RAND_MAX) * M_TWOPI; carve::geom3d::Vector ray_dir = carve::geom::VECTOR(sin(a1) * sin(a2), cos(a1) * sin(a2), cos(a2)); #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{testing ray: " << ray_dir << "}" << std::endl; #endif carve::geom::vector<3> v2 = v + ray_dir * ray_len; bool failed = false; carve::geom::linesegment<3> line(v, v2); carve::geom::vector<3> intersection; near_faces.clear(); manifold_intersections.clear(); face_rtree->search(line, std::back_inserter(near_faces)); for (unsigned i = 0; !failed && i < near_faces.size(); i++) { if (mesh != NULL && mesh != near_faces[i]->mesh) continue; if (!near_faces[i]->mesh->isClosed()) continue; switch (near_faces[i]->lineSegmentIntersection(line, intersection)) { case INTERSECT_FACE: { #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{intersects face: " << near_faces[i] << " dp: " << dot(ray_dir, near_faces[i]->plane.N) << "}" << std::endl; #endif if (!even_odd && fabs(dot(ray_dir, near_faces[i]->plane.N)) < EPSILON) { #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{failing(small dot product)}" << std::endl; #endif failed = true; break; } manifold_intersections.push_back(std::make_pair(near_faces[i], intersection)); break; } case INTERSECT_NONE: { break; } default: { #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{failing(degenerate intersection)}" << std::endl; #endif failed = true; break; } } } if (!failed) { if (even_odd) { return (manifold_intersections.size() & 1) ? POINT_IN : POINT_OUT; } #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{intersections ok [count:" << manifold_intersections.size() << "], sorting}" << std::endl; #endif carve::geom3d::sortInDirectionOfRay(ray_dir, manifold_intersections.begin(), manifold_intersections.end(), carve::geom3d::vec_adapt_pair_second()); std::map *, int> crossings; for (size_t i = 0; i < manifold_intersections.size(); ++i) { const carve::mesh::Face<3> *f = manifold_intersections[i].first; if (dot(ray_dir, f->plane.N) < 0.0) { crossings[f->mesh]++; } else { crossings[f->mesh]--; } } #if defined(DEBUG_CONTAINS_VERTEX) for (std::map *, int>::const_iterator i = crossings.begin(); i != crossings.end(); ++i) { std::cerr << "{mesh " << (*i).first << " crossing count: " << (*i).second << "}" << std::endl; } #endif for (size_t i = 0; i < manifold_intersections.size(); ++i) { const carve::mesh::Face<3> *f = manifold_intersections[i].first; #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{intersection at " << manifold_intersections[i].second << " mesh: " << f->mesh << " count: " << crossings[f->mesh] << "}" << std::endl; #endif if (crossings[f->mesh] < 0) { // inside this manifold. #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{final:IN}" << std::endl; #endif return POINT_IN; } else if (crossings[f->mesh] > 0) { // outside this manifold, but it's an infinite manifold. (for instance, an inverted cube) #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{final:OUT}" << std::endl; #endif return POINT_OUT; } } #if defined(DEBUG_CONTAINS_VERTEX) std::cerr << "{final:OUT(default)}" << std::endl; #endif return POINT_OUT; } } }