dust3d/thirdparty/carve-1.4.0/include/carve/csg_triangulator.hpp

417 lines
14 KiB
C++
Raw Normal View History

// 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 <carve/csg.hpp>
#include <carve/tag.hpp>
#include <carve/triangulator.hpp>
#include <deque>
namespace carve {
namespace csg {
namespace detail {
template<bool with_improvement>
class CarveTriangulator : public csg::CSG::Hook {
public:
CarveTriangulator() {
}
virtual ~CarveTriangulator() {
}
virtual void processOutputFace(std::vector<poly::Polyhedron::face_t *> &faces,
const poly::Polyhedron::face_t *orig,
bool flipped) {
std::vector<poly::Polyhedron::face_t *> 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<triangulate::tri_idx> result;
std::vector<const poly::Polyhedron::vertex_t *> vloop;
face->getVertexLoop(vloop);
triangulate::triangulate(face->projector(), vloop, result);
if (with_improvement) {
triangulate::improve(face->projector(), vloop, result);
}
std::vector<const poly::Polyhedron::vertex_t *> 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<false> CarveTriangulator;
typedef detail::CarveTriangulator<true> CarveTriangulatorWithImprovement;
class CarveTriangulationImprover : public csg::CSG::Hook {
public:
CarveTriangulationImprover() {
}
virtual ~CarveTriangulationImprover() {
}
virtual void processOutputFace(std::vector<poly::Polyhedron::face_t *> &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<const poly::Polyhedron::vertex_t *, size_t> vert_map_t;
std::vector<poly::Polyhedron::face_t *> out_faces;
vert_map_t vert_map;
out_faces.reserve(faces.size());
poly::p2_adapt_project<3> projector(faces[0]->project);
std::vector<triangulate::tri_idx> 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<const poly::Polyhedron::vertex_t *> 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<const poly::Polyhedron::vertex_t *> 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<V2, F2> 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<poly::Polyhedron::face_t *> &faces,
const poly::Polyhedron::face_t *orig,
bool flipped) {
if (faces.size() == 1) return;
std::vector<poly::Polyhedron::face_t *> 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<poly::Polyhedron::face_t *>((*i).second.first);
poly::Polyhedron::face_t *b = const_cast<poly::Polyhedron::face_t *>((*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<const poly::Polyhedron::vertex_t *> &vertices,
std::list<std::pair<size_t, size_t> > &edge_pos) {
std::map<V2, size_t> 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<V2, size_t>::iterator i = edges.begin(); i != edges.end(); ++i) {
V2 rev = V2((*i).first.second, (*i).first.first);
std::map<V2, size_t>::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<size_t> &grp,
const std::vector<triangulate::tri_idx> &tris,
const std::map<std::pair<size_t, size_t>, size_t> &tri_edge) {
grp[t1] = new_grp_1;
grp[t2] = new_grp_2;
std::deque<size_t> to_visit;
to_visit.push_back(t1);
to_visit.push_back(t2);
std::vector<std::pair<size_t, size_t> > 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<std::pair<size_t, size_t>, 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<triangulate::tri_idx> &tris,
const std::vector<const poly::Polyhedron::vertex_t *> &verts,
std::vector<const poly::Polyhedron::vertex_t *> &out) {
std::map<std::pair<size_t, size_t>, 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<size_t, size_t> unpaired;
for (std::map<std::pair<size_t, size_t>, 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<poly::Polyhedron::face_t *> &faces,
const poly::Polyhedron::face_t *orig,
bool flipped) {
std::vector<poly::Polyhedron::face_t *> 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<const poly::Polyhedron::vertex_t *> vloop;
face->getVertexLoop(vloop);
std::list<std::pair<size_t, size_t> > rep_edges;
if (!findRepeatedEdges(vloop, rep_edges)) {
out_faces.push_back(face);
continue;
}
std::vector<triangulate::tri_idx> result;
triangulate::triangulate(face->projector(), vloop, result);
std::map<std::pair<size_t, size_t>, 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<size_t> grp;
grp.resize(result.size(), 0);
size_t grp_max = 0;
while (rep_edges.size()) {
std::pair<size_t, size_t> 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<size_t> groups;
std::copy(grp.begin(), grp.end(), std::inserter(groups, groups.begin()));
// now construct perimeters for each group.
std::vector<triangulate::tri_idx> grp_tris;
grp_tris.reserve(result.size());
for (std::set<size_t>::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<const poly::Polyhedron::vertex_t *> grp_perim;
findPerimeter(grp_tris, vloop, grp_perim);
out_faces.push_back(face->create(grp_perim, false));
}
}
std::swap(faces, out_faces);
}
};
}
}