// Begin License: // Copyright (C) 2006-2008 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 #include #include "intersect_common.hpp" #include "intersect_classify_common.hpp" #if 0 class edge_graph_t : public std::hash_map >, hash_vector_ptr> { public: typedef mapped_type data_type; }; #if defined(CARVE_DEBUG) static void drawEdgeGraph(edge_graph_t &eg) { for (edge_graph_t::const_iterator j = eg.begin(), je = eg.end(); j != je; ++j) { const Vector *v1 = (*j).first; for (std::set::const_iterator k = (*j).second.second.begin(), ke = (*j).second.second.end(); k != ke; ++k) { const Vector *v2 = (*k); HOOK(drawEdge(v1, v2, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 3.0f);); } } } #endif static bool find_cycles(edge_graph_t &eg, std::list &cycles) { for (edge_graph_t::const_iterator i = eg.begin(), e = eg.end(); i != e; ++i) { if ((*i).second.second.size() != 1) { #if defined(CARVE_DEBUG) // HOOK(drawEdgeGraph(eg);); #endif std::cerr << "WARNING:edge loop graph does not consist of simple cycles" << std::endl; return false; } } while (eg.size()) { #if defined(CARVE_DEBUG) std::cerr << "eg.size(): " << eg.size() << std::endl; #endif std::list path; edge_graph_t::iterator i; int c = 0; const Vector *v; i = eg.begin(); path.push_back(v = (*i).first); (*i).second.first = c++; while (1) { const Vector *v2 = *((*i).second.second.begin()); path.push_back(v2); i = eg.find(v2); if (i == eg.end()) { break; } if ((*i).second.first < c) { break; } (*i).second.first = c++; } #if defined(CARVE_DEBUG) std::cerr << (i != eg.end() ? "found" : "didn't find") << " cycle " << std::endl << "path.size(): " << path.size() << std::endl; #endif if (i != eg.end()) { c = (*i).second.first; cycles.push_back(V2Set()); } const Vector *src = NULL, *tgt = NULL; for (std::list::const_iterator j = path.begin(), je = path.end(); j != je; ++j) { tgt = (*j); if (src) { edge_graph_t::iterator k = eg.find(src); if ((*k).second.first >= c) { cycles.back().insert(std::make_pair(src, tgt)); } (*k).second.first = INT_MAX; (*k).second.second.erase(tgt); if ((*k).second.second.size() == 0) { eg.erase(k); } } src = tgt; } } return true; } static bool eliminate_simple(const LoopEdges &edge_map, V2Set &edges, std::set &loops, V2Set &extra_edges) { const Vector *v1, *v2; loops.clear(); #if defined(CARVE_DEBUG) std::cerr << edges.size() << " edges to eliminate" << std::endl; #endif while (edges.size()) { LoopEdges::const_iterator i = edge_map.find(*(edges.begin())); if (i == edge_map.end()) { std::pair t = *(edges.begin()); return false; } if ((*i).second.size() != 1) { throw intersect_exception( "eliminate_simple failed: topology too complex"); } FaceLoop *to_remove = *((*i).second.begin()); #if defined(CARVE_DEBUG) std::cerr << " removing face loop " << to_remove << std::endl; #endif loops.insert(to_remove); v1 = to_remove->vertices[to_remove->vertices.size() - 1]; for (unsigned j = 0; j < to_remove->vertices.size(); ++j) { v2 = to_remove->vertices[j]; V2Set::iterator v = edges.find(std::make_pair(v1, v2)); if (v != edges.end()) { #if defined(CARVE_DEBUG) std::cerr << " removing edge " << v1 << "-" << v2 << std::endl; #endif edges.erase(v); } else { #if defined(CARVE_DEBUG) std::cerr << " adding edge " << v2 << "-" << v1 << std::endl; #endif edges.insert(std::make_pair(v2, v1)); extra_edges.insert(std::make_pair(std::min(v1, v2), std::max(v1, v2))); } v1 = v2; } #if defined(CARVE_DEBUG) std::cerr << " " << edges.size() << " edges remain." << std::endl; #endif } #if defined(CARVE_DEBUG) std::cerr << "eliminate succeeded" << std::endl; #endif return true; } static void classifyEdges(const V2Set &edges, const Polyhedron *poly, int &n_in, int &n_on, int &n_out) { n_in = n_on = n_out = 0; for (V2Set::const_iterator i = edges.begin(), e = edges.end(); i != e; ++i) { Vector c((*(*i).first + *(*i).second) / 2.0); switch (poly->containsVertex(c, NULL)) { case POINT_IN: n_in++; break; case POINT_OUT: n_out++; break; case POINT_ON: n_on++; break; default: break; } } } static FaceClass faceClassificationBasedOnEdges(const V2Set &edges, const Polyhedron *poly, FaceClass on_hint) { int n_in, n_on, n_out; classifyEdges(edges, poly, n_in, n_on, n_out); std::cerr << ">>> classification: n_in: " << n_in << " n_on: " << n_on << " n_out: " << n_out << std::endl; CARVE_ASSERT(n_in == (int)edges.size() || n_out == (int)edges.size() || n_on == (int)edges.size()); if (n_in) return FACE_IN; if (n_out) return FACE_OUT; return on_hint; } static void classifySimpleOnFaces(FaceLoopList &a_face_loops, FaceLoopList &b_face_loops, const Polyhedron *poly_a, const Polyhedron *poly_b, CSG::Collector &collector) { LoopEdges edge_map; for (FaceLoop *i = b_face_loops.head; i; i = i->next) { edge_map.addFaceLoop(i); } for (FaceLoop *i = a_face_loops.head; i;) { LoopEdges::const_iterator t; t = edge_map.find(std::make_pair(i->vertices[0], i->vertices[1])); if (t != edge_map.end()) { for (std::list::const_iterator u = (*t).second.begin(), ue = (*t).second.end(); u != ue; ++u) { FaceLoop *j(*u); int k = is_same(i->vertices, j->vertices); CARVE_ASSERT(k != -1); if (k == +1) { collector.collect(i->orig_face, i->vertices, i->orig_face->normal, true, FACE_ON_ORIENT_OUT); collector.collect(j->orig_face, j->vertices, j->orig_face->normal, false, FACE_ON_ORIENT_OUT); edge_map.removeFaceLoop(j); i = a_face_loops.remove(i); b_face_loops.remove(j); goto next_loop; } } } t = edge_map.find(std::make_pair(i->vertices[1], i->vertices[0])); if (t != edge_map.end()) { for (std::list::const_iterator u = (*t).second.begin(), ue = (*t).second.end(); u != ue; ++u) { FaceLoop *j(*u); int k = is_same(i->vertices, j->vertices); CARVE_ASSERT(k != +1); if (k == -1) { collector.collect(i->orig_face, i->vertices, i->orig_face->normal, true, FACE_ON_ORIENT_IN); collector.collect(j->orig_face, j->vertices, j->orig_face->normal, false, FACE_ON_ORIENT_IN); edge_map.removeFaceLoop(j); i = a_face_loops.remove(i); b_face_loops.remove(j); goto next_loop; } } } i = i->next; next_loop:; } } static void classifyOnFaces_2(FaceLoopList &a_face_loops, FaceLoopList &b_face_loops, const VertexClassification &vclass, const Polyhedron *poly_a, const Polyhedron *poly_b, CSG::Collector &collector) { LoopEdges edge_map_a; LoopEdges edge_map_b; std::list cycles; for (FaceLoop *i = a_face_loops.head; i; i = i->next) { edge_map_a.addFaceLoop(i); } for (FaceLoop *i = b_face_loops.head; i; i = i->next) { edge_map_b.addFaceLoop(i); } edge_graph_t edge_graph; edge_graph.clear(); for (LoopEdges::const_iterator i = edge_map_a.begin(), e = edge_map_a.end(); i != e; ++i) { const Vector *v1((*i).first.first), *v2((*i).first.second); LoopEdges::const_iterator j = edge_map_b.find(std::make_pair(v2, v1)); if (j != edge_map_b.end()) { edge_graph_t::data_type &data(edge_graph[v1]); data.first = INT_MAX; data.second.insert(v2); #if defined(CARVE_DEBUG) std::cerr << "--- " << v1 << "-" << v2 << std::endl; #endif } } cycles.clear(); if (!find_cycles(edge_graph, cycles)) return; #if defined(CARVE_DEBUG) std::cerr << cycles.size() << " reverse cycles" << std::endl; #endif for (std::list::iterator i = cycles.begin(), e = cycles.end(); i != e; ++i) { std::set a_loopset, b_loopset; V2Set b_cycle; V2Set a_extra_edges, b_extra_edges; for (V2Set::const_iterator j = (*i).begin(), je = (*i).end(); j != je; ++j) { b_cycle.insert(std::make_pair((*j).second, (*j).first)); } if (eliminate_simple(edge_map_a, (*i), a_loopset, a_extra_edges) && eliminate_simple(edge_map_b, b_cycle, b_loopset, b_extra_edges)) { #if defined(CARVE_DEBUG) std::cerr << "paired " << a_loopset.size() << " poly_a faces with " << b_loopset.size() << " poly_b faces in reverse loop (" << a_extra_edges.size() << " extra edges in set a, " << b_extra_edges.size() << " extra edges in set b)" << std::endl; #endif FaceClass a_class, b_class; a_class = faceClassificationBasedOnEdges(a_extra_edges, poly_b, FACE_ON_ORIENT_IN); b_class = faceClassificationBasedOnEdges(b_extra_edges, poly_a, FACE_ON_ORIENT_IN); #if defined(CARVE_DEBUG) std::cerr << "a_class = " << ENUM(a_class) << std::endl << "b_class = " << ENUM(b_class) << std::endl; #endif if (a_class == FACE_ON_ORIENT_IN && b_class != FACE_ON_ORIENT_IN) a_class = b_class; else if (a_class != FACE_ON_ORIENT_IN && b_class == FACE_ON_ORIENT_IN) b_class = a_class; #if defined(CARVE_DEBUG) std::cerr << "modified a_class = " << ENUM(a_class) << std::endl << " b_class = " << ENUM(b_class) << std::endl; #endif for (std::set::const_iterator j = a_loopset.begin(), je = a_loopset.end(); j != je; ++j) { collector.collect((*j)->orig_face, (*j)->vertices, (*j)->orig_face->normal, true, a_class); edge_map_a.removeFaceLoop((*j)); a_face_loops.remove((*j)); } for (std::set::const_iterator j = b_loopset.begin(), je = b_loopset.end(); j != je; ++j) { collector.collect((*j)->orig_face, (*j)->vertices, (*j)->orig_face->normal, false, a_class); edge_map_b.removeFaceLoop((*j)); b_face_loops.remove((*j)); } } else { #if defined(CARVE_DEBUG) std::cerr << "pairing failed" << std::endl; #endif } } edge_graph.clear(); for (LoopEdges::const_iterator i = edge_map_a.begin(), e = edge_map_a.end(); i != e; ++i) { const Vector *v1((*i).first.first), *v2((*i).first.second); LoopEdges::const_iterator j = edge_map_b.find(std::make_pair(v1, v2)); if (j != edge_map_b.end()) { edge_graph_t::data_type &data(edge_graph[v1]); data.first = INT_MAX; data.second.insert(v2); #if defined(CARVE_DEBUG) std::cerr << "+++ " << v1 << "-" << v2 << std::endl; #endif } } cycles.clear(); if (!find_cycles(edge_graph, cycles)) return; #if defined(CARVE_DEBUG) std::cerr << cycles.size() << " forward cycles" << std::endl; #endif for (std::list::iterator i = cycles.begin(), e = cycles.end(); i != e; ++i) { std::set a_loopset, b_loopset; V2Set b_cycle((*i)); V2Set a_extra_edges, b_extra_edges; if (eliminate_simple(edge_map_a, (*i), a_loopset, a_extra_edges) && eliminate_simple(edge_map_b, b_cycle, b_loopset, b_extra_edges)) { #if defined(CARVE_DEBUG) std::cerr << "paired " << a_loopset.size() << " poly_a faces with " << b_loopset.size() << " poly_b faces in forward loop" << a_extra_edges.size() << " extra edges in set a, " << b_extra_edges.size() << " extra edges in set b)" << std::endl; #endif FaceClass a_class, b_class; a_class = faceClassificationBasedOnEdges(a_extra_edges, poly_b, FACE_ON_ORIENT_OUT); b_class = faceClassificationBasedOnEdges(b_extra_edges, poly_a, FACE_ON_ORIENT_OUT); #if defined(CARVE_DEBUG) std::cerr << "a_class = " << ENUM(a_class) << std::endl << "b_class = " << ENUM(b_class) << std::endl; #endif if (a_class == FACE_ON_ORIENT_OUT && b_class != FACE_ON_ORIENT_OUT) a_class = (FaceClass)-b_class; else if (a_class != FACE_ON_ORIENT_OUT && b_class == FACE_ON_ORIENT_OUT) b_class = (FaceClass)-a_class; #if defined(CARVE_DEBUG) std::cerr << "modified a_class = " << ENUM(a_class) << std::endl << " b_class = " << ENUM(b_class) << std::endl; #endif for (std::set::const_iterator j = a_loopset.begin(), je = a_loopset.end(); j != je; ++j) { collector.collect((*j)->orig_face, (*j)->vertices, (*j)->orig_face->normal, true, a_class); edge_map_a.removeFaceLoop((*j)); a_face_loops.remove((*j)); } for (std::set::const_iterator j = b_loopset.begin(), je = b_loopset.end(); j != je; ++j) { collector.collect((*j)->orig_face, (*j)->vertices, (*j)->orig_face->normal, false, b_class); edge_map_b.removeFaceLoop((*j)); b_face_loops.remove((*j)); } } else { #if defined(CARVE_DEBUG) std::cerr << "pairing failed" << std::endl; #endif } } } void classifyEasyFaces(FaceLoopList &face_loops, VertexClassification &vclass, const Polyhedron *other_poly, int other_poly_num, Intersections &intersections, CSG::Collector &collector) { for (FaceLoop *i = face_loops.head; i;) { unsigned j; const std::vector &f_loop(i->vertices); PointClass pc = POINT_ON; unsigned test = 0; for (j = 0; j < f_loop.size(); j++) { PointClass temp = vclass[f_loop[j]].cls[other_poly_num]; if (temp == POINT_ON) continue; pc = temp; test = j; if (pc != POINT_UNK) break; } if (pc != POINT_ON) { // EASY CASE // IF a face uses a POINT_OUT vertex, it is OUT, and no face // vertices may be POINT_IN // IF a face uses a POINT_IN vertex, it is IN, and no face // vertices may be POINT_OUT if (pc == POINT_UNK) { pc = other_poly->containsVertex(*f_loop[test]); #if defined(CARVE_DEBUG) std::cerr << "testing " << f_loop[test] << " pc = " << pc << std::endl; #endif #if defined(CARVE_DEBUG) if (pc != POINT_IN && pc != POINT_OUT) { HOOK(drawFaceLoop(f_loop, i->orig_face->normal, 1.0, 0.0, 0.0, 0.5, true);); HOOK(drawFaceLoopWireframe(f_loop, i->orig_face->normal, 1.0, 1.0, 0.0, 1.0);); HOOK(drawPoint(f_loop[test], 1, 1, 1, 1, 8.0);); } #endif CARVE_ASSERT(pc == POINT_OUT || pc == POINT_IN); } for (j = 0; j < f_loop.size(); j++) { PC2 &pc2(vclass[f_loop[j]]); if (pc2.cls[other_poly_num] == POINT_UNK) { pc2.cls[other_poly_num] = pc; } else { CARVE_ASSERT(pc2.cls[other_poly_num] == POINT_ON || pc2.cls[other_poly_num] == pc); } } // this face loop is trivially either IN or OUT. { std::vector ifaces; intersections.commonFaces(f_loop, ifaces); if (ifaces.size()) { std::cerr << "ERROR: JUST CLASSIFIED A FACE AS IN OR OUT WHEN " "ifaces.size() == " << ifaces.size() << " [ "; for (std::vector::const_iterator x = ifaces.begin(), xe = ifaces.end(); x != xe; ++x) { std::cerr << (*x) << " "; } std::cerr << "]" << std::endl; } } if (pc == POINT_IN) { collector.collect(i->orig_face, f_loop, i->orig_face->normal, other_poly_num == 1, FACE_IN); } else { collector.collect(i->orig_face, f_loop, i->orig_face->normal, other_poly_num == 1, FACE_OUT); } i = face_loops.remove(i); } else { i = i ->next; } } } #endif void carve::csg::CSG::classifyFaceGroupsSimple(const V2Set &shared_edges, VertexClassification &vclass, const carve::poly::Polyhedron *poly_a, FLGroupList &a_loops_grouped, const LoopEdges &a_edge_map, const carve::poly::Polyhedron *poly_b, FLGroupList &b_loops_grouped, const LoopEdges &b_edge_map, Collector &collector) { #if 0 // Old proto void carve::csg::CSG::classifyFaces(carve::csg::FaceLoopList &a_face_loops, size_t a_edge_count, carve::csg::FaceLoopList &b_face_loops, size_t b_edge_count, VertexClassification &vclass, const carve::poly::Polyhedron *poly_a, const carve::poly::Polyhedron *poly_b, Collector &collector); std::cerr << "classify: " << a_face_loops.size() << " cases in polyhedron a" << std::endl; std::cerr << "classify: " << b_face_loops.size() << " cases in polyhedron b" << std::endl; classifyEasyFaces(a_face_loops, vclass, poly_b, 1, intersections, collector); classifyEasyFaces(b_face_loops, vclass, poly_a, 0, intersections, collector); #if 0 && defined(CARVE_DEBUG) HOOK(drawFaceLoopList(a_face_loops, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f);); HOOK(drawFaceLoopList(b_face_loops, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f);); #endif std::cerr << "classify: " << a_face_loops.size() << " hard cases in polyhedron a" << std::endl; std::cerr << "classify: " << b_face_loops.size() << " hard cases in polyhedron b" << std::endl; classifySimpleOnFaces(a_face_loops, b_face_loops, poly_a, poly_b, collector); std::cerr << "classify: " << a_face_loops.size() << " after elimination of easy pairs: polyhedron a" << std::endl; std::cerr << "classify: " << b_face_loops.size() << " after elimination of easy pairs: polyhedron b" << std::endl; classifyOnFaces_2(a_face_loops, b_face_loops, vclass, poly_a, poly_b, collector); std::cerr << "classify: " << a_face_loops.size() << " after elimination of hard pairs: polyhedron a" << std::endl; std::cerr << "classify: " << b_face_loops.size() << " after elimination of hard pairs: polyhedron b" << std::endl; FaceLoop *fla, *flb; for (fla = a_face_loops.head; fla; fla = fla->next) { const Face *f(fla->orig_face); std::vector &loop(fla->vertices); std::vector proj; proj.reserve(loop.size()); for (unsigned i = 0; i < loop.size(); ++i) { proj.push_back((f->*(f->project))(*loop[i])); } P2 pv; if (!pickContainedPoint(proj, pv)) { CARVE_FAIL("pickContainedPoint failed"); } Vector v = (f->*(f->unproject))(pv); const Face *hit_face; PointClass pc = poly_b->containsVertex(v, &hit_face); FaceClass fc = FACE_ON; if (pc != POINT_IN && pc != POINT_OUT) { std::cerr << "WARNING: last resort classifier found an ON face" << std::endl; } switch (pc) { case POINT_IN: fc = FACE_IN; break; case POINT_OUT: fc = FACE_OUT; break; case POINT_ON: { double d = dot(hit_face->normal, f->normal); fc = d < 0.0 ? FACE_ON_ORIENT_IN : FACE_ON_ORIENT_OUT; break; } default: CARVE_FAIL("should not happen"); } // CARVE_ASSERT(pc == POINT_IN || pc == POINT_OUT); #if defined(CARVE_DEBUG) { float r,g,b,a; switch(fc) { case FACE_IN: r=1; g=0; b=0; a=1; break; case FACE_OUT: r=0; g=0; b=1; a=1; break; case FACE_ON_ORIENT_OUT: r=1; g=1; b=0; a=1; break; case FACE_ON_ORIENT_IN: r=0; g=1; b=1; a=1; break; } if (fc != FACE_IN && fc != FACE_OUT) { HOOK(drawFaceLoopWireframe(loop, f->normal, r, g, b, a);); HOOK(drawPoint(&v, 1, 1, 1, 1, 30.0);); } } #endif collector.collect(f, loop, f->normal, true, fc); } for (flb = b_face_loops.head; flb; flb = flb->next) { const Face *f(flb->orig_face); std::vector &loop(flb->vertices); std::vector proj; proj.reserve(loop.size()); for (unsigned i = 0; i < loop.size(); ++i) { proj.push_back((f->*(f->project))(*loop[i])); } P2 pv; if (!pickContainedPoint(proj, pv)) { CARVE_FAIL("pickContainedPoint failed"); } Vector v = (f->*(f->unproject))(pv); const Face *hit_face; PointClass pc = poly_a->containsVertex(v, &hit_face); FaceClass fc = FACE_ON; if (pc != POINT_IN && pc != POINT_OUT) { std::cerr << "WARNING: last resort classifier found an ON face" << std::endl; } switch (pc) { case POINT_IN: fc = FACE_IN; break; case POINT_OUT: fc = FACE_OUT; break; case POINT_ON: { double d = dot(hit_face->normal, f->normal); fc = d < 0.0 ? FACE_ON_ORIENT_IN : FACE_ON_ORIENT_OUT; break; } default: CARVE_FAIL("should not happen"); } // CARVE_ASSERT(pc == POINT_IN || pc == POINT_OUT); #if defined(CARVE_DEBUG) { float r,g,b,a; switch(fc) { case FACE_IN: r=1; g=0; b=0; a=1; break; case FACE_OUT: r=0; g=0; b=1; a=1; break; case FACE_ON_ORIENT_OUT: r=1; g=1; b=0; a=1; break; case FACE_ON_ORIENT_IN: r=0; g=1; b=1; a=1; break; } // HOOK(drawFaceLoop(loop, f->normal, r, g, b, a);); if (fc != FACE_IN && fc != FACE_OUT) { HOOK(drawFaceLoopWireframe(loop, f->normal, r, g, b, a);); HOOK(drawPoint(&v, 1, 1, 1, 1, 30.0);); } } #endif collector.collect(f, loop, f->normal, false, fc); } #endif }