Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2018-02-07 16:44:59 +0300
committerSergey Sharybin <sergey.vfx@gmail.com>2018-02-08 17:37:44 +0300
commite0597baed57fa7a9dfaf6dff6d0fa120784d21ea (patch)
treeee9976051369ac5ae1c2e65e4cb9fa5392d17243 /extern/carve/lib/intersect_face_division.cpp
parent9dc7dca3a22502f79131728a6d4441ea1851595c (diff)
Remove Carve boolean
We've got quite comprehensive BMesh based implementation, which is way easier for maintenance than abandoned Carve library. After all the time BMesh implementation was working on the same level of limitations about manifold meshes and touching edges than Carve. Is better to focus on maintaining one boolean implementation now. Reviewers: campbellbarton Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D3050
Diffstat (limited to 'extern/carve/lib/intersect_face_division.cpp')
-rw-r--r--extern/carve/lib/intersect_face_division.cpp1765
1 files changed, 0 insertions, 1765 deletions
diff --git a/extern/carve/lib/intersect_face_division.cpp b/extern/carve/lib/intersect_face_division.cpp
deleted file mode 100644
index 6554ef500ed..00000000000
--- a/extern/carve/lib/intersect_face_division.cpp
+++ /dev/null
@@ -1,1765 +0,0 @@
-// Begin License:
-// Copyright (C) 2006-2014 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 either the GNU General
-// Public License version 2 or 3 (at your option) as published by the
-// Free Software Foundation and appearing in the files LICENSE.GPL2
-// and LICENSE.GPL3 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 <carve_config.h>
-#endif
-
-#include <carve/csg.hpp>
-#include <carve/polyline.hpp>
-#include <carve/debug_hooks.hpp>
-#include <carve/timing.hpp>
-#include <carve/triangulator.hpp>
-
-#include <list>
-#include <set>
-#include <iostream>
-
-#include <algorithm>
-
-#include "csg_detail.hpp"
-#include "csg_data.hpp"
-
-#include "intersect_common.hpp"
-
-
-
-#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
-void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii);
-#endif
-
-
-
-namespace {
-
-
-
-#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
- template<typename iter_t>
- void dumpFacesAndHoles(iter_t f_begin, iter_t f_end,
- iter_t h_begin, iter_t h_end,
- const std::string &fname) {
- std::cerr << "dumping " << std::distance(f_begin, f_end) << " faces, " << std::distance(h_begin, h_end) << " holes." << std::endl;
- std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t> v_included;
-
- for (iter_t i = f_begin; i != f_end; ++i) {
- for (size_t j = 0; j < (*i).size(); ++j) {
- if (v_included.find((*i)[j]) == v_included.end()) {
- size_t &p = v_included[(*i)[j]];
- p = v_included.size() - 1;
- }
- }
- }
-
- for (iter_t i = h_begin; i != h_end; ++i) {
- for (size_t j = 0; j < (*i).size(); ++j) {
- if (v_included.find((*i)[j]) == v_included.end()) {
- size_t &p = v_included[(*i)[j]];
- p = v_included.size() - 1;
- }
- }
- }
-
- carve::line::PolylineSet fh;
- fh.vertices.resize(v_included.size());
- for (std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t>::const_iterator
- i = v_included.begin(); i != v_included.end(); ++i) {
- fh.vertices[(*i).second].v = (*i).first->v;
- }
-
- {
- std::vector<size_t> connected;
- for (iter_t i = f_begin; i != f_end; ++i) {
- connected.clear();
- for (size_t j = 0; j < (*i).size(); ++j) {
- connected.push_back(v_included[(*i)[j]]);
- }
- fh.addPolyline(true, connected.begin(), connected.end());
- }
- for (iter_t i = h_begin; i != h_end; ++i) {
- connected.clear();
- for (size_t j = 0; j < (*i).size(); ++j) {
- connected.push_back(v_included[(*i)[j]]);
- }
- fh.addPolyline(true, connected.begin(), connected.end());
- }
- }
-
- ::writePLY(fname, &fh, true);
- }
-#endif
-
-
-
- template<typename T>
- void populateVectorFromList(std::list<T> &l, std::vector<T> &v) {
- v.clear();
- v.reserve(l.size());
- for (typename std::list<T>::iterator i = l.begin(); i != l.end(); ++i) {
- v.push_back(T());
- std::swap(*i, v.back());
- }
- l.clear();
- }
-
- template<typename T>
- void populateListFromVector(std::vector<T> &v, std::list<T> &l) {
- l.clear();
- for (size_t i = 0; i < v.size(); ++i) {
- l.push_back(T());
- std::swap(v[i], l.back());
- }
- v.clear();
- }
-
-
-
- struct GraphEdge {
- GraphEdge *next;
- GraphEdge *prev;
- GraphEdge *loop_next;
- carve::mesh::MeshSet<3>::vertex_t *src;
- carve::mesh::MeshSet<3>::vertex_t *tgt;
- double ang;
- int visited;
-
- GraphEdge(carve::mesh::MeshSet<3>::vertex_t *_src, carve::mesh::MeshSet<3>::vertex_t *_tgt) :
- next(NULL), prev(NULL), loop_next(NULL),
- src(_src), tgt(_tgt),
- ang(0.0), visited(-1) {
- }
- };
-
-
-
- struct GraphEdges {
- GraphEdge *edges;
- carve::geom2d::P2 proj;
-
- GraphEdges() : edges(NULL), proj() {
- }
- };
-
-
-
- struct Graph {
- typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, GraphEdges> graph_t;
-
- graph_t graph;
-
- Graph() : graph() {
- }
-
- ~Graph() {
- int c = 0;
-
- GraphEdge *edge;
- for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) {
- edge = (*i).second.edges;
- while (edge) {
- GraphEdge *temp = edge;
- ++c;
- edge = edge->next;
- delete temp;
- }
- }
-
- if (c) {
- std::cerr << "warning: "
- << c
- << " edges should have already been removed at graph destruction time"
- << std::endl;
- }
- }
-
- const carve::geom2d::P2 &projection(carve::mesh::MeshSet<3>::vertex_t *v) const {
- graph_t::const_iterator i = graph.find(v);
- CARVE_ASSERT(i != graph.end());
- return (*i).second.proj;
- }
-
- void computeProjection(carve::mesh::MeshSet<3>::face_t *face) {
- for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) {
- (*i).second.proj = face->project((*i).first->v);
- }
- for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) {
- for (GraphEdge *e = (*i).second.edges; e; e = e->next) {
- e->ang = carve::math::ANG(carve::geom2d::atan2(projection(e->tgt) - projection(e->src)));
- }
- }
- }
-
- void print(std::ostream &out, const carve::csg::VertexIntersections *vi) const {
- for (graph_t::const_iterator i = graph.begin(), e = graph.end(); i != e; ++i) {
- out << (*i).first << (*i).first->v << '(' << projection((*i).first).x << ',' << projection((*i).first).y << ") :";
- for (const GraphEdge *e = (*i).second.edges; e; e = e->next) {
- out << ' ' << e->tgt << e->tgt->v << '(' << projection(e->tgt).x << ',' << projection(e->tgt).y << ')';
- }
- out << std::endl;
- if (vi) {
- carve::csg::VertexIntersections::const_iterator j = vi->find((*i).first);
- if (j != vi->end()) {
- out << " (int) ";
- for (carve::csg::IObjPairSet::const_iterator
- k = (*j).second.begin(), ke = (*j).second.end(); k != ke; ++k) {
- if ((*k).first < (*k).second) {
- out << (*k).first << ".." << (*k).second << "; ";
- }
- }
- out << std::endl;
- }
- }
- }
- }
-
- void addEdge(carve::mesh::MeshSet<3>::vertex_t *v1, carve::mesh::MeshSet<3>::vertex_t *v2) {
- GraphEdges &edges = graph[v1];
- GraphEdge *edge = new GraphEdge(v1, v2);
- if (edges.edges) edges.edges->prev = edge;
- edge->next = edges.edges;
- edges.edges = edge;
- }
-
- void removeEdge(GraphEdge *edge) {
- if (edge->prev != NULL) {
- edge->prev->next = edge->next;
- } else {
- if (edge->next != NULL) {
- GraphEdges &edges = (graph[edge->src]);
- edges.edges = edge->next;
- } else {
- graph.erase(edge->src);
- }
- }
- if (edge->next != NULL) {
- edge->next->prev = edge->prev;
- }
- delete edge;
- }
-
- bool empty() const {
- return graph.size() == 0;
- }
-
- GraphEdge *pickStartEdge() {
- // Try and find a vertex from which there is only one outbound edge. Won't always succeed.
- for (graph_t::iterator i = graph.begin(); i != graph.end(); ++i) {
- GraphEdges &ge = i->second;
- if (ge.edges->next == NULL) {
- return ge.edges;
- }
- }
- return (*graph.begin()).second.edges;
- }
-
- GraphEdge *outboundEdges(carve::mesh::MeshSet<3>::vertex_t *v) {
- return graph[v].edges;
- }
- };
-
-
-
- /**
- * \brief Take a set of new edges and split a face based upon those edges.
- *
- * @param[in] face The face to be split.
- * @param[in] edges
- * @param[out] face_loops Output list of face loops
- * @param[out] hole_loops Output list of hole loops
- * @param vi
- */
- static void splitFace(carve::mesh::MeshSet<3>::face_t *face,
- const carve::csg::V2Set &edges,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops,
- const carve::csg::VertexIntersections & /* vi */) {
- Graph graph;
-
- for (carve::csg::V2Set::const_iterator
- i = edges.begin(), e = edges.end();
- i != e;
- ++i) {
- carve::mesh::MeshSet<3>::vertex_t *v1 = ((*i).first), *v2 = ((*i).second);
- if (carve::geom::equal(v1->v, v2->v)) std::cerr << "WARNING! " << v1->v << "==" << v2->v << std::endl;
- graph.addEdge(v1, v2);
- }
-
- graph.computeProjection(face);
-
- while (!graph.empty()) {
- GraphEdge *edge;
- GraphEdge *start;
- start = edge = graph.pickStartEdge();
-
- edge->visited = 0;
-
- int len = 0;
-
- for (;;) {
- double in_ang = M_PI + edge->ang;
- if (in_ang > M_TWOPI) in_ang -= M_TWOPI;
-
- GraphEdge *opts;
- GraphEdge *out = NULL;
- double best = M_TWOPI + 1.0;
-
- for (opts = graph.outboundEdges(edge->tgt); opts; opts = opts->next) {
- if (opts->tgt == edge->src) {
- if (out == NULL && opts->next == NULL) out = opts;
- } else {
- double out_ang = carve::math::ANG(in_ang - opts->ang);
-
- if (out == NULL || out_ang < best) {
- out = opts;
- best = out_ang;
- }
- }
- }
-
- CARVE_ASSERT(out != NULL);
-
- edge->loop_next = out;
-
- if (out->visited >= 0) {
- while (start != out) {
- GraphEdge *e = start;
- start = start->loop_next;
- e->loop_next = NULL;
- e->visited = -1;
- }
- len = edge->visited - out->visited + 1;
- break;
- }
-
- out->visited = edge->visited + 1;
- edge = out;
- }
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> loop(len);
- std::vector<carve::geom2d::P2> projected(len);
-
- edge = start;
- for (int i = 0; i < len; ++i) {
- GraphEdge *next = edge->loop_next;
- loop[i] = edge->src;
- projected[i] = graph.projection(edge->src);
- graph.removeEdge(edge);
- edge = next;
- }
-
- CARVE_ASSERT(edge == start);
-
- if (carve::geom2d::signedArea(projected) < 0) {
- face_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- face_loops.back().swap(loop);
- } else {
- hole_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- hole_loops.back().swap(loop);
- }
- }
- }
-
-
-
- /**
- * \brief Determine the relationship between a face loop and a hole loop.
- *
- * Determine whether a face and hole share an edge, or a vertex,
- * or do not touch. Find a hole vertex that is not part of the
- * face, and a hole,face vertex pair that are coincident, if such
- * a pair exists.
- *
- * @param[in] f A face loop.
- * @param[in] f_sort A vector indexing \a f in address order
- * @param[in] h A hole loop.
- * @param[in] h_sort A vector indexing \a h in address order
- * @param[out] f_idx Index of a face vertex that is shared with the hole.
- * @param[out] h_idx Index of the hole vertex corresponding to \a f_idx.
- * @param[out] unmatched_h_idx Index of a hole vertex that is not part of the face.
- * @param[out] shares_vertex Boolean indicating that the face and the hole share a vertex.
- * @param[out] shares_edge Boolean indicating that the face and the hole share an edge.
- */
- static void compareFaceLoopAndHoleLoop(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f,
- const std::vector<unsigned> &f_sort,
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h,
- const std::vector<unsigned> &h_sort,
- unsigned &f_idx,
- unsigned &h_idx,
- int &unmatched_h_idx,
- bool &shares_vertex,
- bool &shares_edge) {
- const size_t F = f.size();
- const size_t H = h.size();
-
- shares_vertex = shares_edge = false;
- unmatched_h_idx = -1;
-
- unsigned I, J;
- for (I = J = 0; I < F && J < H;) {
- unsigned i = f_sort[I], j = h_sort[J];
- if (f[i] == h[j]) {
- shares_vertex = true;
- f_idx = i;
- h_idx = j;
- if (f[(i + F - 1) % F] == h[(j + 1) % H]) {
- shares_edge = true;
- }
- carve::mesh::MeshSet<3>::vertex_t *t = f[i];
- do { ++I; } while (I < F && f[f_sort[I]] == t);
- do { ++J; } while (J < H && h[h_sort[J]] == t);
- } else if (f[i] < h[j]) {
- ++I;
- } else {
- unmatched_h_idx = j;
- ++J;
- }
- }
- if (J < H) {
- unmatched_h_idx = h_sort[J];
- }
- }
-
-
-
- /**
- * \brief Compute an embedding for a set of face loops and hole loops.
- *
- * Because face and hole loops may be contained within each other,
- * it must be determined which hole loops are directly contained
- * within a face loop.
- *
- * @param[in] face The face from which these face and hole loops derive.
- * @param[in] face_loops
- * @param[in] hole_loops
- * @param[out] containing_faces A vector which for each hole loop
- * lists the indices of the face
- * loops it is containined in.
- * @param[out] hole_shared_vertices A map from a face,hole pair to
- * a shared vertex pair.
- */
- static void computeContainment(carve::mesh::MeshSet<3>::face_t *face,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops,
- std::vector<std::vector<int> > &containing_faces,
- std::map<int, std::map<int, std::pair<unsigned, unsigned> > > &hole_shared_vertices) {
-#if defined(CARVE_DEBUG)
- std::cerr << "input: "
- << face_loops.size() << "faces, "
- << hole_loops.size() << "holes."
- << std::endl;
-#endif
-
- std::vector<std::vector<carve::geom2d::P2> > face_loops_projected, hole_loops_projected;
- std::vector<carve::geom::aabb<2> > face_loop_aabb, hole_loop_aabb;
- std::vector<std::vector<unsigned> > face_loops_sorted, hole_loops_sorted;
-
- std::vector<double> face_loop_areas, hole_loop_areas;
-
- face_loops_projected.resize(face_loops.size());
- face_loops_sorted.resize(face_loops.size());
- face_loop_aabb.resize(face_loops.size());
- face_loop_areas.resize(face_loops.size());
-
- hole_loops_projected.resize(hole_loops.size());
- hole_loops_sorted.resize(hole_loops.size());
- hole_loop_aabb.resize(hole_loops.size());
- hole_loop_areas.resize(hole_loops.size());
-
- // produce a projection of each face loop onto a 2D plane, and an
- // index vector which sorts vertices by address.
- for (size_t m = 0; m < face_loops.size(); ++m) {
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f_loop = (face_loops[m]);
- face_loops_projected[m].reserve(f_loop.size());
- face_loops_sorted[m].reserve(f_loop.size());
- for (size_t n = 0; n < f_loop.size(); ++n) {
- face_loops_projected[m].push_back(face->project(f_loop[n]->v));
- face_loops_sorted[m].push_back(n);
- }
- face_loop_areas.push_back(carve::geom2d::signedArea(face_loops_projected[m]));
-
- std::sort(face_loops_sorted[m].begin(), face_loops_sorted[m].end(),
- carve::make_index_sort(face_loops[m].begin()));
- face_loop_aabb[m].fit(face_loops_projected[m].begin(), face_loops_projected[m].end());
- }
-
- // produce a projection of each hole loop onto a 2D plane, and an
- // index vector which sorts vertices by address.
- for (size_t m = 0; m < hole_loops.size(); ++m) {
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h_loop = (hole_loops[m]);
- hole_loops_projected[m].reserve(h_loop.size());
- hole_loops_projected[m].reserve(h_loop.size());
- for (size_t n = 0; n < h_loop.size(); ++n) {
- hole_loops_projected[m].push_back(face->project(h_loop[n]->v));
- hole_loops_sorted[m].push_back(n);
- }
- hole_loop_areas.push_back(carve::geom2d::signedArea(hole_loops_projected[m]));
-
- std::sort(hole_loops_sorted[m].begin(), hole_loops_sorted[m].end(),
- carve::make_index_sort(hole_loops[m].begin()));
- hole_loop_aabb[m].fit(hole_loops_projected[m].begin(), hole_loops_projected[m].end());
- }
-
- containing_faces.resize(hole_loops.size());
-
- for (unsigned i = 0; i < hole_loops.size(); ++i) {
-
- for (unsigned j = 0; j < face_loops.size(); ++j) {
- if (!face_loop_aabb[j].completelyContains(hole_loop_aabb[i])) {
-#if defined(CARVE_DEBUG)
- std::cerr << "face: " << j
- << " hole: " << i
- << " skipped test (aabb fail)"
- << std::endl;
-#endif
- continue;
- }
-
- unsigned f_idx, h_idx;
- int unmatched_h_idx;
- bool shares_vertex, shares_edge;
- compareFaceLoopAndHoleLoop(face_loops[j],
- face_loops_sorted[j],
- hole_loops[i],
- hole_loops_sorted[i],
- f_idx, h_idx,
- unmatched_h_idx,
- shares_vertex,
- shares_edge);
-
-#if defined(CARVE_DEBUG)
- std::cerr << "face: " << j
- << " hole: " << i
- << " shares_vertex: " << shares_vertex
- << " shares_edge: " << shares_edge
- << std::endl;
-#endif
-
- carve::geom3d::Vector test = hole_loops[i][0]->v;
- carve::geom2d::P2 test_p = face->project(test);
-
- if (shares_vertex) {
- hole_shared_vertices[i][j] = std::make_pair(h_idx, f_idx);
- // Hole touches face. Should be able to connect it up
- // trivially. Still need to record its containment, so that
- // the assignment below works.
- if (unmatched_h_idx != -1) {
-#if defined(CARVE_DEBUG)
- std::cerr << "using unmatched vertex: " << unmatched_h_idx << std::endl;
-#endif
- test = hole_loops[i][unmatched_h_idx]->v;
- test_p = face->project(test);
- } else {
- // XXX: hole shares ALL vertices with face. Pick a point
- // internal to the projected poly.
- if (shares_edge) {
- // Hole shares edge with face => face can't contain hole.
- continue;
- }
-
- // XXX: how is this possible? Doesn't share an edge, but
- // also doesn't have any vertices that are not in
- // common. Degenerate hole?
-
- // XXX: come up with a test case for this.
- CARVE_FAIL("implement me");
- }
- }
-
-
- // XXX: use loop area to avoid some point-in-poly tests? Loop
- // area is faster, but not sure which is more robust.
- if (carve::geom2d::pointInPolySimple(face_loops_projected[j], test_p)) {
-#if defined(CARVE_DEBUG)
- std::cerr << "contains: " << i << " - " << j << std::endl;
-#endif
- containing_faces[i].push_back(j);
- } else {
-#if defined(CARVE_DEBUG)
- std::cerr << "does not contain: " << i << " - " << j << std::endl;
-#endif
- }
- }
-
-#if defined(CARVE_DEBUG)
- if (containing_faces[i].size() == 0) {
- //HOOK(drawFaceLoopWireframe(hole_loops[i], face->normal, 1.0, 0.0, 0.0, 1.0););
- std::cerr << "hole loop: ";
- for (unsigned j = 0; j < hole_loops[i].size(); ++j) {
- std::cerr << " " << hole_loops[i][j] << ":" << hole_loops[i][j]->v;
- }
- std::cerr << std::endl;
- for (unsigned j = 0; j < face_loops.size(); ++j) {
- //HOOK(drawFaceLoopWireframe(face_loops[j], face->normal, 0.0, 1.0, 0.0, 1.0););
- }
- }
-#endif
-
- // CARVE_ASSERT(containing_faces[i].size() >= 1);
- }
- }
-
-
-
- /**
- * \brief Merge face loops and hole loops to produce a set of face loops without holes.
- *
- * @param[in] face The face from which these face loops derive.
- * @param[in,out] f_loops A list of face loops.
- * @param[in] h_loops A list of hole loops to be incorporated into face loops.
- */
- static void mergeFacesAndHoles(carve::mesh::MeshSet<3>::face_t *face,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &f_loops,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &h_loops,
- carve::csg::CSG::Hooks & /* hooks */) {
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops;
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops;
-
- std::vector<std::vector<int> > containing_faces;
- std::map<int, std::map<int, std::pair<unsigned, unsigned> > > hole_shared_vertices;
-
-#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
- dumpFacesAndHoles(f_loops.begin(), f_loops.end(), h_loops.begin(), h_loops.end(), "/tmp/pre_merge.ply");
-#endif
-
- {
- // move input face and hole loops to temp vectors.
- size_t m;
- face_loops.resize(f_loops.size());
- m = 0;
- for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::iterator
- i = f_loops.begin(), ie = f_loops.end();
- i != ie;
- ++i, ++m) {
- face_loops[m].swap((*i));
- }
-
- hole_loops.resize(h_loops.size());
- m = 0;
- for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::iterator
- i = h_loops.begin(), ie = h_loops.end();
- i != ie;
- ++i, ++m) {
- hole_loops[m].swap((*i));
- }
- f_loops.clear();
- h_loops.clear();
- }
-
- // work out the embedding of holes and faces.
- computeContainment(face, face_loops, hole_loops, containing_faces, hole_shared_vertices);
-
- int unassigned = (int)hole_loops.size();
-
- std::vector<std::vector<int> > face_holes;
- face_holes.resize(face_loops.size());
-
- for (unsigned i = 0; i < containing_faces.size(); ++i) {
- if (containing_faces[i].size() == 0) {
- std::map<int, std::map<int, std::pair<unsigned, unsigned> > >::iterator it = hole_shared_vertices.find(i);
- if (it != hole_shared_vertices.end()) {
- std::map<int, std::pair<unsigned, unsigned> >::iterator it2 = (*it).second.begin();
- int f = (*it2).first;
- unsigned h_idx = (*it2).second.first;
- unsigned f_idx = (*it2).second.second;
-
- // patch the hole into the face directly. because
- // f_loop[f_idx] == h_loop[h_idx], we don't need to
- // duplicate the f_loop vertex.
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f_loop = face_loops[f];
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h_loop = hole_loops[i];
-
- f_loop.insert(f_loop.begin() + f_idx + 1, h_loop.size(), NULL);
-
- unsigned p = f_idx + 1;
- for (unsigned a = h_idx + 1; a < h_loop.size(); ++a, ++p) {
- f_loop[p] = h_loop[a];
- }
- for (unsigned a = 0; a <= h_idx; ++a, ++p) {
- f_loop[p] = h_loop[a];
- }
-
-#if defined(CARVE_DEBUG)
- std::cerr << "hook face " << f << " to hole " << i << "(vertex)" << std::endl;
-#endif
- } else {
- std::cerr << "uncontained hole loop does not share vertices with any face loop!" << std::endl;
- }
- unassigned--;
- }
- }
-
-
- // work out which holes are directly contained within which faces.
- while (unassigned) {
- std::set<int> removed;
-
- for (unsigned i = 0; i < containing_faces.size(); ++i) {
- if (containing_faces[i].size() == 1) {
- int f = containing_faces[i][0];
- face_holes[f].push_back(i);
-#if defined(CARVE_DEBUG)
- std::cerr << "hook face " << f << " to hole " << i << std::endl;
-#endif
- removed.insert(f);
- unassigned--;
- }
- }
-
- if (!removed.size())
- throw carve::exception("Failed to merge holes");
-
- for (std::set<int>::iterator f = removed.begin(); f != removed.end(); ++f) {
- for (unsigned i = 0; i < containing_faces.size(); ++i) {
- containing_faces[i].erase(std::remove(containing_faces[i].begin(),
- containing_faces[i].end(),
- *f),
- containing_faces[i].end());
- }
- }
- }
-
-#if 0
- // use old templated projection code to patch holes into faces.
- for (unsigned i = 0; i < face_loops.size(); ++i) {
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_hole_loops;
- face_hole_loops.resize(face_holes[i].size());
- for (unsigned j = 0; j < face_holes[i].size(); ++j) {
- face_hole_loops[j].swap(hole_loops[face_holes[i][j]]);
- }
- if (face_hole_loops.size()) {
-
- f_loops.push_back(carve::triangulate::incorporateHolesIntoPolygon(
- carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project),
- face_loops[i],
- face_hole_loops));
- } else {
- f_loops.push_back(face_loops[i]);
- }
- }
-
-#else
- // use new 2d-only hole patching code.
- for (size_t i = 0; i < face_loops.size(); ++i) {
- if (!face_holes[i].size()) {
- f_loops.push_back(face_loops[i]);
- continue;
- }
-
- std::vector<std::vector<carve::geom2d::P2> > projected_poly;
- projected_poly.resize(face_holes[i].size() + 1);
- projected_poly[0].reserve(face_loops[i].size());
- for (size_t j = 0; j < face_loops[i].size(); ++j) {
- projected_poly[0].push_back(face->project(face_loops[i][j]->v));
- }
- for (size_t j = 0; j < face_holes[i].size(); ++j) {
- projected_poly[j+1].reserve(hole_loops[face_holes[i][j]].size());
- for (size_t k = 0; k < hole_loops[face_holes[i][j]].size(); ++k) {
- projected_poly[j+1].push_back(face->project(hole_loops[face_holes[i][j]][k]->v));
- }
- }
-
- std::vector<std::pair<size_t, size_t> > result = carve::triangulate::incorporateHolesIntoPolygon(projected_poly);
-
- f_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &out = f_loops.back();
- out.reserve(result.size());
- for (size_t j = 0; j < result.size(); ++j) {
- if (result[j].first == 0) {
- out.push_back(face_loops[i][result[j].second]);
- } else {
- out.push_back(hole_loops[face_holes[i][result[j].first-1]][result[j].second]);
- }
- }
- }
-#endif
-#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
- dumpFacesAndHoles(f_loops.begin(), f_loops.end(), h_loops.begin(), h_loops.end(), "/tmp/post_merge.ply");
-#endif
-
- }
-
-
-
- /**
- * \brief Assemble the base loop for a face.
- *
- * The base loop is the original face loop, including vertices
- * created by intersections crossing any of its edges.
- *
- * @param[in] face The face to process.
- * @param[in] vmap
- * @param[in] face_split_edges
- * @param[in] divided_edges A mapping from edge pointer to sets of
- * ordered vertices corrsponding to the intersection points
- * on that edge.
- * @param[out] base_loop A vector of the vertices of the base loop.
- */
- static bool assembleBaseLoop(carve::mesh::MeshSet<3>::face_t *face,
- const carve::csg::detail::Data &data,
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop,
- carve::csg::CSG::Hooks &hooks) {
- base_loop.clear();
-
- // XXX: assumes that face->edges is in the same order as
- // face->vertices. (Which it is)
- carve::mesh::MeshSet<3>::edge_t *e = face->edge;
- size_t e_idx = 0;
- bool face_edge_intersected = false;
- do {
- base_loop.push_back(carve::csg::map_vertex(data.vmap, e->vert));
-
- carve::csg::detail::EVVMap::const_iterator ev = data.divided_edges.find(e);
-
- if (ev != data.divided_edges.end()) {
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &ev_vec = ((*ev).second);
-
- for (size_t k = 0, ke = ev_vec.size(); k < ke;) {
- base_loop.push_back(ev_vec[k++]);
- }
-
- if (ev_vec.size() && hooks.hasHook(carve::csg::CSG::Hooks::EDGE_DIVISION_HOOK)) {
- carve::mesh::MeshSet<3>::vertex_t *v1 = e->vert;
- carve::mesh::MeshSet<3>::vertex_t *v2;
- for (size_t k = 0, ke = ev_vec.size(); k < ke;) {
- v2 = ev_vec[k++];
- hooks.edgeDivision(e, e_idx, v1, v2);
- v1 = v2;
- }
- v2 = e->v2();
- hooks.edgeDivision(e, e_idx, v1, v2);
- }
-
- face_edge_intersected = true;
- }
- e = e->next;
- ++e_idx;
- } while (e != face->edge);
-
- return face_edge_intersected;
- }
-
-
-
- // the crossing_data structure holds temporary information regarding
- // paths, and their relationship to the loop of edges that forms the
- // face perimeter.
- struct crossing_data {
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> *path;
- size_t edge_idx[2];
-
- crossing_data(std::vector<carve::mesh::MeshSet<3>::vertex_t *> *p, size_t e1, size_t e2) : path(p) {
- edge_idx[0] = e1; edge_idx[1] = e2;
- }
-
- bool operator<(const crossing_data &c) const {
- // the sort order for paths is in order of increasing initial
- // position on the edge loop, but decreasing final position.
- return edge_idx[0] < c.edge_idx[0] || (edge_idx[0] == c.edge_idx[0] && edge_idx[1] > c.edge_idx[1]);
- }
- };
-
-
-
- bool processCrossingEdges(carve::mesh::MeshSet<3>::face_t *face,
- const carve::csg::VertexIntersections &vertex_intersections,
- carve::csg::CSG::Hooks &hooks,
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &paths,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops_out) {
- const size_t N = base_loop.size();
- std::vector<crossing_data> endpoint_indices;
-
- endpoint_indices.reserve(paths.size());
-
- for (size_t i = 0; i < paths.size(); ++i) {
- endpoint_indices.push_back(crossing_data(&paths[i], N, N));
- }
-
- // Step 1:
- // locate endpoints of paths on the base loop.
- for (size_t i = 0; i < N; ++i) {
- for (size_t j = 0; j < paths.size(); ++j) {
- // test beginning of path.
- if (paths[j].front() == base_loop[i]) {
- if (endpoint_indices[j].edge_idx[0] == N) {
- endpoint_indices[j].edge_idx[0] = i;
- } else {
- // there is a duplicated vertex in the face perimeter. The
- // path might attach to either of the duplicate instances
- // so we have to work out which is the right one to attach
- // to. We assume it's the index currently being examined,
- // if the path heads in a direction that's internal to the
- // angle made by the prior and next edges of the face
- // perimeter. Otherwise, leave it as the currently
- // selected index (until another duplicate is found, if it
- // exists, and is tested).
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p = *endpoint_indices[j].path;
- const size_t pN = p.size();
-
- carve::mesh::MeshSet<3>::vertex_t *a, *b, *c;
- a = base_loop[(i+N-1)%N];
- b = base_loop[i];
- c = base_loop[(i+1)%N];
-
- carve::mesh::MeshSet<3>::vertex_t *adj = (p[0] == base_loop[i]) ? p[1] : p[pN-2];
-
- if (carve::geom2d::internalToAngle(face->project(c->v),
- face->project(b->v),
- face->project(a->v),
- face->project(adj->v))) {
- endpoint_indices[j].edge_idx[0] = i;
- }
- }
- }
-
- // test end of path.
- if (paths[j].back() == base_loop[i]) {
- if (endpoint_indices[j].edge_idx[1] == N) {
- endpoint_indices[j].edge_idx[1] = i;
- } else {
- // Work out which of the duplicated vertices is the right
- // one to attach to, as above.
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p = *endpoint_indices[j].path;
- const size_t pN = p.size();
-
- carve::mesh::MeshSet<3>::vertex_t *a, *b, *c;
- a = base_loop[(i+N-1)%N];
- b = base_loop[i];
- c = base_loop[(i+1)%N];
-
- carve::mesh::MeshSet<3>::vertex_t *adj = (p[0] == base_loop[i]) ? p[1] : p[pN-2];
-
- if (carve::geom2d::internalToAngle(face->project(c->v),
- face->project(b->v),
- face->project(a->v),
- face->project(adj->v))) {
- endpoint_indices[j].edge_idx[1] = i;
- }
- }
- }
- }
- }
-
-#if defined(CARVE_DEBUG)
- std::cerr << "### N: " << N << std::endl;
- for (size_t i = 0; i < paths.size(); ++i) {
- std::cerr << "### path: " << i << " endpoints: " << endpoint_indices[i].edge_idx[0] << " - " << endpoint_indices[i].edge_idx[1] << std::endl;
- }
-#endif
-
-
- // Step 2:
- // divide paths up into those that connect to the base loop in two
- // places (cross), and those that do not (noncross).
- std::vector<crossing_data> cross, noncross;
- cross.reserve(endpoint_indices.size() + 1);
- noncross.reserve(endpoint_indices.size());
-
- for (size_t i = 0; i < endpoint_indices.size(); ++i) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### orienting path: " << i << " endpoints: " << endpoint_indices[i].edge_idx[0] << " - " << endpoint_indices[i].edge_idx[1] << std::endl;
-#endif
- if (endpoint_indices[i].edge_idx[0] != N && endpoint_indices[i].edge_idx[1] != N) {
- // Orient each path correctly. Paths should progress from
- // smaller perimeter index to larger, but if the path starts
- // and ends at the same perimeter index, then the decision
- // needs to be made based upon area.
- if (endpoint_indices[i].edge_idx[0] == endpoint_indices[i].edge_idx[1]) {
- // The path forms a loop that starts and ends at the same
- // vertex of the perimeter. In this case, we need to orient
- // the path so that the constructed loop has the right
- // signed area.
- double area = carve::geom2d::signedArea(endpoint_indices[i].path->begin() + 1,
- endpoint_indices[i].path->end(),
- carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project));
- if (area < 0) {
- // XXX: Create test case to check that this is the correct sign for the area.
- std::reverse(endpoint_indices[i].path->begin(), endpoint_indices[i].path->end());
- }
- } else {
- if (endpoint_indices[i].edge_idx[0] > endpoint_indices[i].edge_idx[1]) {
- std::swap(endpoint_indices[i].edge_idx[0], endpoint_indices[i].edge_idx[1]);
- std::reverse(endpoint_indices[i].path->begin(), endpoint_indices[i].path->end());
- }
- }
- }
-
- if (endpoint_indices[i].edge_idx[0] != N &&
- endpoint_indices[i].edge_idx[1] != N &&
- endpoint_indices[i].edge_idx[0] != endpoint_indices[i].edge_idx[1]) {
- cross.push_back(endpoint_indices[i]);
- } else {
- noncross.push_back(endpoint_indices[i]);
- }
- }
-
- // Step 3:
- // add a temporary crossing path that connects the beginning and the
- // end of the base loop. this stops us from needing special case
- // code to handle the left over loop after all the other crossing
- // paths are considered.
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop_temp_path;
- base_loop_temp_path.reserve(2);
- base_loop_temp_path.push_back(base_loop.front());
- base_loop_temp_path.push_back(base_loop.back());
-
- cross.push_back(crossing_data(&base_loop_temp_path, 0, base_loop.size() - 1));
-#if defined(CARVE_DEBUG)
- std::cerr << "### crossing edge count (with sentinel): " << cross.size() << std::endl;
-#endif
-
- // Step 4:
- // sort paths by increasing beginning point and decreasing ending point.
- std::sort(cross.begin(), cross.end());
- std::sort(noncross.begin(), noncross.end());
-
- // Step 5:
- // divide up the base loop based upon crossing paths.
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > divided_base_loop;
- divided_base_loop.reserve(cross.size());
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> out;
-
- for (size_t i = 0; i < cross.size(); ++i) {
- size_t j;
- for (j = i + 1;
- j < cross.size() &&
- cross[i].edge_idx[0] == cross[j].edge_idx[0] &&
- cross[i].edge_idx[1] == cross[j].edge_idx[1];
- ++j) {}
- if (j - i >= 2) {
- // when there are multiple paths that begin and end at the
- // same point, they need to be ordered so that the constructed
- // loops have the right orientation. this means that the loop
- // made by taking path(i+1) forward, then path(i) backward
- // needs to have negative area. this combined area is equal to
- // the area of path(i+1) minus the area of path(i). in turn
- // this means that the loop made by path path(i+1) alone has
- // to have smaller signed area than loop made by path(i).
- // thus, we sort paths in order of decreasing area.
-
- std::vector<std::pair<double, std::vector<carve::mesh::MeshSet<3>::vertex_t *> *> > order;
- order.reserve(j - i);
- for (size_t k = i; k < j; ++k) {
- double area = carve::geom2d::signedArea(cross[k].path->begin(),
- cross[k].path->end(),
- carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project));
-#if defined(CARVE_DEBUG)
- std::cerr << "### k=" << k << " area=" << area << std::endl;
-#endif
- order.push_back(std::make_pair(-area, cross[k].path));
- }
- std::sort(order.begin(), order.end());
- for (size_t k = i; k < j; ++k) {
- cross[k].path = order[k-i].second;
-#if defined(CARVE_DEBUG)
- std::cerr << "### post-sort k=" << k << " cross[k].path->size()=" << cross[k].path->size() << std::endl;
-#endif
- }
- }
- }
-
- // Step 6:
- for (size_t i = 0; i < cross.size(); ++i) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### i=" << i << " working on edge: " << cross[i].edge_idx[0] << " - " << cross[i].edge_idx[1] << std::endl;
-#endif
- size_t e1_0 = cross[i].edge_idx[0];
- size_t e1_1 = cross[i].edge_idx[1];
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p1 = *cross[i].path;
-#if defined(CARVE_DEBUG)
- std::cerr << "### path size = " << p1.size() << std::endl;
-#endif
-
- out.clear();
-
- if (i < cross.size() - 1 &&
- cross[i+1].edge_idx[1] <= cross[i].edge_idx[1]) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### complex case" << std::endl;
-#endif
- // complex case. crossing path with other crossing paths embedded within.
- size_t pos = e1_0;
-
- size_t skip = i+1;
-
- while (pos != e1_1) {
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p2 = *cross[skip].path;
- size_t e2_0 = cross[skip].edge_idx[0];
- size_t e2_1 = cross[skip].edge_idx[1];
-
- // copy up to the beginning of the next path.
- std::copy(base_loop.begin() + pos, base_loop.begin() + e2_0, std::back_inserter(out));
-
- CARVE_ASSERT(base_loop[e2_0] == p2[0]);
- // copy the next path in the right direction.
- std::copy(p2.begin(), p2.end() - 1, std::back_inserter(out));
-
- // move to the position of the end of the path.
- pos = e2_1;
-
- // advance to the next hit path.
- do {
- ++skip;
- } while(skip != cross.size() && cross[skip].edge_idx[0] < e2_1);
-
- if (skip == cross.size()) break;
-
- // if the next hit path is past the start point of the current path, we're done.
- if (cross[skip].edge_idx[0] >= e1_1) break;
- }
-
- // copy up to the end of the path.
- if (pos < e1_1) {
- std::copy(base_loop.begin() + pos, base_loop.begin() + e1_1, std::back_inserter(out));
- }
-
- CARVE_ASSERT(base_loop[e1_1] == p1.back());
- std::copy(p1.rbegin(), p1.rend() - 1, std::back_inserter(out));
- } else {
- size_t loop_size = (e1_1 - e1_0) + (p1.size() - 1);
- out.reserve(loop_size);
-
- std::copy(base_loop.begin() + e1_0, base_loop.begin() + e1_1, std::back_inserter(out));
- std::copy(p1.rbegin(), p1.rend() - 1, std::back_inserter(out));
-
- CARVE_ASSERT(out.size() == loop_size);
- }
- divided_base_loop.push_back(out);
-
-#if defined(CARVE_DEBUG)
- {
- std::vector<carve::geom2d::P2> projected;
- projected.reserve(out.size());
- for (size_t n = 0; n < out.size(); ++n) {
- projected.push_back(face->project(out[n]->v));
- }
-
- double A = carve::geom2d::signedArea(projected);
- std::cerr << "### out area=" << A << std::endl;
- CARVE_ASSERT(A <= 0);
- }
-#endif
- }
-
- if (!noncross.size()) {
- // If there are no non-crossing paths then we're done.
- populateListFromVector(divided_base_loop, face_loops_out);
- return true;
- }
-
- // for each divided base loop, work out which noncrossing paths and
- // loops are part of it. use the old algorithm to combine these into
- // the divided base loop. if none, the divided base loop is just
- // output.
- std::vector<std::vector<carve::geom2d::P2> > proj;
- std::vector<carve::geom::aabb<2> > proj_aabb;
- proj.resize(divided_base_loop.size());
- proj_aabb.resize(divided_base_loop.size());
-
- // calculate an aabb for each divided base loop, to avoid expensive
- // point-in-poly tests.
- for (size_t i = 0; i < divided_base_loop.size(); ++i) {
- proj[i].reserve(divided_base_loop[i].size());
- for (size_t j = 0; j < divided_base_loop[i].size(); ++j) {
- proj[i].push_back(face->project(divided_base_loop[i][j]->v));
- }
- proj_aabb[i].fit(proj[i].begin(), proj[i].end());
- }
-
- for (size_t i = 0; i < divided_base_loop.size(); ++i) {
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> *> inc;
- carve::geom2d::P2 test;
-
- // for each noncrossing path, choose an endpoint that isn't on the
- // base loop as a test point.
- for (size_t j = 0; j < noncross.size(); ++j) {
- if (noncross[j].edge_idx[0] < N) {
- if (noncross[j].path->front() == base_loop[noncross[j].edge_idx[0]]) {
- // noncrossing paths may be loops that run from the edge, back to the same vertex.
- if (noncross[j].path->front() == noncross[j].path->back()) {
- CARVE_ASSERT(noncross[j].path->size() > 2);
- test = face->project((*noncross[j].path)[1]->v);
- } else {
- test = face->project(noncross[j].path->back()->v);
- }
- } else {
- test = face->project(noncross[j].path->front()->v);
- }
- } else {
- test = face->project(noncross[j].path->front()->v);
- }
-
- if (proj_aabb[i].intersects(test) &&
- carve::geom2d::pointInPoly(proj[i], test).iclass != carve::POINT_OUT) {
- inc.push_back(noncross[j].path);
- }
- }
-
-#if defined(CARVE_DEBUG)
- std::cerr << "### divided base loop:" << i << " inc.size()=" << inc.size() << std::endl;
- std::cerr << "### inc = [";
- for (size_t j = 0; j < inc.size(); ++j) {
- std::cerr << " " << inc[j];
- }
- std::cerr << " ]" << std::endl;
-#endif
-
- if (inc.size()) {
- carve::csg::V2Set face_edges;
-
- for (size_t j = 0; j < divided_base_loop[i].size() - 1; ++j) {
- face_edges.insert(std::make_pair(divided_base_loop[i][j],
- divided_base_loop[i][j+1]));
- }
-
- face_edges.insert(std::make_pair(divided_base_loop[i].back(),
- divided_base_loop[i].front()));
-
- for (size_t j = 0; j < inc.size(); ++j) {
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> &path = *inc[j];
- for (size_t k = 0; k < path.size() - 1; ++k) {
- face_edges.insert(std::make_pair(path[k], path[k+1]));
- face_edges.insert(std::make_pair(path[k+1], path[k]));
- }
- }
-
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops;
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops;
-
- splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections);
-
- if (hole_loops.size()) {
- mergeFacesAndHoles(face, face_loops, hole_loops, hooks);
- }
- std::copy(face_loops.begin(), face_loops.end(), std::back_inserter(face_loops_out));
- } else {
- face_loops_out.push_back(divided_base_loop[i]);
- }
- }
- return true;
- }
-
-
-
- void composeEdgesIntoPaths(const carve::csg::V2Set &edges,
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &extra_endpoints,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &paths,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &cuts,
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &loops) {
- using namespace carve::csg;
-
- detail::VVSMap vertex_graph;
- detail::VSet endpoints;
- detail::VSet cut_endpoints;
-
- typedef std::vector<carve::mesh::MeshSet<3>::vertex_t *> vvec_t;
- vvec_t path;
-
- std::list<vvec_t> path_list, cut_list, loop_list;
-
- // build graph from edges.
- for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### edge: " << (*i).first << " - " << (*i).second << std::endl;
-#endif
- vertex_graph[(*i).first].insert((*i).second);
- vertex_graph[(*i).second].insert((*i).first);
- }
-
- // find the endpoints in the graph.
- // every vertex with number of incident edges != 2 is an endpoint.
- for (detail::VVSMap::const_iterator i = vertex_graph.begin(); i != vertex_graph.end(); ++i) {
- if ((*i).second.size() != 2) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### endpoint: " << (*i).first << std::endl;
-#endif
- endpoints.insert((*i).first);
- if ((*i).second.size() == 1) {
- cut_endpoints.insert((*i).first);
- }
- }
- }
-
- // every vertex on the perimeter of the face is also an endpoint.
- for (size_t i = 0; i < extra_endpoints.size(); ++i) {
- if (vertex_graph.find(extra_endpoints[i]) != vertex_graph.end()) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### extra endpoint: " << extra_endpoints[i] << std::endl;
-#endif
- endpoints.insert(extra_endpoints[i]);
- cut_endpoints.erase(extra_endpoints[i]);
- }
- }
-
- while (endpoints.size()) {
- carve::mesh::MeshSet<3>::vertex_t *v = *endpoints.begin();
- detail::VVSMap::iterator p = vertex_graph.find(v);
- if (p == vertex_graph.end()) {
- endpoints.erase(endpoints.begin());
- continue;
- }
-
- path.clear();
- path.push_back(v);
-
- for (;;) {
- CARVE_ASSERT(p != vertex_graph.end());
-
- // pick a connected vertex to move to.
- if ((*p).second.size() == 0) break;
-
- carve::mesh::MeshSet<3>::vertex_t *n = *((*p).second.begin());
- detail::VVSMap::iterator q = vertex_graph.find(n);
-
- // remove the link.
- (*p).second.erase(n);
- (*q).second.erase(v);
-
- // move on.
- v = n;
- path.push_back(v);
-
- if ((*p).second.size() == 0) vertex_graph.erase(p);
- if ((*q).second.size() == 0) {
- vertex_graph.erase(q);
- q = vertex_graph.end();
- }
-
- p = q;
-
- if (v == path[0] || p == vertex_graph.end() || endpoints.find(v) != endpoints.end()) break;
- }
- CARVE_ASSERT(endpoints.find(path.back()) != endpoints.end());
-
- bool is_cut =
- cut_endpoints.find(path.front()) != cut_endpoints.end() &&
- cut_endpoints.find(path.back()) != cut_endpoints.end();
-
- if (is_cut) {
- cut_list.push_back(vvec_t()); path.swap(cut_list.back());
- } else {
- path_list.push_back(vvec_t()); path.swap(path_list.back());
- }
- }
-
- populateVectorFromList(path_list, paths);
- populateVectorFromList(cut_list, cuts);
-
- // now only loops should remain in the graph.
- while (vertex_graph.size()) {
- detail::VVSMap::iterator p = vertex_graph.begin();
- carve::mesh::MeshSet<3>::vertex_t *v = (*p).first;
- CARVE_ASSERT((*p).second.size() == 2);
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> path;
- path.clear();
- path.push_back(v);
-
- for (;;) {
- CARVE_ASSERT(p != vertex_graph.end());
- // pick a connected vertex to move to.
-
- carve::mesh::MeshSet<3>::vertex_t *n = *((*p).second.begin());
- detail::VVSMap::iterator q = vertex_graph.find(n);
-
- // remove the link.
- (*p).second.erase(n);
- (*q).second.erase(v);
-
- // move on.
- v = n;
- path.push_back(v);
-
- if ((*p).second.size() == 0) vertex_graph.erase(p);
- if ((*q).second.size() == 0) vertex_graph.erase(q);
-
- p = q;
-
- if (v == path[0]) break;
- }
-
- loop_list.push_back(vvec_t()); path.swap(loop_list.back());
- }
-
- populateVectorFromList(loop_list, loops);
- }
-
-
-
- template<typename T>
- std::string ptrstr(const T *ptr) {
- std::ostringstream s;
- s << ptr;
- return s.str().substr(1);
- }
-
-#if 0
- void dumpAsGraph(carve::mesh::MeshSet<3>::face_t *face,
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop,
- const carve::csg::V2Set &face_edges,
- const carve::csg::V2Set &split_edges) {
- std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2> proj;
-
- for (size_t i = 0; i < base_loop.size(); ++i) {
- proj[base_loop[i]] = face->project(base_loop[i]->v);
- }
- for (carve::csg::V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) {
- proj[(*i).first] = face->project((*i).first->v);
- proj[(*i).second] = face->project((*i).second->v);
- }
-
- {
- carve::geom2d::P2 lo, hi;
- std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2>::iterator i;
- i = proj.begin();
- lo = hi = (*i).second;
- for (; i != proj.end(); ++i) {
- lo.x = std::min(lo.x, (*i).second.x); lo.y = std::min(lo.y, (*i).second.y);
- hi.x = std::max(hi.x, (*i).second.x); hi.y = std::max(hi.y, (*i).second.y);
- }
- for (i = proj.begin(); i != proj.end(); ++i) {
- (*i).second.x = ((*i).second.x - lo.x) / (hi.x - lo.x) * 10;
- (*i).second.y = ((*i).second.y - lo.y) / (hi.y - lo.y) * 10;
- }
- }
-
- std::cerr << "graph G {\nnode [shape=circle,style=filled,fixedsize=true,width=\".1\",height=\".1\"];\nedge [len=4]\n";
- for (std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2>::iterator i = proj.begin(); i != proj.end(); ++i) {
- std::cerr << " " << ptrstr((*i).first) << " [pos=\"" << (*i).second.x << "," << (*i).second.y << "!\"];\n";
- }
- for (carve::csg::V2Set::const_iterator i = face_edges.begin(); i != face_edges.end(); ++i) {
- std::cerr << " " << ptrstr((*i).first) << " -- " << ptrstr((*i).second) << ";\n";
- }
- for (carve::csg::V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) {
- std::cerr << " " << ptrstr((*i).first) << " -- " << ptrstr((*i).second) << " [color=\"blue\"];\n";
- }
- std::cerr << "};\n";
- }
-#endif
-
- void generateOneFaceLoop(carve::mesh::MeshSet<3>::face_t *face,
- const carve::csg::detail::Data &data,
- const carve::csg::VertexIntersections &vertex_intersections,
- carve::csg::CSG::Hooks &hooks,
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops) {
- using namespace carve::csg;
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop;
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops;
-
- /*bool face_edge_intersected = */assembleBaseLoop(face, data, base_loop, hooks);
-
- detail::FV2SMap::const_iterator fse_iter = data.face_split_edges.find(face);
-
- face_loops.clear();
-
- if (fse_iter == data.face_split_edges.end()) {
- // simple case: input face is output face (possibly with the
- // addition of vertices at intersections).
- face_loops.push_back(base_loop);
- return;
- }
-
- // complex case: input face is split into multiple output faces.
- V2Set face_edges;
-
- for (size_t j = 0, je = base_loop.size() - 1; j < je; ++j) {
- face_edges.insert(std::make_pair(base_loop[j], base_loop[j + 1]));
- }
- face_edges.insert(std::make_pair(base_loop.back(), base_loop[0]));
-
- // collect the split edges (as long as they're not on the perimeter)
- const detail::FV2SMap::mapped_type &fse = ((*fse_iter).second);
-
- // split_edges contains all of the edges created by intersections
- // that aren't part of the perimeter of the face.
- V2Set split_edges;
-
- for (detail::FV2SMap::mapped_type::const_iterator
- j = fse.begin(), je = fse.end();
- j != je;
- ++j) {
- carve::mesh::MeshSet<3>::vertex_t *v1 = ((*j).first), *v2 = ((*j).second);
-
- if (face_edges.find(std::make_pair(v1, v2)) == face_edges.end() &&
- face_edges.find(std::make_pair(v2, v1)) == face_edges.end()) {
- // If the edge isn't part of the face perimeter, add it to
- // split_edges.
- split_edges.insert(ordered_edge(v1, v2));
- }
- }
-
- // face is unsplit.
- if (!split_edges.size()) {
- face_loops.push_back(base_loop);
- return;
- }
-
-#if defined(CARVE_DEBUG)
- dumpAsGraph(face, base_loop, face_edges, split_edges);
-#endif
-
-#if 0
- // old face splitting method.
- for (V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) {
- face_edges.insert(std::make_pair((*i).first, (*i).second));
- face_edges.insert(std::make_pair((*i).second, (*i).first));
- }
- splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections);
-
- if (hole_loops.size()) {
- mergeFacesAndHoles(face, face_loops, hole_loops, hooks);
- }
- return;
-#endif
-
-#if defined(CARVE_DEBUG)
- std::cerr << "### split_edges.size(): " << split_edges.size() << std::endl;
-#endif
- if (split_edges.size() == 1) {
- // handle the common case of a face that's split by a single edge.
- carve::mesh::MeshSet<3>::vertex_t *v1 = split_edges.begin()->first;
- carve::mesh::MeshSet<3>::vertex_t *v2 = split_edges.begin()->second;
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *>::iterator vi1 = std::find(base_loop.begin(), base_loop.end(), v1);
- std::vector<carve::mesh::MeshSet<3>::vertex_t *>::iterator vi2 = std::find(base_loop.begin(), base_loop.end(), v2);
-
- if (vi1 != base_loop.end() && vi2 != base_loop.end()) {
- // this is an inserted edge that connects two points on the base loop. nice and simple.
- if (vi2 < vi1) std::swap(vi1, vi2);
-
- size_t loop1_size = vi2 - vi1 + 1;
- size_t loop2_size = base_loop.size() + 2 - loop1_size;
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> l1;
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> l2;
-
- l1.reserve(loop1_size);
- l2.reserve(loop2_size);
-
- std::copy(vi1, vi2+1, std::back_inserter(l1));
- std::copy(vi2, base_loop.end(), std::back_inserter(l2));
- std::copy(base_loop.begin(), vi1+1, std::back_inserter(l2));
-
- CARVE_ASSERT(l1.size() == loop1_size);
- CARVE_ASSERT(l2.size() == loop2_size);
-
- face_loops.push_back(l1);
- face_loops.push_back(l2);
-
- return;
- }
-
- // Consider handling cases where one end of the edge touches the
- // perimeter, and where neither end does.
- }
-
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > paths;
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > cuts;
- std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > loops;
-
- // Take the split edges and compose them into a set of paths and
- // loops. Loops are edge paths that do not touch the boundary, or
- // any other path or loop - they are holes cut out of the centre
- // of the face. Paths are made up of all the other edge segments,
- // and start and end at the face perimeter, or where they meet
- // another path (sometimes both cases will be true).
- composeEdgesIntoPaths(split_edges, base_loop, paths, cuts, loops);
-
-#if defined(CARVE_DEBUG)
- std::cerr << "### paths.size(): " << paths.size() << std::endl;
- std::cerr << "### cuts.size(): " << cuts.size() << std::endl;
- std::cerr << "### loops.size(): " << loops.size() << std::endl;
-#endif
-
- if (!paths.size()) {
- // No complex paths.
- face_loops.push_back(base_loop);
- } else {
- if (processCrossingEdges(face, vertex_intersections, hooks, base_loop, paths, face_loops)) {
- // Worked.
- } else {
- // complex case - fall back to old edge tracing code.
-#if defined(CARVE_DEBUG)
- std::cerr << "### processCrossingEdges failed. Falling back to edge tracing code" << std::endl;
-#endif
- for (size_t i = 0; i < paths.size(); ++i) {
- for (size_t j = 0; j < paths[i].size() - 1; ++j) {
- face_edges.insert(std::make_pair(paths[i][j], paths[i][j+1]));
- face_edges.insert(std::make_pair(paths[i][j+1], paths[i][j]));
- }
- }
- splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections);
- }
- }
-
- // Now merge cuts and loops into face loops.
-
- // every cut creates a hole.
- for (size_t i = 0; i < cuts.size(); ++i) {
- hole_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- hole_loops.back().reserve(2 * cuts[i].size() - 2);
- std::copy(cuts[i].begin(), cuts[i].end(), std::back_inserter(hole_loops.back()));
- if (cuts[i].size() > 2) {
- std::copy(cuts[i].rbegin() + 1, cuts[i].rend() - 1, std::back_inserter(hole_loops.back()));
- }
- }
-
- // every loop creates a hole and a corresponding face.
- for (size_t i = 0; i < loops.size(); ++i) {
- hole_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- hole_loops.back().reserve(loops[i].size()-1);
- std::copy(loops[i].begin(), loops[i].end()-1, std::back_inserter(hole_loops.back()));
-
- face_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>());
- face_loops.back().reserve(loops[i].size()-1);
- std::copy(loops[i].rbegin()+1, loops[i].rend(), std::back_inserter(face_loops.back()));
-
- std::vector<carve::geom2d::P2> projected;
- projected.reserve(face_loops.back().size());
- for (size_t i = 0; i < face_loops.back().size(); ++i) {
- projected.push_back(face->project(face_loops.back()[i]->v));
- }
-
- if (carve::geom2d::signedArea(projected) > 0.0) {
- std::swap(face_loops.back(), hole_loops.back());
- }
- }
-
- // if there are holes, then they need to be merged with faces.
- if (hole_loops.size()) {
- mergeFacesAndHoles(face, face_loops, hole_loops, hooks);
- }
- }
-}
-
-
-
-/**
- * \brief Build a set of face loops for all (split) faces of a Polyhedron.
- *
- * @param[in] poly The polyhedron to process
- * @param[in] data Internal intersection data
- * @param[out] face_loops_out The resulting face loops
- *
- * @return The number of edges generated.
- */
-size_t carve::csg::CSG::generateFaceLoops(carve::mesh::MeshSet<3> *poly,
- const detail::Data &data,
- FaceLoopList &face_loops_out) {
- static carve::TimingName FUNC_NAME("CSG::generateFaceLoops()");
- carve::TimingBlock block(FUNC_NAME);
- size_t generated_edges = 0;
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop;
- std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops;
-
- for (carve::mesh::MeshSet<3>::face_iter i = poly->faceBegin(); i != poly->faceEnd(); ++i) {
- carve::mesh::MeshSet<3>::face_t *face = (*i);
-
-#if defined(CARVE_DEBUG)
- double in_area = 0.0, out_area = 0.0;
-
- {
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop;
- assembleBaseLoop(face, data, base_loop);
-
- {
- std::vector<carve::geom2d::P2> projected;
- projected.reserve(base_loop.size());
- for (size_t n = 0; n < base_loop.size(); ++n) {
- projected.push_back(face->project(base_loop[n]->v));
- }
-
- in_area = carve::geom2d::signedArea(projected);
- std::cerr << "### in_area=" << in_area << std::endl;
- }
- }
-#endif
-
- generateOneFaceLoop(face, data, vertex_intersections, hooks, face_loops);
-
-#if defined(CARVE_DEBUG)
- {
- V2Set face_edges;
-
- std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop;
- assembleBaseLoop(face, data, base_loop);
-
- for (size_t j = 0, je = base_loop.size() - 1; j < je; ++j) {
- face_edges.insert(std::make_pair(base_loop[j+1], base_loop[j]));
- }
- face_edges.insert(std::make_pair(base_loop[0], base_loop.back()));
- for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator fli = face_loops.begin(); fli != face_loops.end(); ++ fli) {
-
- {
- std::vector<carve::geom2d::P2> projected;
- projected.reserve((*fli).size());
- for (size_t n = 0; n < (*fli).size(); ++n) {
- projected.push_back(face->project((*fli)[n]->v));
- }
-
- double area = carve::geom2d::signedArea(projected);
- std::cerr << "### loop_area[" << std::distance((std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator)face_loops.begin(), fli) << "]=" << area << std::endl;
- out_area += area;
- }
-
- const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &fl = *fli;
- for (size_t j = 0, je = fl.size() - 1; j < je; ++j) {
- face_edges.insert(std::make_pair(fl[j], fl[j+1]));
- }
- face_edges.insert(std::make_pair(fl.back(), fl[0]));
- }
- for (V2Set::const_iterator j = face_edges.begin(); j != face_edges.end(); ++j) {
- if (face_edges.find(std::make_pair((*j).second, (*j).first)) == face_edges.end()) {
- std::cerr << "### error: unmatched edge [" << (*j).first << "-" << (*j).second << "]" << std::endl;
- }
- }
- std::cerr << "### out_area=" << out_area << std::endl;
- if (out_area != in_area) {
- std::cerr << "### error: area does not match. delta = " << (out_area - in_area) << std::endl;
- // CARVE_ASSERT(fabs(out_area - in_area) < 1e-5);
- }
- }
-#endif
-
- // now record all the resulting face loops.
-#if defined(CARVE_DEBUG)
- std::cerr << "### ======" << std::endl;
-#endif
- for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator
- f = face_loops.begin(), fe = face_loops.end();
- f != fe;
- ++f) {
-#if defined(CARVE_DEBUG)
- std::cerr << "### loop:";
- for (size_t i = 0; i < (*f).size(); ++i) {
- std::cerr << " " << (*f)[i];
- }
- std::cerr << std::endl;
-#endif
-
- face_loops_out.append(new FaceLoop(face, *f));
- generated_edges += (*f).size();
- }
-#if defined(CARVE_DEBUG)
- std::cerr << "### ======" << std::endl;
-#endif
- }
- return generated_edges;
-}