// 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: #include #include #include #include namespace carve { namespace csg { namespace detail { template class CarveTriangulator : public csg::CSG::Hook { public: CarveTriangulator() { } virtual ~CarveTriangulator() { } virtual void processOutputFace(std::vector &faces, const poly::Polyhedron::face_t *orig, bool flipped) { std::vector out_faces; size_t n_tris = 0; for (size_t f = 0; f < faces.size(); ++f) { CARVE_ASSERT(faces[f]->nVertices() >= 3); n_tris += faces[f]->nVertices() - 2; } out_faces.reserve(n_tris); for (size_t f = 0; f < faces.size(); ++f) { poly::Polyhedron::face_t *face = faces[f]; if (face->nVertices() == 3) { out_faces.push_back(face); continue; } std::vector result; std::vector vloop; face->getVertexLoop(vloop); triangulate::triangulate(face->projector(), vloop, result); if (with_improvement) { triangulate::improve(face->projector(), vloop, result); } std::vector fv; fv.resize(3); for (size_t i = 0; i < result.size(); ++i) { fv[0] = vloop[result[i].a]; fv[1] = vloop[result[i].b]; fv[2] = vloop[result[i].c]; out_faces.push_back(face->create(fv, false)); } delete face; } std::swap(faces, out_faces); } }; } typedef detail::CarveTriangulator CarveTriangulator; typedef detail::CarveTriangulator CarveTriangulatorWithImprovement; class CarveTriangulationImprover : public csg::CSG::Hook { public: CarveTriangulationImprover() { } virtual ~CarveTriangulationImprover() { } virtual void processOutputFace(std::vector &faces, const poly::Polyhedron::face_t *orig, bool flipped) { if (faces.size() == 1) return; // doing improvement as a separate hook is much messier than // just incorporating it into the triangulation hook. typedef std::map vert_map_t; std::vector out_faces; vert_map_t vert_map; out_faces.reserve(faces.size()); poly::p2_adapt_project<3> projector(faces[0]->project); std::vector result; for (size_t f = 0; f < faces.size(); ++f) { poly::Polyhedron::face_t *face = faces[f]; if (face->nVertices() != 3) { out_faces.push_back(face); } else { triangulate::tri_idx tri; for (size_t i = 0; i < 3; ++i) { size_t v = 0; vert_map_t::iterator j = vert_map.find(face->vertex(i)); if (j == vert_map.end()) { v = vert_map.size(); vert_map[face->vertex(i)] = v; } else { v = (*j).second; } tri.v[i] = v; } result.push_back(tri); delete face; } } std::vector verts; verts.resize(vert_map.size()); for (vert_map_t::iterator i = vert_map.begin(); i != vert_map.end(); ++i) { verts[(*i).second] = (*i).first; } triangulate::improve(projector, verts, result); std::vector fv; fv.resize(3); for (size_t i = 0; i < result.size(); ++i) { fv[0] = verts[result[i].a]; fv[1] = verts[result[i].b]; fv[2] = verts[result[i].c]; out_faces.push_back(orig->create(fv, false)); } std::swap(faces, out_faces); } }; class CarveTriangulationQuadMerger : public csg::CSG::Hook { // this code is incomplete. typedef std::map edge_map_t; public: CarveTriangulationQuadMerger() { } virtual ~CarveTriangulationQuadMerger() { } double scoreQuad(edge_map_t::iterator i, edge_map_t &edge_map) { if (!(*i).second.first || !(*i).second.second) return -1; } poly::Polyhedron::face_t *mergeQuad(edge_map_t::iterator i, edge_map_t &edge_map) { return NULL; } void recordEdge(const poly::Polyhedron::vertex_t *v1, const poly::Polyhedron::vertex_t *v2, const poly::Polyhedron::face_t *f, edge_map_t &edge_map) { if (v1 < v2) { edge_map[V2(v1, v2)].first = f; } else { edge_map[V2(v2, v1)].second = f; } } virtual void processOutputFace(std::vector &faces, const poly::Polyhedron::face_t *orig, bool flipped) { if (faces.size() == 1) return; std::vector out_faces; edge_map_t edge_map; out_faces.reserve(faces.size()); poly::p2_adapt_project<3> projector(faces[0]->project); for (size_t f = 0; f < faces.size(); ++f) { poly::Polyhedron::face_t *face = faces[f]; if (face->nVertices() != 3) { out_faces.push_back(face); } else { recordEdge(face->vertex(0), face->vertex(1), face, edge_map); recordEdge(face->vertex(1), face->vertex(2), face, edge_map); recordEdge(face->vertex(2), face->vertex(0), face, edge_map); } } for (edge_map_t::iterator i = edge_map.begin(); i != edge_map.end();) { if ((*i).second.first && (*i).second.second) { ++i; } else { edge_map.erase(i++); } } while (edge_map.size()) { edge_map_t::iterator i = edge_map.begin(); edge_map_t::iterator best = i; double best_score = scoreQuad(i, edge_map); for (++i; i != edge_map.end(); ++i) { double score = scoreQuad(i, edge_map); if (score > best_score) best = i; } if (best_score < 0) break; out_faces.push_back(mergeQuad(best, edge_map)); } if (edge_map.size()) { tagable::tag_begin(); for (edge_map_t::iterator i = edge_map.begin(); i != edge_map.end(); ++i) { poly::Polyhedron::face_t *a = const_cast((*i).second.first); poly::Polyhedron::face_t *b = const_cast((*i).second.first); if (a && a->tag_once()) out_faces.push_back(a); if (b && b->tag_once()) out_faces.push_back(b); } } std::swap(faces, out_faces); } }; class CarveHoleResolver : public csg::CSG::Hook { public: CarveHoleResolver() { } virtual ~CarveHoleResolver() { } bool findRepeatedEdges(const std::vector &vertices, std::list > &edge_pos) { std::map edges; for (size_t i = 0; i < vertices.size() - 1; ++i) { edges[std::make_pair(vertices[i], vertices[i+1])] = i; } edges[std::make_pair(vertices[vertices.size()-1], vertices[0])] = vertices.size() - 1; for (std::map::iterator i = edges.begin(); i != edges.end(); ++i) { V2 rev = V2((*i).first.second, (*i).first.first); std::map::iterator j = edges.find(rev); if (j != edges.end()) { edge_pos.push_back(std::make_pair((*i).second, (*j).second)); } } return edge_pos.size() > 0; } void flood(size_t t1, size_t t2, size_t old_grp, size_t new_grp_1, size_t new_grp_2, std::vector &grp, const std::vector &tris, const std::map, size_t> &tri_edge) { grp[t1] = new_grp_1; grp[t2] = new_grp_2; std::deque to_visit; to_visit.push_back(t1); to_visit.push_back(t2); std::vector > rev; rev.resize(3); while (to_visit.size()) { size_t curr = to_visit.front(); to_visit.pop_front(); triangulate::tri_idx ct = tris[curr]; rev[0] = std::make_pair(ct.b, ct.a); rev[1] = std::make_pair(ct.c, ct.b); rev[2] = std::make_pair(ct.a, ct.c); for (size_t i = 0; i < 3; ++i) { std::map, size_t>::const_iterator adj = tri_edge.find(rev[i]); if (adj == tri_edge.end()) continue; size_t next = (*adj).second; if (grp[next] != old_grp) continue; grp[next] = grp[curr]; to_visit.push_back(next); } } } void findPerimeter(const std::vector &tris, const std::vector &verts, std::vector &out) { std::map, size_t> edges; for (size_t i = 0; i < tris.size(); ++i) { edges[std::make_pair(tris[i].a, tris[i].b)] = i; edges[std::make_pair(tris[i].b, tris[i].c)] = i; edges[std::make_pair(tris[i].c, tris[i].a)] = i; } std::map unpaired; for (std::map, size_t>::iterator i = edges.begin(); i != edges.end(); ++i) { if (edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { CARVE_ASSERT(unpaired.find((*i).first.first) == unpaired.end()); unpaired[(*i).first.first] = (*i).first.second; } } out.clear(); out.reserve(unpaired.size()); size_t start = (*unpaired.begin()).first; size_t vert = start; do { out.push_back(verts[vert]); CARVE_ASSERT(unpaired.find(vert) != unpaired.end()); vert = unpaired[vert]; } while (vert != start); } virtual void processOutputFace(std::vector &faces, const poly::Polyhedron::face_t *orig, bool flipped) { std::vector out_faces; for (size_t f = 0; f < faces.size(); ++f) { poly::Polyhedron::face_t *face = faces[f]; if (face->nVertices() == 3) { out_faces.push_back(face); continue; } std::vector vloop; face->getVertexLoop(vloop); std::list > rep_edges; if (!findRepeatedEdges(vloop, rep_edges)) { out_faces.push_back(face); continue; } std::vector result; triangulate::triangulate(face->projector(), vloop, result); std::map, size_t> tri_edge; for (size_t i = 0; i < result.size(); ++i) { tri_edge[std::make_pair(result[i].a, result[i].b)] = i; tri_edge[std::make_pair(result[i].b, result[i].c)] = i; tri_edge[std::make_pair(result[i].c, result[i].a)] = i; } std::vector grp; grp.resize(result.size(), 0); size_t grp_max = 0; while (rep_edges.size()) { std::pair e1, e2; e1.first = rep_edges.front().first; e1.second = (e1.first + 1) % vloop.size(); e2.first = rep_edges.front().second; e2.second = (e2.first + 1) % vloop.size(); rep_edges.pop_front(); CARVE_ASSERT(tri_edge.find(e1) != tri_edge.end()); size_t t1 = tri_edge[e1]; CARVE_ASSERT(tri_edge.find(e2) != tri_edge.end()); size_t t2 = tri_edge[e2]; if (grp[t1] != grp[t2]) { continue; } size_t t1g = ++grp_max; size_t t2g = ++grp_max; flood(t1, t2, grp[t1], t1g, t2g, grp, result, tri_edge); } std::set groups; std::copy(grp.begin(), grp.end(), std::inserter(groups, groups.begin())); // now construct perimeters for each group. std::vector grp_tris; grp_tris.reserve(result.size()); for (std::set::iterator i = groups.begin(); i != groups.end(); ++i) { size_t grp_id = *i; grp_tris.clear(); for (size_t j = 0; j < grp.size(); ++j) { if (grp[j] == grp_id) { grp_tris.push_back(result[j]); } } std::vector grp_perim; findPerimeter(grp_tris, vloop, grp_perim); out_faces.push_back(face->create(grp_perim, false)); } } std::swap(faces, out_faces); } }; } }