417 lines
14 KiB
C++
417 lines
14 KiB
C++
|
// 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);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|