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>2012-01-16 20:46:00 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2012-01-16 20:46:00 +0400
commite81f2853c8785b0a84ecebf7c4db433af434c5c8 (patch)
treef3145e592452e5d2cb2d162ec53813f0e552a2fe /extern/carve/lib
parentc150d0084f0e1cf86f063d993b31d02fc3e9c71f (diff)
Carve booleans library integration
================================== Merging Carve library integration project into the trunk. This commit switches Boolean modifier to another library which handles mesh boolean operations in much stable and faster way, resolving old well-known limitations of intern boolop library. Carve is integrating as alternative interface for boolop library and which makes it totally transparent for blender sources to switch between old-fashioned boolop and new Carve backends. Detailed changes in this commit: - Integrated needed subset of Carve library sources into extern/ Added script for re-bundling it (currently works only if repo was cloned by git-svn). - Added BOP_CarveInterface for boolop library which can be used by Boolean modifier. - Carve backend is enabled by default, can be disabled by WITH_BF_CARVE SCons option and WITH_CARVE CMake option. - If Boost library is found in build environment it'll be used for unordered collections. If Boost isn't found, it'll fallback to TR1 implementation for GCC compilers. Boost is obligatory if MSVC is used. Tested on Linux 64bit and Windows 7 64bit. NOTE: behavior of flat objects was changed. E.g. Plane-Sphere now gives plane with circle hole, not plane with semisphere. Don't think it's really issue because it's not actually defined behavior in such situations and both of ways might be useful. Since it's only known "regression" think it's OK to deal with it. Details are there http://wiki.blender.org/index.php/User:Nazg-gul/CarveBooleans Special thanks to: - Ken Hughes: author of original carve integration patch. - Campbell Barton: help in project development, review tests. - Tobias Sargeant: author of Carve library, help in resolving some merge stoppers, bug fixing.
Diffstat (limited to 'extern/carve/lib')
-rw-r--r--extern/carve/lib/aabb.cpp29
-rw-r--r--extern/carve/lib/carve.cpp29
-rw-r--r--extern/carve/lib/convex_hull.cpp100
-rw-r--r--extern/carve/lib/csg.cpp93
-rw-r--r--extern/carve/lib/csg_collector.cpp371
-rw-r--r--extern/carve/lib/csg_collector.hpp24
-rw-r--r--extern/carve/lib/csg_data.hpp52
-rw-r--r--extern/carve/lib/csg_detail.hpp71
-rw-r--r--extern/carve/lib/edge.cpp23
-rw-r--r--extern/carve/lib/face.cpp278
-rw-r--r--extern/carve/lib/geom2d.cpp260
-rw-r--r--extern/carve/lib/geom3d.cpp164
-rw-r--r--extern/carve/lib/intersect.cpp1668
-rw-r--r--extern/carve/lib/intersect_classify_common.hpp46
-rw-r--r--extern/carve/lib/intersect_classify_common_impl.hpp362
-rw-r--r--extern/carve/lib/intersect_classify_edge.cpp820
-rw-r--r--extern/carve/lib/intersect_classify_group.cpp220
-rw-r--r--extern/carve/lib/intersect_common.hpp83
-rw-r--r--extern/carve/lib/intersect_debug.cpp65
-rw-r--r--extern/carve/lib/intersect_debug.hpp29
-rw-r--r--extern/carve/lib/intersect_face_division.cpp1709
-rw-r--r--extern/carve/lib/intersect_group.cpp232
-rw-r--r--extern/carve/lib/intersect_half_classify_group.cpp199
-rw-r--r--extern/carve/lib/intersection.cpp92
-rw-r--r--extern/carve/lib/math.cpp347
-rw-r--r--extern/carve/lib/mesh.cpp1203
-rw-r--r--extern/carve/lib/octree.cpp399
-rw-r--r--extern/carve/lib/pointset.cpp59
-rw-r--r--extern/carve/lib/polyhedron.cpp1103
-rw-r--r--extern/carve/lib/polyline.cpp67
-rw-r--r--extern/carve/lib/tag.cpp24
-rw-r--r--extern/carve/lib/timing.cpp436
-rw-r--r--extern/carve/lib/triangulator.cpp1211
33 files changed, 11868 insertions, 0 deletions
diff --git a/extern/carve/lib/aabb.cpp b/extern/carve/lib/aabb.cpp
new file mode 100644
index 00000000000..188929a8cfa
--- /dev/null
+++ b/extern/carve/lib/aabb.cpp
@@ -0,0 +1,29 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/aabb.hpp>
+#include <carve/geom3d.hpp>
+
+namespace carve {
+ namespace geom3d {
+ }
+}
+
diff --git a/extern/carve/lib/carve.cpp b/extern/carve/lib/carve.cpp
new file mode 100644
index 00000000000..9af2d0408fb
--- /dev/null
+++ b/extern/carve/lib/carve.cpp
@@ -0,0 +1,29 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/carve.hpp>
+
+#define DEF_EPSILON 1.4901161193847656e-08
+
+namespace carve {
+ double EPSILON = DEF_EPSILON;
+ double EPSILON2 = DEF_EPSILON * DEF_EPSILON;
+}
diff --git a/extern/carve/lib/convex_hull.cpp b/extern/carve/lib/convex_hull.cpp
new file mode 100644
index 00000000000..616d8cbe561
--- /dev/null
+++ b/extern/carve/lib/convex_hull.cpp
@@ -0,0 +1,100 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/convex_hull.hpp>
+
+#include <algorithm>
+
+namespace {
+
+ bool grahamScan(const std::vector<carve::geom2d::P2> &points,
+ int vpp, int vp,
+ const std::vector<int> &ordered,
+ int start,
+ std::vector<int> &result, int _i = 0) {
+ carve::geom2d::P2 v1 = points[vp] - points[vpp];
+ if (start == (int)ordered.size()) return true;
+
+ for (int i = start; i < (int)ordered.size(); ++i) {
+ int v = ordered[i];
+ carve::geom2d::P2 v2 = points[v] - points[vp];
+
+ double cp = v1.x * v2.y - v2.x * v1.y;
+ if (cp < 0) return false;
+
+ int j = i + 1;
+ while (j < (int)ordered.size() && points[ordered[j]] == points[v]) j++;
+
+ result.push_back(v);
+ if (grahamScan(points, vp, v, ordered, j, result, _i + 1)) return true;
+ result.pop_back();
+ }
+
+ return false;
+ }
+
+}
+
+namespace carve {
+ namespace geom {
+
+ std::vector<int> convexHull(const std::vector<carve::geom2d::P2> &points) {
+ double max_x = points[0].x;
+ unsigned max_v = 0;
+
+ for (unsigned i = 1; i < points.size(); ++i) {
+ if (points[i].x > max_x) {
+ max_x = points[i].x;
+ max_v = i;
+ }
+ }
+
+ std::vector<std::pair<double, double> > angle_dist;
+ std::vector<int> ordered;
+ angle_dist.reserve(points.size());
+ ordered.reserve(points.size() - 1);
+ for (unsigned i = 0; i < points.size(); ++i) {
+ if (i == max_v) continue;
+ angle_dist[i] = std::make_pair(carve::math::ANG(carve::geom2d::atan2(points[i] - points[max_v])), distance2(points[i], points[max_v]));
+ ordered.push_back(i);
+ }
+
+ std::sort(ordered.begin(),
+ ordered.end(),
+ make_index_sort(angle_dist.begin()));
+
+ std::vector<int> result;
+ result.push_back(max_v);
+ result.push_back(ordered[0]);
+
+ if (!grahamScan(points, max_v, ordered[0], ordered, 1, result)) {
+ result.clear();
+ throw carve::exception("convex hull failed!");
+ }
+
+ return result;
+ }
+
+ }
+}
+
+
diff --git a/extern/carve/lib/csg.cpp b/extern/carve/lib/csg.cpp
new file mode 100644
index 00000000000..3d3dfb8bf75
--- /dev/null
+++ b/extern/carve/lib/csg.cpp
@@ -0,0 +1,93 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include "csg_detail.hpp"
+
+
+const char *carve::csg::ENUM(carve::csg::FaceClass f) {
+ if (f == FACE_ON_ORIENT_OUT) return "FACE_ON_ORIENT_OUT";
+ if (f == FACE_OUT) return "FACE_OUT";
+ if (f == FACE_IN) return "FACE_IN";
+ if (f == FACE_ON_ORIENT_IN) return "FACE_ON_ORIENT_IN";
+ return "???";
+}
+
+
+
+const char *carve::csg::ENUM(carve::PointClass p) {
+ if (p == POINT_UNK) return "POINT_UNK";
+ if (p == POINT_OUT) return "POINT_OUT";
+ if (p == POINT_ON) return "POINT_ON";
+ if (p == POINT_IN) return "POINT_IN";
+ if (p == POINT_VERTEX) return "POINT_VERTEX";
+ if (p == POINT_EDGE) return "POINT_EDGE";
+ return "???";
+}
+
+
+
+void carve::csg::detail::LoopEdges::addFaceLoop(FaceLoop *fl) {
+ carve::mesh::MeshSet<3>::vertex_t *v1, *v2;
+ v1 = fl->vertices[fl->vertices.size() - 1];
+ for (unsigned j = 0; j < fl->vertices.size(); ++j) {
+ v2 = fl->vertices[j];
+ (*this)[std::make_pair(v1, v2)].push_back(fl);
+ v1 = v2;
+ }
+}
+
+
+
+void carve::csg::detail::LoopEdges::sortFaceLoopLists() {
+ for (super::iterator i = begin(), e = end(); i != e; ++i) {
+ (*i).second.sort();
+ }
+}
+
+
+
+void carve::csg::detail::LoopEdges::removeFaceLoop(FaceLoop *fl) {
+ carve::mesh::MeshSet<3>::vertex_t *v1, *v2;
+ v1 = fl->vertices[fl->vertices.size() - 1];
+ for (unsigned j = 0; j < fl->vertices.size(); ++j) {
+ v2 = fl->vertices[j];
+ iterator l(find(std::make_pair(v1, v2)));
+ if (l != end()) {
+ (*l).second.remove(fl);
+ if (!(*l).second.size()) {
+ erase(l);
+ }
+ }
+ v1 = v2;
+ }
+}
+
+
+
+carve::csg::FaceClass carve::csg::FaceLoopGroup::classificationAgainst(const carve::mesh::MeshSet<3>::mesh_t *mesh) const {
+ for (std::list<ClassificationInfo>::const_iterator i = classification.begin(); i != classification.end(); ++i) {
+ if ((*i).intersected_mesh == mesh) {
+ return (*i).classification;
+ }
+ }
+ return FACE_UNCLASSIFIED;
+}
diff --git a/extern/carve/lib/csg_collector.cpp b/extern/carve/lib/csg_collector.cpp
new file mode 100644
index 00000000000..6e86b128b51
--- /dev/null
+++ b/extern/carve/lib/csg_collector.cpp
@@ -0,0 +1,371 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <iostream>
+#include "intersect_debug.hpp"
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii);
+#endif
+
+
+namespace carve {
+ namespace csg {
+ namespace {
+
+ class BaseCollector : public CSG::Collector {
+ BaseCollector();
+ BaseCollector(const BaseCollector &);
+ BaseCollector &operator=(const BaseCollector &);
+
+ protected:
+ struct face_data_t {
+ carve::mesh::MeshSet<3>::face_t *face;
+ const carve::mesh::MeshSet<3>::face_t *orig_face;
+ bool flipped;
+ face_data_t(carve::mesh::MeshSet<3>::face_t *_face,
+ const carve::mesh::MeshSet<3>::face_t *_orig_face,
+ bool _flipped) : face(_face), orig_face(_orig_face), flipped(_flipped) {
+ };
+ };
+
+ std::list<face_data_t> faces;
+
+ const carve::mesh::MeshSet<3> *src_a;
+ const carve::mesh::MeshSet<3> *src_b;
+
+ BaseCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : CSG::Collector(), src_a(_src_a), src_b(_src_b) {
+ }
+
+ virtual ~BaseCollector() {
+ }
+
+ void FWD(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector /* normal */,
+ bool /* poly_a */,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ std::vector<carve::mesh::MeshSet<3>::face_t *> new_faces;
+ new_faces.reserve(1);
+ new_faces.push_back(orig_face->create(vertices.begin(), vertices.end(), false));
+ hooks.processOutputFace(new_faces, orig_face, false);
+ for (size_t i = 0; i < new_faces.size(); ++i) {
+ faces.push_back(face_data_t(new_faces[i], orig_face, false));
+ }
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_PRINT_RESULT_FACES)
+ std::cerr << "+" << ENUM(face_class) << " ";
+ for (unsigned i = 0; i < vertices.size(); ++i) std::cerr << " " << vertices[i] << ":" << *vertices[i];
+ std::cerr << std::endl;
+#endif
+ }
+
+ void REV(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector /* normal */,
+ bool /* poly_a */,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ // normal = -normal;
+ std::vector<carve::mesh::MeshSet<3>::face_t *> new_faces;
+ new_faces.reserve(1);
+ new_faces.push_back(orig_face->create(vertices.begin(), vertices.end(), true));
+ hooks.processOutputFace(new_faces, orig_face, true);
+ for (size_t i = 0; i < new_faces.size(); ++i) {
+ faces.push_back(face_data_t(new_faces[i], orig_face, true));
+ }
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_PRINT_RESULT_FACES)
+ std::cerr << "-" << ENUM(face_class) << " ";
+ for (unsigned i = 0; i < vertices.size(); ++i) std::cerr << " " << vertices[i] << ":" << *vertices[i];
+ std::cerr << std::endl;
+#endif
+ }
+
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) =0;
+
+ virtual void collect(FaceLoopGroup *grp, CSG::Hooks &hooks) {
+ std::list<ClassificationInfo> &cinfo = (grp->classification);
+
+ if (cinfo.size() == 0) {
+ std::cerr << "WARNING! group " << grp << " has no classification info!" << std::endl;
+ return;
+ }
+
+ FaceClass fc = FACE_UNCLASSIFIED;
+
+ unsigned fc_closed_bits = 0;
+ unsigned fc_open_bits = 0;
+ unsigned fc_bits = 0;
+
+ for (std::list<ClassificationInfo>::const_iterator i = grp->classification.begin(), e = grp->classification.end(); i != e; ++i) {
+
+ if ((*i).intersected_mesh == NULL) {
+ // classifier only returns global info
+ fc_closed_bits = class_to_class_bit((*i).classification);
+ break;
+ }
+
+ if ((*i).classification == FACE_UNCLASSIFIED) continue;
+ if ((*i).intersectedMeshIsClosed()) {
+ fc_closed_bits |= class_to_class_bit((*i).classification);
+ } else {
+ fc_open_bits |= class_to_class_bit((*i).classification);
+ }
+ }
+
+ if (fc_closed_bits) {
+ fc_bits = fc_closed_bits;
+ } else {
+ fc_bits = fc_open_bits;
+ }
+
+ fc = class_bit_to_class(fc_bits);
+
+ // handle the complex cases where a group is classified differently with respect to two or more closed manifolds.
+ if (fc == FACE_UNCLASSIFIED) {
+ unsigned inout_bits = fc_bits & FACE_NOT_ON_BIT;
+ unsigned on_bits = fc_bits & FACE_ON_BIT;
+
+ // both in and out. indicates an invalid manifold embedding.
+ if (inout_bits == (FACE_IN_BIT | FACE_OUT_BIT)) goto out;
+
+ // on, both orientations. could be caused by two manifolds touching at a face.
+ if (on_bits == (FACE_ON_ORIENT_IN_BIT | FACE_ON_ORIENT_OUT_BIT)) goto out;
+
+ // in or out, but also on (with orientation). the on classification takes precedence.
+ fc = class_bit_to_class(on_bits);
+ }
+
+ out:
+
+ if (fc == FACE_UNCLASSIFIED) {
+ std::cerr << "group " << grp << " is unclassified!" << std::endl;
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ static int uc_count = 0;
+
+ std::vector<carve::mesh::MeshSet<3>::face_t *> faces;
+
+ for (FaceLoop *f = grp->face_loops.head; f; f = f->next) {
+ carve::mesh::MeshSet<3>::face_t *temp = f->orig_face->create(f->vertices.begin(), f->vertices.end(), false);
+ faces.push_back(temp);
+ }
+
+ carve::mesh::MeshSet<3> *p = new carve::mesh::MeshSet<3>(faces);
+
+ std::ostringstream filename;
+ filename << "classifier_fail_" << ++uc_count << ".ply";
+ std::string out(filename.str().c_str());
+ ::writePLY(out, p, false);
+
+ delete p;
+#endif
+
+ return;
+ }
+
+ bool is_poly_a = grp->src == src_a;
+
+ for (FaceLoop *f = grp->face_loops.head; f; f = f->next) {
+ collect(f->orig_face, f->vertices, f->orig_face->plane.N, is_poly_a, fc, hooks);
+ }
+ }
+
+ virtual carve::mesh::MeshSet<3> *done(CSG::Hooks &hooks) {
+ std::vector<carve::mesh::MeshSet<3>::face_t *> f;
+ f.reserve(faces.size());
+ for (std::list<face_data_t>::iterator i = faces.begin(); i != faces.end(); ++i) {
+ f.push_back((*i).face);
+ }
+
+ carve::mesh::MeshSet<3> *p = new carve::mesh::MeshSet<3>(f);
+
+ if (hooks.hasHook(carve::csg::CSG::Hooks::RESULT_FACE_HOOK)) {
+ for (std::list<face_data_t>::iterator i = faces.begin(); i != faces.end(); ++i) {
+ hooks.resultFace((*i).face, (*i).orig_face, (*i).flipped);
+ }
+ }
+
+ return p;
+ }
+ };
+
+
+
+ class AllCollector : public BaseCollector {
+ public:
+ AllCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~AllCollector() {
+ }
+ virtual void collect(FaceLoopGroup *grp, CSG::Hooks &hooks) {
+ for (FaceLoop *f = grp->face_loops.head; f; f = f->next) {
+ FWD(f->orig_face, f->vertices, f->orig_face->plane.N, f->orig_face->mesh->meshset == src_a, FACE_OUT, hooks);
+ }
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ };
+
+
+
+ class UnionCollector : public BaseCollector {
+ public:
+ UnionCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~UnionCollector() {
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ if (face_class == FACE_OUT || (poly_a && face_class == FACE_ON_ORIENT_OUT)) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ }
+ };
+
+
+
+ class IntersectionCollector : public BaseCollector {
+ public:
+ IntersectionCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~IntersectionCollector() {
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ if (face_class == FACE_IN || (poly_a && face_class == FACE_ON_ORIENT_OUT)) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ }
+ };
+
+
+
+ class SymmetricDifferenceCollector : public BaseCollector {
+ public:
+ SymmetricDifferenceCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~SymmetricDifferenceCollector() {
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ if (face_class == FACE_OUT) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ } else if (face_class == FACE_IN) {
+ REV(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ }
+ };
+
+
+
+ class AMinusBCollector : public BaseCollector {
+ public:
+ AMinusBCollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~AMinusBCollector() {
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ if ((face_class == FACE_OUT || face_class == FACE_ON_ORIENT_IN) && poly_a) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ } else if (face_class == FACE_IN && !poly_a) {
+ REV(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ }
+ };
+
+
+
+ class BMinusACollector : public BaseCollector {
+ public:
+ BMinusACollector(const carve::mesh::MeshSet<3> *_src_a,
+ const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) {
+ }
+ virtual ~BMinusACollector() {
+ }
+ virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices,
+ carve::geom3d::Vector normal,
+ bool poly_a,
+ FaceClass face_class,
+ CSG::Hooks &hooks) {
+ if ((face_class == FACE_OUT || face_class == FACE_ON_ORIENT_IN) && !poly_a) {
+ FWD(orig_face, vertices, normal, poly_a, face_class, hooks);
+ } else if (face_class == FACE_IN && poly_a) {
+ REV(orig_face, vertices, normal, poly_a, face_class, hooks);
+ }
+ }
+ };
+
+ }
+
+ CSG::Collector *makeCollector(CSG::OP op,
+ const carve::mesh::MeshSet<3> *poly_a,
+ const carve::mesh::MeshSet<3> *poly_b) {
+ switch (op) {
+ case CSG::UNION: return new UnionCollector(poly_a, poly_b);
+ case CSG::INTERSECTION: return new IntersectionCollector(poly_a, poly_b);
+ case CSG::A_MINUS_B: return new AMinusBCollector(poly_a, poly_b);
+ case CSG::B_MINUS_A: return new BMinusACollector(poly_a, poly_b);
+ case CSG::SYMMETRIC_DIFFERENCE: return new SymmetricDifferenceCollector(poly_a, poly_b);
+ case CSG::ALL: return new AllCollector(poly_a, poly_b);
+ }
+ return NULL;
+ }
+ }
+}
diff --git a/extern/carve/lib/csg_collector.hpp b/extern/carve/lib/csg_collector.hpp
new file mode 100644
index 00000000000..c68d3f3aa42
--- /dev/null
+++ b/extern/carve/lib/csg_collector.hpp
@@ -0,0 +1,24 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+namespace carve {
+ namespace csg {
+ CSG::Collector *makeCollector(CSG::OP op,
+ const carve::mesh::MeshSet<3> *poly_a,
+ const carve::mesh::MeshSet<3> *poly_b);
+ }
+}
diff --git a/extern/carve/lib/csg_data.hpp b/extern/carve/lib/csg_data.hpp
new file mode 100644
index 00000000000..085d05ce8d5
--- /dev/null
+++ b/extern/carve/lib/csg_data.hpp
@@ -0,0 +1,52 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+#pragma once
+
+#include <carve/csg.hpp>
+
+#include "csg_detail.hpp"
+
+struct carve::csg::detail::Data {
+// * @param[out] vmap A mapping from vertex pointer to intersection point.
+// * @param[out] emap A mapping from edge pointer to intersection points.
+// * @param[out] fmap A mapping from face pointer to intersection points.
+// * @param[out] fmap_rev A mapping from intersection points to face pointers.
+ // map from intersected vertex to intersection point.
+ VVMap vmap;
+
+ // map from intersected edge to intersection points.
+ EVSMap emap;
+
+ // map from intersected face to intersection points.
+ FVSMap fmap;
+
+ // map from intersection point to intersected faces.
+ VFSMap fmap_rev;
+
+ // created by divideEdges().
+ // holds, for each edge, a
+ EVVMap divided_edges;
+
+ // created by faceSplitEdges.
+ FV2SMap face_split_edges;
+
+ // mapping from vertex to edge for potentially intersected
+ // faces. Saves building the vertex to edge map for all faces of
+ // both meshes.
+ VEVecMap vert_to_edges;
+};
diff --git a/extern/carve/lib/csg_detail.hpp b/extern/carve/lib/csg_detail.hpp
new file mode 100644
index 00000000000..4b8fca3d2d2
--- /dev/null
+++ b/extern/carve/lib/csg_detail.hpp
@@ -0,0 +1,71 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+#pragma once
+
+#include <carve/carve.hpp>
+
+#include <carve/polyhedron_base.hpp>
+
+namespace carve {
+ namespace csg {
+ namespace detail {
+
+ typedef std::unordered_set<carve::mesh::MeshSet<3>::vertex_t *> VSet;
+ typedef std::unordered_set<carve::mesh::MeshSet<3>::face_t *> FSet;
+
+ typedef std::set<carve::mesh::MeshSet<3>::vertex_t *> VSetSmall;
+ typedef std::set<csg::V2> V2SetSmall;
+ typedef std::set<carve::mesh::MeshSet<3>::face_t *> FSetSmall;
+
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, VSetSmall> VVSMap;
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::edge_t *, VSetSmall> EVSMap;
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::face_t *, VSetSmall> FVSMap;
+
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, FSetSmall> VFSMap;
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::face_t *, V2SetSmall> FV2SMap;
+
+ typedef std::unordered_map<
+ carve::mesh::MeshSet<3>::edge_t *,
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> > EVVMap;
+
+ typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *,
+ std::vector<carve::mesh::MeshSet<3>::edge_t *> > VEVecMap;
+
+
+ class LoopEdges : public std::unordered_map<V2, std::list<FaceLoop *> > {
+ typedef std::unordered_map<V2, std::list<FaceLoop *> > super;
+
+ public:
+ void addFaceLoop(FaceLoop *fl);
+ void sortFaceLoopLists();
+ void removeFaceLoop(FaceLoop *fl);
+ };
+
+ }
+ }
+}
+
+
+
+static inline std::ostream &operator<<(std::ostream &o, const carve::csg::detail::FSet &s) {
+ const char *sep="";
+ for (carve::csg::detail::FSet::const_iterator i = s.begin(); i != s.end(); ++i) {
+ o << sep << *i; sep=",";
+ }
+ return o;
+}
diff --git a/extern/carve/lib/edge.cpp b/extern/carve/lib/edge.cpp
new file mode 100644
index 00000000000..4414e6496f3
--- /dev/null
+++ b/extern/carve/lib/edge.cpp
@@ -0,0 +1,23 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/poly.hpp>
+
diff --git a/extern/carve/lib/face.cpp b/extern/carve/lib/face.cpp
new file mode 100644
index 00000000000..c0718923cbb
--- /dev/null
+++ b/extern/carve/lib/face.cpp
@@ -0,0 +1,278 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/poly.hpp>
+
+double CALC_X(const carve::geom::plane<3> &p, double y, double z) { return -(p.d + p.N.y * y + p.N.z * z) / p.N.x; }
+double CALC_Y(const carve::geom::plane<3> &p, double x, double z) { return -(p.d + p.N.x * x + p.N.z * z) / p.N.y; }
+double CALC_Z(const carve::geom::plane<3> &p, double x, double y) { return -(p.d + p.N.x * x + p.N.y * y) / p.N.z; }
+
+namespace carve {
+ namespace poly {
+
+ carve::geom2d::P2 _project_1(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.z, v.y);
+ }
+
+ carve::geom2d::P2 _project_2(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.x, v.z);
+ }
+
+ carve::geom2d::P2 _project_3(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.y, v.x);
+ }
+
+ carve::geom2d::P2 _project_4(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.y, v.z);
+ }
+
+ carve::geom2d::P2 _project_5(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.z, v.x);
+ }
+
+ carve::geom2d::P2 _project_6(const carve::geom3d::Vector &v) {
+ return carve::geom::VECTOR(v.x, v.y);
+ }
+
+
+ carve::geom3d::Vector _unproject_1(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(CALC_X(plane_eqn, p.y, p.x), p.y, p.x);
+ }
+
+ carve::geom3d::Vector _unproject_2(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(p.x, CALC_Y(plane_eqn, p.x, p.y), p.y);
+ }
+
+ carve::geom3d::Vector _unproject_3(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(p.y, p.x, CALC_Z(plane_eqn, p.y, p.x));
+ }
+
+ carve::geom3d::Vector _unproject_4(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(CALC_X(plane_eqn, p.x, p.y), p.x, p.y);
+ }
+
+ carve::geom3d::Vector _unproject_5(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(p.y, CALC_Y(plane_eqn, p.y, p.x), p.x);
+ }
+
+ carve::geom3d::Vector _unproject_6(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) {
+ return carve::geom::VECTOR(p.x, p.y, CALC_Z(plane_eqn, p.x, p.y));
+ }
+
+ static carve::geom2d::P2 (*project_tab[2][3])(const carve::geom3d::Vector &) = {
+ { &_project_1, &_project_2, &_project_3 },
+ { &_project_4, &_project_5, &_project_6 }
+ };
+
+ static carve::geom3d::Vector (*unproject_tab[2][3])(const carve::geom2d::P2 &, const carve::geom3d::Plane &) = {
+ { &_unproject_1, &_unproject_2, &_unproject_3 },
+ { &_unproject_4, &_unproject_5, &_unproject_6 }
+ };
+
+ // only implemented for 3d.
+ template<unsigned ndim>
+ typename Face<ndim>::project_t Face<ndim>::getProjector(bool positive_facing, int axis) {
+ return NULL;
+ }
+
+ template<>
+ Face<3>::project_t Face<3>::getProjector(bool positive_facing, int axis) {
+ return project_tab[positive_facing ? 1 : 0][axis];
+ }
+
+ template<unsigned ndim>
+ typename Face<ndim>::unproject_t Face<ndim>::getUnprojector(bool positive_facing, int axis) {
+ return NULL;
+ }
+
+ template<>
+ Face<3>::unproject_t Face<3>::getUnprojector(bool positive_facing, int axis) {
+ return unproject_tab[positive_facing ? 1 : 0][axis];
+ }
+
+
+
+ template<unsigned ndim>
+ Face<ndim>::Face(const std::vector<const vertex_t *> &_vertices,
+ bool delay_recalc) : tagable() {
+ vertices = _vertices;
+ edges.resize(nVertices(), NULL);
+ if (!delay_recalc && !recalc()) { }
+ }
+
+ template<unsigned ndim>
+ Face<ndim>::Face(const vertex_t *a,
+ const vertex_t *b,
+ const vertex_t *c,
+ bool delay_recalc) : tagable() {
+ vertices.reserve(3);
+ vertices.push_back(a);
+ vertices.push_back(b);
+ vertices.push_back(c);
+ edges.resize(3, NULL);
+ if (!delay_recalc && !recalc()) { }
+ }
+
+ template<unsigned ndim>
+ Face<ndim>::Face(const vertex_t *a,
+ const vertex_t *b,
+ const vertex_t *c,
+ const vertex_t *d,
+ bool delay_recalc) : tagable() {
+ vertices.reserve(4);
+ vertices.push_back(a);
+ vertices.push_back(b);
+ vertices.push_back(c);
+ vertices.push_back(d);
+ edges.resize(4, NULL);
+ if (!delay_recalc && !recalc()) { }
+ }
+
+ template<unsigned ndim>
+ void Face<ndim>::invert() {
+ size_t n_verts = vertices.size();
+ std::reverse(vertices.begin(), vertices.end());
+
+ if (project != NULL) {
+ plane_eqn.negate();
+
+ int da = carve::geom::largestAxis(plane_eqn.N);
+
+ project = getProjector(plane_eqn.N.v[da] > 0, da);
+ unproject = getUnprojector(plane_eqn.N.v[da] > 0, da);
+ }
+
+ std::reverse(edges.begin(), edges.end() - 1);
+ for (size_t i = 0; i < n_verts; i++) {
+ const vertex_t *v1 = vertices[i];
+ const vertex_t *v2 = vertices[(i+1) % n_verts];
+ CARVE_ASSERT((edges[i]->v1 == v1 && edges[i]->v2 == v2) || (edges[i]->v1 == v2 && edges[i]->v2 == v1));
+ }
+ }
+
+ template<unsigned ndim>
+ bool Face<ndim>::recalc() {
+ aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr());
+
+ if (!carve::geom3d::fitPlane(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr(), plane_eqn)) {
+ return false;
+ }
+
+ int da = carve::geom::largestAxis(plane_eqn.N);
+ project = getProjector(false, da);
+
+ double A = carve::geom2d::signedArea(vertices, projector());
+ if ((A < 0.0) ^ (plane_eqn.N.v[da] < 0.0)) {
+ plane_eqn.negate();
+ }
+
+ project = getProjector(plane_eqn.N.v[da] > 0, da);
+ unproject = getUnprojector(plane_eqn.N.v[da] > 0, da);
+
+ return true;
+ }
+
+ template<unsigned ndim>
+ Face<ndim> *Face<ndim>::init(const Face *base, const std::vector<const vertex_t *> &_vertices, bool flipped) {
+ return init(base, _vertices.begin(), _vertices.end(), flipped);
+ }
+
+ template<unsigned ndim>
+ bool Face<ndim>::containsPoint(const vector_t &p) const {
+ if (!carve::math::ZERO(carve::geom::distance(plane_eqn, p))) return false;
+ // return pointInPolySimple(vertices, projector(), (this->*project)(p));
+ return carve::geom2d::pointInPoly(vertices, projector(), face::project(this, p)).iclass != POINT_OUT;
+ }
+
+ template<unsigned ndim>
+ bool Face<ndim>::containsPointInProjection(const vector_t &p) const {
+ return carve::geom2d::pointInPoly(vertices, projector(), face::project(this, p)).iclass != POINT_OUT;
+ }
+
+ template<unsigned ndim>
+ bool Face<ndim>::simpleLineSegmentIntersection(const carve::geom::linesegment<ndim> &line,
+ vector_t &intersection) const {
+ if (!line.OK()) return false;
+
+ carve::geom3d::Vector p;
+ IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane_eqn,
+ line,
+ p);
+ if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) {
+ return false;
+ }
+
+ carve::geom2d::P2 proj_p(face::project(this, p));
+ if (carve::geom2d::pointInPolySimple(vertices, projector(), proj_p)) {
+ intersection = p;
+ return true;
+ }
+ return false;
+ }
+
+ // XXX: should try to return a pre-existing vertex in the case of a
+ // line-vertex intersection. as it stands, this code isn't used,
+ // so... meh.
+ template<unsigned ndim>
+ IntersectionClass Face<ndim>::lineSegmentIntersection(const carve::geom::linesegment<ndim> &line,
+ vector_t &intersection) const {
+ if (!line.OK()) return INTERSECT_NONE;
+
+
+ carve::geom3d::Vector p;
+ IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane_eqn,
+ line,
+ p);
+ if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) {
+ return intersects;
+ }
+
+ carve::geom2d::P2 proj_p(face::project(this, p));
+
+ carve::geom2d::PolyInclusionInfo pi = carve::geom2d::pointInPoly(vertices, projector(), proj_p);
+ switch (pi.iclass) {
+ case POINT_VERTEX:
+ intersection = p;
+ return INTERSECT_VERTEX;
+
+ case POINT_EDGE:
+ intersection = p;
+ return INTERSECT_EDGE;
+
+ case POINT_IN:
+ intersection = p;
+ return INTERSECT_FACE;
+
+ case POINT_OUT:
+ return INTERSECT_NONE;
+
+ default:
+ break;
+ }
+ return INTERSECT_NONE;
+ }
+
+
+ }
+}
+
+// explicit instantiations.
+template class carve::poly::Face<3>;
diff --git a/extern/carve/lib/geom2d.cpp b/extern/carve/lib/geom2d.cpp
new file mode 100644
index 00000000000..bfa84f5fd24
--- /dev/null
+++ b/extern/carve/lib/geom2d.cpp
@@ -0,0 +1,260 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/geom2d.hpp>
+#include <carve/math.hpp>
+#include <carve/aabb.hpp>
+
+#include <algorithm>
+#include <iostream>
+
+namespace carve {
+ namespace geom2d {
+
+ bool lineSegmentIntersection_simple(const P2 &l1v1, const P2 &l1v2,
+ const P2 &l2v1, const P2 &l2v2) {
+ geom::aabb<2> l1_aabb, l2_aabb;
+ l1_aabb.fit(l1v1, l1v2);
+ l2_aabb.fit(l2v1, l2v2);
+
+ if (l1_aabb.maxAxisSeparation(l2_aabb) > 0.0) {
+ return false;
+ }
+
+ double l1v1_side = orient2d(l2v1, l2v2, l1v1);
+ double l1v2_side = orient2d(l2v1, l2v2, l1v2);
+
+ double l2v1_side = orient2d(l1v1, l1v2, l2v1);
+ double l2v2_side = orient2d(l1v1, l1v2, l2v2);
+
+ if (l1v1_side * l1v2_side > 0.0 || l2v1_side * l2v2_side > 0.0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool lineSegmentIntersection_simple(const LineSegment2 &l1,
+ const LineSegment2 &l2) {
+ return lineSegmentIntersection_simple(l1.v1, l1.v2, l2.v1, l2.v2);
+ }
+
+ LineIntersectionInfo lineSegmentIntersection(const P2 &l1v1, const P2 &l1v2,
+ const P2 &l2v1, const P2 &l2v2) {
+ geom::aabb<2> l1_aabb, l2_aabb;
+ l1_aabb.fit(l1v1, l1v2);
+ l2_aabb.fit(l2v1, l2v2);
+
+ if (l1_aabb.maxAxisSeparation(l2_aabb) > EPSILON) {
+ return LineIntersectionInfo(NO_INTERSECTION);
+ }
+
+ if (carve::geom::equal(l1v1, l1v2) || carve::geom::equal(l2v1, l2v2)) {
+ throw carve::exception("zero length line in intersection test");
+ }
+
+ double dx13 = l1v1.x - l2v1.x;
+ double dy13 = l1v1.y - l2v1.y;
+ double dx43 = l2v2.x - l2v1.x;
+ double dy43 = l2v2.y - l2v1.y;
+ double dx21 = l1v2.x - l1v1.x;
+ double dy21 = l1v2.y - l1v1.y;
+ double ua_n = dx43 * dy13 - dy43 * dx13;
+ double ub_n = dx21 * dy13 - dy21 * dx13;
+ double u_d = dy43 * dx21 - dx43 * dy21;
+
+ if (carve::math::ZERO(u_d)) {
+ if (carve::math::ZERO(ua_n)) {
+ if (carve::geom::equal(l1v2, l2v1)) {
+ return LineIntersectionInfo(INTERSECTION_PP, l1v2, 1, 2);
+ }
+ if (carve::geom::equal(l1v1, l2v2)) {
+ return LineIntersectionInfo(INTERSECTION_PP, l1v1, 0, 4);
+ }
+ if (l1v2.x > l2v1.x && l1v1.x < l2v2.x) {
+ return LineIntersectionInfo(COLINEAR);
+ }
+ }
+ return LineIntersectionInfo(NO_INTERSECTION);
+ }
+
+ double ua = ua_n / u_d;
+ double ub = ub_n / u_d;
+
+ if (-EPSILON <= ua && ua <= 1.0 + EPSILON && -EPSILON <= ub && ub <= 1.0 + EPSILON) {
+ double x = l1v1.x + ua * (l1v2.x - l1v1.x);
+ double y = l1v1.y + ua * (l1v2.y - l1v1.y);
+
+ P2 p = carve::geom::VECTOR(x, y);
+
+ double d1 = distance2(p, l1v1);
+ double d2 = distance2(p, l1v2);
+ double d3 = distance2(p, l2v1);
+ double d4 = distance2(p, l2v2);
+
+ int n = -1;
+
+ if (std::min(d1, d2) < EPSILON2) {
+ if (d1 < d2) {
+ p = l1v1; n = 0;
+ } else {
+ p = l1v2; n = 1;
+ }
+ if (std::min(d3, d4) < EPSILON2) {
+ if (d3 < d4) {
+ return LineIntersectionInfo(INTERSECTION_PP, p, n, 2);
+ } else {
+ return LineIntersectionInfo(INTERSECTION_PP, p, n, 3);
+ }
+ } else {
+ return LineIntersectionInfo(INTERSECTION_PL, p, n, -1);
+ }
+ } else if (std::min(d3, d4) < EPSILON2) {
+ if (d3 < d4) {
+ return LineIntersectionInfo(INTERSECTION_LP, l2v1, -1, 2);
+ } else {
+ return LineIntersectionInfo(INTERSECTION_LP, l2v2, -1, 3);
+ }
+ } else {
+ return LineIntersectionInfo(INTERSECTION_LL, p, -1, -1);
+ }
+ }
+ return LineIntersectionInfo(NO_INTERSECTION);
+ }
+
+ LineIntersectionInfo lineSegmentIntersection(const LineSegment2 &l1,
+ const LineSegment2 &l2) {
+ return lineSegmentIntersection(l1.v1, l1.v2, l2.v1, l2.v2);
+ }
+
+ double signedArea(const P2Vector &points) {
+ return signedArea(points, p2_adapt_ident());
+ }
+
+ bool pointInPolySimple(const P2Vector &points, const P2 &p) {
+ return pointInPolySimple(points, p2_adapt_ident(), p);
+ }
+
+ PolyInclusionInfo pointInPoly(const P2Vector &points, const P2 &p) {
+ return pointInPoly(points, p2_adapt_ident(), p);
+ }
+
+ int lineSegmentPolyIntersections(const P2Vector &points,
+ LineSegment2 line,
+ std::vector<PolyIntersectionInfo> &out) {
+ int count = 0;
+
+ if (line.v2 < line.v1) { line.flip(); }
+ out.clear();
+
+ for (P2Vector::size_type i = 0, l = points.size(); i < l; i++) {
+ P2Vector::size_type j = (i + 1) % l;
+ LineIntersectionInfo e =
+ lineSegmentIntersection(LineSegment2(points[i], points[j]), line);
+
+ switch (e.iclass) {
+ case INTERSECTION_PL: {
+ out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, e.ipoint, i));
+ count++;
+ break;
+ }
+ case INTERSECTION_PP: {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, e.ipoint, i + e.p2 - 2));
+ count++;
+ break;
+ }
+ case INTERSECTION_LP: {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, e.ipoint, i + e.p2 - 2));
+ count++;
+ break;
+ }
+ case INTERSECTION_LL: {
+ out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, e.ipoint, i));
+ count++;
+ break;
+ }
+ case COLINEAR: {
+ int n1 = (int)i, n2 = (int)j;
+ P2 q1 = points[i], q2 = points[j];
+
+ if (q2 < q1) { std::swap(q1, q2); std::swap(n1, n2); }
+
+ if (equal(q1, line.v1)) {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q1, n1));
+ } else if (q1.x < line.v1.x) {
+ out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, line.v1, i));
+ } else {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q1, n1));
+ }
+ if (equal(q2, line.v2)) {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q2, n2));
+ } else if (line.v2.x < q2.x) {
+ out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, line.v2, i));
+ } else {
+ out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q2, n2));
+ }
+
+ count += 2;
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return count;
+ }
+
+ struct FwdSort {
+ bool operator()(const PolyIntersectionInfo &a,
+ const PolyIntersectionInfo &b) const {
+ return a.ipoint < b.ipoint;
+ }
+ };
+
+ struct RevSort {
+ bool operator()(const PolyIntersectionInfo &a,
+ const PolyIntersectionInfo &b) const {
+ return a.ipoint < b.ipoint;
+ }
+ };
+
+ int sortedLineSegmentPolyIntersections(const P2Vector &points,
+ LineSegment2 line,
+ std::vector<PolyIntersectionInfo> &out) {
+
+ bool swapped = line.v2 < line.v1;
+
+ int count = lineSegmentPolyIntersections(points, line, out);
+ if (swapped) {
+ std::sort(out.begin(), out.end(), RevSort());
+ } else {
+ std::sort(out.begin(), out.end(), FwdSort());
+ }
+ return count;
+ }
+
+ bool pickContainedPoint(const std::vector<P2> &poly, P2 &result) {
+ return pickContainedPoint(poly, p2_adapt_ident(), result);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/geom3d.cpp b/extern/carve/lib/geom3d.cpp
new file mode 100644
index 00000000000..061dfe91802
--- /dev/null
+++ b/extern/carve/lib/geom3d.cpp
@@ -0,0 +1,164 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/math.hpp>
+#include <carve/geom3d.hpp>
+
+#include <algorithm>
+
+namespace carve {
+ namespace geom3d {
+
+ namespace {
+ int is_same(const std::vector<const Vector *> &a,
+ const std::vector<const Vector *> &b) {
+ if (a.size() != b.size()) return false;
+
+ const size_t S = a.size();
+ size_t i, j, p;
+
+ for (p = 0; p < S; ++p) {
+ if (a[0] == b[p]) break;
+ }
+ if (p == S) return 0;
+
+ for (i = 1, j = p + 1; j < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd;
+ for ( j = 0; i < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd;
+ return +1;
+
+not_fwd:
+ for (i = 1, j = p - 1; j != (size_t)-1; ++i, --j) if (a[i] != b[j]) goto not_rev;
+ for ( j = S - 1; i < S; ++i, --j) if (a[i] != b[j]) goto not_rev;
+ return -1;
+
+not_rev:
+ return 0;
+ }
+ }
+
+ bool planeIntersection(const Plane &a, const Plane &b, Ray &r) {
+ Vector N = cross(a.N, b.N);
+ if (N.isZero()) {
+ return false;
+ }
+ N.normalize();
+
+ double dot_aa = dot(a.N, a.N);
+ double dot_bb = dot(b.N, b.N);
+ double dot_ab = dot(a.N, b.N);
+
+ double determinant = dot_aa * dot_bb - dot_ab * dot_ab;
+
+ double c1 = ( a.d * dot_bb - b.d * dot_ab) / determinant;
+ double c2 = ( b.d * dot_aa - a.d * dot_ab) / determinant;
+
+ r.D = N;
+ r.v = c1 * a.N + c2 * b.N;
+
+ return true;
+ }
+
+ IntersectionClass rayPlaneIntersection(const Plane &p,
+ const Vector &v1,
+ const Vector &v2,
+ Vector &v,
+ double &t) {
+ Vector Rd = v2 - v1;
+ double Vd = dot(p.N, Rd);
+ double V0 = dot(p.N, v1) + p.d;
+
+ if (carve::math::ZERO(Vd)) {
+ if (carve::math::ZERO(V0)) {
+ return INTERSECT_BAD;
+ } else {
+ return INTERSECT_NONE;
+ }
+ }
+
+ t = -V0 / Vd;
+ v = v1 + t * Rd;
+ return INTERSECT_PLANE;
+ }
+
+ IntersectionClass lineSegmentPlaneIntersection(const Plane &p,
+ const LineSegment &line,
+ Vector &v) {
+ double t;
+ IntersectionClass r = rayPlaneIntersection(p, line.v1, line.v2, v, t);
+
+ if (r <= 0) return r;
+
+ if ((t < 0.0 && !equal(v, line.v1)) || (t > 1.0 && !equal(v, line.v2)))
+ return INTERSECT_NONE;
+
+ return INTERSECT_PLANE;
+ }
+
+ RayIntersectionClass rayRayIntersection(const Ray &r1,
+ const Ray &r2,
+ Vector &v1,
+ Vector &v2,
+ double &mu1,
+ double &mu2) {
+ if (!r1.OK() || !r2.OK()) return RR_DEGENERATE;
+
+ Vector v_13 = r1.v - r2.v;
+
+ double d1343 = dot(v_13, r2.D);
+ double d4321 = dot(r2.D, r1.D);
+ double d1321 = dot(v_13, r1.D);
+ double d4343 = dot(r2.D, r2.D);
+ double d2121 = dot(r1.D, r1.D);
+
+ double numer = d1343 * d4321 - d1321 * d4343;
+ double denom = d2121 * d4343 - d4321 * d4321;
+
+ // dc - eb
+ // -------
+ // ab - cc
+
+ // dc/eb - 1
+ // ---------
+ // a/e - cc/eb
+
+ // dc/b - e
+ // --------
+ // a - cc/b
+
+ // d/b - e/c
+ // ---------
+ // a/c - c/b
+
+ if (fabs(denom) * double(1<<10) <= fabs(numer)) {
+ return RR_PARALLEL;
+ }
+
+ mu1 = numer / denom;
+ mu2 = (d1343 + d4321 * mu1) / d4343;
+
+ v1 = r1.v + mu1 * r1.D;
+ v2 = r2.v + mu2 * r2.D;
+
+ return (equal(v1, v2)) ? RR_INTERSECTION : RR_NO_INTERSECTION;
+ }
+
+ }
+}
diff --git a/extern/carve/lib/intersect.cpp b/extern/carve/lib/intersect.cpp
new file mode 100644
index 00000000000..35166a6411e
--- /dev/null
+++ b/extern/carve/lib/intersect.cpp
@@ -0,0 +1,1668 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/pointset.hpp>
+#include <carve/polyline.hpp>
+
+#include <list>
+#include <set>
+#include <iostream>
+
+#include <algorithm>
+
+#include "csg_detail.hpp"
+#include "csg_data.hpp"
+
+#include "intersect_debug.hpp"
+#include "intersect_common.hpp"
+#include "intersect_classify_common.hpp"
+
+#include "csg_collector.hpp"
+
+#include <carve/timing.hpp>
+#include <carve/colour.hpp>
+
+
+
+
+carve::csg::VertexPool::VertexPool() {
+}
+
+carve::csg::VertexPool::~VertexPool() {
+}
+
+void carve::csg::VertexPool::reset() {
+ pool.clear();
+}
+
+carve::csg::VertexPool::vertex_t *carve::csg::VertexPool::get(const vertex_t::vector_t &v) {
+ if (!pool.size() || pool.back().size() == blocksize) {
+ pool.push_back(std::vector<vertex_t>());
+ pool.back().reserve(blocksize);
+ }
+ pool.back().push_back(vertex_t(v));
+ return &pool.back().back();
+}
+
+bool carve::csg::VertexPool::inPool(vertex_t *v) const {
+ for (pool_t::const_iterator i = pool.begin(); i != pool.end(); ++i) {
+ if (v >= &(i->front()) && v <= &(i->back())) return true;
+ }
+ return false;
+}
+
+
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+void writePLY(const std::string &out_file, const carve::point::PointSet *points, bool ascii);
+void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii);
+void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii);
+
+static carve::mesh::MeshSet<3> *faceLoopsToPolyhedron(const carve::csg::FaceLoopList &fl) {
+ std::vector<carve::mesh::MeshSet<3>::face_t *> faces;
+ faces.reserve(fl.size());
+ for (carve::csg::FaceLoop *f = fl.head; f; f = f->next) {
+ faces.push_back(f->orig_face->create(f->vertices.begin(), f->vertices.end(), false));
+ }
+ carve::mesh::MeshSet<3> *poly = new carve::mesh::MeshSet<3>(faces);
+
+ return poly;
+}
+#endif
+
+namespace {
+ /**
+ * \brief Sort a range [\a beg, \a end) of vertices in order of increasing dot product of vertex - \a base on \dir.
+ *
+ * @tparam[in] T a forward iterator type.
+ * @param[in] dir The direction in which to sort vertices.
+ * @param[in] base
+ * @param[in] beg The start of the vertex range to sort.
+ * @param[in] end The end of the vertex range to sort.
+ * @param[out] out The sorted vertex result.
+ * @param[in] size_hint A hint regarding the size of the output
+ * vector (to avoid needing to be able to calculate \a
+ * end - \a beg).
+ */
+ template<typename iter_t>
+ void orderVertices(iter_t beg, const iter_t end,
+ const carve::mesh::MeshSet<3>::vertex_t::vector_t &dir,
+ const carve::mesh::MeshSet<3>::vertex_t::vector_t &base,
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &out) {
+ typedef std::vector<std::pair<double, carve::mesh::MeshSet<3>::vertex_t *> > DVVector;
+ std::vector<std::pair<double, carve::mesh::MeshSet<3>::vertex_t *> > ordered_vertices;
+
+ ordered_vertices.reserve(std::distance(beg, end));
+
+ for (; beg != end; ++beg) {
+ carve::mesh::MeshSet<3>::vertex_t *v = (*beg);
+ ordered_vertices.push_back(std::make_pair(carve::geom::dot(v->v - base, dir), v));
+ }
+
+ std::sort(ordered_vertices.begin(), ordered_vertices.end());
+
+ out.clear();
+ out.reserve(ordered_vertices.size());
+ for (DVVector::const_iterator
+ i = ordered_vertices.begin(), e = ordered_vertices.end();
+ i != e;
+ ++i) {
+ out.push_back((*i).second);
+ }
+ }
+
+
+
+ /**
+ *
+ *
+ * @param dir
+ * @param base
+ * @param beg
+ * @param end
+ */
+ template<typename iter_t>
+ void selectOrderingProjection(iter_t beg, const iter_t end,
+ carve::mesh::MeshSet<3>::vertex_t::vector_t &dir,
+ carve::mesh::MeshSet<3>::vertex_t::vector_t &base) {
+ double dx, dy, dz;
+ carve::mesh::MeshSet<3>::vertex_t *min_x, *min_y, *min_z, *max_x, *max_y, *max_z;
+ if (beg == end) return;
+ min_x = max_x = min_y = max_y = min_z = max_z = *beg++;
+ for (; beg != end; ++beg) {
+ if (min_x->v.x > (*beg)->v.x) min_x = *beg;
+ if (min_y->v.y > (*beg)->v.y) min_y = *beg;
+ if (min_z->v.z > (*beg)->v.z) min_z = *beg;
+ if (max_x->v.x < (*beg)->v.x) max_x = *beg;
+ if (max_y->v.y < (*beg)->v.y) max_y = *beg;
+ if (max_z->v.z < (*beg)->v.z) max_z = *beg;
+ }
+
+ dx = max_x->v.x - min_x->v.x;
+ dy = max_y->v.y - min_y->v.y;
+ dz = max_z->v.z - min_z->v.z;
+
+ if (dx > dy) {
+ if (dx > dz) {
+ dir = max_x->v - min_x->v; base = min_x->v;
+ } else {
+ dir = max_z->v - min_z->v; base = min_z->v;
+ }
+ } else {
+ if (dy > dz) {
+ dir = max_y->v - min_y->v; base = min_y->v;
+ } else {
+ dir = max_z->v - min_z->v; base = min_z->v;
+ }
+ }
+ }
+}
+
+namespace {
+ struct dump_data {
+ carve::mesh::MeshSet<3>::vertex_t *i_pt;
+ carve::csg::IObj i_src;
+ carve::csg::IObj i_tgt;
+ dump_data(carve::mesh::MeshSet<3>::vertex_t *_i_pt,
+ carve::csg::IObj _i_src,
+ carve::csg::IObj _i_tgt) : i_pt(_i_pt), i_src(_i_src), i_tgt(_i_tgt) {
+ }
+ };
+
+
+
+ struct dump_sort {
+ bool operator()(const dump_data &a, const dump_data &b) const {
+ if (a.i_pt->v.x < b.i_pt->v.x) return true;
+ if (a.i_pt->v.x > b.i_pt->v.x) return false;
+ if (a.i_pt->v.y < b.i_pt->v.y) return true;
+ if (a.i_pt->v.y > b.i_pt->v.y) return false;
+ if (a.i_pt->v.z < b.i_pt->v.z) return true;
+ if (a.i_pt->v.z > b.i_pt->v.z) return false;
+ return false;
+ }
+ };
+
+
+
+ void dump_intersections(std::ostream &out, carve::csg::Intersections &csg_intersections) {
+ std::vector<dump_data> temp;
+
+ for (carve::csg::Intersections::const_iterator
+ i = csg_intersections.begin(),
+ ie = csg_intersections.end();
+ i != ie;
+ ++i) {
+ const carve::csg::IObj &i_src = ((*i).first);
+
+ for (carve::csg::Intersections::mapped_type::const_iterator
+ j = (*i).second.begin(),
+ je = (*i).second.end();
+ j != je;
+ ++j) {
+ const carve::csg::IObj &i_tgt = ((*j).first);
+ carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second);
+ temp.push_back(dump_data(i_pt, i_src, i_tgt));
+ }
+ }
+
+ std::sort(temp.begin(), temp.end(), dump_sort());
+
+ for (size_t i = 0; i < temp.size(); ++i) {
+ const carve::csg::IObj &i_src = temp[i].i_src;
+ const carve::csg::IObj &i_tgt = temp[i].i_tgt;
+ out
+ << "INTERSECTION: " << temp[i].i_pt << " (" << temp[i].i_pt->v << ") "
+ << "is " << i_src << ".." << i_tgt << std::endl;
+ }
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ std::vector<carve::geom3d::Vector> vertices;
+
+ for (carve::csg::Intersections::const_iterator
+ i = csg_intersections.begin(),
+ ie = csg_intersections.end();
+ i != ie;
+ ++i) {
+ for (carve::csg::Intersections::mapped_type::const_iterator
+ j = (*i).second.begin(),
+ je = (*i).second.end();
+ j != je;
+ ++j) {
+ carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second);
+ vertices.push_back(i_pt->v);
+ }
+ }
+
+ carve::point::PointSet points(vertices);
+
+ std::string outf("/tmp/intersection-points.ply");
+ ::writePLY(outf, &points, true);
+#endif
+ }
+
+
+
+ /**
+ * \brief Populate a collection with the faces adjoining an edge.
+ *
+ * @tparam face_set_t A collection type.
+ * @param e The edge for which to collect adjoining faces.
+ * @param faces
+ */
+ template<typename face_set_t>
+ inline void facesForVertex(carve::mesh::MeshSet<3>::vertex_t *v,
+ const carve::csg::detail::VEVecMap &ve,
+ face_set_t &faces) {
+ carve::csg::detail::VEVecMap::const_iterator vi = ve.find(v);
+ if (vi != ve.end()) {
+ for (carve::csg::detail::VEVecMap::data_type::const_iterator i = (*vi).second.begin(); i != (*vi).second.end(); ++i) {
+ faces.insert((*i)->face);
+ }
+ }
+ }
+
+ /**
+ * \brief Populate a collection with the faces adjoining an edge.
+ *
+ * @tparam face_set_t A collection type.
+ * @param e The edge for which to collect adjoining faces.
+ * @param faces
+ */
+ template<typename face_set_t>
+ inline void facesForEdge(carve::mesh::MeshSet<3>::edge_t *e,
+ face_set_t &faces) {
+ faces.insert(e->face);
+ }
+
+ /**
+ * \brief Populate a collection with the faces adjoining a face.
+ *
+ * @tparam face_set_t A collection type.
+ * @param f The face for which to collect adjoining faces.
+ * @param faces
+ */
+ template<typename face_set_t>
+ inline void facesForFace(carve::mesh::MeshSet<3>::face_t *f,
+ face_set_t &faces) {
+ faces.insert(f);
+ }
+
+ /**
+ * \brief Populate a collection with the faces adjoining an intersection object.
+ *
+ * @tparam face_set_t A collection type holding const carve::poly::Polyhedron::face_t *.
+ * @param obj The intersection object for which to collect adjoining faces.
+ * @param faces
+ */
+ template<typename face_set_t>
+ void facesForObject(const carve::csg::IObj &obj,
+ const carve::csg::detail::VEVecMap &ve,
+ face_set_t &faces) {
+ switch (obj.obtype) {
+ case carve::csg::IObj::OBTYPE_VERTEX:
+ facesForVertex(obj.vertex, ve, faces);
+ break;
+
+ case carve::csg::IObj::OBTYPE_EDGE:
+ facesForEdge(obj.edge, faces);
+ break;
+
+ case carve::csg::IObj::OBTYPE_FACE:
+ facesForFace(obj.face, faces);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+
+
+}
+
+
+
+bool carve::csg::CSG::Hooks::hasHook(unsigned hook_num) {
+ return hooks[hook_num].size() > 0;
+}
+
+void carve::csg::CSG::Hooks::intersectionVertex(const carve::mesh::MeshSet<3>::vertex_t *vertex,
+ const IObjPairSet &intersections) {
+ for (std::list<Hook *>::iterator j = hooks[INTERSECTION_VERTEX_HOOK].begin();
+ j != hooks[INTERSECTION_VERTEX_HOOK].end();
+ ++j) {
+ (*j)->intersectionVertex(vertex, intersections);
+ }
+}
+
+void carve::csg::CSG::Hooks::processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces,
+ const carve::mesh::MeshSet<3>::face_t *orig_face,
+ bool flipped) {
+ for (std::list<Hook *>::iterator j = hooks[PROCESS_OUTPUT_FACE_HOOK].begin();
+ j != hooks[PROCESS_OUTPUT_FACE_HOOK].end();
+ ++j) {
+ (*j)->processOutputFace(faces, orig_face, flipped);
+ }
+}
+
+void carve::csg::CSG::Hooks::resultFace(const carve::mesh::MeshSet<3>::face_t *new_face,
+ const carve::mesh::MeshSet<3>::face_t *orig_face,
+ bool flipped) {
+ for (std::list<Hook *>::iterator j = hooks[RESULT_FACE_HOOK].begin();
+ j != hooks[RESULT_FACE_HOOK].end();
+ ++j) {
+ (*j)->resultFace(new_face, orig_face, flipped);
+ }
+}
+
+void carve::csg::CSG::Hooks::registerHook(Hook *hook, unsigned hook_bits) {
+ for (unsigned i = 0; i < HOOK_MAX; ++i) {
+ if (hook_bits & (1U << i)) {
+ hooks[i].push_back(hook);
+ }
+ }
+}
+
+void carve::csg::CSG::Hooks::unregisterHook(Hook *hook) {
+ for (unsigned i = 0; i < HOOK_MAX; ++i) {
+ hooks[i].erase(std::remove(hooks[i].begin(), hooks[i].end(), hook), hooks[i].end());
+ }
+}
+
+void carve::csg::CSG::Hooks::reset() {
+ for (unsigned i = 0; i < HOOK_MAX; ++i) {
+ for (std::list<Hook *>::iterator j = hooks[i].begin(); j != hooks[i].end(); ++j) {
+ delete (*j);
+ }
+ hooks[i].clear();
+ }
+}
+
+carve::csg::CSG::Hooks::Hooks() : hooks() {
+ hooks.resize(HOOK_MAX);
+}
+
+carve::csg::CSG::Hooks::~Hooks() {
+ reset();
+}
+
+
+
+void carve::csg::CSG::makeVertexIntersections() {
+ static carve::TimingName FUNC_NAME("CSG::makeVertexIntersections()");
+ carve::TimingBlock block(FUNC_NAME);
+ vertex_intersections.clear();
+ for (Intersections::const_iterator
+ i = intersections.begin(),
+ ie = intersections.end();
+ i != ie;
+ ++i) {
+ const IObj &i_src = ((*i).first);
+
+ for (Intersections::mapped_type::const_iterator
+ j = (*i).second.begin(),
+ je = (*i).second.end();
+ j != je;
+ ++j) {
+ const IObj &i_tgt = ((*j).first);
+ carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second);
+
+ vertex_intersections[i_pt].insert(std::make_pair(i_src, i_tgt));
+ }
+ }
+}
+
+
+
+static carve::mesh::MeshSet<3>::vertex_t *chooseWeldPoint(
+ const carve::csg::detail::VSet &equivalent,
+ carve::csg::VertexPool &vertex_pool) {
+ // XXX: choose a better weld point.
+ if (!equivalent.size()) return NULL;
+
+ for (carve::csg::detail::VSet::const_iterator
+ i = equivalent.begin(), e = equivalent.end();
+ i != e;
+ ++i) {
+ if (!vertex_pool.inPool((*i))) return (*i);
+ }
+ return *equivalent.begin();
+}
+
+
+
+static const carve::mesh::MeshSet<3>::vertex_t *weld(
+ const carve::csg::detail::VSet &equivalent,
+ carve::csg::VertexIntersections &vertex_intersections,
+ carve::csg::VertexPool &vertex_pool) {
+ carve::mesh::MeshSet<3>::vertex_t *weld_point = chooseWeldPoint(equivalent, vertex_pool);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "weld: " << equivalent.size() << " vertices ( ";
+ for (carve::csg::detail::VSet::const_iterator
+ i = equivalent.begin(), e = equivalent.end();
+ i != e;
+ ++i) {
+ const carve::mesh::MeshSet<3>::vertex_t *v = (*i);
+ std::cerr << " " << v;
+ }
+ std::cerr << ") to " << weld_point << std::endl;
+#endif
+
+ if (!weld_point) return NULL;
+
+ carve::csg::VertexIntersections::mapped_type &weld_tgt = (vertex_intersections[weld_point]);
+
+ for (carve::csg::detail::VSet::const_iterator
+ i = equivalent.begin(), e = equivalent.end();
+ i != e;
+ ++i) {
+ carve::mesh::MeshSet<3>::vertex_t *v = (*i);
+
+ if (v != weld_point) {
+ carve::csg::VertexIntersections::iterator j = vertex_intersections.find(v);
+
+ if (j != vertex_intersections.end()) {
+ weld_tgt.insert((*j).second.begin(), (*j).second.end());
+ vertex_intersections.erase(j);
+ }
+ }
+ }
+ return weld_point;
+}
+
+
+
+void carve::csg::CSG::groupIntersections() {
+#if 0 // old code, to be removed.
+ static carve::TimingName GROUP_INTERSECTONS("groupIntersections()");
+
+ carve::TimingBlock block(GROUP_INTERSECTONS);
+
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> vertices;
+ detail::VVSMap graph;
+#if defined(CARVE_DEBUG)
+ std::cerr << "groupIntersections()" << ": vertex_intersections.size()==" << vertex_intersections.size() << std::endl;
+#endif
+
+ vertices.reserve(vertex_intersections.size());
+ for (carve::csg::VertexIntersections::const_iterator
+ i = vertex_intersections.begin(),
+ e = vertex_intersections.end();
+ i != e;
+ ++i)
+ {
+ vertices.push_back((*i).first);
+ }
+ carve::geom3d::AABB aabb;
+ aabb.fit(vertices.begin(), vertices.end(), carve::poly::vec_adapt_vertex_ptr());
+ Octree vertex_intersections_octree;
+ vertex_intersections_octree.setBounds(aabb);
+
+ vertex_intersections_octree.addVertices(vertices);
+
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> out;
+ for (size_t i = 0, l = vertices.size(); i != l; ++i) {
+ // let's find all the vertices near this one.
+ out.clear();
+ vertex_intersections_octree.findVerticesNearAllowDupes(vertices[i]->v, out);
+
+ for (size_t j = 0; j < out.size(); ++j) {
+ if (vertices[i] != out[j] && carve::geom::equal(vertices[i]->v, out[j]->v)) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "EQ: " << vertices[i] << "," << out[j] << " " << vertices[i]->v << "," << out[j]->v << std::endl;
+#endif
+ graph[vertices[i]].insert(out[j]);
+ graph[out[j]].insert(vertices[i]);
+ }
+ }
+ }
+
+ detail::VSet visited, open;
+ while (graph.size()) {
+ visited.clear();
+ open.clear();
+ detail::VVSMap::iterator i = graph.begin();
+ open.insert((*i).first);
+ while (open.size()) {
+ detail::VSet::iterator t = open.begin();
+ const carve::mesh::MeshSet<3>::vertex_t *o = (*t);
+ open.erase(t);
+ i = graph.find(o);
+ CARVE_ASSERT(i != graph.end());
+ visited.insert(o);
+ for (detail::VVSMap::mapped_type::const_iterator
+ j = (*i).second.begin(),
+ je = (*i).second.end();
+ j != je;
+ ++j) {
+ if (visited.count((*j)) == 0) {
+ open.insert((*j));
+ }
+ }
+ graph.erase(i);
+ }
+ weld(visited, vertex_intersections, vertex_pool);
+ }
+#endif
+}
+
+
+
+void carve::csg::CSG::intersectingFacePairs(detail::Data &data) {
+ static carve::TimingName FUNC_NAME("CSG::intersectingFacePairs()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ // iterate over all intersection points.
+ for (VertexIntersections::const_iterator i = vertex_intersections.begin(), ie = vertex_intersections.end(); i != ie; ++i) {
+ carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*i).first);
+ detail::VFSMap::mapped_type &face_set = (data.fmap_rev[i_pt]);
+
+ // for all pairs of intersecting objects at this point
+ for (VertexIntersections::data_type::const_iterator j = (*i).second.begin(), je = (*i).second.end(); j != je; ++j) {
+ const IObj &i_src = ((*j).first);
+ const IObj &i_tgt = ((*j).second);
+
+ // work out the faces involved. this updates fmap_rev.
+ facesForObject(i_src, data.vert_to_edges, face_set);
+ facesForObject(i_tgt, data.vert_to_edges, face_set);
+
+ // record the intersection with respect to any involved vertex.
+ if (i_src.obtype == IObj::OBTYPE_VERTEX) data.vmap[i_src.vertex] = i_pt;
+ if (i_tgt.obtype == IObj::OBTYPE_VERTEX) data.vmap[i_tgt.vertex] = i_pt;
+
+ // record the intersection with respect to any involved edge.
+ if (i_src.obtype == IObj::OBTYPE_EDGE) data.emap[i_src.edge].insert(i_pt);
+ if (i_tgt.obtype == IObj::OBTYPE_EDGE) data.emap[i_tgt.edge].insert(i_pt);
+ }
+
+ // record the intersection with respect to each face.
+ for (carve::csg::detail::VFSMap::mapped_type::const_iterator k = face_set.begin(), ke = face_set.end(); k != ke; ++k) {
+ carve::mesh::MeshSet<3>::face_t *f = (*k);
+ data.fmap[f].insert(i_pt);
+ }
+ }
+}
+
+
+
+void carve::csg::CSG::_generateVertexVertexIntersections(carve::mesh::MeshSet<3>::vertex_t *va,
+ carve::mesh::MeshSet<3>::edge_t *eb) {
+ if (intersections.intersects(va, eb->v1())) {
+ return;
+ }
+
+ double d_v1 = carve::geom::distance2(va->v, eb->v1()->v);
+
+ if (d_v1 < carve::EPSILON2) {
+ intersections.record(va, eb->v1(), va);
+ }
+}
+
+
+
+void carve::csg::CSG::generateVertexVertexIntersections(carve::mesh::MeshSet<3>::face_t *a,
+ const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) {
+ carve::mesh::MeshSet<3>::edge_t *ea, *eb;
+
+ ea = a->edge;
+ do {
+ for (size_t i = 0; i < b.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *t = b[i];
+ eb = t->edge;
+ do {
+ _generateVertexVertexIntersections(ea->v1(), eb);
+ eb = eb->next;
+ } while (eb != t->edge);
+ }
+ ea = ea->next;
+ } while (ea != a->edge);
+}
+
+
+
+void carve::csg::CSG::_generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::vertex_t *va,
+ carve::mesh::MeshSet<3>::edge_t *eb) {
+ if (intersections.intersects(va, eb)) {
+ return;
+ }
+
+ if (std::min(eb->v1()->v.x, eb->v2()->v.x) - carve::EPSILON > va->v.x ||
+ std::max(eb->v1()->v.x, eb->v2()->v.x) + carve::EPSILON < va->v.x ||
+ std::min(eb->v1()->v.y, eb->v2()->v.y) - carve::EPSILON > va->v.y ||
+ std::max(eb->v1()->v.y, eb->v2()->v.y) + carve::EPSILON < va->v.y ||
+ std::min(eb->v1()->v.z, eb->v2()->v.z) - carve::EPSILON > va->v.z ||
+ std::max(eb->v1()->v.z, eb->v2()->v.z) + carve::EPSILON < va->v.z) {
+ return;
+ }
+
+ double a = cross(eb->v2()->v - eb->v1()->v, va->v - eb->v1()->v).length2();
+ double b = (eb->v2()->v - eb->v1()->v).length2();
+
+ if (a < b * carve::EPSILON2) {
+ // vertex-edge intersection
+ intersections.record(eb, va, va);
+ if (eb->rev) intersections.record(eb->rev, va, va);
+ }
+}
+
+
+
+void carve::csg::CSG::generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a,
+ const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) {
+ carve::mesh::MeshSet<3>::edge_t *ea, *eb;
+
+ ea = a->edge;
+ do {
+ for (size_t i = 0; i < b.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *t = b[i];
+ eb = t->edge;
+ do {
+ _generateVertexEdgeIntersections(ea->v1(), eb);
+ eb = eb->next;
+ } while (eb != t->edge);
+ }
+ ea = ea->next;
+ } while (ea != a->edge);
+}
+
+
+
+void carve::csg::CSG::_generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::edge_t *ea,
+ carve::mesh::MeshSet<3>::edge_t *eb) {
+ if (intersections.intersects(ea, eb)) {
+ return;
+ }
+
+ carve::mesh::MeshSet<3>::vertex_t *v1 = ea->v1(), *v2 = ea->v2();
+ carve::mesh::MeshSet<3>::vertex_t *v3 = eb->v1(), *v4 = eb->v2();
+
+ carve::geom::aabb<3> ea_aabb, eb_aabb;
+ ea_aabb.fit(v1->v, v2->v);
+ eb_aabb.fit(v3->v, v4->v);
+ if (ea_aabb.maxAxisSeparation(eb_aabb) > EPSILON) return;
+
+ carve::mesh::MeshSet<3>::vertex_t::vector_t p1, p2;
+ double mu1, mu2;
+
+ switch (carve::geom3d::rayRayIntersection(carve::geom3d::Ray(v2->v - v1->v, v1->v),
+ carve::geom3d::Ray(v4->v - v3->v, v3->v),
+ p1, p2, mu1, mu2)) {
+ case carve::RR_INTERSECTION: {
+ // edges intersect
+ if (mu1 >= 0.0 && mu1 <= 1.0 && mu2 >= 0.0 && mu2 <= 1.0) {
+ carve::mesh::MeshSet<3>::vertex_t *p = vertex_pool.get((p1 + p2) / 2.0);
+ intersections.record(ea, eb, p);
+ if (ea->rev) intersections.record(ea->rev, eb, p);
+ if (eb->rev) intersections.record(ea, eb->rev, p);
+ if (ea->rev && eb->rev) intersections.record(ea->rev, eb->rev, p);
+ }
+ break;
+ }
+ case carve::RR_PARALLEL: {
+ // edges parallel. any intersection of this type should have
+ // been handled by generateVertexEdgeIntersections().
+ break;
+ }
+ case carve::RR_DEGENERATE: {
+ throw carve::exception("degenerate edge");
+ break;
+ }
+ case carve::RR_NO_INTERSECTION: {
+ break;
+ }
+ }
+}
+
+
+
+void carve::csg::CSG::generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a,
+ const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) {
+ carve::mesh::MeshSet<3>::edge_t *ea, *eb;
+
+ ea = a->edge;
+ do {
+ for (size_t i = 0; i < b.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *t = b[i];
+ eb = t->edge;
+ do {
+ _generateEdgeEdgeIntersections(ea, eb);
+ eb = eb->next;
+ } while (eb != t->edge);
+ }
+ ea = ea->next;
+ } while (ea != a->edge);
+}
+
+
+
+void carve::csg::CSG::_generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa,
+ carve::mesh::MeshSet<3>::edge_t *eb) {
+ if (intersections.intersects(eb->v1(), fa)) {
+ return;
+ }
+
+ double d1 = carve::geom::distance(fa->plane, eb->v1()->v);
+
+ if (fabs(d1) < carve::EPSILON &&
+ fa->containsPoint(eb->v1()->v)) {
+ intersections.record(eb->v1(), fa, eb->v1());
+ }
+}
+
+
+
+void carve::csg::CSG::generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *a,
+ const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) {
+ carve::mesh::MeshSet<3>::edge_t *ea, *eb;
+
+ for (size_t i = 0; i < b.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *t = b[i];
+ eb = t->edge;
+ do {
+ _generateVertexFaceIntersections(a, eb);
+ eb = eb->next;
+ } while (eb != t->edge);
+ }
+}
+
+
+
+void carve::csg::CSG::_generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa,
+ carve::mesh::MeshSet<3>::edge_t *eb) {
+ if (intersections.intersects(eb, fa)) {
+ return;
+ }
+
+ carve::mesh::MeshSet<3>::vertex_t::vector_t _p;
+ if (fa->simpleLineSegmentIntersection(carve::geom3d::LineSegment(eb->v1()->v, eb->v2()->v), _p)) {
+ carve::mesh::MeshSet<3>::vertex_t *p = vertex_pool.get(_p);
+ intersections.record(eb, fa, p);
+ if (eb->rev) intersections.record(eb->rev, fa, p);
+ }
+}
+
+
+
+void carve::csg::CSG::generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *a,
+ const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) {
+ carve::mesh::MeshSet<3>::edge_t *ea, *eb;
+
+ for (size_t i = 0; i < b.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *t = b[i];
+ eb = t->edge;
+ do {
+ _generateEdgeFaceIntersections(a, eb);
+ eb = eb->next;
+ } while (eb != t->edge);
+ }
+}
+
+
+
+void carve::csg::CSG::generateIntersectionCandidates(carve::mesh::MeshSet<3> *a,
+ const face_rtree_t *a_node,
+ carve::mesh::MeshSet<3> *b,
+ const face_rtree_t *b_node,
+ face_pairs_t &face_pairs,
+ bool descend_a) {
+ if (!a_node->bbox.intersects(b_node->bbox)) {
+ return;
+ }
+
+ if (a_node->child && (descend_a || !b_node->child)) {
+ for (face_rtree_t *node = a_node->child; node; node = node->sibling) {
+ generateIntersectionCandidates(a, node, b, b_node, face_pairs, false);
+ }
+ } else if (b_node->child) {
+ for (face_rtree_t *node = b_node->child; node; node = node->sibling) {
+ generateIntersectionCandidates(a, a_node, b, node, face_pairs, true);
+ }
+ } else {
+ for (size_t i = 0; i < a_node->data.size(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *fa = a_node->data[i];
+ carve::geom::aabb<3> aabb_a = fa->getAABB();
+ if (aabb_a.maxAxisSeparation(b_node->bbox) > carve::EPSILON) continue;
+
+ for (size_t j = 0; j < b_node->data.size(); ++j) {
+ carve::mesh::MeshSet<3>::face_t *fb = b_node->data[j];
+ carve::geom::aabb<3> aabb_b = fb->getAABB();
+ if (aabb_b.maxAxisSeparation(aabb_a) > carve::EPSILON) continue;
+
+ std::pair<double, double> a_ra = fa->rangeInDirection(fa->plane.N, fa->edge->vert->v);
+ std::pair<double, double> b_ra = fb->rangeInDirection(fa->plane.N, fa->edge->vert->v);
+ if (carve::rangeSeparation(a_ra, b_ra) > carve::EPSILON) continue;
+
+ std::pair<double, double> a_rb = fa->rangeInDirection(fb->plane.N, fb->edge->vert->v);
+ std::pair<double, double> b_rb = fb->rangeInDirection(fb->plane.N, fb->edge->vert->v);
+ if (carve::rangeSeparation(a_rb, b_rb) > carve::EPSILON) continue;
+
+ if (!facesAreCoplanar(fa, fb)) {
+ face_pairs[fa].push_back(fb);
+ face_pairs[fb].push_back(fa);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+void carve::csg::CSG::generateIntersections(carve::mesh::MeshSet<3> *a,
+ const face_rtree_t *a_rtree,
+ carve::mesh::MeshSet<3> *b,
+ const face_rtree_t *b_rtree,
+ detail::Data &data) {
+ face_pairs_t face_pairs;
+ generateIntersectionCandidates(a, a_rtree, b, b_rtree, face_pairs);
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ carve::mesh::MeshSet<3>::face_t *f = (*i).first;
+ carve::mesh::MeshSet<3>::edge_t *e = f->edge;
+ do {
+ data.vert_to_edges[e->v1()].push_back(e);
+ e = e->next;
+ } while (e != f->edge);
+ }
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ generateVertexVertexIntersections((*i).first, (*i).second);
+ }
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ generateVertexEdgeIntersections((*i).first, (*i).second);
+ }
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ generateEdgeEdgeIntersections((*i).first, (*i).second);
+ }
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ generateVertexFaceIntersections((*i).first, (*i).second);
+ }
+
+ for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) {
+ generateEdgeFaceIntersections((*i).first, (*i).second);
+ }
+
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "makeVertexIntersections" << std::endl;
+#endif
+ makeVertexIntersections();
+
+#if defined(CARVE_DEBUG)
+ std::cerr << " intersections.size() " << intersections.size() << std::endl;
+ map_histogram(std::cerr, intersections);
+ std::cerr << " vertex_intersections.size() " << vertex_intersections.size() << std::endl;
+ map_histogram(std::cerr, vertex_intersections);
+#endif
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_INTERSECTIONS)
+ HOOK(drawIntersections(vertex_intersections););
+#endif
+
+#if defined(CARVE_DEBUG)
+ std::cerr << " intersections.size() " << intersections.size() << std::endl;
+ std::cerr << " vertex_intersections.size() " << vertex_intersections.size() << std::endl;
+#endif
+
+ // notify about intersections.
+ if (hooks.hasHook(Hooks::INTERSECTION_VERTEX_HOOK)) {
+ for (VertexIntersections::const_iterator i = vertex_intersections.begin();
+ i != vertex_intersections.end();
+ ++i) {
+ hooks.intersectionVertex((*i).first, (*i).second);
+ }
+ }
+
+ // from here on, only vertex_intersections is used for intersection
+ // information.
+
+ // intersections still contains the vertex_to_face map. maybe that
+ // should be moved out into another class.
+ static_cast<Intersections::super>(intersections).clear();
+}
+
+
+
+carve::csg::CSG::CSG() {
+}
+
+
+
+/**
+ * \brief For each intersected edge, decompose into a set of vertex pairs representing an ordered set of edge fragments.
+ *
+ * @tparam[in,out] data Internal intersection data. data.emap is used to produce data.divided_edges.
+ */
+void carve::csg::CSG::divideIntersectedEdges(detail::Data &data) {
+ static carve::TimingName FUNC_NAME("CSG::divideIntersectedEdges()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ for (detail::EVSMap::const_iterator i = data.emap.begin(), ei = data.emap.end(); i != ei; ++i) {
+ carve::mesh::MeshSet<3>::edge_t *edge = (*i).first;
+ const detail::EVSMap::mapped_type &vertices = (*i).second;
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &verts = data.divided_edges[edge];
+ orderVertices(vertices.begin(), vertices.end(),
+ edge->v2()->v - edge->v1()->v, edge->v1()->v,
+ verts);
+ }
+}
+
+
+
+carve::csg::CSG::~CSG() {
+}
+
+
+
+void carve::csg::CSG::makeFaceEdges(carve::csg::EdgeClassification &eclass,
+ detail::Data &data) {
+ detail::FSet face_b_set;
+ for (detail::FVSMap::const_iterator
+ i = data.fmap.begin(), ie = data.fmap.end();
+ i != ie;
+ ++i) {
+ carve::mesh::MeshSet<3>::face_t *face_a = (*i).first;
+ const detail::FVSMap::mapped_type &face_a_intersections = ((*i).second);
+ face_b_set.clear();
+
+ // work out the set of faces from the opposing polyhedron that intersect face_a.
+ for (detail::FVSMap::mapped_type::const_iterator
+ j = face_a_intersections.begin(), je = face_a_intersections.end();
+ j != je;
+ ++j) {
+ for (detail::VFSMap::mapped_type::const_iterator
+ k = data.fmap_rev[*j].begin(), ke = data.fmap_rev[*j].end();
+ k != ke;
+ ++k) {
+ carve::mesh::MeshSet<3>::face_t *face_b = (*k);
+ if (face_a != face_b && face_b->mesh->meshset != face_a->mesh->meshset) {
+ face_b_set.insert(face_b);
+ }
+ }
+ }
+
+ // run through each intersecting face.
+ for (detail::FSet::const_iterator
+ j = face_b_set.begin(), je = face_b_set.end();
+ j != je;
+ ++j) {
+ carve::mesh::MeshSet<3>::face_t *face_b = (*j);
+ const detail::FVSMap::mapped_type &face_b_intersections = (data.fmap[face_b]);
+
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> vertices;
+ vertices.reserve(std::min(face_a_intersections.size(), face_b_intersections.size()));
+
+ // record the points of intersection between face_a and face_b
+ std::set_intersection(face_a_intersections.begin(),
+ face_a_intersections.end(),
+ face_b_intersections.begin(),
+ face_b_intersections.end(),
+ std::back_inserter(vertices));
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "face pair: "
+ << face_a << ":" << face_b
+ << " N(verts) " << vertices.size() << std::endl;
+ for (std::vector<carve::mesh::MeshSet<3>::vertex_t *>::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
+ std::cerr << (*i) << " " << (*i)->v << " ("
+ << carve::geom::distance(face_a->plane, (*i)->v) << ","
+ << carve::geom::distance(face_b->plane, (*i)->v) << ")"
+ << std::endl;
+ //CARVE_ASSERT(carve::geom3d::distance(face_a->plane_eqn, *(*i)) < EPSILON);
+ //CARVE_ASSERT(carve::geom3d::distance(face_b->plane_eqn, *(*i)) < EPSILON);
+ }
+#endif
+
+ // if there are two points of intersection, then the added edge is simple to determine.
+ if (vertices.size() == 2) {
+ carve::mesh::MeshSet<3>::vertex_t *v1 = vertices[0];
+ carve::mesh::MeshSet<3>::vertex_t *v2 = vertices[1];
+ carve::geom3d::Vector c = (v1->v + v2->v) / 2;
+
+ // determine whether the midpoint of the implied edge is contained in face_a and face_b
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "face_a->nVertices() = " << face_a->nVertices() << " face_a->containsPointInProjection(c) = " << face_a->containsPointInProjection(c) << std::endl;
+ std::cerr << "face_b->nVertices() = " << face_b->nVertices() << " face_b->containsPointInProjection(c) = " << face_b->containsPointInProjection(c) << std::endl;
+#endif
+
+ if (face_a->containsPointInProjection(c) && face_b->containsPointInProjection(c)) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "adding edge: " << v1 << "-" << v2 << std::endl;
+#if defined(DEBUG_DRAW_FACE_EDGES)
+ HOOK(drawEdge(v1, v2, 1, 1, 1, 1, 1, 1, 1, 1, 2.0););
+#endif
+#endif
+ // record the edge, with class information.
+ if (v1 > v2) std::swap(v1, v2);
+ eclass[ordered_edge(v1, v2)] = carve::csg::EC2(carve::csg::EDGE_ON, carve::csg::EDGE_ON);
+ data.face_split_edges[face_a].insert(std::make_pair(v1, v2));
+ data.face_split_edges[face_b].insert(std::make_pair(v1, v2));
+ }
+ continue;
+ }
+
+ // otherwise, it's more complex.
+ carve::geom3d::Vector base, dir;
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> ordered;
+
+ // skip coplanar edges. this simplifies the resulting
+ // mesh. eventually all coplanar face regions of two polyhedra
+ // must reach a point where they are no longer coplanar (or the
+ // polyhedra are identical).
+ if (!facesAreCoplanar(face_a, face_b)) {
+ // order the intersection vertices (they must lie along a
+ // vector, as the faces aren't coplanar).
+ selectOrderingProjection(vertices.begin(), vertices.end(), dir, base);
+ orderVertices(vertices.begin(), vertices.end(), dir, base, ordered);
+
+ // for each possible edge in the ordering, test the midpoint,
+ // and record if it's contained in face_a and face_b.
+ for (int k = 0, ke = (int)ordered.size() - 1; k < ke; ++k) {
+ carve::mesh::MeshSet<3>::vertex_t *v1 = ordered[k];
+ carve::mesh::MeshSet<3>::vertex_t *v2 = ordered[k + 1];
+ carve::geom3d::Vector c = (v1->v + v2->v) / 2;
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "testing edge: " << v1 << "-" << v2 << " at " << c << std::endl;
+ std::cerr << "a: " << face_a->containsPointInProjection(c) << " b: " << face_b->containsPointInProjection(c) << std::endl;
+ std::cerr << "face_a->containsPointInProjection(c): " << face_a->containsPointInProjection(c) << std::endl;
+ std::cerr << "face_b->containsPointInProjection(c): " << face_b->containsPointInProjection(c) << std::endl;
+#endif
+
+ if (face_a->containsPointInProjection(c) && face_b->containsPointInProjection(c)) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "adding edge: " << v1 << "-" << v2 << std::endl;
+#if defined(DEBUG_DRAW_FACE_EDGES)
+ HOOK(drawEdge(v1, v2, .5, .5, .5, 1, .5, .5, .5, 1, 2.0););
+#endif
+#endif
+ // record the edge, with class information.
+ if (v1 > v2) std::swap(v1, v2);
+ eclass[ordered_edge(v1, v2)] = carve::csg::EC2(carve::csg::EDGE_ON, carve::csg::EDGE_ON);
+ data.face_split_edges[face_a].insert(std::make_pair(v1, v2));
+ data.face_split_edges[face_b].insert(std::make_pair(v1, v2));
+ }
+ }
+ }
+ }
+ }
+
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ {
+ V2Set edges;
+ for (detail::FV2SMap::const_iterator i = data.face_split_edges.begin(); i != data.face_split_edges.end(); ++i) {
+ edges.insert((*i).second.begin(), (*i).second.end());
+ }
+
+ detail::VSet vertices;
+ for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) {
+ vertices.insert((*i).first);
+ vertices.insert((*i).second);
+ }
+
+ carve::line::PolylineSet intersection_graph;
+ intersection_graph.vertices.resize(vertices.size());
+ std::map<const carve::mesh::MeshSet<3>::vertex_t *, size_t> vmap;
+
+ size_t j = 0;
+ for (detail::VSet::const_iterator i = vertices.begin(); i != vertices.end(); ++i) {
+ intersection_graph.vertices[j].v = (*i)->v;
+ vmap[(*i)] = j++;
+ }
+
+ for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) {
+ size_t line[2];
+ line[0] = vmap[(*i).first];
+ line[1] = vmap[(*i).second];
+ intersection_graph.addPolyline(false, line, line + 2);
+ }
+
+ std::string out("/tmp/intersection-edges.ply");
+ ::writePLY(out, &intersection_graph, true);
+ }
+#endif
+}
+
+
+
+/**
+ *
+ *
+ * @param fll
+ */
+static void checkFaceLoopIntegrity(carve::csg::FaceLoopList &fll) {
+ static carve::TimingName FUNC_NAME("CSG::checkFaceLoopIntegrity()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ std::unordered_map<carve::csg::V2, int> counts;
+ for (carve::csg::FaceLoop *fl = fll.head; fl; fl = fl->next) {
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (fl->vertices);
+ carve::mesh::MeshSet<3>::vertex_t *v1, *v2;
+ v1 = loop[loop.size() - 1];
+ for (unsigned i = 0; i < loop.size(); ++i) {
+ v2 = loop[i];
+ if (v1 < v2) {
+ counts[std::make_pair(v1, v2)]++;
+ } else {
+ counts[std::make_pair(v2, v1)]--;
+ }
+ v1 = v2;
+ }
+ }
+ for (std::unordered_map<carve::csg::V2, int>::const_iterator
+ x = counts.begin(), xe = counts.end(); x != xe; ++x) {
+ if ((*x).second) {
+ std::cerr << "FACE LOOP ERROR: " << (*x).first.first << "-" << (*x).first.second << " : " << (*x).second << std::endl;
+ }
+ }
+}
+
+
+
+/**
+ *
+ *
+ * @param a
+ * @param b
+ * @param vclass
+ * @param eclass
+ * @param a_face_loops
+ * @param b_face_loops
+ * @param a_edge_count
+ * @param b_edge_count
+ * @param hooks
+ */
+void carve::csg::CSG::calc(carve::mesh::MeshSet<3> *a,
+ const face_rtree_t *a_rtree,
+ carve::mesh::MeshSet<3> *b,
+ const face_rtree_t *b_rtree,
+ carve::csg::VertexClassification &vclass,
+ carve::csg::EdgeClassification &eclass,
+ carve::csg::FaceLoopList &a_face_loops,
+ carve::csg::FaceLoopList &b_face_loops,
+ size_t &a_edge_count,
+ size_t &b_edge_count) {
+ detail::Data data;
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "init" << std::endl;
+#endif
+ init();
+
+ generateIntersections(a, a_rtree, b, b_rtree, data);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "intersectingFacePairs" << std::endl;
+#endif
+ intersectingFacePairs(data);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "emap:" << std::endl;
+ map_histogram(std::cerr, data.emap);
+ std::cerr << "fmap:" << std::endl;
+ map_histogram(std::cerr, data.fmap);
+ std::cerr << "fmap_rev:" << std::endl;
+ map_histogram(std::cerr, data.fmap_rev);
+#endif
+
+ // std::cerr << "removeCoplanarFaces" << std::endl;
+ // fp_intersections.removeCoplanarFaces();
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_OCTREE)
+ HOOK(drawOctree(a->octree););
+ HOOK(drawOctree(b->octree););
+#endif
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "divideIntersectedEdges" << std::endl;
+#endif
+ divideIntersectedEdges(data);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "makeFaceEdges" << std::endl;
+#endif
+ // makeFaceEdges(data.face_split_edges, eclass, data.fmap, data.fmap_rev);
+ makeFaceEdges(eclass, data);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "generateFaceLoops" << std::endl;
+#endif
+ a_edge_count = generateFaceLoops(a, data, a_face_loops);
+ b_edge_count = generateFaceLoops(b, data, b_face_loops);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "generated " << a_edge_count << " edges for poly a" << std::endl;
+ std::cerr << "generated " << b_edge_count << " edges for poly b" << std::endl;
+#endif
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ {
+ std::string out("/tmp/a_split.ply");
+ writePLY(out, faceLoopsToPolyhedron(a_face_loops), false);
+ }
+ {
+ std::string out("/tmp/b_split.ply");
+ writePLY(out, faceLoopsToPolyhedron(b_face_loops), false);
+ }
+#endif
+
+ checkFaceLoopIntegrity(a_face_loops);
+ checkFaceLoopIntegrity(b_face_loops);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "classify" << std::endl;
+#endif
+ // initialize some classification information.
+ for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator
+ i = a->vertex_storage.begin(), e = a->vertex_storage.end(); i != e; ++i) {
+ vclass[map_vertex(data.vmap, &(*i))].cls[0] = POINT_ON;
+ }
+ for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator
+ i = b->vertex_storage.begin(), e = b->vertex_storage.end(); i != e; ++i) {
+ vclass[map_vertex(data.vmap, &(*i))].cls[1] = POINT_ON;
+ }
+ for (VertexIntersections::const_iterator
+ i = vertex_intersections.begin(), e = vertex_intersections.end(); i != e; ++i) {
+ vclass[(*i).first] = PC2(POINT_ON, POINT_ON);
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << data.divided_edges.size() << " edges are split" << std::endl;
+ std::cerr << data.face_split_edges.size() << " faces are split" << std::endl;
+
+ std::cerr << "poly a: " << a_face_loops.size() << " face loops" << std::endl;
+ std::cerr << "poly b: " << b_face_loops.size() << " face loops" << std::endl;
+#endif
+
+ // std::cerr << "OCTREE A:" << std::endl;
+ // dump_octree_stats(a->octree.root, 0);
+ // std::cerr << "OCTREE B:" << std::endl;
+ // dump_octree_stats(b->octree.root, 0);
+}
+
+
+
+/**
+ *
+ *
+ * @param shared_edges
+ * @param result_list
+ * @param shared_edge_ptr
+ */
+void returnSharedEdges(carve::csg::V2Set &shared_edges,
+ std::list<carve::mesh::MeshSet<3> *> &result_list,
+ carve::csg::V2Set *shared_edge_ptr) {
+ // need to convert shared edges to point into result
+ typedef std::map<carve::geom3d::Vector, carve::mesh::MeshSet<3>::vertex_t *> remap_type;
+ remap_type remap;
+ for (std::list<carve::mesh::MeshSet<3> *>::iterator list_it =
+ result_list.begin(); list_it != result_list.end(); list_it++) {
+ carve::mesh::MeshSet<3> *result = *list_it;
+ if (result) {
+ for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator it =
+ result->vertex_storage.begin(); it != result->vertex_storage.end(); it++) {
+ remap.insert(std::make_pair((*it).v, &(*it)));
+ }
+ }
+ }
+ for (carve::csg::V2Set::iterator it = shared_edges.begin();
+ it != shared_edges.end(); it++) {
+ remap_type::iterator first_it = remap.find(((*it).first)->v);
+ remap_type::iterator second_it = remap.find(((*it).second)->v);
+ CARVE_ASSERT(first_it != remap.end() && second_it != remap.end());
+ shared_edge_ptr->insert(std::make_pair(first_it->second, second_it->second));
+ }
+}
+
+
+
+/**
+ *
+ *
+ * @param a
+ * @param b
+ * @param collector
+ * @param hooks
+ * @param shared_edges_ptr
+ * @param classify_type
+ *
+ * @return
+ */
+carve::mesh::MeshSet<3> *carve::csg::CSG::compute(carve::mesh::MeshSet<3> *a,
+ carve::mesh::MeshSet<3> *b,
+ carve::csg::CSG::Collector &collector,
+ carve::csg::V2Set *shared_edges_ptr,
+ CLASSIFY_TYPE classify_type) {
+ static carve::TimingName FUNC_NAME("CSG::compute");
+ carve::TimingBlock block(FUNC_NAME);
+
+ VertexClassification vclass;
+ EdgeClassification eclass;
+
+ FLGroupList a_loops_grouped;
+ FLGroupList b_loops_grouped;
+
+ FaceLoopList a_face_loops;
+ FaceLoopList b_face_loops;
+
+ size_t a_edge_count;
+ size_t b_edge_count;
+
+ face_rtree_t *a_rtree = face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4);
+ face_rtree_t *b_rtree = face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4);
+
+ {
+ static carve::TimingName FUNC_NAME("CSG::compute - calc()");
+ carve::TimingBlock block(FUNC_NAME);
+ calc(a, a_rtree, b, b_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
+ }
+
+ detail::LoopEdges a_edge_map;
+ detail::LoopEdges b_edge_map;
+
+ {
+ static carve::TimingName FUNC_NAME("CSG::compute - makeEdgeMap()");
+ carve::TimingBlock block(FUNC_NAME);
+ makeEdgeMap(a_face_loops, a_edge_count, a_edge_map);
+ makeEdgeMap(b_face_loops, b_edge_count, b_edge_map);
+
+ }
+
+ {
+ static carve::TimingName FUNC_NAME("CSG::compute - sortFaceLoopLists()");
+ carve::TimingBlock block(FUNC_NAME);
+ a_edge_map.sortFaceLoopLists();
+ b_edge_map.sortFaceLoopLists();
+ }
+
+ V2Set shared_edges;
+
+ {
+ static carve::TimingName FUNC_NAME("CSG::compute - findSharedEdges()");
+ carve::TimingBlock block(FUNC_NAME);
+ findSharedEdges(a_edge_map, b_edge_map, shared_edges);
+ }
+
+ {
+ static carve::TimingName FUNC_NAME("CSG::compute - groupFaceLoops()");
+ carve::TimingBlock block(FUNC_NAME);
+ groupFaceLoops(a, a_face_loops, a_edge_map, shared_edges, a_loops_grouped);
+ groupFaceLoops(b, b_face_loops, b_edge_map, shared_edges, b_loops_grouped);
+#if defined(CARVE_DEBUG)
+ std::cerr << "*** a_loops_grouped.size(): " << a_loops_grouped.size() << std::endl;
+ std::cerr << "*** b_loops_grouped.size(): " << b_loops_grouped.size() << std::endl;
+#endif
+ }
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_GROUPS)
+ {
+ float n = 1.0 / (a_loops_grouped.size() + b_loops_grouped.size() + 1);
+ float H = 0.0, S = 1.0, V = 1.0;
+ float r, g, b;
+ for (FLGroupList::const_iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ carve::colour::HSV2RGB(H, S, V, r, g, b); H += n;
+ drawFaceLoopList((*i).face_loops, r, g, b, 1.0, r * .5, g * .5, b * .5, 1.0, true);
+ }
+ for (FLGroupList::const_iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ carve::colour::HSV2RGB(H, S, V, r, g, b); H += n;
+ drawFaceLoopList((*i).face_loops, r, g, b, 1.0, r * .5, g * .5, b * .5, 1.0, true);
+ }
+
+ for (FLGroupList::const_iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ drawFaceLoopListWireframe((*i).face_loops);
+ }
+ for (FLGroupList::const_iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ drawFaceLoopListWireframe((*i).face_loops);
+ }
+ }
+#endif
+
+ switch (classify_type) {
+ case CLASSIFY_EDGE:
+ classifyFaceGroupsEdge(shared_edges,
+ vclass,
+ a,
+ a_rtree,
+ a_loops_grouped,
+ a_edge_map,
+ b,
+ b_rtree,
+ b_loops_grouped,
+ b_edge_map,
+ collector);
+ break;
+ case CLASSIFY_NORMAL:
+ classifyFaceGroups(shared_edges,
+ vclass,
+ a,
+ a_rtree,
+ a_loops_grouped,
+ a_edge_map,
+ b,
+ b_rtree,
+ b_loops_grouped,
+ b_edge_map,
+ collector);
+ break;
+ }
+
+ carve::mesh::MeshSet<3> *result = collector.done(hooks);
+ if (result != NULL && shared_edges_ptr != NULL) {
+ std::list<carve::mesh::MeshSet<3> *> result_list;
+ result_list.push_back(result);
+ returnSharedEdges(shared_edges, result_list, shared_edges_ptr);
+ }
+ return result;
+}
+
+
+
+/**
+ *
+ *
+ * @param a
+ * @param b
+ * @param op
+ * @param hooks
+ * @param shared_edges
+ * @param classify_type
+ *
+ * @return
+ */
+carve::mesh::MeshSet<3> *carve::csg::CSG::compute(carve::mesh::MeshSet<3> *a,
+ carve::mesh::MeshSet<3> *b,
+ carve::csg::CSG::OP op,
+ carve::csg::V2Set *shared_edges,
+ CLASSIFY_TYPE classify_type) {
+ Collector *coll = makeCollector(op, a, b);
+ if (!coll) return NULL;
+
+ carve::mesh::MeshSet<3> *result = compute(a, b, *coll, shared_edges, classify_type);
+
+ delete coll;
+
+ return result;
+}
+
+
+
+/**
+ *
+ *
+ * @param closed
+ * @param open
+ * @param FaceClass
+ * @param result
+ * @param hooks
+ * @param shared_edges_ptr
+ *
+ * @return
+ */
+bool carve::csg::CSG::sliceAndClassify(carve::mesh::MeshSet<3> *closed,
+ carve::mesh::MeshSet<3> *open,
+ std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &result,
+ carve::csg::V2Set *shared_edges_ptr) {
+ if (!closed->isClosed()) return false;
+ carve::csg::VertexClassification vclass;
+ carve::csg::EdgeClassification eclass;
+
+ carve::csg::FLGroupList a_loops_grouped;
+ carve::csg::FLGroupList b_loops_grouped;
+
+ carve::csg::FaceLoopList a_face_loops;
+ carve::csg::FaceLoopList b_face_loops;
+
+ size_t a_edge_count;
+ size_t b_edge_count;
+
+ face_rtree_t *closed_rtree = face_rtree_t::construct_STR(closed->faceBegin(), closed->faceEnd(), 4, 4);
+ face_rtree_t *open_rtree = face_rtree_t::construct_STR(open->faceBegin(), open->faceEnd(), 4, 4);
+
+ calc(closed, closed_rtree, open, open_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
+
+ detail::LoopEdges a_edge_map;
+ detail::LoopEdges b_edge_map;
+
+ makeEdgeMap(a_face_loops, a_edge_count, a_edge_map);
+ makeEdgeMap(b_face_loops, b_edge_count, b_edge_map);
+
+ carve::csg::V2Set shared_edges;
+
+ findSharedEdges(a_edge_map, b_edge_map, shared_edges);
+
+ groupFaceLoops(closed, a_face_loops, a_edge_map, shared_edges, a_loops_grouped);
+ groupFaceLoops(open, b_face_loops, b_edge_map, shared_edges, b_loops_grouped);
+
+ halfClassifyFaceGroups(shared_edges,
+ vclass,
+ closed,
+ closed_rtree,
+ a_loops_grouped,
+ a_edge_map,
+ open,
+ open_rtree,
+ b_loops_grouped,
+ b_edge_map,
+ result);
+
+ if (shared_edges_ptr != NULL) {
+ std::list<carve::mesh::MeshSet<3> *> result_list;
+ for (std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> >::iterator it = result.begin(); it != result.end(); it++) {
+ result_list.push_back(it->second);
+ }
+ returnSharedEdges(shared_edges, result_list, shared_edges_ptr);
+ }
+ return true;
+}
+
+
+
+/**
+ *
+ *
+ * @param a
+ * @param b
+ * @param a_sliced
+ * @param b_sliced
+ * @param hooks
+ * @param shared_edges_ptr
+ */
+void carve::csg::CSG::slice(carve::mesh::MeshSet<3> *a,
+ carve::mesh::MeshSet<3> *b,
+ std::list<carve::mesh::MeshSet<3> *> &a_sliced,
+ std::list<carve::mesh::MeshSet<3> *> &b_sliced,
+ carve::csg::V2Set *shared_edges_ptr) {
+ carve::csg::VertexClassification vclass;
+ carve::csg::EdgeClassification eclass;
+
+ carve::csg::FLGroupList a_loops_grouped;
+ carve::csg::FLGroupList b_loops_grouped;
+
+ carve::csg::FaceLoopList a_face_loops;
+ carve::csg::FaceLoopList b_face_loops;
+
+ size_t a_edge_count;
+ size_t b_edge_count;
+
+ face_rtree_t *a_rtree = face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4);
+ face_rtree_t *b_rtree = face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4);
+
+ calc(a, a_rtree, b, b_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
+
+ detail::LoopEdges a_edge_map;
+ detail::LoopEdges b_edge_map;
+
+ makeEdgeMap(a_face_loops, a_edge_count, a_edge_map);
+ makeEdgeMap(b_face_loops, b_edge_count, b_edge_map);
+
+ carve::csg::V2Set shared_edges;
+
+ findSharedEdges(a_edge_map, b_edge_map, shared_edges);
+
+ groupFaceLoops(a, a_face_loops, a_edge_map, shared_edges, a_loops_grouped);
+ groupFaceLoops(b, b_face_loops, b_edge_map, shared_edges, b_loops_grouped);
+
+ for (carve::csg::FLGroupList::iterator
+ i = a_loops_grouped.begin(), e = a_loops_grouped.end();
+ i != e; ++i) {
+ Collector *all = makeCollector(ALL, a, b);
+ all->collect(&*i, hooks);
+ a_sliced.push_back(all->done(hooks));
+
+ delete all;
+ }
+
+ for (carve::csg::FLGroupList::iterator
+ i = b_loops_grouped.begin(), e = b_loops_grouped.end();
+ i != e; ++i) {
+ Collector *all = makeCollector(ALL, a, b);
+ all->collect(&*i, hooks);
+ b_sliced.push_back(all->done(hooks));
+
+ delete all;
+ }
+ if (shared_edges_ptr != NULL) {
+ std::list<carve::mesh::MeshSet<3> *> result_list;
+ result_list.insert(result_list.end(), a_sliced.begin(), a_sliced.end());
+ result_list.insert(result_list.end(), b_sliced.begin(), b_sliced.end());
+ returnSharedEdges(shared_edges, result_list, shared_edges_ptr);
+ }
+}
+
+
+
+/**
+ *
+ *
+ */
+void carve::csg::CSG::init() {
+ intersections.clear();
+ vertex_intersections.clear();
+ vertex_pool.reset();
+}
diff --git a/extern/carve/lib/intersect_classify_common.hpp b/extern/carve/lib/intersect_classify_common.hpp
new file mode 100644
index 00000000000..359c9af0e64
--- /dev/null
+++ b/extern/carve/lib/intersect_classify_common.hpp
@@ -0,0 +1,46 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+#pragma once
+
+#include "intersect_common.hpp"
+
+template<typename T>
+static int is_same(const std::vector<T> &a,
+ const std::vector<T> &b) {
+ if (a.size() != b.size()) return false;
+
+ const size_t S = a.size();
+ size_t i, j, p;
+
+ for (p = 0; p < S; ++p) {
+ if (a[0] == b[p]) break;
+ }
+ if (p == S) return 0;
+
+ for (i = 1, j = p + 1; j < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd;
+ for ( j = 0; i < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd;
+ return +1;
+
+not_fwd:
+ for (i = 1, j = p - 1; j != (size_t)-1; ++i, --j) if (a[i] != b[j]) goto not_rev;
+ for ( j = S - 1; i < S; ++i, --j) if (a[i] != b[j]) goto not_rev;
+ return -1;
+
+not_rev:
+ return 0;
+}
diff --git a/extern/carve/lib/intersect_classify_common_impl.hpp b/extern/carve/lib/intersect_classify_common_impl.hpp
new file mode 100644
index 00000000000..3c141c81151
--- /dev/null
+++ b/extern/carve/lib/intersect_classify_common_impl.hpp
@@ -0,0 +1,362 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+#pragma once
+
+namespace carve {
+ namespace csg {
+ typedef std::unordered_map<
+ carve::mesh::MeshSet<3>::vertex_t *,
+ std::list<FLGroupList::iterator> > GroupLookup;
+
+
+ inline bool isSameFwd(const V2Set &a, const V2Set &b) {
+ if (a.size() != b.size()) return false;
+ for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) {
+ if (b.find((*i)) == b.end()) return false;
+ }
+ return true;
+ }
+
+ inline bool isSameRev(const V2Set &a, const V2Set &b) {
+ if (a.size() != b.size()) return false;
+ for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) {
+ if (b.find(std::make_pair((*i).second, (*i).first)) == b.end()) return false;
+ }
+ return true;
+ }
+
+
+ static void performClassifySimpleOnFaceGroups(FLGroupList &a_groups,
+ FLGroupList &b_groups,
+ carve::mesh::MeshSet<3> *poly_a,
+ carve::mesh::MeshSet<3> *poly_b,
+ CSG::Collector &collector,
+ CSG::Hooks &hooks) {
+ // Simple ON faces groups are face groups that consist of a single
+ // face, and which have copy in both inputs. These are trivially ON.
+ // This has the side effect of short circuiting the case where the
+ // two inputs share geometry.
+ GroupLookup a_map, b_map;
+
+ // First, hash FaceLoopGroups with one FaceLoop based upon their
+ // minimum vertex pointer - this pointer must be shared between
+ // FaceLoops that this test catches.
+ for (FLGroupList::iterator i = a_groups.begin(); i != a_groups.end(); ++i) {
+ if ((*i).face_loops.size() != 1) continue;
+ FaceLoop *f = (*i).face_loops.head;
+ carve::mesh::MeshSet<3>::vertex_t *v = *std::min_element(f->vertices.begin(), f->vertices.end());
+ a_map[v].push_back(i);
+ }
+
+ for (FLGroupList::iterator i = b_groups.begin(); i != b_groups.end(); ++i) {
+ if ((*i).face_loops.size() != 1) continue;
+ FaceLoop *f = (*i).face_loops.head;
+ carve::mesh::MeshSet<3>::vertex_t *v = *std::min_element(f->vertices.begin(), f->vertices.end());
+ if (a_map.find(v) != a_map.end()) {
+ b_map[v].push_back(i);
+ }
+ }
+
+ // Then, iterate through the FaceLoops hashed in the first map, and
+ // find candidate matches in the second map.
+ for (GroupLookup::iterator j = b_map.begin(), je = b_map.end(); j != je; ++j) {
+ carve::mesh::MeshSet<3>::vertex_t *v = (*j).first;
+ GroupLookup::iterator i = a_map.find(v);
+
+ for (std::list<FLGroupList::iterator>::iterator bi = (*j).second.begin(), be = (*j).second.end(); bi != be;) {
+ FLGroupList::iterator b(*bi);
+ FaceLoop *f_b = (*b).face_loops.head;
+
+ // For each candidate match pair, see if their vertex pointers
+ // are the same, allowing for rotation and inversion.
+ for (std::list<FLGroupList::iterator>::iterator ai = (*i).second.begin(), ae = (*i).second.end(); ai != ae; ++ai) {
+ FLGroupList::iterator a(*ai);
+ FaceLoop *f_a = (*a).face_loops.head;
+
+ int s = is_same(f_a->vertices, f_b->vertices);
+ if (!s) continue;
+
+ // if they are ordered in the same direction, then they are
+ // oriented out, otherwise oriented in.
+ FaceClass fc = s == +1 ? FACE_ON_ORIENT_OUT : FACE_ON_ORIENT_IN;
+
+ (*a).classification.push_back(ClassificationInfo(NULL, fc));
+ (*b).classification.push_back(ClassificationInfo(NULL, fc));
+
+ collector.collect(&*a, hooks);
+ collector.collect(&*b, hooks);
+
+ a_groups.erase(a);
+ b_groups.erase(b);
+
+ (*i).second.erase(ai);
+ bi = (*j).second.erase(bi);
+
+ goto done;
+ }
+ ++bi;
+ done:;
+ }
+ }
+ }
+
+ template <typename CLASSIFIER>
+ static void performClassifyEasyFaceGroups(FLGroupList &group,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ VertexClassification &vclass,
+ const CLASSIFIER &classifier,
+ CSG::Collector &collector,
+ CSG::Hooks &hooks) {
+
+ for (FLGroupList::iterator i = group.begin(); i != group.end();) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "............group " << &(*i) << std::endl;
+#endif
+ FaceLoopGroup &grp = (*i);
+ FaceLoopList &curr = (grp.face_loops);
+ FaceClass fc;
+
+ for (FaceLoop *f = curr.head; f; f = f->next) {
+ for (size_t j = 0; j < f->vertices.size(); ++j) {
+ if (!classifier.pointOn(vclass, f, j)) {
+ PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, f->vertices[j]->v);
+ if (pc == POINT_IN || pc == POINT_OUT) {
+ classifier.explain(f, j, pc);
+ }
+ if (pc == POINT_IN) { fc = FACE_IN; goto accept; }
+ if (pc == POINT_OUT) { fc = FACE_OUT; goto accept; }
+ }
+ }
+ }
+ ++i;
+ continue;
+ accept: {
+ grp.classification.push_back(ClassificationInfo(NULL, fc));
+ collector.collect(&grp, hooks);
+ i = group.erase(i);
+ }
+ }
+ }
+
+
+ template <typename CLASSIFIER>
+ static void performClassifyHardFaceGroups(FLGroupList &group,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ const CLASSIFIER & /* classifier */,
+ CSG::Collector &collector,
+ CSG::Hooks &hooks) {
+ for (FLGroupList::iterator
+ i = group.begin(); i != group.end();) {
+ int n_in = 0, n_out = 0, n_on = 0;
+ FaceLoopGroup &grp = (*i);
+ FaceLoopList &curr = (grp.face_loops);
+ V2Set &perim = ((*i).perimeter);
+ FaceClass fc =FACE_UNCLASSIFIED;
+
+ for (FaceLoop *f = curr.head; f; f = f->next) {
+ carve::mesh::MeshSet<3>::vertex_t *v1, *v2;
+ v1 = f->vertices.back();
+ for (size_t j = 0; j < f->vertices.size(); ++j) {
+ v2 = f->vertices[j];
+ if (v1 < v2 && perim.find(std::make_pair(v1, v2)) == perim.end()) {
+ carve::geom3d::Vector c = (v1->v + v2->v) / 2.0;
+
+ PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, c);
+
+ switch (pc) {
+ case POINT_IN: n_in++; break;
+ case POINT_OUT: n_out++; break;
+ case POINT_ON: n_on++; break;
+ default: break; // does not happen.
+ }
+ }
+ v1 = v2;
+ }
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << ">>> n_in: " << n_in << " n_on: " << n_on << " n_out: " << n_out << std::endl;
+#endif
+
+ if (!n_in && !n_out) {
+ ++i;
+ continue;
+ }
+
+ if (n_in) fc = FACE_IN;
+ if (n_out) fc = FACE_OUT;
+
+ grp.classification.push_back(ClassificationInfo(NULL, fc));
+ collector.collect(&grp, hooks);
+ i = group.erase(i);
+ }
+ }
+
+ template <typename CLASSIFIER>
+ void performFaceLoopWork(carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ FLGroupList &b_loops_grouped,
+ const CLASSIFIER &classifier,
+ CSG::Collector &collector,
+ CSG::Hooks &hooks) {
+ for (FLGroupList::iterator i = b_loops_grouped.begin(), e = b_loops_grouped.end(); i != e;) {
+ FaceClass fc;
+
+ if (classifier.faceLoopSanityChecker(*i)) {
+ std::cerr << "UNEXPECTED face loop with size != 1." << std::endl;
+ ++i;
+ continue;
+ }
+ CARVE_ASSERT((*i).face_loops.size() == 1);
+
+ FaceLoop *fla = (*i).face_loops.head;
+
+ const carve::mesh::MeshSet<3>::face_t *f = (fla->orig_face);
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (fla->vertices);
+ std::vector<carve::geom2d::P2> proj;
+ proj.reserve(loop.size());
+ for (unsigned j = 0; j < loop.size(); ++j) {
+ proj.push_back(f->project(loop[j]->v));
+ }
+ carve::geom2d::P2 pv;
+ if (!carve::geom2d::pickContainedPoint(proj, pv)) {
+ CARVE_FAIL("Failed");
+ }
+ carve::geom3d::Vector v = f->unproject(pv, f->plane);
+
+ const carve::mesh::MeshSet<3>::face_t *hit_face;
+ PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, v, false, NULL, &hit_face);
+ switch (pc) {
+ case POINT_IN: fc = FACE_IN; break;
+ case POINT_OUT: fc = FACE_OUT; break;
+ case POINT_ON: {
+ double d = carve::geom::distance(hit_face->plane, v);
+#if defined(CARVE_DEBUG)
+ std::cerr << "d = " << d << std::endl;
+#endif
+ fc = d < 0 ? FACE_IN : FACE_OUT;
+ break;
+ }
+ default:
+ CARVE_FAIL("unhandled switch case -- should not happen");
+ }
+#if defined(CARVE_DEBUG)
+ std::cerr << "CLASS: " << (fc == FACE_IN ? "FACE_IN" : "FACE_OUT" ) << std::endl;
+#endif
+
+ (*i).classification.push_back(ClassificationInfo(NULL, fc));
+ collector.collect(&*i, hooks);
+ i = b_loops_grouped.erase(i);
+ }
+
+ }
+
+ template <typename CLASSIFIER>
+ void performClassifyFaceGroups(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification &vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree,
+ const CLASSIFIER &classifier,
+ CSG::Collector &collector,
+ CSG::Hooks &hooks) {
+
+ classifier.classifySimple(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_b);
+ classifier.classifyEasy(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree);
+ classifier.classifyHard(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree);
+
+ {
+ GroupLookup a_map;
+ FLGroupList::iterator i, j;
+ FaceClass fc;
+
+ for (i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ V2Set::iterator it_end = (*i).perimeter.end();
+ V2Set::iterator it_begin = (*i).perimeter.begin();
+
+ if(it_begin != it_end) {
+ a_map[std::min_element(it_begin, it_end)->first].push_back(i);
+ }
+ }
+
+ for (i = b_loops_grouped.begin(); i != b_loops_grouped.end();) {
+ GroupLookup::iterator a = a_map.end();
+
+ V2Set::iterator it_end = (*i).perimeter.end();
+ V2Set::iterator it_begin = (*i).perimeter.begin();
+
+ if(it_begin != it_end) {
+ a = a_map.find(std::min_element(it_begin, it_end)->first);
+ }
+
+ if (a == a_map.end()) { ++i; continue; }
+
+ for (std::list<FLGroupList::iterator>::iterator ji = (*a).second.begin(), je = (*a).second.end(); ji != je; ++ji) {
+ j = (*ji);
+ if (isSameFwd((*i).perimeter, (*j).perimeter)) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "SAME FWD PAIR" << std::endl;
+#endif
+ fc = FACE_ON_ORIENT_OUT;
+ goto face_pair;
+ } else if (isSameRev((*i).perimeter, (*j).perimeter)) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "SAME REV PAIR" << std::endl;
+#endif
+ fc = FACE_ON_ORIENT_IN;
+ goto face_pair;
+ }
+ }
+ ++i;
+ continue;
+
+ face_pair: {
+ V2Set::iterator it_end = (*j).perimeter.end();
+ V2Set::iterator it_begin = (*j).perimeter.begin();
+
+ if(it_begin != it_end) {
+ a_map[std::min_element(it_begin, it_end)->first].remove(j);
+ }
+
+ (*i).classification.push_back(ClassificationInfo(NULL, fc));
+ (*j).classification.push_back(ClassificationInfo(NULL, fc));
+
+ collector.collect(&*i, hooks);
+ collector.collect(&*j, hooks);
+
+ j = a_loops_grouped.erase(j);
+ i = b_loops_grouped.erase(i);
+ }
+ }
+ }
+
+ // XXX: this may leave some face groups that are IN or OUT, and
+ // consist of a single face loop.
+ classifier.postRemovalCheck(a_loops_grouped, b_loops_grouped);
+
+ classifier.faceLoopWork(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree);
+
+ classifier.finish(a_loops_grouped, b_loops_grouped);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/intersect_classify_edge.cpp b/extern/carve/lib/intersect_classify_edge.cpp
new file mode 100644
index 00000000000..d2c1fdd7c24
--- /dev/null
+++ b/extern/carve/lib/intersect_classify_edge.cpp
@@ -0,0 +1,820 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#if defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/debug_hooks.hpp>
+#include <carve/colour.hpp>
+
+#include <list>
+#include <set>
+#include <iostream>
+
+#include <algorithm>
+
+#include "csg_detail.hpp"
+
+#include "intersect_common.hpp"
+#include "intersect_classify_common.hpp"
+
+#define ANGLE_EPSILON 1e-6
+
+namespace carve {
+ namespace csg {
+
+ namespace {
+
+ inline bool single_bit_set(uint32_t v) {
+ v &= v - 1;
+ return v == 0;
+ }
+
+ struct EdgeSurface {
+ FaceLoop *fwd;
+ double fwd_ang;
+ FaceLoop *rev;
+ double rev_ang;
+
+ EdgeSurface() : fwd(NULL), fwd_ang(0.0), rev(NULL), rev_ang(0.0) { }
+ };
+
+
+ typedef std::map<const carve::mesh::MeshSet<3>::mesh_t *, EdgeSurface> GrpEdgeSurfMap;
+
+ typedef std::pair<FaceLoopGroup *, const carve::mesh::MeshSet<3>::mesh_t *> ClassificationKey;
+
+ struct ClassificationData {
+ uint32_t class_bits : 5;
+ uint32_t class_decided : 1;
+
+ int c[5];
+
+ ClassificationData() {
+ class_bits = FACE_ANY_BIT;
+ class_decided = 0;
+ memset(c, 0, sizeof(c));
+ }
+ };
+
+ struct hash_classification {
+ size_t operator()(const ClassificationKey &f) const {
+ return (size_t)f.first ^ (size_t)f.second;
+ }
+ };
+
+ typedef std::unordered_map<ClassificationKey, ClassificationData, hash_classification> Classification;
+
+
+ struct hash_group_ptr {
+ size_t operator()(const FaceLoopGroup * const &f) const {
+ return (size_t)f;
+ }
+ };
+
+
+ typedef std::pair<size_t, const carve::mesh::MeshSet<3>::vertex_t *> PerimKey;
+
+ struct hash_perim_key {
+ size_t operator()(const PerimKey &v) const {
+ return (size_t)v.first ^ (size_t)v.second;
+ }
+ };
+
+ typedef std::unordered_map<std::pair<size_t, const carve::mesh::MeshSet<3>::vertex_t *>,
+ std::unordered_set<FaceLoopGroup *, hash_group_ptr>,
+ hash_perim_key> PerimMap;
+
+
+
+ struct hash_group_pair {
+ size_t operator()(const std::pair<int, const FaceLoopGroup *> &v) const {
+ return (size_t)v.first ^ (size_t)v.second;
+ }
+ };
+
+ typedef std::unordered_map<const FaceLoopGroup *,
+ std::unordered_set<std::pair<int, const FaceLoopGroup *>, hash_group_pair>,
+ hash_group_ptr> CandidateOnMap;
+
+
+
+ static inline void remove(carve::mesh::MeshSet<3>::vertex_t *a,
+ carve::mesh::MeshSet<3>::vertex_t *b,
+ carve::csg::detail::VVSMap &shared_edge_graph) {
+ carve::csg::detail::VVSMap::iterator i = shared_edge_graph.find(a);
+ CARVE_ASSERT(i != shared_edge_graph.end());
+ size_t n = (*i).second.erase(b);
+ CARVE_ASSERT(n == 1);
+ if ((*i).second.size() == 0) shared_edge_graph.erase(i);
+ }
+
+
+
+ static inline void remove(V2 edge,
+ carve::csg::detail::VVSMap &shared_edge_graph) {
+ remove(edge.first, edge.second, shared_edge_graph);
+ remove(edge.second, edge.first, shared_edge_graph);
+ }
+
+
+
+ static void walkGraphSegment(carve::csg::detail::VVSMap &shared_edge_graph,
+ const carve::csg::detail::VSet &branch_points,
+ V2 initial,
+ const carve::csg::detail::LoopEdges & /* a_edge_map */,
+ const carve::csg::detail::LoopEdges & /* b_edge_map */,
+ std::list<V2> &out) {
+ V2 curr;
+ curr = initial;
+ bool closed = false;
+
+ out.clear();
+ for (;;) {
+ // walk forward.
+ out.push_back(curr);
+ remove(curr, shared_edge_graph);
+
+ if (curr.second == initial.first) { closed = true; break; }
+ if (branch_points.find(curr.second) != branch_points.end()) break;
+ carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.second);
+ if (o == shared_edge_graph.end()) break;
+ CARVE_ASSERT((*o).second.size() == 1);
+ curr.first = curr.second;
+ curr.second = *((*o).second.begin());
+ // test here that the set of incident groups hasn't changed.
+ }
+
+ if (!closed) {
+ // walk backward.
+ curr = initial;
+ for (;;) {
+ if (branch_points.find(curr.first) != branch_points.end()) break;
+ carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.first);
+ if (o == shared_edge_graph.end()) break;
+ curr.second = curr.first;
+ curr.first = *((*o).second.begin());
+ // test here that the set of incident groups hasn't changed.
+
+ out.push_front(curr);
+ remove(curr, shared_edge_graph);
+ }
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "intersection segment: " << out.size() << " edges." << std::endl;
+#if defined(DEBUG_DRAW_INTERSECTION_LINE)
+ {
+ static float H = 0.0, S = 1.0, V = 1.0;
+ float r, g, b;
+
+ H = fmod((H + .37), 1.0);
+ S = 0.5 + fmod((S - 0.37), 0.5);
+ carve::colour::HSV2RGB(H, S, V, r, g, b);
+
+ if (out.size() > 1) {
+ drawEdges(out.begin(), ++out.begin(),
+ 0.0, 0.0, 0.0, 1.0,
+ r, g, b, 1.0,
+ 3.0);
+ drawEdges(++out.begin(), --out.end(),
+ r, g, b, 1.0,
+ r, g, b, 1.0,
+ 3.0);
+ drawEdges(--out.end(), out.end(),
+ r, g, b, 1.0,
+ 1.0, 1.0, 1.0, 1.0,
+ 3.0);
+ } else {
+ drawEdges(out.begin(), out.end(),
+ r, g, b, 1.0,
+ r, g, b, 1.0,
+ 3.0);
+ }
+ }
+#endif
+#endif
+ }
+
+
+
+ static carve::geom3d::Vector perpendicular(const carve::geom3d::Vector &v) {
+ if (fabs(v.x) < fabs(v.y)) {
+ if (fabs(v.x) < fabs(v.z)) {
+ return cross(v, carve::geom::VECTOR(1.0, 0.0, 0.0)).normalized();
+ } else {
+ return cross(v, carve::geom::VECTOR(0.0, 0.0, 1.0)).normalized();
+ }
+ } else {
+ if (fabs(v.y) < fabs(v.z)) {
+ return cross(v, carve::geom::VECTOR(0.0, 1.0, 0.0)).normalized();
+ } else {
+ return cross(v, carve::geom::VECTOR(1.0, 0.0, 1.0)).normalized();
+ }
+ }
+ }
+
+
+
+ static void classifyAB(const GrpEdgeSurfMap &a_edge_surfaces,
+ const GrpEdgeSurfMap &b_edge_surfaces,
+ Classification &classifications) {
+ // two faces in the a surface
+ for (GrpEdgeSurfMap::const_iterator ib = b_edge_surfaces.begin(), eb = b_edge_surfaces.end(); ib != eb; ++ib) {
+
+ if ((*ib).second.fwd) {
+ FaceLoopGroup *b_grp = ((*ib).second.fwd->group);
+
+ for (GrpEdgeSurfMap::const_iterator ia = a_edge_surfaces.begin(), ea = a_edge_surfaces.end(); ia != ea; ++ia) {
+
+ if ((*ia).second.fwd && (*ia).second.rev) {
+ const carve::mesh::MeshSet<3>::mesh_t *a_gid = (*ia).first;
+
+ ClassificationData &data = classifications[std::make_pair(b_grp, a_gid)];
+ if (data.class_decided) continue;
+
+ // an angle between (*ia).fwd_ang and (*ia).rev_ang is outside/above group a.
+ FaceClass fc;
+
+ if (fabs((*ib).second.fwd_ang - (*ia).second.fwd_ang) < ANGLE_EPSILON) {
+ fc = FACE_ON_ORIENT_OUT;
+ } else if (fabs((*ib).second.fwd_ang - (*ia).second.rev_ang) < ANGLE_EPSILON) {
+ fc = FACE_ON_ORIENT_IN;
+ } else {
+ double a1 = (*ia).second.fwd_ang;
+ double a2 = (*ia).second.rev_ang;
+ if (a1 < a2) {
+ if (a1 < (*ib).second.fwd_ang && (*ib).second.fwd_ang < a2) {
+ fc = FACE_IN;
+ } else {
+ fc = FACE_OUT;
+ }
+ } else {
+ if (a2 < (*ib).second.fwd_ang && (*ib).second.fwd_ang < a1) {
+ fc = FACE_OUT;
+ } else {
+ fc = FACE_IN;
+ }
+ }
+ }
+ data.c[fc + 2]++;
+ }
+ }
+ }
+
+ if ((*ib).second.rev) {
+ FaceLoopGroup *b_grp = ((*ib).second.rev->group);
+
+ for (GrpEdgeSurfMap::const_iterator ia = a_edge_surfaces.begin(), ea = a_edge_surfaces.end(); ia != ea; ++ia) {
+
+ if ((*ia).second.fwd && (*ia).second.rev) {
+ const carve::mesh::MeshSet<3>::mesh_t *a_gid = (*ia).first;
+
+ ClassificationData &data = (classifications[std::make_pair(b_grp, a_gid)]);
+ if (data.class_decided) continue;
+
+ // an angle between (*ia).fwd_ang and (*ia).rev_ang is outside/above group a.
+ FaceClass fc;
+
+ if (fabs((*ib).second.rev_ang - (*ia).second.fwd_ang) < ANGLE_EPSILON) {
+ fc = FACE_ON_ORIENT_IN;
+ } else if (fabs((*ib).second.rev_ang - (*ia).second.rev_ang) < ANGLE_EPSILON) {
+ fc = FACE_ON_ORIENT_OUT;
+ } else {
+ double a1 = (*ia).second.fwd_ang;
+ double a2 = (*ia).second.rev_ang;
+ if (a1 < a2) {
+ if (a1 < (*ib).second.rev_ang && (*ib).second.rev_ang < a2) {
+ fc = FACE_IN;
+ } else {
+ fc = FACE_OUT;
+ }
+ } else {
+ if (a2 < (*ib).second.rev_ang && (*ib).second.rev_ang < a1) {
+ fc = FACE_OUT;
+ } else {
+ fc = FACE_IN;
+ }
+ }
+ }
+ data.c[fc + 2]++;
+ }
+ }
+ }
+ }
+ }
+
+
+ static bool processForwardEdgeSurfaces(GrpEdgeSurfMap &edge_surfaces,
+ const std::list<FaceLoop *> &fwd,
+ const carve::geom3d::Vector &edge_vector,
+ const carve::geom3d::Vector &base_vector) {
+ for (std::list<FaceLoop *>::const_iterator i = fwd.begin(), e = fwd.end(); i != e; ++i) {
+ EdgeSurface &es = (edge_surfaces[(*i)->orig_face->mesh]);
+ if (es.fwd != NULL) return false;
+ es.fwd = (*i);
+ es.fwd_ang = carve::geom3d::antiClockwiseAngle((*i)->orig_face->plane.N, base_vector, edge_vector);
+ }
+ return true;
+ }
+
+ static bool processReverseEdgeSurfaces(GrpEdgeSurfMap &edge_surfaces,
+ const std::list<FaceLoop *> &rev,
+ const carve::geom3d::Vector &edge_vector,
+ const carve::geom3d::Vector &base_vector) {
+ for (std::list<FaceLoop *>::const_iterator i = rev.begin(), e = rev.end(); i != e; ++i) {
+ EdgeSurface &es = (edge_surfaces[(*i)->orig_face->mesh]);
+ if (es.rev != NULL) return false;
+ es.rev = (*i);
+ es.rev_ang = carve::geom3d::antiClockwiseAngle(-(*i)->orig_face->plane.N, base_vector, edge_vector);
+ }
+ return true;
+ }
+
+
+
+ static void processOneEdge(const V2 &edge,
+ const carve::csg::detail::LoopEdges &a_edge_map,
+ const carve::csg::detail::LoopEdges &b_edge_map,
+ Classification &a_classification,
+ Classification &b_classification) {
+ GrpEdgeSurfMap a_edge_surfaces;
+ GrpEdgeSurfMap b_edge_surfaces;
+
+ carve::geom3d::Vector edge_vector = (edge.second->v - edge.first->v).normalized();
+ carve::geom3d::Vector base_vector = perpendicular(edge_vector);
+
+ carve::csg::detail::LoopEdges::const_iterator ae_f = a_edge_map.find(edge);
+ carve::csg::detail::LoopEdges::const_iterator ae_r = a_edge_map.find(flip(edge));
+ CARVE_ASSERT(ae_f != a_edge_map.end() || ae_r != a_edge_map.end());
+
+ carve::csg::detail::LoopEdges::const_iterator be_f = b_edge_map.find(edge);
+ carve::csg::detail::LoopEdges::const_iterator be_r = b_edge_map.find(flip(edge));
+ CARVE_ASSERT(be_f != b_edge_map.end() || be_r != b_edge_map.end());
+
+ if (ae_f != a_edge_map.end() && !processForwardEdgeSurfaces(a_edge_surfaces, (*ae_f).second, edge_vector, base_vector)) return;
+ if (ae_r != a_edge_map.end() && !processReverseEdgeSurfaces(a_edge_surfaces, (*ae_r).second, edge_vector, base_vector)) return;
+ if (be_f != b_edge_map.end() && !processForwardEdgeSurfaces(b_edge_surfaces, (*be_f).second, edge_vector, base_vector)) return;
+ if (be_r != b_edge_map.end() && !processReverseEdgeSurfaces(b_edge_surfaces, (*be_r).second, edge_vector, base_vector)) return;
+
+ classifyAB(a_edge_surfaces, b_edge_surfaces, b_classification);
+ classifyAB(b_edge_surfaces, a_edge_surfaces, a_classification);
+ }
+
+
+
+ static void traceIntersectionGraph(const V2Set &shared_edges,
+ const FLGroupList & /* a_loops_grouped */,
+ const FLGroupList & /* b_loops_grouped */,
+ const carve::csg::detail::LoopEdges &a_edge_map,
+ const carve::csg::detail::LoopEdges &b_edge_map) {
+
+ carve::csg::detail::VVSMap shared_edge_graph;
+ carve::csg::detail::VSet branch_points;
+
+ // first, make the intersection graph.
+ for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) {
+ const V2Set::key_type &edge = (*i);
+ carve::csg::detail::VVSMap::mapped_type &out = (shared_edge_graph[edge.first]);
+ out.insert(edge.second);
+ if (out.size() == 3) branch_points.insert(edge.first);
+
+#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_INTERSECTION_LINE)
+ HOOK(drawEdge(edge.first, edge.second, 1, 1, 1, 1, 1, 1, 1, 1, 1.0););
+#endif
+ }
+#if defined(CARVE_DEBUG)
+ std::cerr << "graph nodes: " << shared_edge_graph.size() << std::endl;
+ std::cerr << "branch nodes: " << branch_points.size() << std::endl;
+#endif
+
+ std::list<V2> out;
+ while (shared_edge_graph.size()) {
+ carve::csg::detail::VVSMap::iterator i = shared_edge_graph.begin();
+ carve::mesh::MeshSet<3>::vertex_t *v1 = (*i).first;
+ carve::mesh::MeshSet<3>::vertex_t *v2 = *((*i).second.begin());
+ walkGraphSegment(shared_edge_graph, branch_points, V2(v1, v2), a_edge_map, b_edge_map, out);
+ }
+ }
+
+ void hashByPerimeter(FLGroupList &grp, PerimMap &perim_map) {
+ for (FLGroupList::iterator i = grp.begin(); i != grp.end(); ++i) {
+ size_t perim_size = (*i).perimeter.size();
+ // can be the case for non intersecting groups. (and groups that intersect at a point?)
+ if (!perim_size) continue;
+ const carve::mesh::MeshSet<3>::vertex_t *perim_min = std::min_element((*i).perimeter.begin(), (*i).perimeter.end())->first;
+ perim_map[std::make_pair(perim_size, perim_min)].insert(&(*i));
+ }
+ }
+
+
+
+ bool same_edge_set_fwd(const V2Set &a, const V2Set &b) {
+ if (a.size() != b.size()) return false;
+ for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) {
+ if (b.find(*i) == b.end()) return false;
+ }
+ return true;
+ }
+
+
+
+ bool same_edge_set_rev(const V2Set &a, const V2Set &b) {
+ if (a.size() != b.size()) return false;
+ for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) {
+ if (b.find(std::make_pair((*i).second, (*i).first)) == b.end()) return false;
+ }
+ return true;
+ }
+
+
+
+ int same_edge_set(const V2Set &a, const V2Set &b) {
+ if (same_edge_set_fwd(a, b)) return +1;
+ if (same_edge_set_rev(a, b)) return -1;
+ return 0;
+ }
+
+
+
+ void generateCandidateOnSets(FLGroupList &a_grp,
+ FLGroupList &b_grp,
+ CandidateOnMap &candidate_on_map,
+ Classification &a_classification,
+ Classification &b_classification) {
+ PerimMap a_grp_by_perim, b_grp_by_perim;
+
+ hashByPerimeter(a_grp, a_grp_by_perim);
+ hashByPerimeter(b_grp, b_grp_by_perim);
+
+ for (PerimMap::iterator i = a_grp_by_perim.begin(), ie = a_grp_by_perim.end(); i != ie; ++i) {
+ PerimMap::iterator j = b_grp_by_perim.find((*i).first);
+ if (j == b_grp_by_perim.end()) continue;
+
+ for (PerimMap::mapped_type::iterator a = (*i).second.begin(), ae = (*i).second.end(); a != ae; ++a) {
+ for (PerimMap::mapped_type::iterator b = (*j).second.begin(), be = (*j).second.end(); b != be; ++b) {
+ int x = same_edge_set((*a)->perimeter, (*b)->perimeter);
+ if (!x) continue;
+ candidate_on_map[(*a)].insert(std::make_pair(x, (*b)));
+ if ((*a)->face_loops.count == 1 && (*b)->face_loops.count == 1) {
+ uint32_t fcb = x == +1 ? FACE_ON_ORIENT_OUT_BIT : FACE_ON_ORIENT_IN_BIT;
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "paired groups: " << (*a) << ", " << (*b) << std::endl;
+#endif
+
+ ClassificationData &a_data = a_classification[std::make_pair((*a), (*b)->face_loops.head->orig_face->mesh)];
+ a_data.class_bits = fcb; a_data.class_decided = 1;
+
+ ClassificationData &b_data = b_classification[std::make_pair((*b), (*a)->face_loops.head->orig_face->mesh)];
+ b_data.class_bits = fcb; b_data.class_decided = 1;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+
+ static inline std::string CODE(const FaceLoopGroup *grp) {
+ const std::list<ClassificationInfo> &cinfo = (grp->classification);
+ if (cinfo.size() == 0) {
+ return "?";
+ }
+
+ FaceClass fc = FACE_UNCLASSIFIED;
+
+ for (std::list<ClassificationInfo>::const_iterator i = grp->classification.begin(), e = grp->classification.end(); i != e; ++i) {
+ if ((*i).intersected_mesh == NULL) {
+ // classifier only returns global info
+ fc = (*i).classification;
+ break;
+ }
+
+ if ((*i).intersectedMeshIsClosed()) {
+ if ((*i).classification == FACE_UNCLASSIFIED) continue;
+ if (fc == FACE_UNCLASSIFIED) {
+ fc = (*i).classification;
+ } else if (fc != (*i).classification) {
+ return "X";
+ }
+ }
+ }
+ if (fc == FACE_IN) return "I";
+ if (fc == FACE_ON_ORIENT_IN) return "<";
+ if (fc == FACE_ON_ORIENT_OUT) return ">";
+ if (fc == FACE_OUT) return "O";
+ return "*";
+ }
+
+ void CSG::classifyFaceGroupsEdge(const V2Set &shared_edges,
+ VertexClassification &vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const face_rtree_t *poly_a_rtree,
+ FLGroupList &a_loops_grouped,
+ const detail::LoopEdges &a_edge_map,
+ carve::mesh::MeshSet<3> *poly_b,
+ const face_rtree_t *poly_b_rtree,
+ FLGroupList &b_loops_grouped,
+ const detail::LoopEdges &b_edge_map,
+ CSG::Collector &collector) {
+ Classification a_classification;
+ Classification b_classification;
+
+ CandidateOnMap candidate_on_map;
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "a input loops (" << a_loops_grouped.size() << "): ";
+ for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ std::cerr << &*i << " ";
+ }
+ std::cerr << std::endl;
+ std::cerr << "b input loops (" << b_loops_grouped.size() << "): ";
+ for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ std::cerr << &*i << " ";
+ }
+ std::cerr << std::endl;
+#endif
+
+#if defined(DISPLAY_GRP_GRAPH)
+ // XXX: this is hopelessly inefficient.
+ std::map<const FaceLoopGroup *, std::set<const FaceLoopGroup *> > grp_graph_fwd, grp_graph_rev;
+ {
+ for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ FaceLoopGroup *src = &(*i);
+ for (V2Set::const_iterator k = src->perimeter.begin(); k != src->perimeter.end(); ++k) {
+ V2 fwd = *k;
+ V2 rev = std::make_pair(fwd.second, fwd.first);
+ for (FLGroupList::iterator j = a_loops_grouped.begin(); j != a_loops_grouped.end(); ++j) {
+ FaceLoopGroup *tgt = &(*j);
+ if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); }
+ if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); }
+ }
+ for (FLGroupList::iterator j = b_loops_grouped.begin(); j != b_loops_grouped.end(); ++j) {
+ FaceLoopGroup *tgt = &(*j);
+ if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); }
+ if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); }
+ }
+ }
+ }
+ for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ FaceLoopGroup *src = &(*i);
+ for (V2Set::const_iterator k = src->perimeter.begin(); k != src->perimeter.end(); ++k) {
+ V2 fwd = *k;
+ V2 rev = std::make_pair(fwd.second, fwd.first);
+ for (FLGroupList::iterator j = a_loops_grouped.begin(); j != a_loops_grouped.end(); ++j) {
+ FaceLoopGroup *tgt = &(*j);
+ if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); }
+ if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); }
+ }
+ for (FLGroupList::iterator j = b_loops_grouped.begin(); j != b_loops_grouped.end(); ++j) {
+ FaceLoopGroup *tgt = &(*j);
+ if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); }
+ if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); }
+ }
+ }
+ }
+ }
+#endif
+
+ generateCandidateOnSets(a_loops_grouped, b_loops_grouped, candidate_on_map, a_classification, b_classification);
+
+
+ for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) {
+ const V2 &edge = (*i);
+ processOneEdge(edge, a_edge_map, b_edge_map, a_classification, b_classification);
+ }
+
+
+ for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) {
+ if (!(*i).second.class_decided) {
+ if ((*i).second.c[FACE_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_IN_BIT;
+ if ((*i).second.c[FACE_ON_ORIENT_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_IN_BIT;
+ if ((*i).second.c[FACE_ON_ORIENT_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_OUT_BIT;
+ if ((*i).second.c[FACE_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_OUT_BIT;
+
+ // XXX: this is the wrong thing to do. It's intended just as a test.
+ if ((*i).second.class_bits == (FACE_IN_BIT | FACE_OUT_BIT)) {
+ if ((*i).second.c[FACE_OUT + 2] > (*i).second.c[FACE_IN + 2]) {
+ (*i).second.class_bits = FACE_OUT_BIT;
+ } else {
+ (*i).second.class_bits = FACE_IN_BIT;
+ }
+ }
+
+ if (single_bit_set((*i).second.class_bits)) (*i).second.class_decided = 1;
+ }
+ }
+
+ for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) {
+ if (!(*i).second.class_decided) {
+ if ((*i).second.c[FACE_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_IN_BIT;
+ if ((*i).second.c[FACE_ON_ORIENT_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_IN_BIT;
+ if ((*i).second.c[FACE_ON_ORIENT_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_OUT_BIT;
+ if ((*i).second.c[FACE_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_OUT_BIT;
+
+ // XXX: this is the wrong thing to do. It's intended just as a test.
+ if ((*i).second.class_bits == (FACE_IN_BIT | FACE_OUT_BIT)) {
+ if ((*i).second.c[FACE_OUT + 2] > (*i).second.c[FACE_IN + 2]) {
+ (*i).second.class_bits = FACE_OUT_BIT;
+ } else {
+ (*i).second.class_bits = FACE_IN_BIT;
+ }
+ }
+
+ if (single_bit_set((*i).second.class_bits)) (*i).second.class_decided = 1;
+ }
+ }
+
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "poly a:" << std::endl;
+ for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) {
+ FaceLoopGroup *grp = ((*i).first.first);
+
+ std::cerr << " group: " << grp << " gid: " << (*i).first.second
+ << " "
+ << ((*i).second.class_decided ? "+" : "-")
+ << " "
+ << ((*i).second.class_bits & FACE_IN_BIT ? "I" : ".")
+ << ((*i).second.class_bits & FACE_ON_ORIENT_IN_BIT ? "<" : ".")
+ << ((*i).second.class_bits & FACE_ON_ORIENT_OUT_BIT ? ">" : ".")
+ << ((*i).second.class_bits & FACE_OUT_BIT ? "O" : ".")
+ << " ["
+ << std::setw(4) << (*i).second.c[0] << " "
+ << std::setw(4) << (*i).second.c[1] << " "
+ << std::setw(4) << (*i).second.c[2] << " "
+ << std::setw(4) << (*i).second.c[3] << " "
+ << std::setw(4) << (*i).second.c[4] << "]" << std::endl;
+ }
+
+ std::cerr << "poly b:" << std::endl;
+ for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) {
+ FaceLoopGroup *grp = ((*i).first.first);
+
+ std::cerr << " group: " << grp << " gid: " << (*i).first.second
+ << " "
+ << ((*i).second.class_decided ? "+" : "-")
+ << " "
+ << ((*i).second.class_bits & FACE_IN_BIT ? "I" : ".")
+ << ((*i).second.class_bits & FACE_ON_ORIENT_IN_BIT ? "<" : ".")
+ << ((*i).second.class_bits & FACE_ON_ORIENT_OUT_BIT ? ">" : ".")
+ << ((*i).second.class_bits & FACE_OUT_BIT ? "O" : ".")
+ << " ["
+ << std::setw(4) << (*i).second.c[0] << " "
+ << std::setw(4) << (*i).second.c[1] << " "
+ << std::setw(4) << (*i).second.c[2] << " "
+ << std::setw(4) << (*i).second.c[3] << " "
+ << std::setw(4) << (*i).second.c[4] << "]" << std::endl;
+ }
+#endif
+
+ for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) {
+ FaceLoopGroup *grp = ((*i).first.first);
+
+ grp->classification.push_back(ClassificationInfo());
+ ClassificationInfo &info = grp->classification.back();
+
+ info.intersected_mesh = (*i).first.second;
+
+ if ((*i).second.class_decided) {
+ info.classification = class_bit_to_class((*i).second.class_bits);
+ } else {
+ info.classification = FACE_UNCLASSIFIED;
+ }
+ }
+
+ for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) {
+ FaceLoopGroup *grp = ((*i).first.first);
+
+ grp->classification.push_back(ClassificationInfo());
+ ClassificationInfo &info = grp->classification.back();
+
+ info.intersected_mesh = (*i).first.second;
+
+ if ((*i).second.class_decided) {
+ info.classification = class_bit_to_class((*i).second.class_bits);
+ } else {
+ info.classification = FACE_UNCLASSIFIED;
+ }
+ }
+
+ for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ if ((*i).classification.size() == 0) {
+#if defined(CARVE_DEBUG)
+ std::cerr << " non intersecting group (poly a): " << &(*i) << std::endl;
+#endif
+ bool classified = false;
+ for (FaceLoop *fl = (*i).face_loops.head; !classified && fl != NULL; fl = fl->next) {
+ for (size_t fli = 0; !classified && fli < fl->vertices.size(); ++fli) {
+ if (vclass[fl->vertices[fli]].cls[1] == POINT_UNK) {
+ vclass[fl->vertices[fli]].cls[1] = carve::mesh::classifyPoint(poly_b, poly_b_rtree, fl->vertices[fli]->v);
+ }
+ switch (vclass[fl->vertices[fli]].cls[1]) {
+ case POINT_IN:
+ (*i).classification.push_back(ClassificationInfo(NULL, FACE_IN));
+ classified = true;
+ break;
+ case POINT_OUT:
+ (*i).classification.push_back(ClassificationInfo(NULL, FACE_OUT));
+ classified = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (!classified) {
+ throw carve::exception("non intersecting group is not IN or OUT! (poly_a)");
+ }
+ }
+ }
+
+ for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ if ((*i).classification.size() == 0) {
+#if defined(CARVE_DEBUG)
+ std::cerr << " non intersecting group (poly b): " << &(*i) << std::endl;
+#endif
+ bool classified = false;
+ for (FaceLoop *fl = (*i).face_loops.head; !classified && fl != NULL; fl = fl->next) {
+ for (size_t fli = 0; !classified && fli < fl->vertices.size(); ++fli) {
+ if (vclass[fl->vertices[fli]].cls[0] == POINT_UNK) {
+ vclass[fl->vertices[fli]].cls[0] = carve::mesh::classifyPoint(poly_a, poly_a_rtree, fl->vertices[fli]->v);
+ }
+ switch (vclass[fl->vertices[fli]].cls[0]) {
+ case POINT_IN:
+ (*i).classification.push_back(ClassificationInfo(NULL, FACE_IN));
+ classified = true;
+ break;
+ case POINT_OUT:
+ (*i).classification.push_back(ClassificationInfo(NULL, FACE_OUT));
+ classified = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (!classified) {
+ throw carve::exception("non intersecting group is not IN or OUT! (poly_b)");
+ }
+ }
+ }
+
+#if defined(DISPLAY_GRP_GRAPH)
+#define POLY(grp) (std::string((grp)->face_loops.head->orig_face->polyhedron == poly_a ? "[A:" : "[B:") + CODE(grp) + "]")
+
+ for (std::map<const FaceLoopGroup *, std::set<const FaceLoopGroup *> >::iterator i = grp_graph_fwd.begin(); i != grp_graph_fwd.end(); ++i) {
+ const FaceLoopGroup *grp = (*i).first;
+
+ std::cerr << "GRP: " << grp << POLY(grp) << std::endl;
+
+ std::set<const FaceLoopGroup *> &fwd_set = grp_graph_fwd[grp];
+ std::set<const FaceLoopGroup *> &rev_set = grp_graph_rev[grp];
+ std::cerr << " FWD: ";
+ for (std::set<const FaceLoopGroup *>::const_iterator j = fwd_set.begin(); j != fwd_set.end(); ++j) {
+ std::cerr << " " << (*j) << POLY(*j);
+ }
+ std::cerr << std::endl;
+ std::cerr << " REV: ";
+ for (std::set<const FaceLoopGroup *>::const_iterator j = rev_set.begin(); j != rev_set.end(); ++j) {
+ std::cerr << " " << (*j) << POLY(*j);
+ }
+ std::cerr << std::endl;
+ }
+#endif
+
+ for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) {
+ collector.collect(&*i, hooks);
+ }
+
+ for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) {
+ collector.collect(&*i, hooks);
+ }
+
+ // traceIntersectionGraph(shared_edges, a_loops_grouped, b_loops_grouped, a_edge_map, b_edge_map);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/intersect_classify_group.cpp b/extern/carve/lib/intersect_classify_group.cpp
new file mode 100644
index 00000000000..4251af63f89
--- /dev/null
+++ b/extern/carve/lib/intersect_classify_group.cpp
@@ -0,0 +1,220 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/debug_hooks.hpp>
+
+#include <list>
+#include <set>
+#include <iostream>
+
+#include <algorithm>
+
+#include "intersect_common.hpp"
+#include "intersect_classify_common.hpp"
+#include "intersect_classify_common_impl.hpp"
+
+
+namespace carve {
+ namespace csg {
+
+ namespace {
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+ // VC++ 6.0 gets an internal compiler when compiling
+ // the FaceMaker template. Not sure why but for now we just bypass
+ // the template
+ class FaceMaker0 {
+ public:
+ CSG::Collector &collector;
+ CSG::Hooks &hooks;
+
+ FaceMaker0(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) {
+ }
+ bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const {
+ return vclass[f->vertices[index]].cls[1] == POINT_ON;
+ }
+ void explain(FaceLoop *f, size_t index, PointClass pc) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "face loop " << f << " from poly " << "ab"[0] << " is easy because vertex " << index << " (" << *f->vertices[index] << ") is " << ENUM(pc) << std::endl;
+#endif
+ }
+ };
+ class FaceMaker1 {
+ public:
+ CSG::Collector &collector;
+ CSG::Hooks &hooks;
+
+ FaceMaker1(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) {
+ }
+ bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const {
+ return vclass[f->vertices[index]].cls[0] == POINT_ON;
+ }
+ void explain(FaceLoop *f, size_t index, PointClass pc) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "face loop " << f << " from poly " << "ab"[1] << " is easy because vertex " << index << " (" << *f->vertices[index] << ") is " << ENUM(pc) << std::endl;
+#endif
+ }
+ };
+#else
+ template <int poly_num>
+ class FaceMaker {
+ FaceMaker &operator=(const FaceMaker &);
+
+ public:
+ CSG::Collector &collector;
+ CSG::Hooks &hooks;
+
+ FaceMaker(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) {
+ }
+
+ bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const {
+ return vclass[f->vertices[index]].cls[1 - poly_num] == POINT_ON;
+ }
+
+ void explain(FaceLoop *f, size_t index, PointClass pc) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "face loop " << f << " from poly " << "ab"[poly_num] << " is easy because vertex " << index << " (" << f->vertices[index]->v << ") is " << ENUM(pc) << std::endl;
+#endif
+ }
+ };
+ typedef FaceMaker<0> FaceMaker0;
+ typedef FaceMaker<1> FaceMaker1;
+#endif
+ class ClassifyFaceGroups {
+ ClassifyFaceGroups &operator=(const ClassifyFaceGroups &);
+
+ public:
+ CSG::Collector &collector;
+ CSG::Hooks &hooks;
+
+ ClassifyFaceGroups(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) {
+ }
+
+ void classifySimple(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ carve::mesh::MeshSet<3> *poly_b) const {
+ if (a_loops_grouped.size() < b_loops_grouped.size()) {
+ performClassifySimpleOnFaceGroups(a_loops_grouped, b_loops_grouped, poly_a, poly_b, collector, hooks);
+ } else {
+ performClassifySimpleOnFaceGroups(b_loops_grouped, a_loops_grouped, poly_b, poly_a, collector, hooks);
+ }
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of simple on groups: " << a_loops_grouped.size() << " a groups" << std::endl;
+ std::cerr << "after removal of simple on groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ void classifyEasy(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification &vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ performClassifyEasyFaceGroups(a_loops_grouped, poly_b, poly_b_rtree, vclass, FaceMaker0(collector, hooks), collector, hooks);
+ performClassifyEasyFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, vclass, FaceMaker1(collector, hooks), collector, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of easy groups: " << a_loops_grouped.size() << " a groups" << std::endl;
+ std::cerr << "after removal of easy groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ void classifyHard(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ performClassifyHardFaceGroups(a_loops_grouped, poly_b, poly_b_rtree, FaceMaker0(collector, hooks), collector, hooks);
+ performClassifyHardFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, FaceMaker1(collector, hooks), collector, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of hard groups: " << a_loops_grouped.size() << " a groups" << std::endl;
+ std::cerr << "after removal of hard groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ void faceLoopWork(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ performFaceLoopWork(poly_b, poly_b_rtree, a_loops_grouped, *this, collector, hooks);
+ performFaceLoopWork(poly_a, poly_a_rtree, b_loops_grouped, *this, collector, hooks);
+ }
+
+ void postRemovalCheck(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of on groups: " << a_loops_grouped.size() << " a groups" << std::endl;
+ std::cerr << "after removal of on groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ bool faceLoopSanityChecker(FaceLoopGroup &i) const {
+ return i.face_loops.size() != 1;
+ }
+
+ void finish(FLGroupList &a_loops_grouped,FLGroupList &b_loops_grouped) const {
+#if defined(CARVE_DEBUG)
+ if (a_loops_grouped.size() || b_loops_grouped.size())
+ std::cerr << "UNCLASSIFIED! a=" << a_loops_grouped.size() << ", b=" << b_loops_grouped.size() << std::endl;
+#endif
+ }
+ };
+ }
+
+ void CSG::classifyFaceGroups(const V2Set & /* shared_edges */,
+ VertexClassification &vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ FLGroupList &a_loops_grouped,
+ const detail::LoopEdges & /* a_edge_map */,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree,
+ FLGroupList &b_loops_grouped,
+ const detail::LoopEdges & /* b_edge_map */,
+ CSG::Collector &collector) {
+ ClassifyFaceGroups classifier(collector, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "initial groups: " << a_loops_grouped.size() << " a groups" << std::endl;
+ std::cerr << "initial groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ performClassifyFaceGroups(
+ a_loops_grouped,
+ b_loops_grouped,
+ vclass,
+ poly_a,
+ poly_a_rtree,
+ poly_b,
+ poly_b_rtree,
+ classifier,
+ collector,
+ hooks);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/intersect_common.hpp b/extern/carve/lib/intersect_common.hpp
new file mode 100644
index 00000000000..06f3cfdd4ec
--- /dev/null
+++ b/extern/carve/lib/intersect_common.hpp
@@ -0,0 +1,83 @@
+// Begin License:
+// Copyright (C) 2006-2011 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:
+
+
+#pragma once
+
+
+static inline bool facesAreCoplanar(const carve::mesh::MeshSet<3>::face_t *a, const carve::mesh::MeshSet<3>::face_t *b) {
+ carve::geom3d::Ray temp;
+ // XXX: Find a better definition. This may be a source of problems
+ // if floating point inaccuracies cause an incorrect answer.
+ return !carve::geom3d::planeIntersection(a->plane, b->plane, temp);
+}
+
+#if defined(CARVE_DEBUG)
+
+#include <carve/debug_hooks.hpp>
+
+#endif
+
+namespace carve {
+ namespace csg {
+
+ static inline carve::mesh::MeshSet<3>::vertex_t *map_vertex(const VVMap &vmap, carve::mesh::MeshSet<3>::vertex_t *v) {
+ VVMap::const_iterator i = vmap.find(v);
+ if (i == vmap.end()) return v;
+ return (*i).second;
+ }
+
+#if defined(CARVE_DEBUG)
+
+ class IntersectDebugHooks;
+ extern IntersectDebugHooks *g_debug;
+
+#define HOOK(x) do { if (g_debug) { g_debug->x } } while(0)
+
+ static inline void drawFaceLoopList(const FaceLoopList &ll,
+ float rF, float gF, float bF, float aF,
+ float rB, float gB, float bB, float aB,
+ bool lit) {
+ for (FaceLoop *flb = ll.head; flb; flb = flb->next) {
+ const carve::mesh::MeshSet<3>::face_t *f = (flb->orig_face);
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = flb->vertices;
+ HOOK(drawFaceLoop2(loop, f->plane.N, rF, gF, bF, aF, rB, gB, bB, aB, true, lit););
+ HOOK(drawFaceLoopWireframe(loop, f->plane.N, 1, 1, 1, 0.1f););
+ }
+ }
+
+ static inline void drawFaceLoopListWireframe(const FaceLoopList &ll) {
+ for (FaceLoop *flb = ll.head; flb; flb = flb->next) {
+ const carve::mesh::MeshSet<3>::face_t *f = (flb->orig_face);
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = flb->vertices;
+ HOOK(drawFaceLoopWireframe(loop, f->plane.N, 1, 1, 1, 0.1f););
+ }
+ }
+
+ template<typename T>
+ static inline void drawEdges(T begin, T end,
+ float rB, float gB, float bB, float aB,
+ float rE, float gE, float bE, float aE,
+ float w) {
+ for (; begin != end; ++begin) {
+ HOOK(drawEdge((*begin).first, (*begin).second, rB, gB, bB, aB, rE, gE, bE, aE, w););
+ }
+ }
+
+#endif
+
+ }
+}
diff --git a/extern/carve/lib/intersect_debug.cpp b/extern/carve/lib/intersect_debug.cpp
new file mode 100644
index 00000000000..c16854d5655
--- /dev/null
+++ b/extern/carve/lib/intersect_debug.cpp
@@ -0,0 +1,65 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+
+#include <list>
+#include <set>
+#include <iostream>
+
+#include <algorithm>
+
+#include "intersect_debug.hpp"
+
+namespace carve {
+ namespace csg {
+
+#if defined(CARVE_DEBUG)
+
+#define DEBUG_DRAW_FACE_EDGES
+#define DEBUG_DRAW_INTERSECTIONS
+// #define DEBUG_DRAW_OCTREE
+#define DEBUG_DRAW_INTERSECTION_LINE
+// #define DEBUG_DRAW_GROUPS
+// #define DEBUG_PRINT_RESULT_FACES
+
+ IntersectDebugHooks *g_debug = NULL;
+
+ IntersectDebugHooks *intersect_installDebugHooks(IntersectDebugHooks *hooks) {
+ IntersectDebugHooks *h = g_debug;
+ g_debug = hooks;
+ return h;
+ }
+
+ bool intersect_debugEnabled() { return true; }
+
+#else
+
+ IntersectDebugHooks *intersect_installDebugHooks(IntersectDebugHooks * /* hooks */) {
+ return NULL;
+ }
+
+ bool intersect_debugEnabled() { return false; }
+
+#endif
+
+ }
+}
diff --git a/extern/carve/lib/intersect_debug.hpp b/extern/carve/lib/intersect_debug.hpp
new file mode 100644
index 00000000000..be73a7c3dae
--- /dev/null
+++ b/extern/carve/lib/intersect_debug.hpp
@@ -0,0 +1,29 @@
+// Begin License:
+// Copyright (C) 2006-2011 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/debug_hooks.hpp>
+
+#if defined(CARVE_DEBUG)
+
+#define DEBUG_DRAW_FACE_EDGES
+#define DEBUG_DRAW_INTERSECTIONS
+// #define DEBUG_DRAW_OCTREE
+#define DEBUG_DRAW_INTERSECTION_LINE
+// #define DEBUG_DRAW_GROUPS
+// #define DEBUG_PRINT_RESULT_FACES
+
+#endif
diff --git a/extern/carve/lib/intersect_face_division.cpp b/extern/carve/lib/intersect_face_division.cpp
new file mode 100644
index 00000000000..6f2aa65ed67
--- /dev/null
+++ b/extern/carve/lib/intersect_face_division.cpp
@@ -0,0 +1,1709 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <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 {
+
+
+
+ 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;
+
+ {
+ // 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--;
+ }
+ }
+ 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
+ }
+
+
+
+ /**
+ * \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 void assembleBaseLoop(carve::mesh::MeshSet<3>::face_t *face,
+ const carve::csg::detail::Data &data,
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop) {
+ 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;
+ 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++]);
+ }
+ }
+ e = e->next;
+ } while (e != face->edge);
+ }
+
+
+
+ // 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::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &loops,
+ 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));
+ }
+
+ // 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
+
+
+ // 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));
+ std::cerr << "HITS THIS CODE - area=" << area << std::endl;
+ 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]);
+ }
+ }
+
+ // 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
+
+ // sort paths by increasing beginning point and decreasing ending point.
+ std::sort(cross.begin(), cross.end());
+ std::sort(noncross.begin(), noncross.end());
+
+ // 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
+ }
+ }
+ }
+
+ 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.
+ 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() && !loops.size()) {
+ 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);
+ }
+ }
+
+ // for each loop, just test with any point.
+ for (size_t j = 0; j < loops.size(); ++j) {
+ test = face->project(loops[j].front()->v);
+
+ if (proj_aabb[i].intersects(test) &&
+ carve::geom2d::pointInPoly(proj[i], test).iclass != carve::POINT_OUT) {
+ inc.push_back(&loops[j]);
+ }
+ }
+
+#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 *> > &loops) {
+ using namespace carve::csg;
+
+ detail::VVSMap vertex_graph;
+ detail::VSet endpoints;
+
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> path;
+
+ std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > temp;
+
+ // 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);
+ }
+ }
+
+ // 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]);
+ }
+ }
+
+ 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());
+
+ temp.push_back(path);
+ }
+
+ populateVectorFromList(temp, paths);
+ temp.clear();
+
+ // 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;
+ }
+
+ temp.push_back(path);
+ }
+ populateVectorFromList(temp, loops);
+ }
+
+
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ void dumpFacesAndHoles(const std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops,
+ const std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops) {
+ std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t> v_included;
+
+ for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator
+ i = face_loops.begin(); i != face_loops.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 (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator
+ i = hole_loops.begin(); i != hole_loops.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 (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator
+ i = face_loops.begin(); i != face_loops.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 (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator
+ i = hole_loops.begin(); i != hole_loops.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());
+ }
+ }
+
+ std::string out("/tmp/hole_merge.ply");
+ ::writePLY(out, &fh, true);
+ }
+#endif
+
+
+
+ template<typename T>
+ std::string ptrstr(const T *ptr) {
+ std::ostringstream s;
+ s << ptr;
+ return s.str().substr(1);
+ }
+
+ 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";
+ }
+
+ 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;
+
+ assembleBaseLoop(face, data, base_loop);
+
+ 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()) {
+
+ 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;
+ }
+ }
+
+ std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > paths;
+ 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, loops);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "### paths.size(): " << paths.size() << std::endl;
+ std::cerr << "### loops.size(): " << loops.size() << std::endl;
+#endif
+
+ if (!paths.size()) {
+ // Loops found by composeEdgesIntoPaths() can't touch the
+ // boundary, or each other, so we can deal with the no paths
+ // case simply. The hole loops are the loops produced by
+ // composeEdgesIntoPaths() oriented so that their signed area
+ // wrt. the face is negative. The face loops are the base loop
+ // plus the hole loops, reversed.
+ face_loops.push_back(base_loop);
+
+ 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);
+ }
+ } else {
+ if (!processCrossingEdges(face, vertex_intersections, hooks, base_loop, paths, loops, face_loops)) {
+ // 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 (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);
+ }
+ }
+ }
+ }
+
+
+
+}
+
+
+
+/**
+ * \brief Build a set of face loops for all (split) faces of a Polyhedron.
+ *
+ * @param[in] poly The polyhedron to process
+ * @param vmap
+ * @param face_split_edges
+ * @param divided_edges
+ * @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;
+}
diff --git a/extern/carve/lib/intersect_group.cpp b/extern/carve/lib/intersect_group.cpp
new file mode 100644
index 00000000000..a1528569c01
--- /dev/null
+++ b/extern/carve/lib/intersect_group.cpp
@@ -0,0 +1,232 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/timing.hpp>
+
+#include "csg_detail.hpp"
+#include "intersect_common.hpp"
+
+void carve::csg::CSG::makeEdgeMap(const carve::csg::FaceLoopList &loops,
+ size_t edge_count,
+ detail::LoopEdges &edge_map) {
+#if defined(UNORDERED_COLLECTIONS_SUPPORT_RESIZE)
+ edge_map.resize(edge_count);
+#endif
+
+ for (carve::csg::FaceLoop *i = loops.head; i; i = i->next) {
+ edge_map.addFaceLoop(i);
+ i->group = NULL;
+ }
+}
+
+#include <carve/polyline.hpp>
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii);
+void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii);
+#endif
+
+void carve::csg::CSG::findSharedEdges(const detail::LoopEdges &edge_map_a,
+ const detail::LoopEdges &edge_map_b,
+ V2Set &shared_edges) {
+ for (detail::LoopEdges::const_iterator
+ i = edge_map_a.begin(), e = edge_map_a.end();
+ i != e;
+ ++i) {
+ detail::LoopEdges::const_iterator j = edge_map_b.find((*i).first);
+ if (j != edge_map_b.end()) {
+ shared_edges.insert((*i).first);
+ }
+ }
+
+#if defined(CARVE_DEBUG)
+ detail::VVSMap edge_graph;
+
+ for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) {
+ edge_graph[(*i).first].insert((*i).second);
+ edge_graph[(*i).second].insert((*i).first);
+ }
+
+ std::cerr << "*** testing consistency of edge graph" << std::endl;
+ for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) {
+ if ((*i).second.size() > 2) {
+ std::cerr << "branch at: " << (*i).first << std::endl;
+ }
+ if ((*i).second.size() == 1) {
+ std::cerr << "endpoint at: " << (*i).first << std::endl;
+ std::cerr << "coordinate: " << (*i).first->v << std::endl;
+ }
+ }
+
+ {
+ carve::line::PolylineSet intersection_graph;
+ intersection_graph.vertices.resize(edge_graph.size());
+ std::map<const carve::mesh::MeshSet<3>::vertex_t *, size_t> vmap;
+
+ size_t j = 0;
+ for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) {
+ intersection_graph.vertices[j].v = (*i).first->v;
+ vmap[(*i).first] = j++;
+ }
+
+ while (edge_graph.size()) {
+ detail::VVSMap::iterator prior_i = edge_graph.begin();
+ carve::mesh::MeshSet<3>::vertex_t *prior = (*prior_i).first;
+ std::vector<size_t> connected;
+ connected.push_back(vmap[prior]);
+ while (prior_i != edge_graph.end() && (*prior_i).second.size()) {
+ carve::mesh::MeshSet<3>::vertex_t *next = *(*prior_i).second.begin();
+ detail::VVSMap::iterator next_i = edge_graph.find(next);
+ CARVE_ASSERT(next_i != edge_graph.end());
+ connected.push_back(vmap[next]);
+ (*prior_i).second.erase(next);
+ (*next_i).second.erase(prior);
+ if (!(*prior_i).second.size()) { edge_graph.erase(prior_i); prior_i = edge_graph.end(); }
+ if (!(*next_i).second.size()) { edge_graph.erase(next_i); next_i = edge_graph.end(); }
+ prior_i = next_i;
+ prior = next;
+ }
+ bool closed = connected.front() == connected.back();
+ for (size_t k = 0; k < connected.size(); ++k) {
+ std::cerr << " " << connected[k];
+ }
+ std::cerr << std::endl;
+ intersection_graph.addPolyline(closed, connected.begin(), connected.end());
+ }
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ std::string out("/tmp/intersection.ply");
+ ::writePLY(out, &intersection_graph, true);
+#endif
+ }
+
+ std::cerr << "*** edge graph consistency test done" << std::endl;
+#endif
+}
+
+
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+static carve::mesh::MeshSet<3> *groupToPolyhedron(const carve::csg::FaceLoopGroup &grp) {
+ const carve::csg::FaceLoopList &fl = grp.face_loops;
+ std::vector<carve::mesh::MeshSet<3>::face_t *> faces;
+ faces.reserve(fl.size());
+ for (carve::csg::FaceLoop *f = fl.head; f; f = f->next) {
+ faces.push_back(f->orig_face->create(f->vertices.begin(), f->vertices.end(), false));
+ }
+ carve::mesh::MeshSet<3> *poly = new carve::mesh::MeshSet<3>(faces);
+
+ poly->canonicalize();
+ return poly;
+}
+#endif
+
+
+
+void carve::csg::CSG::groupFaceLoops(carve::mesh::MeshSet<3> *src,
+ carve::csg::FaceLoopList &face_loops,
+ const carve::csg::detail::LoopEdges &loop_edges,
+ const carve::csg::V2Set &no_cross,
+ carve::csg::FLGroupList &out_loops) {
+ // Find all the groups of face loops that are connected by edges
+ // that are not part of no_cross.
+ // this could potentially be done with a disjoint set data-structure.
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ static int call_num = 0;
+ call_num++;
+#endif
+
+ static carve::TimingName GROUP_FACE_LOOPS("groupFaceLoops()");
+
+ carve::TimingBlock block(GROUP_FACE_LOOPS);
+
+ int tag_num = 0;
+ while (face_loops.size()) {
+ out_loops.push_back(FaceLoopGroup(src));
+ carve::csg::FaceLoopGroup &group = (out_loops.back());
+ carve::csg::FaceLoopList &curr = (group.face_loops);
+ carve::csg::V2Set &perim = (group.perimeter);
+
+ carve::csg::FaceLoop *expand = face_loops.head;
+
+ expand->group = &group;
+ face_loops.remove(expand);
+ curr.append(expand);
+
+ while (expand) {
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (expand->vertices);
+ carve::mesh::MeshSet<3>::vertex_t *v1, *v2;
+
+ v1 = loop.back();
+ for (size_t i = 0; i < loop.size(); ++i) {
+ v2 = loop[i];
+
+ carve::csg::V2Set::const_iterator nc = no_cross.find(std::make_pair(v1, v2));
+ if (nc == no_cross.end()) {
+ carve::csg::detail::LoopEdges::const_iterator j;
+
+ j = loop_edges.find(std::make_pair(v1, v2));
+ if (j != loop_edges.end()) {
+ for (std::list<carve::csg::FaceLoop *>::const_iterator
+ k = (*j).second.begin(), ke = (*j).second.end();
+ k != ke; ++k) {
+ if ((*k)->group != NULL ||
+ (*k)->orig_face->mesh != expand->orig_face->mesh) continue;
+ face_loops.remove((*k));
+ curr.append((*k));
+ (*k)->group = &group;
+ }
+ }
+
+ j = loop_edges.find(std::make_pair(v2, v1));
+ if (j != loop_edges.end()) {
+ for (std::list<carve::csg::FaceLoop *>::const_iterator
+ k = (*j).second.begin(), ke = (*j).second.end();
+ k != ke; ++k) {
+ if ((*k)->group != NULL ||
+ (*k)->orig_face->mesh != expand->orig_face->mesh) continue;
+ face_loops.remove((*k));
+ curr.append((*k));
+ (*k)->group = &group;
+ }
+ }
+ } else {
+ perim.insert(std::make_pair(v1, v2));
+ }
+ v1 = v2;
+ }
+ expand = expand->next;
+ }
+ tag_num++;
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ {
+ carve::mesh::MeshSet<3> *poly = groupToPolyhedron(group);
+ char buf[128];
+ sprintf(buf, "/tmp/group-%d-%p.ply", call_num, &curr);
+ std::string out(buf);
+ ::writePLY(out, poly, false);
+ delete poly;
+ }
+#endif
+ }
+}
diff --git a/extern/carve/lib/intersect_half_classify_group.cpp b/extern/carve/lib/intersect_half_classify_group.cpp
new file mode 100644
index 00000000000..97915c784a0
--- /dev/null
+++ b/extern/carve/lib/intersect_half_classify_group.cpp
@@ -0,0 +1,199 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/debug_hooks.hpp>
+
+#include <list>
+#include <set>
+#include <iostream>
+
+#include <algorithm>
+
+#include "intersect_common.hpp"
+#include "intersect_classify_common.hpp"
+#include "intersect_classify_common_impl.hpp"
+
+namespace carve {
+ namespace csg {
+
+ namespace {
+ struct GroupPoly : public CSG::Collector {
+ carve::mesh::MeshSet<3> *want_groups_from;
+ std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &out;
+
+ GroupPoly(carve::mesh::MeshSet<3> *poly,
+ std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &_out) : CSG::Collector(), want_groups_from(poly), out(_out) {
+ }
+
+ virtual ~GroupPoly() {
+ }
+
+ virtual void collect(FaceLoopGroup *grp, CSG::Hooks & /* hooks */) {
+ if (grp->face_loops.head->orig_face->mesh->meshset != want_groups_from) return;
+
+ std::list<ClassificationInfo> &cinfo = (grp->classification);
+ if (cinfo.size() == 0) {
+ std::cerr << "WARNING! group " << grp << " has no classification info!" << std::endl;
+ return;
+ }
+ // XXX: check all the cinfo elements for consistency.
+ FaceClass fc = cinfo.front().classification;
+
+ std::vector<carve::mesh::MeshSet<3>::face_t *> faces;
+ faces.reserve(grp->face_loops.size());
+ for (FaceLoop *loop = grp->face_loops.head; loop != NULL; loop = loop->next) {
+ faces.push_back(loop->orig_face->create(loop->vertices.begin(), loop->vertices.end(), false));
+ }
+
+ out.push_back(std::make_pair(fc, new carve::mesh::MeshSet<3>(faces)));
+ }
+
+ virtual carve::mesh::MeshSet<3> *done(CSG::Hooks & /* hooks */) {
+ return NULL;
+ }
+ };
+
+ class FaceMaker {
+ public:
+
+ bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const {
+ return vclass[f->vertices[index]].cls[0] == POINT_ON;
+ }
+
+ void explain(FaceLoop *f, size_t index, PointClass pc) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "face loop " << f << " from poly b is easy because vertex " << index << " (" << f->vertices[index]->v << ") is " << ENUM(pc) << std::endl;
+#endif
+ }
+ };
+
+ class HalfClassifyFaceGroups {
+ HalfClassifyFaceGroups &operator=(const HalfClassifyFaceGroups &);
+
+ public:
+ std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &b_out;
+ CSG::Hooks &hooks;
+
+ HalfClassifyFaceGroups(std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &c, CSG::Hooks &h) : b_out(c), hooks(h) {
+ }
+
+ void classifySimple(FLGroupList &a_loops_grouped,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ carve::mesh::MeshSet<3> *poly_b) const {
+ GroupPoly group_poly(poly_b, b_out);
+ performClassifySimpleOnFaceGroups(a_loops_grouped, b_loops_grouped, poly_a, poly_b, group_poly, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of simple on groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ void classifyEasy(FLGroupList & /* a_loops_grouped */,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ GroupPoly group_poly(poly_b, b_out);
+ performClassifyEasyFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, vclass, FaceMaker(), group_poly, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of easy groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ void classifyHard(FLGroupList & /* a_loops_grouped */,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ GroupPoly group_poly(poly_b, b_out);
+ performClassifyHardFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, FaceMaker(), group_poly, hooks);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of hard groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+
+ }
+
+ void faceLoopWork(FLGroupList & /* a_loops_grouped */,
+ FLGroupList &b_loops_grouped,
+ VertexClassification & /* vclass */,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const {
+ GroupPoly group_poly(poly_b, b_out);
+ performFaceLoopWork(poly_a, poly_a_rtree, b_loops_grouped, *this, group_poly, hooks);
+ }
+
+ void postRemovalCheck(FLGroupList & /* a_loops_grouped */,
+ FLGroupList &b_loops_grouped) const {
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removal of on groups: " << b_loops_grouped.size() << " b groups" << std::endl;
+#endif
+ }
+
+ bool faceLoopSanityChecker(FaceLoopGroup &i) const {
+ return false;
+ return i.face_loops.size() != 1;
+ }
+
+ void finish(FLGroupList &a_loops_grouped,FLGroupList &b_loops_grouped) const {
+#if defined(CARVE_DEBUG)
+ if (a_loops_grouped.size() || b_loops_grouped.size())
+ std::cerr << "UNCLASSIFIED! a=" << a_loops_grouped.size() << ", b=" << b_loops_grouped.size() << std::endl;
+#endif
+ }
+ };
+ }
+
+ void CSG::halfClassifyFaceGroups(const V2Set & /* shared_edges */,
+ VertexClassification &vclass,
+ carve::mesh::MeshSet<3> *poly_a,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree,
+ FLGroupList &a_loops_grouped,
+ const detail::LoopEdges & /* a_edge_map */,
+ carve::mesh::MeshSet<3> *poly_b,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree,
+ FLGroupList &b_loops_grouped,
+ const detail::LoopEdges & /* b_edge_map */,
+ std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &b_out) {
+ HalfClassifyFaceGroups classifier(b_out, hooks);
+ GroupPoly group_poly(poly_b, b_out);
+ performClassifyFaceGroups(
+ a_loops_grouped,
+ b_loops_grouped,
+ vclass,
+ poly_a,
+ poly_a_rtree,
+ poly_b,
+ poly_b_rtree,
+ classifier,
+ group_poly,
+ hooks);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/intersection.cpp b/extern/carve/lib/intersection.cpp
new file mode 100644
index 00000000000..2aa97131f7f
--- /dev/null
+++ b/extern/carve/lib/intersection.cpp
@@ -0,0 +1,92 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <algorithm>
+
+#include <carve/carve.hpp>
+#include <carve/poly.hpp>
+#include <carve/timing.hpp>
+#include <carve/intersection.hpp>
+
+
+
+void carve::csg::Intersections::collect(const IObj &obj,
+ std::vector<carve::mesh::MeshSet<3>::vertex_t *> *collect_v,
+ std::vector<carve::mesh::MeshSet<3>::edge_t *> *collect_e,
+ std::vector<carve::mesh::MeshSet<3>::face_t *> *collect_f) const {
+ carve::csg::Intersections::const_iterator i = find(obj);
+ if (i != end()) {
+ Intersections::mapped_type::const_iterator a, b;
+ for (a = (*i).second.begin(), b = (*i).second.end(); a != b; ++a) {
+ switch ((*a).first.obtype) {
+ case carve::csg::IObj::OBTYPE_VERTEX:
+ if (collect_v) collect_v->push_back((*a).first.vertex);
+ break;
+ case carve::csg::IObj::OBTYPE_EDGE:
+ if (collect_e) collect_e->push_back((*a).first.edge);
+ break;
+ case carve::csg::IObj::OBTYPE_FACE:
+ if (collect_f) collect_f->push_back((*a).first.face);
+ break;
+ default:
+ throw carve::exception("should not happen " __FILE__ ":" XSTR(__LINE__));
+ }
+ }
+ }
+}
+
+
+
+bool carve::csg::Intersections::intersectsFace(carve::mesh::MeshSet<3>::vertex_t *v,
+ carve::mesh::MeshSet<3>::face_t *f) const {
+ const_iterator i = find(v);
+ if (i != end()) {
+ mapped_type::const_iterator a, b;
+
+ for (a = (*i).second.begin(), b = (*i).second.end(); a != b; ++a) {
+ switch ((*a).first.obtype) {
+ case IObj::OBTYPE_VERTEX: {
+ const carve::mesh::MeshSet<3>::edge_t *edge = f->edge;
+ do {
+ if (edge->vert == (*a).first.vertex) return true;
+ edge = edge->next;
+ } while (edge != f->edge);
+ break;
+ }
+ case carve::csg::IObj::OBTYPE_EDGE: {
+ const carve::mesh::MeshSet<3>::edge_t *edge = f->edge;
+ do {
+ if (edge == (*a).first.edge) return true;
+ edge = edge->next;
+ } while (edge != f->edge);
+ break;
+ }
+ case carve::csg::IObj::OBTYPE_FACE: {
+ if ((*a).first.face == f) return true;
+ break;
+ }
+ default:
+ throw carve::exception("should not happen " __FILE__ ":" XSTR(__LINE__));
+ }
+ }
+ }
+ return false;
+}
diff --git a/extern/carve/lib/math.cpp b/extern/carve/lib/math.cpp
new file mode 100644
index 00000000000..811312c313e
--- /dev/null
+++ b/extern/carve/lib/math.cpp
@@ -0,0 +1,347 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/math.hpp>
+#include <carve/matrix.hpp>
+
+#include <iostream>
+#include <limits>
+
+#include <stdio.h>
+
+#define M_2PI_3 2.0943951023931953
+#define M_SQRT_3_4 0.8660254037844386
+#define EPS std::numeric_limits<double>::epsilon()
+
+namespace carve {
+ namespace math {
+
+ struct Root {
+ double root;
+ int multiplicity;
+
+ Root(double r) : root(r), multiplicity(1) {}
+ Root(double r, int m) : root(r), multiplicity(m) {}
+ };
+
+ void cplx_sqrt(double re, double im,
+ double &re_1, double &im_1,
+ double &re_2, double &im_2) {
+ if (re == 0.0 && im == 0.0) {
+ re_1 = re_2 = re;
+ im_1 = im_2 = im;
+ } else {
+ double d = sqrt(re * re + im * im);
+ re_1 = sqrt((d + re) / 2.0);
+ re_2 = re_1;
+ im_1 = fabs(sqrt((d - re) / 2.0));
+ im_2 = -im_1;
+ }
+ }
+
+ void cplx_cbrt(double re, double im,
+ double &re_1, double &im_1,
+ double &re_2, double &im_2,
+ double &re_3, double &im_3) {
+ if (re == 0.0 && im == 0.0) {
+ re_1 = re_2 = re_3 = re;
+ im_1 = im_2 = im_3 = im;
+ } else {
+ double r = cbrt(sqrt(re * re + im * im));
+ double t = atan2(im, re) / 3.0;
+ re_1 = r * cos(t);
+ im_1 = r * sin(t);
+ re_2 = r * cos(t + M_TWOPI / 3.0);
+ im_2 = r * sin(t + M_TWOPI / 3.0);
+ re_3 = r * cos(t + M_TWOPI * 2.0 / 3.0);
+ im_3 = r * sin(t + M_TWOPI * 2.0 / 3.0);
+ }
+ }
+
+ void add_root(std::vector<Root> &roots, double root) {
+ for (size_t i = 0; i < roots.size(); ++i) {
+ if (roots[i].root == root) {
+ roots[i].multiplicity++;
+ return;
+ }
+ }
+ roots.push_back(Root(root));
+ }
+
+ void linear_roots(double c1, double c0, std::vector<Root> &roots) {
+ roots.push_back(Root(c0 / c1));
+ }
+
+ void quadratic_roots(double c2, double c1, double c0, std::vector<Root> &roots) {
+ if (fabs(c2) < EPS) {
+ linear_roots(c1, c0, roots);
+ return;
+ }
+
+ double p = 0.5 * c1 / c2;
+ double dis = p * p - c0 / c2;
+
+ if (dis > 0.0) {
+ dis = sqrt(dis);
+ if (-p - dis != -p + dis) {
+ roots.push_back(Root(-p - dis));
+ roots.push_back(Root(-p + dis));
+ } else {
+ roots.push_back(Root(-p, 2));
+ }
+ }
+ }
+
+ void cubic_roots(double c3, double c2, double c1, double c0, std::vector<Root> &roots) {
+ int n_sol = 0;
+ double _r[3];
+
+ if (fabs(c3) < EPS) {
+ quadratic_roots(c2, c1, c0, roots);
+ return;
+ }
+
+ if (fabs(c0) < EPS) {
+ quadratic_roots(c3, c2, c1, roots);
+ add_root(roots, 0.0);
+ return;
+ }
+
+ double xN = -c2 / (3.0 * c3);
+ double yN = c0 + xN * (c1 + xN * (c2 + c3 * xN));
+
+ double delta_sq = (c2 * c2 - 3.0 * c3 * c1) / (9.0 * c3 * c3);
+ double h_sq = 4.0 / 9.0 * (c2 * c2 - 3.0 * c3 * c1) * (delta_sq * delta_sq);
+ double dis = yN * yN - h_sq;
+
+ if (dis > EPS) {
+ // One real root, two complex roots.
+
+ double dis_sqrt = sqrt(dis);
+ double r_p = yN - dis_sqrt;
+ double r_q = yN + dis_sqrt;
+ double p = cbrt(fabs(r_p)/(2.0 * c3));
+ double q = cbrt(fabs(r_q)/(2.0 * c3));
+
+ if (r_p > 0.0) p = -p;
+ if (r_q > 0.0) q = -q;
+
+ _r[0] = xN + p + q;
+ n_sol = 1;
+
+ double re = xN - p * .5 - q * .5;
+ double im = p * M_SQRT_3_4 - q * M_SQRT_3_4;
+
+ // root 2: xN + p * exp(M_2PI_3.i) + q * exp(-M_2PI_3.i);
+ // root 3: complex conjugate of root 2
+
+ if (im < EPS) {
+ _r[1] = _r[2] = re;
+ n_sol += 2;
+ }
+ } else if (dis < -EPS) {
+ // Three distinct real roots.
+ double theta = acos(-yN / sqrt(h_sq)) / 3.0;
+ double delta = sqrt(c2 * c2 - 3.0 * c3 * c1) / (3.0 * c3);
+
+ _r[0] = xN + (2.0 * delta) * cos(theta);
+ _r[1] = xN + (2.0 * delta) * cos(M_2PI_3 - theta);
+ _r[2] = xN + (2.0 * delta) * cos(M_2PI_3 + theta);
+ n_sol = 3;
+ } else {
+ // Three real roots (two or three equal).
+ double r = yN / (2.0 * c3);
+ double delta = cbrt(r);
+
+ _r[0] = xN + delta;
+ _r[1] = xN + delta;
+ _r[2] = xN - 2.0 * delta;
+ n_sol = 3;
+ }
+
+ for (int i=0; i < n_sol; i++) {
+ add_root(roots, _r[i]);
+ }
+ }
+
+ static void U(const Matrix3 &m,
+ double l,
+ double u[6],
+ double &u_max,
+ int &u_argmax) {
+ u[0] = (m._22 - l) * (m._33 - l) - m._23 * m._23;
+ u[1] = m._13 * m._23 - m._12 * (m._33 - l);
+ u[2] = m._12 * m._23 - m._13 * (m._22 - l);
+ u[3] = (m._11 - l) * (m._33 - l) - m._13 * m._13;
+ u[4] = m._12 * m._13 - m._23 * (m._11 - l);
+ u[5] = (m._11 - l) * (m._22 - l) - m._12 * m._12;
+
+ u_max = -1.0;
+ u_argmax = -1;
+
+ for (int i = 0; i < 6; ++i) {
+ if (u_max < fabs(u[i])) { u_max = fabs(u[i]); u_argmax = i; }
+ }
+ }
+
+ static void eig1(const Matrix3 &m, double l, carve::geom::vector<3> &e) {
+ double u[6];
+ double u_max;
+ int u_argmax;
+
+ U(m, l, u, u_max, u_argmax);
+
+ switch(u_argmax) {
+ case 0:
+ e.x = u[0]; e.y = u[1]; e.z = u[2]; break;
+ case 1: case 3:
+ e.x = u[1]; e.y = u[3]; e.z = u[4]; break;
+ case 2: case 4: case 5:
+ e.x = u[2]; e.y = u[4]; e.z = u[5]; break;
+ }
+ e.normalize();
+ }
+
+ static void eig2(const Matrix3 &m, double l, carve::geom::vector<3> &e1, carve::geom::vector<3> &e2) {
+ double u[6];
+ double u_max;
+ int u_argmax;
+
+ U(m, l, u, u_max, u_argmax);
+
+ switch(u_argmax) {
+ case 0: case 1:
+ e1.x = -m._12; e1.y = m._11; e1.z = 0.0;
+ e2.x = -m._13 * m._11; e2.y = -m._13 * m._12; e2.z = m._11 * m._11 + m._12 * m._12;
+ break;
+ case 2:
+ e1.x = m._12; e1.y = 0.0; e1.z = -m._11;
+ e2.x = -m._12 * m._11; e2.y = m._11 * m._11 + m._13 * m._13; e2.z = -m._12 * m._13;
+ break;
+ case 3: case 4:
+ e1.x = 0.0; e1.y = -m._23; e1.z = -m._22;
+ e2.x = m._22 * m._22 + m._23 * m._23; e2.y = -m._12 * m._22; e2.z = -m._12 * m._23;
+ break;
+ case 5:
+ e1.x = 0.0; e1.y = -m._33; e1.z = m._23;
+ e2.x = m._23 * m._23 + m._33 * m._33; e2.y = -m._13 * m._23; e2.z = -m._13 * m._33;
+ }
+ e1.normalize();
+ e2.normalize();
+ }
+
+ static void eig3(const Matrix3 &m,
+ double l,
+ carve::geom::vector<3> &e1,
+ carve::geom::vector<3> &e2,
+ carve::geom::vector<3> &e3) {
+ e1.x = 1.0; e1.y = 0.0; e1.z = 0.0;
+ e2.x = 0.0; e2.y = 1.0; e2.z = 0.0;
+ e3.x = 0.0; e3.y = 0.0; e3.z = 1.0;
+ }
+
+ void eigSolveSymmetric(const Matrix3 &m,
+ double &l1, carve::geom::vector<3> &e1,
+ double &l2, carve::geom::vector<3> &e2,
+ double &l3, carve::geom::vector<3> &e3) {
+ double c0 =
+ m._11 * m._22 * m._33 +
+ 2.0 * m._12 * m._13 * m._23 -
+ m._11 * m._23 * m._23 -
+ m._22 * m._13 * m._13 -
+ m._33 * m._12 * m._12;
+ double c1 =
+ m._11 * m._22 -
+ m._12 * m._12 +
+ m._11 * m._33 -
+ m._13 * m._13 +
+ m._22 * m._33 -
+ m._23 * m._23;
+ double c2 =
+ m._11 +
+ m._22 +
+ m._33;
+
+ double a = (3.0 * c1 - c2 * c2) / 3.0;
+ double b = (-2.0 * c2 * c2 * c2 + 9.0 * c1 * c2 - 27.0 * c0) / 27.0;
+
+ double Q = b * b / 4.0 + a * a * a / 27.0;
+
+ if (fabs(Q) < 1e-16) {
+ l1 = m._11; e1.x = 1.0; e1.y = 0.0; e1.z = 0.0;
+ l2 = m._22; e2.x = 0.0; e2.y = 1.0; e2.z = 0.0;
+ l3 = m._33; e3.x = 0.0; e3.y = 0.0; e3.z = 1.0;
+ } else if (Q > 0) {
+ l1 = l2 = c2 / 3.0 + cbrt(b / 2.0);
+ l3 = c2 / 3.0 - 2.0 * cbrt(b / 2.0);
+
+ eig2(m, l1, e1, e2);
+ eig1(m, l3, e3);
+ } else if (Q < 0) {
+ double t = atan2(sqrt(-Q), -b / 2.0);
+ double cos_t3 = cos(t / 3.0);
+ double sin_t3 = sin(t / 3.0);
+ double r = cbrt(sqrt(b * b / 4.0 - Q));
+
+ l1 = c2 / 3.0 + 2 * r * cos_t3;
+ l2 = c2 / 3.0 - r * (cos_t3 + M_SQRT_3 * sin_t3);
+ l3 = c2 / 3.0 - r * (cos_t3 - M_SQRT_3 * sin_t3);
+
+ eig1(m, l1, e1);
+ eig1(m, l2, e2);
+ eig1(m, l3, e3);
+ }
+ }
+
+ void eigSolve(const Matrix3 &m, double &l1, double &l2, double &l3) {
+ double c3, c2, c1, c0;
+ std::vector<Root> roots;
+
+ c3 = -1.0;
+ c2 = m._11 + m._22 + m._33;
+ c1 =
+ -(m._22 * m._33 + m._11 * m._22 + m._11 * m._33)
+ +(m._23 * m._32 + m._13 * m._31 + m._12 * m._21);
+ c0 =
+ +(m._11 * m._22 - m._12 * m._21) * m._33
+ -(m._11 * m._23 - m._13 * m._21) * m._32
+ +(m._12 * m._23 - m._13 * m._22) * m._31;
+
+ cubic_roots(c3, c2, c1, c0, roots);
+
+ for (size_t i = 0; i < roots.size(); i++) {
+ Matrix3 M(m);
+ M._11 -= roots[i].root;
+ M._22 -= roots[i].root;
+ M._33 -= roots[i].root;
+ // solve M.v = 0
+ }
+
+ std::cerr << "n_roots=" << roots.size() << std::endl;
+ for (size_t i = 0; i < roots.size(); i++) {
+ fprintf(stderr, " %.24f(%d)", roots[i].root, roots[i].multiplicity);
+ }
+ std::cerr << std::endl;
+ }
+
+ }
+}
+
diff --git a/extern/carve/lib/mesh.cpp b/extern/carve/lib/mesh.cpp
new file mode 100644
index 00000000000..55ab893c10a
--- /dev/null
+++ b/extern/carve/lib/mesh.cpp
@@ -0,0 +1,1203 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/mesh.hpp>
+#include <carve/mesh_impl.hpp>
+#include <carve/rtree.hpp>
+
+#include <carve/poly.hpp>
+
+namespace {
+ inline double CALC_X(const carve::geom::plane<3> &p, double y, double z) { return -(p.d + p.N.y * y + p.N.z * z) / p.N.x; }
+ inline double CALC_Y(const carve::geom::plane<3> &p, double x, double z) { return -(p.d + p.N.x * x + p.N.z * z) / p.N.y; }
+ inline double CALC_Z(const carve::geom::plane<3> &p, double x, double y) { return -(p.d + p.N.x * x + p.N.y * y) / p.N.z; }
+
+ carve::geom::vector<2> _project_1(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.z, v.y);
+ }
+
+ carve::geom::vector<2> _project_2(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.x, v.z);
+ }
+
+ carve::geom::vector<2> _project_3(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.y, v.x);
+ }
+
+ carve::geom::vector<2> _project_4(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.y, v.z);
+ }
+
+ carve::geom::vector<2> _project_5(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.z, v.x);
+ }
+
+ carve::geom::vector<2> _project_6(const carve::geom::vector<3> &v) {
+ return carve::geom::VECTOR(v.x, v.y);
+ }
+
+ carve::geom::vector<3> _unproject_1(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(CALC_X(plane, p.y, p.x), p.y, p.x);
+ }
+
+ carve::geom::vector<3> _unproject_2(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(p.x, CALC_Y(plane, p.x, p.y), p.y);
+ }
+
+ carve::geom::vector<3> _unproject_3(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(p.y, p.x, CALC_Z(plane, p.y, p.x));
+ }
+
+ carve::geom::vector<3> _unproject_4(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(CALC_X(plane, p.x, p.y), p.x, p.y);
+ }
+
+ carve::geom::vector<3> _unproject_5(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(p.y, CALC_Y(plane, p.y, p.x), p.x);
+ }
+
+ carve::geom::vector<3> _unproject_6(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) {
+ return carve::geom::VECTOR(p.x, p.y, CALC_Z(plane, p.x, p.y));
+ }
+
+ static carve::geom::vector<2> (*project_tab[2][3])(const carve::geom::vector<3> &) = {
+ { &_project_1, &_project_2, &_project_3 },
+ { &_project_4, &_project_5, &_project_6 }
+ };
+
+ static carve::geom::vector<3> (*unproject_tab[2][3])(const carve::geom::vector<2> &, const carve::geom3d::Plane &) = {
+ { &_unproject_1, &_unproject_2, &_unproject_3 },
+ { &_unproject_4, &_unproject_5, &_unproject_6 }
+ };
+
+}
+
+namespace carve {
+ namespace mesh {
+
+
+
+ template<unsigned ndim>
+ typename Face<ndim>::project_t Face<ndim>::getProjector(bool positive_facing, int axis) const {
+ return NULL;
+ }
+
+
+
+ template<>
+ Face<3>::project_t Face<3>::getProjector(bool positive_facing, int axis) const {
+ return project_tab[positive_facing ? 1 : 0][axis];
+ }
+
+
+
+ template<unsigned ndim>
+ typename Face<ndim>::unproject_t Face<ndim>::getUnprojector(bool positive_facing, int axis) const {
+ return NULL;
+ }
+
+
+
+ template<>
+ Face<3>::unproject_t Face<3>::getUnprojector(bool positive_facing, int axis) const {
+ return unproject_tab[positive_facing ? 1 : 0][axis];
+ }
+
+
+
+ template<unsigned ndim>
+ bool Face<ndim>::containsPoint(const vector_t &p) const {
+ if (!carve::math::ZERO(carve::geom::distance(plane, p))) return false;
+ // return pointInPolySimple(vertices, projector(), (this->*project)(p));
+ std::vector<carve::geom::vector<2> > verts;
+ getProjectedVertices(verts);
+ return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT;
+ }
+
+
+
+ template<unsigned ndim>
+ bool Face<ndim>::containsPointInProjection(const vector_t &p) const {
+ std::vector<carve::geom::vector<2> > verts;
+ getProjectedVertices(verts);
+ return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT;
+ }
+
+
+
+ template<unsigned ndim>
+ bool Face<ndim>::simpleLineSegmentIntersection(
+ const carve::geom::linesegment<ndim> &line,
+ vector_t &intersection) const {
+ if (!line.OK()) return false;
+
+ carve::mesh::MeshSet<3>::vertex_t::vector_t p;
+ carve::IntersectionClass intersects =
+ carve::geom3d::lineSegmentPlaneIntersection(plane, line, p);
+ if (intersects == carve::INTERSECT_NONE || intersects == carve::INTERSECT_BAD) {
+ return false;
+ }
+
+ std::vector<carve::geom::vector<2> > verts;
+ getProjectedVertices(verts);
+ if (carve::geom2d::pointInPolySimple(verts, project(p))) {
+ intersection = p;
+ return true;
+ }
+ return false;
+ }
+
+
+
+ template<unsigned ndim>
+ IntersectionClass Face<ndim>::lineSegmentIntersection(const carve::geom::linesegment<ndim> &line,
+ vector_t &intersection) const {
+ if (!line.OK()) return INTERSECT_NONE;
+
+
+ vector_t p;
+ IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane, line, p);
+ if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) {
+ return intersects;
+ }
+
+ std::vector<carve::geom::vector<2> > verts;
+ getProjectedVertices(verts);
+ carve::geom2d::PolyInclusionInfo pi = carve::geom2d::pointInPoly(verts, project(p));
+ switch (pi.iclass) {
+ case POINT_VERTEX:
+ intersection = p;
+ return INTERSECT_VERTEX;
+
+ case POINT_EDGE:
+ intersection = p;
+ return INTERSECT_EDGE;
+
+ case POINT_IN:
+ intersection = p;
+ return INTERSECT_FACE;
+
+ case POINT_OUT:
+ return INTERSECT_NONE;
+
+ default:
+ break;
+ }
+ return INTERSECT_NONE;
+ }
+
+
+
+ template<unsigned ndim>
+ Face<ndim> *Face<ndim>::closeLoop(typename Face<ndim>::edge_t *start) {
+ edge_t *e = start;
+ std::vector<edge_t *> loop_edges;
+ do {
+ CARVE_ASSERT(e->rev == NULL);
+ loop_edges.push_back(e);
+ e = e->perimNext();
+ } while (e != start);
+
+ const size_t N = loop_edges.size();
+ for (size_t i = 0; i < N; ++i) {
+ loop_edges[i]->rev = new edge_t(loop_edges[i]->v2(), NULL);
+ }
+
+ for (size_t i = 0; i < N; ++i) {
+ edge_t *e1 = loop_edges[i]->rev;
+ edge_t *e2 = loop_edges[(i+1)%N]->rev;
+ e1->prev = e2;
+ e2->next = e1;
+ }
+
+ Face *f = new Face(start->rev);
+
+ CARVE_ASSERT(f->n_edges == N);
+
+ return f;
+ }
+
+
+
+ namespace detail {
+
+
+
+ bool FaceStitcher::EdgeOrderData::Cmp::operator()(const EdgeOrderData &a, const EdgeOrderData &b) const {
+ int v = carve::geom3d::compareAngles(edge_dir, base_dir, a.face_dir, b.face_dir);
+ double da = carve::geom3d::antiClockwiseAngle(base_dir, a.face_dir, edge_dir);
+ double db = carve::geom3d::antiClockwiseAngle(base_dir, b.face_dir, edge_dir);
+ int v0 = v;
+ v = 0;
+ if (da < db) v = -1;
+ if (db < da) v = +1;
+ if (v0 != v) {
+ std::cerr << "v0= " << v0 << " v= " << v << " da= " << da << " db= " << db << " " << edge_dir << " " << base_dir << " " << a.face_dir << b.face_dir << std::endl;
+ }
+ if (v < 0) return true;
+ if (v == 0) {
+ if (a.is_reversed && !b.is_reversed) return true;
+ if (a.is_reversed == b.is_reversed) {
+ return a.group_id < b.group_id;
+ }
+ }
+ return false;
+ }
+
+
+
+ void FaceStitcher::matchSimpleEdges() {
+ // join faces that share an edge, where no other faces are incident.
+ for (edge_map_t::iterator i = edges.begin(); i != edges.end(); ++i) {
+ const vpair_t &ev = (*i).first;
+ edge_map_t::iterator j = edges.find(vpair_t(ev.second, ev.first));
+ if (j == edges.end()) {
+ for (edgelist_t::iterator k = (*i).second.begin(); k != (*i).second.end(); ++k) {
+ is_open[ (*k)->face->id] = true;
+ }
+ } else if ((*i).second.size() != 1 || (*j).second.size() != 1) {
+ std::swap(complex_edges[(*i).first], (*i).second);
+ } else {
+ // simple edge.
+ edge_t *a = (*i).second.front();
+ edge_t *b = (*j).second.front();
+ if (a < b) {
+ // every simple edge pair is encountered twice. only merge once.
+ a->rev = b;
+ b->rev = a;
+ face_groups.merge_sets(a->face->id, b->face->id);
+ }
+ }
+ }
+ }
+
+
+
+ size_t FaceStitcher::faceGroupID(const Face<3> *face) {
+ return face_groups.find_set_head(face->id);
+ }
+
+
+
+ size_t FaceStitcher::faceGroupID(const Edge<3> *edge) {
+ return face_groups.find_set_head(edge->face->id);
+ }
+
+
+
+ void FaceStitcher::orderForwardAndReverseEdges(std::vector<std::vector<Edge<3> *> > &efwd,
+ std::vector<std::vector<Edge<3> *> > &erev,
+ std::vector<std::vector<EdgeOrderData> > &result) {
+ const size_t Nfwd = efwd.size();
+ const size_t Nrev = erev.size();
+ const size_t N = efwd[0].size();
+
+ result.resize(N);
+
+ for (size_t i = 0; i < N; ++i) {
+ Edge<3> *base = efwd[0][i];
+
+ result[i].reserve(Nfwd + Nrev);
+ for (size_t j = 0; j < Nfwd; ++j) {
+ result[i].push_back(EdgeOrderData(efwd[j][i], j, false));
+ CARVE_ASSERT(efwd[0][i]->v1() == efwd[j][i]->v1());
+ CARVE_ASSERT(efwd[0][i]->v2() == efwd[j][i]->v2());
+ }
+ for (size_t j = 0; j < Nrev; ++j) {
+ result[i].push_back(EdgeOrderData(erev[j][i], j, true));
+ CARVE_ASSERT(erev[0][i]->v1() == erev[j][i]->v1());
+ CARVE_ASSERT(erev[0][i]->v2() == erev[j][i]->v2());
+ }
+
+ std::sort(result[i].begin(),
+ result[i].end(),
+ EdgeOrderData::Cmp(base->v2()->v - base->v1()->v, result[i][0].face_dir));
+ }
+ }
+
+
+
+ void FaceStitcher::edgeIncidentGroups(const vpair_t &e,
+ const edge_map_t &all_edges,
+ std::pair<std::set<size_t>, std::set<size_t> > &groups) {
+ groups.first.clear();
+ groups.second.clear();
+ edge_map_t::const_iterator i;
+
+ i = all_edges.find(e);
+ if (i != all_edges.end()) {
+ for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
+ groups.first.insert(faceGroupID(*j));
+ }
+ }
+
+ i = all_edges.find(vpair_t(e.second, e.first));
+ if (i != all_edges.end()) {
+ for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
+ groups.second.insert(faceGroupID(*j));
+ }
+ }
+ }
+
+
+
+ void FaceStitcher::buildEdgeGraph(const edge_map_t &all_edges) {
+ for (edge_map_t::const_iterator i = all_edges.begin();
+ i != all_edges.end();
+ ++i) {
+ edge_graph[(*i).first.first].insert((*i).first.second);
+ }
+ }
+
+
+
+ void FaceStitcher::extractPath(std::vector<const vertex_t *> &path) {
+ path.clear();
+
+ edge_graph_t::iterator iter = edge_graph.begin();
+
+
+ const vertex_t *init = (*iter).first;
+ const vertex_t *next = *(*iter).second.begin();
+ const vertex_t *prev = NULL;
+ const vertex_t *vert = init;
+
+ while ((*iter).second.size() == 2) {
+ prev = *std::find_if((*iter).second.begin(),
+ (*iter).second.end(),
+ std::bind2nd(std::not_equal_to<const vertex_t *>(), next));
+ next = vert;
+ vert = prev;
+ iter = edge_graph.find(vert);
+ CARVE_ASSERT(iter != edge_graph.end());
+ if (vert == init) break;
+ }
+ init = vert;
+
+ std::vector<const edge_t *> efwd;
+ std::vector<const edge_t *> erev;
+
+ edge_map_t::iterator edgeiter;
+ edgeiter = complex_edges.find(vpair_t(vert, next));
+ std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(efwd));
+
+ edgeiter = complex_edges.find(vpair_t(next, vert));
+ std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(erev));
+
+ path.push_back(vert);
+
+ prev = vert;
+ vert = next;
+ path.push_back(vert);
+ iter = edge_graph.find(vert);
+ CARVE_ASSERT(iter != edge_graph.end());
+
+ while (vert != init && (*iter).second.size() == 2) {
+ next = *std::find_if((*iter).second.begin(),
+ (*iter).second.end(),
+ std::bind2nd(std::not_equal_to<const vertex_t *>(), prev));
+
+ edgeiter = complex_edges.find(vpair_t(vert, next));
+ if ((*edgeiter).second.size() != efwd.size()) goto done;
+
+ for (size_t i = 0; i < efwd.size(); ++i) {
+ Edge<3> *e_next = efwd[i]->perimNext();
+ if (e_next->v2() != next) goto done;
+ efwd[i] = e_next;
+ }
+
+ edgeiter = complex_edges.find(vpair_t(next, vert));
+ if ((*edgeiter).second.size() != erev.size()) goto done;
+
+ for (size_t i = 0; i < erev.size(); ++i) {
+ Edge<3> *e_prev = erev[i]->perimPrev();
+ if (e_prev->v1() != next) goto done;
+ erev[i] = e_prev;
+ }
+
+ prev = vert;
+ vert = next;
+ path.push_back(vert);
+ iter = edge_graph.find(vert);
+ CARVE_ASSERT(iter != edge_graph.end());
+ }
+ done:;
+ }
+
+
+
+ void FaceStitcher::removePath(const std::vector<const vertex_t *> &path) {
+ for (size_t i = 1; i < path.size() - 1; ++i) {
+ edge_graph.erase(path[i]);
+ }
+
+ edge_graph[path[0]].erase(path[1]);
+ if (edge_graph[path[0]].size() == 0) {
+ edge_graph.erase(path[0]);
+ }
+
+ edge_graph[path[path.size()-1]].erase(path[path.size()-2]);
+ if (edge_graph[path[path.size()-1]].size() == 0) {
+ edge_graph.erase(path[path.size()-1]);
+ }
+ }
+
+
+
+ void FaceStitcher::reorder(std::vector<EdgeOrderData> &ordering,
+ size_t grp) {
+ if (!ordering[0].is_reversed && ordering[0].group_id == grp) return;
+ for (size_t i = 1; i < ordering.size(); ++i) {
+ if (!ordering[i].is_reversed && ordering[i].group_id == grp) {
+ std::vector<EdgeOrderData> temp;
+ temp.reserve(ordering.size());
+ std::copy(ordering.begin() + i, ordering.end(), std::back_inserter(temp));
+ std::copy(ordering.begin(), ordering.begin() + i, std::back_inserter(temp));
+ std::copy(temp.begin(), temp.end(), ordering.begin());
+ return;
+ }
+ }
+ }
+
+
+
+ struct lt_second {
+ template<typename pair_t>
+ bool operator()(const pair_t &a, const pair_t &b) const {
+ return a.second < b.second;
+ }
+ };
+
+
+
+ void FaceStitcher::fuseEdges(std::vector<Edge<3> *> &fwd,
+ std::vector<Edge<3> *> &rev) {
+ for (size_t i = 0; i < fwd.size(); ++i) {
+ fwd[i]->rev = rev[i];
+ rev[i]->rev = fwd[i];
+ face_groups.merge_sets(fwd[i]->face->id, rev[i]->face->id);
+ }
+ }
+
+
+
+ void FaceStitcher::joinGroups(std::vector<std::vector<Edge<3> *> > &efwd,
+ std::vector<std::vector<Edge<3> *> > &erev,
+ size_t fwd_grp,
+ size_t rev_grp) {
+ fuseEdges(efwd[fwd_grp], erev[rev_grp]);
+ }
+
+
+
+ void FaceStitcher::matchOrderedEdges(const std::vector<std::vector<EdgeOrderData> >::iterator begin,
+ const std::vector<std::vector<EdgeOrderData> >::iterator end,
+ std::vector<std::vector<Edge<3> *> > &efwd,
+ std::vector<std::vector<Edge<3> *> > &erev) {
+ typedef std::unordered_map<std::pair<size_t, size_t>, size_t> pair_counts_t;
+ for (;;) {
+ pair_counts_t pair_counts;
+
+ for (std::vector<std::vector<EdgeOrderData> >::iterator i = begin; i != end; ++i) {
+ std::vector<EdgeOrderData> &e = *i;
+ for (size_t j = 0; j < e.size(); ++j) {
+ if (!e[j].is_reversed && e[(j+1)%e.size()].is_reversed) {
+ pair_counts[std::make_pair(e[j].group_id,
+ e[(j+1)%e.size()].group_id)]++;
+ }
+ }
+ }
+
+ if (!pair_counts.size()) break;
+
+ std::vector<std::pair<size_t, std::pair<size_t, size_t> > > counts;
+ counts.reserve(pair_counts.size());
+ for (pair_counts_t::iterator iter = pair_counts.begin(); iter != pair_counts.end(); ++iter) {
+ counts.push_back(std::make_pair((*iter).second, (*iter).first));
+ }
+ std::make_heap(counts.begin(), counts.end());
+
+ std::set<size_t> rem_fwd, rem_rev;
+
+ while (counts.size()) {
+ std::pair<size_t, size_t> join = counts.front().second;
+ std::pop_heap(counts.begin(), counts.end());
+ counts.pop_back();
+ if (rem_fwd.find(join.first) != rem_fwd.end()) continue;
+ if (rem_rev.find(join.second) != rem_rev.end()) continue;
+
+ size_t g1 = join.first;
+ size_t g2 = join.second;
+
+ joinGroups(efwd, erev, g1, g2);
+
+ for (std::vector<std::vector<EdgeOrderData> >::iterator i = begin; i != end; ++i) {
+ (*i).erase(std::remove_if((*i).begin(), (*i).end(), EdgeOrderData::TestGroups(g1, g2)), (*i).end());
+ }
+
+ rem_fwd.insert(g1);
+ rem_rev.insert(g2);
+ }
+ }
+ }
+
+
+
+ void FaceStitcher::resolveOpenEdges() {
+ // Remove open regions of mesh. Doing this may make additional
+ // edges simple (for example, removing a fin from the edge of
+ // a cube), and may also expose more open mesh regions. In the
+ // latter case, the process must be repeated to deal with the
+ // newly uncovered regions.
+ std::unordered_set<size_t> open_groups;
+
+ for (size_t i = 0; i < is_open.size(); ++i) {
+ if (is_open[i]) open_groups.insert(face_groups.find_set_head(i));
+ }
+
+ while (!open_groups.empty()) {
+ std::list<vpair_t> edge_0, edge_1;
+
+ for (edge_map_t::iterator i = complex_edges.begin(); i != complex_edges.end(); ++i) {
+ bool was_modified = false;
+ for(edgelist_t::iterator j = (*i).second.begin(); j != (*i).second.end(); ) {
+ if (open_groups.find(faceGroupID(*j)) != open_groups.end()) {
+ j = (*i).second.erase(j);
+ was_modified = true;
+ } else {
+ ++j;
+ }
+ }
+ if (was_modified) {
+ if ((*i).second.empty()) {
+ edge_0.push_back((*i).first);
+ } else if ((*i).second.size() == 1) {
+ edge_1.push_back((*i).first);
+ }
+ }
+ }
+
+ for (std::list<vpair_t>::iterator i = edge_1.begin(); i != edge_1.end(); ++i) {
+ vpair_t e1 = *i;
+ edge_map_t::iterator e1i = complex_edges.find(e1);
+ if (e1i == complex_edges.end()) continue;
+ vpair_t e2 = vpair_t(e1.second, e1.first);
+ edge_map_t::iterator e2i = complex_edges.find(e2);
+ CARVE_ASSERT(e2i != complex_edges.end()); // each complex edge should have a mate.
+
+ if ((*e2i).second.size() == 1) {
+ // merge newly simple edges, delete both from complex_edges.
+ edge_t *a = (*e1i).second.front();
+ edge_t *b = (*e2i).second.front();
+ a->rev = b;
+ b->rev = a;
+ face_groups.merge_sets(a->face->id, b->face->id);
+ complex_edges.erase(e1i);
+ complex_edges.erase(e2i);
+ }
+ }
+
+ open_groups.clear();
+
+ for (std::list<vpair_t>::iterator i = edge_0.begin(); i != edge_0.end(); ++i) {
+ vpair_t e1 = *i;
+ edge_map_t::iterator e1i = complex_edges.find(e1);
+ vpair_t e2 = vpair_t(e1.second, e1.first);
+ edge_map_t::iterator e2i = complex_edges.find(e2);
+ if (e2i == complex_edges.end()) {
+ // This could occur, for example, when two faces share
+ // an edge in the same direction, but are both not
+ // touching anything else. Both get removed by the open
+ // group removal code, leaving an edge map with zero
+ // edges. The edge in the opposite direction does not
+ // exist, because there's no face that adjoins either of
+ // the two open faces.
+ continue;
+ }
+
+ for (edgelist_t::iterator j = (*e2i).second.begin(); j != (*e2i).second.end(); ++j) {
+ open_groups.insert(faceGroupID(*j));
+ }
+ complex_edges.erase(e1i);
+ complex_edges.erase(e2i);
+ }
+ }
+ }
+
+
+
+ void FaceStitcher::extractConnectedEdges(std::vector<const vertex_t *>::iterator begin,
+ std::vector<const vertex_t *>::iterator end,
+ std::vector<std::vector<Edge<3> *> > &efwd,
+ std::vector<std::vector<Edge<3> *> > &erev) {
+ const size_t N = std::distance(begin, end) - 1;
+
+ std::vector<const vertex_t *>::iterator e1, e2;
+ e1 = e2 = begin; ++e2;
+ vpair_t start_f = vpair_t(*e1, *e2);
+ vpair_t start_r = vpair_t(*e2, *e1);
+
+ const size_t Nfwd = complex_edges[start_f].size();
+ const size_t Nrev = complex_edges[start_r].size();
+
+ size_t j;
+ edgelist_t::iterator ji;
+
+ efwd.clear(); efwd.resize(Nfwd);
+ erev.clear(); erev.resize(Nrev);
+
+ for (j = 0, ji = complex_edges[start_f].begin();
+ ji != complex_edges[start_f].end();
+ ++j, ++ji) {
+ efwd[j].reserve(N);
+ efwd[j].push_back(*ji);
+ }
+
+ for (j = 0, ji = complex_edges[start_r].begin();
+ ji != complex_edges[start_r].end();
+ ++j, ++ji) {
+ erev[j].reserve(N);
+ erev[j].push_back(*ji);
+ }
+
+ std::vector<Edge<3> *> temp_f, temp_r;
+ temp_f.resize(Nfwd);
+ temp_r.resize(Nrev);
+
+ for (j = 1; j < N; ++j) {
+ ++e1; ++e2;
+ vpair_t ef = vpair_t(*e1, *e2);
+ vpair_t er = vpair_t(*e2, *e1);
+
+ if (complex_edges[ef].size() != Nfwd || complex_edges[ef].size() != Nrev) break;
+
+ for (size_t k = 0; k < Nfwd; ++k) {
+ Edge<3> *e_next = efwd[k].back()->perimNext();
+ CARVE_ASSERT(e_next == NULL || e_next->rev == NULL);
+ if (e_next == NULL || e_next->v2() != *e2) goto done;
+ CARVE_ASSERT(e_next->v1() == *e1);
+ CARVE_ASSERT(std::find(complex_edges[ef].begin(), complex_edges[ef].end(), e_next) != complex_edges[ef].end());
+ temp_f[k] = e_next;
+ }
+
+ for (size_t k = 0; k < Nrev; ++k) {
+ Edge<3> *e_next = erev[k].back()->perimPrev();
+ if (e_next == NULL || e_next->v1() != *e2) goto done;
+ CARVE_ASSERT(e_next->v2() == *e1);
+ CARVE_ASSERT(std::find(complex_edges[er].begin(), complex_edges[er].end(), e_next) != complex_edges[er].end());
+ temp_r[k] = e_next;
+ }
+
+ for (size_t k = 0; k < Nfwd; ++k) {
+ efwd[k].push_back(temp_f[k]);
+ }
+
+ for (size_t k = 0; k < Nrev; ++k) {
+ erev[k].push_back(temp_r[k]);
+ }
+ }
+ done:;
+ }
+
+
+
+ void FaceStitcher::construct() {
+ matchSimpleEdges();
+ if (!complex_edges.size()) return;
+
+ resolveOpenEdges();
+ if (!complex_edges.size()) return;
+
+ buildEdgeGraph(complex_edges);
+
+ std::list<std::vector<const vertex_t *> > paths;
+
+ while (edge_graph.size()) {
+ paths.push_back(std::vector<const vertex_t *>());
+ extractPath(paths.back());
+ removePath(paths.back());
+ };
+
+
+ for (std::list<std::vector<const vertex_t *> >::iterator path = paths.begin(); path != paths.end(); ++path) {
+ for (size_t i = 0; i < (*path).size() - 1;) {
+ std::vector<std::vector<Edge<3> *> > efwd, erev;
+
+ extractConnectedEdges((*path).begin() + i, (*path).end(), efwd, erev);
+
+ std::vector<std::vector<EdgeOrderData> > orderings;
+ orderForwardAndReverseEdges(efwd, erev, orderings);
+
+ matchOrderedEdges(orderings.begin(), orderings.end(), efwd, erev);
+ i += efwd[0].size();
+ }
+ }
+ }
+ }
+ }
+
+
+
+ // construct a MeshSet from a Polyhedron, maintaining on the
+ // connectivity information in the Polyhedron.
+ mesh::MeshSet<3> *meshFromPolyhedron(const poly::Polyhedron *poly, int manifold_id) {
+ typedef mesh::Vertex<3> vertex_t;
+ typedef mesh::Vertex<3>::vector_t vector_t;
+ typedef mesh::Edge<3> edge_t;
+ typedef mesh::Face<3> face_t;
+ typedef mesh::Mesh<3> mesh_t;
+ typedef mesh::MeshSet<3> meshset_t;
+
+ std::vector<vertex_t> vertex_storage;
+ vertex_storage.reserve(poly->vertices.size());
+ for (size_t i = 0; i < poly->vertices.size(); ++i) {
+ vertex_storage.push_back(vertex_t(poly->vertices[i].v));
+ }
+
+ std::vector<std::vector<face_t *> > faces;
+ faces.resize(poly->manifold_is_closed.size());
+
+ std::unordered_map<std::pair<size_t, size_t>, std::list<edge_t *> > vertex_to_edge;
+
+ std::vector<vertex_t *> vert_ptrs;
+ for (size_t i = 0; i < poly->faces.size(); ++i) {
+ const poly::Polyhedron::face_t &src = poly->faces[i];
+ if (manifold_id != -1 && src.manifold_id != manifold_id) continue;
+ vert_ptrs.clear();
+ vert_ptrs.reserve(src.nVertices());
+ for (size_t j = 0; j < src.nVertices(); ++j) {
+ size_t vi = poly->vertexToIndex_fast(src.vertex(j));
+ vert_ptrs.push_back(&vertex_storage[vi]);
+ }
+ face_t *face = new face_t(vert_ptrs.begin(), vert_ptrs.end());
+ face->id = src.manifold_id;
+ faces[src.manifold_id].push_back(face);
+
+ edge_t *edge = face->edge;
+ do {
+ vertex_to_edge[std::make_pair(size_t(edge->v1() - &vertex_storage[0]),
+ size_t(edge->v2() - &vertex_storage[0]))].push_back(edge);
+ edge = edge->next;
+ } while (edge != face->edge);
+ }
+
+ // copy connectivity from Polyhedron.
+ for (size_t i = 0; i < poly->edges.size(); ++i) {
+ const poly::Polyhedron::edge_t &src = poly->edges[i];
+ size_t v1i = poly->vertexToIndex_fast(src.v1);
+ size_t v2i = poly->vertexToIndex_fast(src.v2);
+
+ std::list<edge_t *> &efwd = vertex_to_edge[std::make_pair(v1i, v2i)];
+ std::list<edge_t *> &erev = vertex_to_edge[std::make_pair(v2i, v1i)];
+
+ const std::vector<const poly::Polyhedron::face_t *> &facepairs = poly->connectivity.edge_to_face[i];
+ for (size_t j = 0; j < facepairs.size(); j += 2) {
+ const poly::Polyhedron::face_t *fa, *fb;
+ fa = facepairs[j];
+ fb = facepairs[j+1];
+ if (!fa || !fb) continue;
+ CARVE_ASSERT(fa->manifold_id == fb->manifold_id);
+ if (manifold_id != -1 && fa->manifold_id != manifold_id) continue;
+
+ std::list<edge_t *>::iterator efwdi, erevi;
+ for (efwdi = efwd.begin(); efwdi != efwd.end() && (*efwdi)->face->id != (size_t)fa->manifold_id; ++efwdi);
+ for (erevi = erev.begin(); erevi != erev.end() && (*erevi)->face->id != (size_t)fa->manifold_id; ++erevi);
+ CARVE_ASSERT(efwdi != efwd.end() && erevi != erev.end());
+
+ (*efwdi)->rev = (*erevi);
+ (*erevi)->rev = (*efwdi);
+ }
+ }
+
+ std::vector<mesh_t *> meshes;
+ meshes.reserve(faces.size());
+ for (size_t i = 0; i < faces.size(); ++i) {
+ if (faces[i].size()) {
+ meshes.push_back(new mesh_t(faces[i]));
+ }
+ }
+
+ return new meshset_t(vertex_storage, meshes);
+ }
+
+
+
+ static void copyMeshFaces(const mesh::Mesh<3> *mesh,
+ size_t manifold_id,
+ const mesh::Vertex<3> *Vbase,
+ poly::Polyhedron *poly,
+ std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > &edges,
+ std::unordered_map<const mesh::Face<3> *, size_t> &face_map) {
+ std::vector<const poly::Polyhedron::vertex_t *> vert_ptr;
+ for (size_t f = 0; f < mesh->faces.size(); ++f) {
+ mesh::Face<3> *src = mesh->faces[f];
+ vert_ptr.clear();
+ vert_ptr.reserve(src->nVertices());
+ mesh::Edge<3> *e = src->edge;
+ do {
+ vert_ptr.push_back(&poly->vertices[e->vert - Vbase]);
+ edges[std::make_pair(e->v1() - Vbase, e->v2() - Vbase)].push_back(e);
+ e = e->next;
+ } while (e != src->edge);
+
+ face_map[src] = poly->faces.size();;
+
+ poly->faces.push_back(poly::Polyhedron::face_t(vert_ptr));
+ poly->faces.back().manifold_id = manifold_id;
+ poly->faces.back().owner = poly;
+ }
+ }
+
+
+
+ // construct a Polyhedron from a MeshSet
+ poly::Polyhedron *polyhedronFromMesh(const mesh::MeshSet<3> *mesh, int manifold_id) {
+ typedef poly::Polyhedron poly_t;
+ typedef poly::Polyhedron::vertex_t vertex_t;
+ typedef poly::Polyhedron::edge_t edge_t;
+ typedef poly::Polyhedron::face_t face_t;
+
+ poly::Polyhedron *poly = new poly::Polyhedron();
+ const mesh::Vertex<3> *Vbase = &mesh->vertex_storage[0];
+
+ poly->vertices.reserve(mesh->vertex_storage.size());
+ for (size_t i = 0; i < mesh->vertex_storage.size(); ++i) {
+ poly->vertices.push_back(vertex_t(mesh->vertex_storage[i].v));
+ poly->vertices.back().owner = poly;
+ }
+
+ size_t n_faces = 0;
+ if (manifold_id == -1) {
+ poly->manifold_is_closed.resize(mesh->meshes.size());
+ poly->manifold_is_negative.resize(mesh->meshes.size());
+ for (size_t m = 0; m < mesh->meshes.size(); ++m) {
+ n_faces += mesh->meshes[m]->faces.size();
+ poly->manifold_is_closed[m] = mesh->meshes[m]->isClosed();
+ poly->manifold_is_negative[m] = mesh->meshes[m]->isNegative();
+ }
+ } else {
+ poly->manifold_is_closed.resize(1);
+ poly->manifold_is_negative.resize(1);
+ n_faces = mesh->meshes[manifold_id]->faces.size();
+ poly->manifold_is_closed[manifold_id] = mesh->meshes[manifold_id]->isClosed();
+ poly->manifold_is_negative[manifold_id] = mesh->meshes[manifold_id]->isNegative();
+ }
+
+ std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > edges;
+ std::unordered_map<const mesh::Face<3> *, size_t> face_map;
+ poly->faces.reserve(n_faces);
+
+ if (manifold_id == -1) {
+ for (size_t m = 0; m < mesh->meshes.size(); ++m) {
+ copyMeshFaces(mesh->meshes[m], m, Vbase, poly, edges, face_map);
+ }
+ } else {
+ copyMeshFaces(mesh->meshes[manifold_id], 0, Vbase, poly, edges, face_map);
+ }
+
+ size_t n_edges = 0;
+ for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edges.begin(); i != edges.end(); ++i) {
+ if ((*i).first.first < (*i).first.second || edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) {
+ n_edges++;
+ }
+ }
+
+ poly->edges.reserve(n_edges);
+ for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edges.begin(); i != edges.end(); ++i) {
+ if ((*i).first.first < (*i).first.second ||
+ edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) {
+ poly->edges.push_back(edge_t(&poly->vertices[(*i).first.first],
+ &poly->vertices[(*i).first.second],
+ poly));
+ }
+ }
+
+ poly->initVertexConnectivity();
+
+ // build edge entries for face.
+ for (size_t f = 0; f < poly->faces.size(); ++f) {
+ face_t &face = poly->faces[f];
+ size_t N = face.nVertices();
+ for (size_t v = 0; v < N; ++v) {
+ size_t v1i = poly->vertexToIndex_fast(face.vertex(v));
+ size_t v2i = poly->vertexToIndex_fast(face.vertex((v+1)%N));
+ std::vector<const edge_t *> found_edge;
+ std::set_intersection(poly->connectivity.vertex_to_edge[v1i].begin(), poly->connectivity.vertex_to_edge[v1i].end(),
+ poly->connectivity.vertex_to_edge[v2i].begin(), poly->connectivity.vertex_to_edge[v2i].end(),
+ std::back_inserter(found_edge));
+ CARVE_ASSERT(found_edge.size() == 1);
+ face.edge(v) = found_edge[0];
+ }
+ }
+
+ poly->connectivity.edge_to_face.resize(poly->edges.size());
+
+ for (size_t i = 0; i < poly->edges.size(); ++i) {
+ size_t v1i = poly->vertexToIndex_fast(poly->edges[i].v1);
+ size_t v2i = poly->vertexToIndex_fast(poly->edges[i].v2);
+ std::list<mesh::Edge<3> *> &efwd = edges[std::make_pair(v1i, v2i)];
+ std::list<mesh::Edge<3> *> &erev = edges[std::make_pair(v1i, v2i)];
+
+ for (std::list<mesh::Edge<3> *>::iterator j = efwd.begin(); j != efwd.end(); ++j) {
+ mesh::Edge<3> *edge = *j;
+ if (face_map.find(edge->face) != face_map.end()) {
+ poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]);
+ if (edge->rev == NULL) {
+ poly->connectivity.edge_to_face[i].push_back(NULL);
+ } else {
+ poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->rev->face]]);
+ }
+ }
+ }
+ for (std::list<mesh::Edge<3> *>::iterator j = erev.begin(); j != erev.end(); ++j) {
+ mesh::Edge<3> *edge = *j;
+ if (face_map.find(edge->face) != face_map.end()) {
+ if (edge->rev == NULL) {
+ poly->connectivity.edge_to_face[i].push_back(NULL);
+ poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]);
+ }
+ }
+ }
+
+ }
+
+ poly->initSpatialIndex();
+
+ // XXX: at this point, manifold_is_negative is not set up. This
+ // info should be computed/stored in Mesh instances.
+
+ return poly;
+ }
+
+
+
+}
+
+
+
+// explicit instantiation for 2D case.
+// XXX: do not compile because of a missing definition for fitPlane in the 2d case.
+
+// template class carve::mesh::Vertex<2>;
+// template class carve::mesh::Edge<2>;
+// template class carve::mesh::Face<2>;
+// template class carve::mesh::Mesh<2>;
+// template class carve::mesh::MeshSet<2>;
+
+// explicit instantiation for 3D case.
+template class carve::mesh::Vertex<3>;
+template class carve::mesh::Edge<3>;
+template class carve::mesh::Face<3>;
+template class carve::mesh::Mesh<3>;
+template class carve::mesh::MeshSet<3>;
+
+
+
+carve::PointClass carve::mesh::classifyPoint(
+ const carve::mesh::MeshSet<3> *meshset,
+ const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *face_rtree,
+ const carve::geom::vector<3> &v,
+ bool even_odd,
+ const carve::mesh::Mesh<3> *mesh,
+ const carve::mesh::Face<3> **hit_face) {
+
+ if (hit_face) *hit_face = NULL;
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{containsVertex " << v << "}" << std::endl;
+#endif
+
+ if (!face_rtree->bbox.containsPoint(v)) {
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT(aabb short circuit)}" << std::endl;
+#endif
+ // XXX: if the top level manifolds are negative, this should be POINT_IN.
+ // for the moment, this only works for a single manifold.
+ if (meshset->meshes.size() == 1 && meshset->meshes[0]->isNegative()) {
+ return POINT_IN;
+ }
+ return POINT_OUT;
+ }
+
+ std::vector<carve::mesh::Face<3> *> near_faces;
+ face_rtree->search(v, std::back_inserter(near_faces));
+
+ for (size_t i = 0; i < near_faces.size(); i++) {
+ if (mesh != NULL && mesh != near_faces[i]->mesh) continue;
+
+ // XXX: Do allow the tested vertex to be ON an open
+ // manifold. This was here originally because of the
+ // possibility of an open manifold contained within a closed
+ // manifold.
+
+ // if (!near_faces[i]->mesh->isClosed()) continue;
+
+ if (near_faces[i]->containsPoint(v)) {
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:ON(hits face " << near_faces[i] << ")}" << std::endl;
+#endif
+ if (hit_face) *hit_face = near_faces[i];
+ return POINT_ON;
+ }
+ }
+
+ double ray_len = face_rtree->bbox.extent.length() * 2;
+
+
+ std::vector<std::pair<const carve::mesh::Face<3> *, carve::geom::vector<3> > > manifold_intersections;
+
+ for (;;) {
+ double a1 = random() / double(RAND_MAX) * M_TWOPI;
+ double a2 = random() / double(RAND_MAX) * M_TWOPI;
+
+ carve::geom3d::Vector ray_dir = carve::geom::VECTOR(sin(a1) * sin(a2), cos(a1) * sin(a2), cos(a2));
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{testing ray: " << ray_dir << "}" << std::endl;
+#endif
+
+ carve::geom::vector<3> v2 = v + ray_dir * ray_len;
+
+ bool failed = false;
+ carve::geom::linesegment<3> line(v, v2);
+ carve::geom::vector<3> intersection;
+
+ near_faces.clear();
+ manifold_intersections.clear();
+ face_rtree->search(line, std::back_inserter(near_faces));
+
+ for (unsigned i = 0; !failed && i < near_faces.size(); i++) {
+ if (mesh != NULL && mesh != near_faces[i]->mesh) continue;
+
+ if (!near_faces[i]->mesh->isClosed()) continue;
+
+ switch (near_faces[i]->lineSegmentIntersection(line, intersection)) {
+ case INTERSECT_FACE: {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersects face: " << near_faces[i]
+ << " dp: " << dot(ray_dir, near_faces[i]->plane.N) << "}" << std::endl;
+#endif
+
+ if (!even_odd && fabs(dot(ray_dir, near_faces[i]->plane.N)) < EPSILON) {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{failing(small dot product)}" << std::endl;
+#endif
+
+ failed = true;
+ break;
+ }
+ manifold_intersections.push_back(std::make_pair(near_faces[i], intersection));
+ break;
+ }
+ case INTERSECT_NONE: {
+ break;
+ }
+ default: {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{failing(degenerate intersection)}" << std::endl;
+#endif
+ failed = true;
+ break;
+ }
+ }
+ }
+
+ if (!failed) {
+ if (even_odd) {
+ return (manifold_intersections.size() & 1) ? POINT_IN : POINT_OUT;
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersections ok [count:"
+ << manifold_intersections.size()
+ << "], sorting}"
+ << std::endl;
+#endif
+
+ carve::geom3d::sortInDirectionOfRay(ray_dir,
+ manifold_intersections.begin(),
+ manifold_intersections.end(),
+ carve::geom3d::vec_adapt_pair_second());
+
+ std::map<const carve::mesh::Mesh<3> *, int> crossings;
+
+ for (size_t i = 0; i < manifold_intersections.size(); ++i) {
+ const carve::mesh::Face<3> *f = manifold_intersections[i].first;
+ if (dot(ray_dir, f->plane.N) < 0.0) {
+ crossings[f->mesh]++;
+ } else {
+ crossings[f->mesh]--;
+ }
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ for (std::map<const carve::mesh::Mesh<3> *, int>::const_iterator i = crossings.begin(); i != crossings.end(); ++i) {
+ std::cerr << "{mesh " << (*i).first << " crossing count: " << (*i).second << "}" << std::endl;
+ }
+#endif
+
+ for (size_t i = 0; i < manifold_intersections.size(); ++i) {
+ const carve::mesh::Face<3> *f = manifold_intersections[i].first;
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersection at "
+ << manifold_intersections[i].second
+ << " mesh: "
+ << f->mesh
+ << " count: "
+ << crossings[f->mesh]
+ << "}"
+ << std::endl;
+#endif
+
+ if (crossings[f->mesh] < 0) {
+ // inside this manifold.
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:IN}" << std::endl;
+#endif
+
+ return POINT_IN;
+ } else if (crossings[f->mesh] > 0) {
+ // outside this manifold, but it's an infinite manifold. (for instance, an inverted cube)
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT}" << std::endl;
+#endif
+
+ return POINT_OUT;
+ }
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT(default)}" << std::endl;
+#endif
+
+ return POINT_OUT;
+ }
+ }
+}
+
+
+
diff --git a/extern/carve/lib/octree.cpp b/extern/carve/lib/octree.cpp
new file mode 100644
index 00000000000..900a9614f47
--- /dev/null
+++ b/extern/carve/lib/octree.cpp
@@ -0,0 +1,399 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/octree_decl.hpp>
+#include <carve/octree_impl.hpp>
+
+#include <carve/poly_decl.hpp>
+
+namespace carve {
+ namespace csg {
+
+ Octree::Node::Node(const carve::geom3d::Vector &newMin, const carve::geom3d::Vector &newMax) :
+ parent(NULL), is_leaf(true), min(newMin), max(newMax) {
+ for (int i = 0; i < 8; ++i) children[i] = NULL;
+ aabb = Octree::makeAABB(this);
+ }
+
+ Octree::Node::Node(Node *p, double x1, double y1, double z1, double x2, double y2, double z2) :
+ parent(p), is_leaf(true), min(carve::geom::VECTOR(x1, y1, z1)), max(carve::geom::VECTOR(x2, y2, z2)) {
+ for (int i = 0; i < 8; ++i) children[i] = NULL;
+ aabb = Octree::makeAABB(this);
+ }
+
+ Octree::Node::~Node() {
+ for (int i = 0; i < 8; ++i) {
+ if (children[i] != NULL) {
+ (*children[i]).~Node();
+ }
+ }
+ if (children[0] != NULL) {
+ char *ptr = (char*)children[0];
+ delete[] ptr;
+ }
+ }
+
+ bool Octree::Node::mightContain(const carve::poly::Face<3> &face) {
+ if (face.nVertices() == 3) {
+ return aabb.intersects(carve::geom::tri<3>(face.vertex(0)->v, face.vertex(1)->v, face.vertex(2)->v));
+ } else {
+ return aabb.intersects(face.aabb) && aabb.intersects(face.plane_eqn);
+ }
+ }
+
+ bool Octree::Node::mightContain(const carve::poly::Edge<3> &edge) {
+ return aabb.intersectsLineSegment(edge.v1->v, edge.v2->v);
+ }
+
+ bool Octree::Node::mightContain(const carve::poly::Vertex<3> &p) {
+ return aabb.containsPoint(p.v);
+ }
+
+ bool Octree::Node::hasChildren() {
+ return !is_leaf;
+ }
+
+ bool Octree::Node::split() {
+ if (is_leaf && hasGeometry()) {
+
+ carve::geom3d::Vector mid = 0.5 * (min + max);
+ char *ptr = new char[sizeof(Node)*8];
+ children[0] = new (ptr + sizeof(Node) * 0) Node(this, min.x, min.y, min.z, mid.x, mid.y, mid.z);
+ children[1] = new (ptr + sizeof(Node) * 1) Node(this, mid.x, min.y, min.z, max.x, mid.y, mid.z);
+ children[2] = new (ptr + sizeof(Node) * 2) Node(this, min.x, mid.y, min.z, mid.x, max.y, mid.z);
+ children[3] = new (ptr + sizeof(Node) * 3) Node(this, mid.x, mid.y, min.z, max.x, max.y, mid.z);
+ children[4] = new (ptr + sizeof(Node) * 4) Node(this, min.x, min.y, mid.z, mid.x, mid.y, max.z);
+ children[5] = new (ptr + sizeof(Node) * 5) Node(this, mid.x, min.y, mid.z, max.x, mid.y, max.z);
+ children[6] = new (ptr + sizeof(Node) * 6) Node(this, min.x, mid.y, mid.z, mid.x, max.y, max.z);
+ children[7] = new (ptr + sizeof(Node) * 7) Node(this, mid.x, mid.y, mid.z, max.x, max.y, max.z);
+
+ for (int i = 0; i < 8; ++i) {
+ putInside(faces, children[i], children[i]->faces);
+ putInside(edges, children[i], children[i]->edges);
+ putInside(vertices, children[i], children[i]->vertices);
+ }
+
+ faces.clear();
+ edges.clear();
+ vertices.clear();
+ is_leaf = false;
+ }
+ return is_leaf;
+ }
+
+ template <class T>
+ void Octree::Node::putInside(const T &input, Node *child, T &output) {
+ for (typename T::const_iterator it = input.begin(), e = input.end(); it != e; ++it) {
+ if (child->mightContain(**it)) {
+ output.push_back(*it);
+ }
+ }
+ }
+
+ bool Octree::Node::hasGeometry() {
+ return faces.size() > 0 || edges.size() > 0 || vertices.size() > 0;
+ }
+
+ Octree::Octree() {
+ root = NULL;
+ }
+
+ Octree::~Octree() {
+ if (root) delete root;
+ }
+
+ void Octree::setBounds(const carve::geom3d::Vector &min, const carve::geom3d::Vector &max) {
+ if (root) delete root;
+ root = new Node(min, max);
+ }
+
+ void Octree::setBounds(carve::geom3d::AABB aabb) {
+ if (root) delete root;
+ aabb.extent = 1.1 * aabb.extent;
+ root = new Node(aabb.min(), aabb.max());
+ }
+
+ void Octree::addEdges(const std::vector<carve::poly::Edge<3> > &e) {
+ root->edges.reserve(root->edges.size() + e.size());
+ for (size_t i = 0; i < e.size(); ++i) {
+ root->edges.push_back(&e[i]);
+ }
+ }
+
+ void Octree::addFaces(const std::vector<carve::poly::Face<3> > &f) {
+ root->faces.reserve(root->faces.size() + f.size());
+ for (size_t i = 0; i < f.size(); ++i) {
+ root->faces.push_back(&f[i]);
+ }
+ }
+
+ void Octree::addVertices(const std::vector<const carve::poly::Vertex<3> *> &p) {
+ root->vertices.insert(root->vertices.end(), p.begin(), p.end());
+ }
+
+ carve::geom3d::AABB Octree::makeAABB(const Node *node) {
+ carve::geom3d::Vector centre = 0.5 * (node->min + node->max);
+ carve::geom3d::Vector size = SLACK_FACTOR * 0.5 * (node->max - node->min);
+ return carve::geom3d::AABB(centre, size);
+ }
+
+ void Octree::doFindEdges(const carve::geom::aabb<3> &aabb,
+ Node *node,
+ std::vector<const carve::poly::Edge<3> *> &out,
+ unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.intersects(aabb)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(aabb, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(aabb, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Edge<3>*>::const_iterator it = node->edges.begin(), e = node->edges.end(); it != e; ++it) {
+ if ((*it)->tag_once()) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+ }
+
+ void Octree::doFindEdges(const carve::geom3d::LineSegment &l,
+ Node *node,
+ std::vector<const carve::poly::Edge<3> *> &out,
+ unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.intersectsLineSegment(l.v1, l.v2)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(l, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(l, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Edge<3>*>::const_iterator it = node->edges.begin(), e = node->edges.end(); it != e; ++it) {
+ if ((*it)->tag_once()) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+ }
+
+ void Octree::doFindEdges(const carve::geom3d::Vector &v,
+ Node *node,
+ std::vector<const carve::poly::Edge<3> *> &out,
+ unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.containsPoint(v)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(v, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindEdges(v, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Edge<3>*>::const_iterator
+ it = node->edges.begin(), e = node->edges.end(); it != e; ++it) {
+ if ((*it)->tag_once()) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+ }
+
+ void Octree::doFindFaces(const carve::geom::aabb<3> &aabb,
+ Node *node,
+ std::vector<const carve::poly::Face<3>*> &out,
+ unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.intersects(aabb)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindFaces(aabb, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->faces.size() > FACE_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindFaces(aabb, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Face<3>*>::const_iterator it = node->faces.begin(), e = node->faces.end(); it != e; ++it) {
+ if ((*it)->tag_once()) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+ }
+
+ void Octree::doFindFaces(const carve::geom3d::LineSegment &l,
+ Node *node,
+ std::vector<const carve::poly::Face<3>*> &out,
+ unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.intersectsLineSegment(l.v1, l.v2)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindFaces(l, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->faces.size() > FACE_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindFaces(l, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Face<3>*>::const_iterator it = node->faces.begin(), e = node->faces.end(); it != e; ++it) {
+ if ((*it)->tag_once()) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+ }
+
+ void Octree::doFindVerticesAllowDupes(const carve::geom3d::Vector &v, Node *node, std::vector<const carve::poly::Vertex<3> *> &out, unsigned depth) const {
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->aabb.containsPoint(v)) {
+ if (node->hasChildren()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindVerticesAllowDupes(v, node->children[i], out, depth + 1);
+ }
+ } else {
+ if (depth < MAX_SPLIT_DEPTH && node->vertices.size() > POINT_SPLIT_THRESHOLD) {
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doFindVerticesAllowDupes(v, node->children[i], out, depth + 1);
+ }
+ return;
+ }
+ }
+ for (std::vector<const carve::poly::Vertex<3> *>::const_iterator it = node->vertices.begin(), e = node->vertices.end(); it != e; ++it) {
+ out.push_back(*it);
+ }
+ }
+ }
+ }
+
+ void Octree::findEdgesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Edge<3>*> &out) const {
+ tagable::tag_begin();
+ doFindEdges(aabb, root, out, 0);
+ }
+
+ void Octree::findEdgesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Edge<3>*> &out) const {
+ tagable::tag_begin();
+ doFindEdges(l, root, out, 0);
+ }
+
+ void Octree::findEdgesNear(const carve::poly::Edge<3> &e, std::vector<const carve::poly::Edge<3>*> &out) const {
+ tagable::tag_begin();
+ doFindEdges(carve::geom3d::LineSegment(e.v1->v, e.v2->v), root, out, 0);
+ }
+
+ void Octree::findEdgesNear(const carve::geom3d::Vector &v, std::vector<const carve::poly::Edge<3>*> &out) const {
+ tagable::tag_begin();
+ doFindEdges(v, root, out, 0);
+ }
+
+ void Octree::findFacesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Face<3>*> &out) const {
+ tagable::tag_begin();
+ doFindFaces(aabb, root, out, 0);
+ }
+
+ void Octree::findFacesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Face<3>*> &out) const {
+ tagable::tag_begin();
+ doFindFaces(l, root, out, 0);
+ }
+
+ void Octree::findFacesNear(const carve::poly::Edge<3> &e, std::vector<const carve::poly::Face<3>*> &out) const {
+ tagable::tag_begin();
+ doFindFaces(carve::geom3d::LineSegment(e.v1->v, e.v2->v), root, out, 0);
+ }
+
+ void Octree::findVerticesNearAllowDupes(const carve::geom3d::Vector &v, std::vector<const carve::poly::Vertex<3> *> &out) const {
+ tagable::tag_begin();
+ doFindVerticesAllowDupes(v, root, out, 0);
+ }
+
+ void Octree::doSplit(int maxSplit, Node *node) {
+ // Don't split down any further than 4 levels.
+ if (maxSplit <= 0 || (node->edges.size() < 5 && node->faces.size() < 5)) {
+ return;
+ }
+
+ if (!node->split()) {
+ for (int i = 0; i < 8; ++i) {
+ doSplit(maxSplit - 1, node->children[i]);
+ }
+ }
+ }
+
+ void Octree::splitTree() {
+ // initially split 4 levels
+ doSplit(0, root);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/pointset.cpp b/extern/carve/lib/pointset.cpp
new file mode 100644
index 00000000000..7ecf0074c69
--- /dev/null
+++ b/extern/carve/lib/pointset.cpp
@@ -0,0 +1,59 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/geom.hpp>
+#include <carve/pointset.hpp>
+
+namespace carve {
+ namespace point {
+
+ PointSet::PointSet(const std::vector<carve::geom3d::Vector> &points) {
+ vertices.resize(points.size());
+ for (size_t i = 0; i < points.size(); ++i) {
+ vertices[i].v = points[i];
+ }
+ aabb.fit(points.begin(), points.end());
+ }
+
+ void PointSet::sortVertices(const carve::geom3d::Vector &axis) {
+ std::vector<std::pair<double, size_t> > temp;
+ temp.reserve(vertices.size());
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ temp.push_back(std::make_pair(dot(axis, vertices[i].v), i));
+ }
+ std::sort(temp.begin(), temp.end());
+
+ std::vector<Vertex> vnew;
+ vnew.reserve(vertices.size());
+
+ // std::vector<int> revmap;
+ // revmap.resize(vertices.size());
+
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ vnew.push_back(vertices[temp[i].second]);
+ // revmap[temp[i].second] = i;
+ }
+
+ vertices.swap(vnew);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/polyhedron.cpp b/extern/carve/lib/polyhedron.cpp
new file mode 100644
index 00000000000..93e667ffaf7
--- /dev/null
+++ b/extern/carve/lib/polyhedron.cpp
@@ -0,0 +1,1103 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#if defined(CARVE_DEBUG)
+#define DEBUG_CONTAINS_VERTEX
+#endif
+
+#include <carve/djset.hpp>
+
+#include <carve/geom.hpp>
+#include <carve/poly.hpp>
+
+#include <carve/octree_impl.hpp>
+
+#include <carve/timing.hpp>
+
+#include <algorithm>
+
+#include <carve/mesh.hpp>
+
+#include BOOST_INCLUDE(random.hpp)
+
+namespace {
+ bool emb_test(carve::poly::Polyhedron *poly,
+ std::map<int, std::set<int> > &embedding,
+ carve::geom3d::Vector v,
+ int m_id) {
+
+ std::map<int, carve::PointClass> result;
+#if defined(CARVE_DEBUG)
+ std::cerr << "test " << v << " (m_id:" << m_id << ")" << std::endl;
+#endif
+ poly->testVertexAgainstClosedManifolds(v, result, true);
+ std::set<int> inside;
+ for (std::map<int, carve::PointClass>::iterator j = result.begin();
+ j != result.end();
+ ++j) {
+ if ((*j).first == m_id) continue;
+ if ((*j).second == carve::POINT_IN) inside.insert((*j).first);
+ else if ((*j).second == carve::POINT_ON) {
+#if defined(CARVE_DEBUG)
+ std::cerr << " FAIL" << std::endl;
+#endif
+ return false;
+ }
+ }
+#if defined(CARVE_DEBUG)
+ std::cerr << " OK (inside.size()==" << inside.size() << ")" << std::endl;
+#endif
+ embedding[m_id] = inside;
+ return true;
+ }
+
+
+
+ struct order_faces {
+ bool operator()(const carve::poly::Polyhedron::face_t * const &a,
+ const carve::poly::Polyhedron::face_t * const &b) const {
+ return std::lexicographical_compare(a->vbegin(), a->vend(), b->vbegin(), b->vend());
+ }
+ };
+
+
+
+}
+
+
+
+namespace carve {
+ namespace poly {
+
+
+
+ bool Polyhedron::initSpatialIndex() {
+ static carve::TimingName FUNC_NAME("Polyhedron::initSpatialIndex()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ octree.setBounds(aabb);
+ octree.addFaces(faces);
+ octree.addEdges(edges);
+ octree.splitTree();
+
+ return true;
+ }
+
+
+
+ void Polyhedron::invertAll() {
+ for (size_t i = 0; i < faces.size(); ++i) {
+ faces[i].invert();
+ }
+
+ for (size_t i = 0; i < edges.size(); ++i) {
+ std::vector<const face_t *> &f = connectivity.edge_to_face[i];
+ for (size_t j = 0; j < (f.size() & ~1U); j += 2) {
+ std::swap(f[j], f[j+1]);
+ }
+ }
+
+ for (size_t i = 0; i < manifold_is_negative.size(); ++i) {
+ manifold_is_negative[i] = !manifold_is_negative[i];
+ }
+ }
+
+
+
+ void Polyhedron::invert(const std::vector<bool> &selected_manifolds) {
+ bool altered = false;
+ for (size_t i = 0; i < faces.size(); ++i) {
+ if (faces[i].manifold_id >= 0 &&
+ (unsigned)faces[i].manifold_id < selected_manifolds.size() &&
+ selected_manifolds[faces[i].manifold_id]) {
+ altered = true;
+ faces[i].invert();
+ }
+ }
+
+ if (altered) {
+ for (size_t i = 0; i < edges.size(); ++i) {
+ std::vector<const face_t *> &f = connectivity.edge_to_face[i];
+ for (size_t j = 0; j < (f.size() & ~1U); j += 2) {
+ int m_id = -1;
+ if (f[j]) m_id = f[j]->manifold_id;
+ if (f[j+1]) m_id = f[j+1]->manifold_id;
+ if (m_id >= 0 && (unsigned)m_id < selected_manifolds.size() && selected_manifolds[m_id]) {
+ std::swap(f[j], f[j+1]);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < std::min(selected_manifolds.size(), manifold_is_negative.size()); ++i) {
+ manifold_is_negative[i] = !manifold_is_negative[i];
+ }
+ }
+ }
+
+
+
+ void Polyhedron::initVertexConnectivity() {
+ static carve::TimingName FUNC_NAME("static Polyhedron initVertexConnectivity()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ // allocate space for connectivity info.
+ connectivity.vertex_to_edge.resize(vertices.size());
+ connectivity.vertex_to_face.resize(vertices.size());
+
+ std::vector<size_t> vertex_face_count;
+
+ vertex_face_count.resize(vertices.size());
+
+ // work out how many faces/edges each vertex is connected to, in
+ // order to save on array reallocs.
+ for (unsigned i = 0; i < faces.size(); ++i) {
+ face_t &f = faces[i];
+ for (unsigned j = 0; j < f.nVertices(); j++) {
+ vertex_face_count[vertexToIndex_fast(f.vertex(j))]++;
+ }
+ }
+
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ connectivity.vertex_to_edge[i].reserve(vertex_face_count[i]);
+ connectivity.vertex_to_face[i].reserve(vertex_face_count[i]);
+ }
+
+ // record connectivity from vertex to edges.
+ for (size_t i = 0; i < edges.size(); ++i) {
+ size_t v1i = vertexToIndex_fast(edges[i].v1);
+ size_t v2i = vertexToIndex_fast(edges[i].v2);
+
+ connectivity.vertex_to_edge[v1i].push_back(&edges[i]);
+ connectivity.vertex_to_edge[v2i].push_back(&edges[i]);
+ }
+
+ // record connectivity from vertex to faces.
+ for (size_t i = 0; i < faces.size(); ++i) {
+ face_t &f = faces[i];
+ for (unsigned j = 0; j < f.nVertices(); j++) {
+ size_t vi = vertexToIndex_fast(f.vertex(j));
+ connectivity.vertex_to_face[vi].push_back(&f);
+ }
+ }
+ }
+
+
+
+ bool Polyhedron::initConnectivity() {
+ static carve::TimingName FUNC_NAME("Polyhedron::initConnectivity()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ // temporary measure: initialize connectivity by creating a
+ // half-edge mesh, and then converting back.
+
+ std::vector<mesh::Vertex<3> > vertex_storage;
+ vertex_storage.reserve(vertices.size());
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ vertex_storage.push_back(mesh::Vertex<3>(vertices[i].v));
+ }
+
+ std::vector<mesh::Face<3> *> mesh_faces;
+ std::unordered_map<const mesh::Face<3> *, size_t> face_map;
+ {
+ std::vector<mesh::Vertex<3> *> vert_ptrs;
+ for (size_t i = 0; i < faces.size(); ++i) {
+ const face_t &src = faces[i];
+ vert_ptrs.clear();
+ vert_ptrs.reserve(src.nVertices());
+ for (size_t j = 0; j < src.nVertices(); ++j) {
+ size_t vi = vertexToIndex_fast(src.vertex(j));
+ vert_ptrs.push_back(&vertex_storage[vi]);
+ }
+ mesh::Face<3> *face = new mesh::Face<3>(vert_ptrs.begin(), vert_ptrs.end());
+ mesh_faces.push_back(face);
+ face_map[face] = i;
+ }
+ }
+
+ std::vector<mesh::Mesh<3> *> meshes;
+ mesh::Mesh<3>::create(mesh_faces.begin(), mesh_faces.end(), meshes);
+ mesh::MeshSet<3> *meshset = new mesh::MeshSet<3>(vertex_storage, meshes);
+
+ manifold_is_closed.resize(meshset->meshes.size());
+ manifold_is_negative.resize(meshset->meshes.size());
+
+ std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > edge_map;
+
+ if (meshset->vertex_storage.size()) {
+ mesh::Vertex<3> *Vbase = &meshset->vertex_storage[0];
+ for (size_t m = 0; m < meshset->meshes.size(); ++m) {
+ mesh::Mesh<3> *mesh = meshset->meshes[m];
+ manifold_is_closed[m] = mesh->isClosed();
+ for (size_t f = 0; f < mesh->faces.size(); ++f) {
+ mesh::Face<3> *src = mesh->faces[f];
+ mesh::Edge<3> *e = src->edge;
+ faces[face_map[src]].manifold_id = m;
+ do {
+ edge_map[std::make_pair(e->v1() - Vbase, e->v2() - Vbase)].push_back(e);
+ e = e->next;
+ } while (e != src->edge);
+ }
+ }
+ }
+
+ size_t n_edges = 0;
+ for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edge_map.begin(); i != edge_map.end(); ++i) {
+ if ((*i).first.first < (*i).first.second || edge_map.find(std::make_pair((*i).first.second, (*i).first.first)) == edge_map.end()) {
+ n_edges++;
+ }
+ }
+
+ edges.clear();
+ edges.reserve(n_edges);
+ for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edge_map.begin(); i != edge_map.end(); ++i) {
+ if ((*i).first.first < (*i).first.second || edge_map.find(std::make_pair((*i).first.second, (*i).first.first)) == edge_map.end()) {
+ edges.push_back(edge_t(&vertices[(*i).first.first], &vertices[(*i).first.second], this));
+ }
+ }
+
+ initVertexConnectivity();
+
+ for (size_t f = 0; f < faces.size(); ++f) {
+ face_t &face = faces[f];
+ size_t N = face.nVertices();
+ for (size_t v = 0; v < N; ++v) {
+ size_t v1i = vertexToIndex_fast(face.vertex(v));
+ size_t v2i = vertexToIndex_fast(face.vertex((v+1)%N));
+ std::vector<const edge_t *> found_edge;
+
+ CARVE_ASSERT(carve::is_sorted(connectivity.vertex_to_edge[v1i].begin(), connectivity.vertex_to_edge[v1i].end()));
+ CARVE_ASSERT(carve::is_sorted(connectivity.vertex_to_edge[v2i].begin(), connectivity.vertex_to_edge[v2i].end()));
+
+ std::set_intersection(connectivity.vertex_to_edge[v1i].begin(), connectivity.vertex_to_edge[v1i].end(),
+ connectivity.vertex_to_edge[v2i].begin(), connectivity.vertex_to_edge[v2i].end(),
+ std::back_inserter(found_edge));
+
+ CARVE_ASSERT(found_edge.size() == 1);
+
+ face.edge(v) = found_edge[0];
+ }
+ }
+
+ connectivity.edge_to_face.resize(edges.size());
+
+ for (size_t i = 0; i < edges.size(); ++i) {
+ size_t v1i = vertexToIndex_fast(edges[i].v1);
+ size_t v2i = vertexToIndex_fast(edges[i].v2);
+ std::list<mesh::Edge<3> *> &efwd = edge_map[std::make_pair(v1i, v2i)];
+ std::list<mesh::Edge<3> *> &erev = edge_map[std::make_pair(v1i, v2i)];
+
+ for (std::list<mesh::Edge<3> *>::iterator j = efwd.begin(); j != efwd.end(); ++j) {
+ mesh::Edge<3> *edge = *j;
+ if (face_map.find(edge->face) != face_map.end()) {
+ connectivity.edge_to_face[i].push_back(&faces[face_map[edge->face]]);
+ if (edge->rev == NULL) {
+ connectivity.edge_to_face[i].push_back(NULL);
+ } else {
+ connectivity.edge_to_face[i].push_back(&faces[face_map[edge->rev->face]]);
+ }
+ }
+ }
+ for (std::list<mesh::Edge<3> *>::iterator j = erev.begin(); j != erev.end(); ++j) {
+ mesh::Edge<3> *edge = *j;
+ if (face_map.find(edge->face) != face_map.end()) {
+ if (edge->rev == NULL) {
+ connectivity.edge_to_face[i].push_back(NULL);
+ connectivity.edge_to_face[i].push_back(&faces[face_map[edge->face]]);
+ }
+ }
+ }
+ }
+
+ delete meshset;
+
+ return true;
+ }
+
+
+
+ bool Polyhedron::calcManifoldEmbedding() {
+ // this could be significantly sped up using bounding box tests
+ // to work out what pairs of manifolds are embedding candidates.
+ // A per-manifold AABB could also be used to speed up
+ // testVertexAgainstClosedManifolds().
+
+ static carve::TimingName FUNC_NAME("Polyhedron::calcManifoldEmbedding()");
+ static carve::TimingName CME_V("Polyhedron::calcManifoldEmbedding() (vertices)");
+ static carve::TimingName CME_E("Polyhedron::calcManifoldEmbedding() (edges)");
+ static carve::TimingName CME_F("Polyhedron::calcManifoldEmbedding() (faces)");
+
+ carve::TimingBlock block(FUNC_NAME);
+
+ const unsigned MCOUNT = manifoldCount();
+ if (MCOUNT < 2) return true;
+
+ std::set<int> vertex_manifolds;
+ std::map<int, std::set<int> > embedding;
+
+ carve::Timing::start(CME_V);
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ vertex_manifolds.clear();
+ if (vertexManifolds(&vertices[i], set_inserter(vertex_manifolds)) != 1) continue;
+ int m_id = *vertex_manifolds.begin();
+ if (embedding.find(m_id) == embedding.end()) {
+ if (emb_test(this, embedding, vertices[i].v, m_id) && embedding.size() == MCOUNT) {
+ carve::Timing::stop();
+ goto done;
+ }
+ }
+ }
+ carve::Timing::stop();
+
+ carve::Timing::start(CME_E);
+ for (size_t i = 0; i < edges.size(); ++i) {
+ if (connectivity.edge_to_face[i].size() == 2) {
+ int m_id;
+ const face_t *f1 = connectivity.edge_to_face[i][0];
+ const face_t *f2 = connectivity.edge_to_face[i][1];
+ if (f1) m_id = f1->manifold_id;
+ if (f2) m_id = f2->manifold_id;
+ if (embedding.find(m_id) == embedding.end()) {
+ if (emb_test(this, embedding, (edges[i].v1->v + edges[i].v2->v) / 2, m_id) && embedding.size() == MCOUNT) {
+ carve::Timing::stop();
+ goto done;
+ }
+ }
+ }
+ }
+ carve::Timing::stop();
+
+ carve::Timing::start(CME_F);
+ for (size_t i = 0; i < faces.size(); ++i) {
+ int m_id = faces[i].manifold_id;
+ if (embedding.find(m_id) == embedding.end()) {
+ carve::geom2d::P2 pv;
+ if (!carve::geom2d::pickContainedPoint(faces[i].projectedVertices(), pv)) continue;
+ carve::geom3d::Vector v = carve::poly::face::unproject(faces[i], pv);
+ if (emb_test(this, embedding, v, m_id) && embedding.size() == MCOUNT) {
+ carve::Timing::stop();
+ goto done;
+ }
+ }
+ }
+ carve::Timing::stop();
+
+ CARVE_FAIL("could not find test points");
+
+ // std::cerr << "could not find test points!!!" << std::endl;
+ // return true;
+ done:;
+ for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) {
+#if defined(CARVE_DEBUG)
+ std::cerr << (*i).first << " : ";
+ std::copy((*i).second.begin(), (*i).second.end(), std::ostream_iterator<int>(std::cerr, ","));
+ std::cerr << std::endl;
+#endif
+ (*i).second.insert(-1);
+ }
+ std::set<int> parents, new_parents;
+ parents.insert(-1);
+
+ while (embedding.size()) {
+ new_parents.clear();
+ for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) {
+ if ((*i).second.size() == 1) {
+ if (parents.find(*(*i).second.begin()) != parents.end()) {
+ new_parents.insert((*i).first);
+#if defined(CARVE_DEBUG)
+ std::cerr << "parent(" << (*i).first << "): " << *(*i).second.begin() << std::endl;
+#endif
+ } else {
+#if defined(CARVE_DEBUG)
+ std::cerr << "no parent: " << (*i).first << " (looking for: " << *(*i).second.begin() << ")" << std::endl;
+#endif
+ }
+ }
+ }
+ for (std::set<int>::const_iterator i = new_parents.begin(); i != new_parents.end(); ++i) {
+ embedding.erase(*i);
+ }
+ for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) {
+ size_t n = 0;
+ for (std::set<int>::const_iterator j = parents.begin(); j != parents.end(); ++j) {
+ n += (*i).second.erase((*j));
+ }
+ CARVE_ASSERT(n != 0);
+ }
+ parents.swap(new_parents);
+ }
+
+ return true;
+ }
+
+
+
+ bool Polyhedron::init() {
+ static carve::TimingName FUNC_NAME("Polyhedron::init()");
+ carve::TimingBlock block(FUNC_NAME);
+
+ aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ref());
+
+ connectivity.vertex_to_edge.clear();
+ connectivity.vertex_to_face.clear();
+ connectivity.edge_to_face.clear();
+
+ if (!initConnectivity()) return false;
+ if (!initSpatialIndex()) return false;
+
+ return true;
+ }
+
+
+
+ void Polyhedron::faceRecalc() {
+ for (size_t i = 0; i < faces.size(); ++i) {
+ if (!faces[i].recalc()) {
+ std::ostringstream out;
+ out << "face " << i << " recalc failed";
+ throw carve::exception(out.str());
+ }
+ }
+ }
+
+
+
+ Polyhedron::Polyhedron(const Polyhedron &poly) {
+ faces.reserve(poly.faces.size());
+
+ for (size_t i = 0; i < poly.faces.size(); ++i) {
+ const face_t &src = poly.faces[i];
+ faces.push_back(src);
+ }
+ commonFaceInit(false); // calls setFaceAndVertexOwner() and init()
+ }
+
+
+
+ Polyhedron::Polyhedron(const Polyhedron &poly, const std::vector<bool> &selected_manifolds) {
+ size_t n_faces = 0;
+
+ for (size_t i = 0; i < poly.faces.size(); ++i) {
+ const face_t &src = poly.faces[i];
+ if (src.manifold_id >= 0 &&
+ (unsigned)src.manifold_id < selected_manifolds.size() &&
+ selected_manifolds[src.manifold_id]) {
+ n_faces++;
+ }
+ }
+
+ faces.reserve(n_faces);
+
+ for (size_t i = 0; i < poly.faces.size(); ++i) {
+ const face_t &src = poly.faces[i];
+ if (src.manifold_id >= 0 &&
+ (unsigned)src.manifold_id < selected_manifolds.size() &&
+ selected_manifolds[src.manifold_id]) {
+ faces.push_back(src);
+ }
+ }
+
+ commonFaceInit(false); // calls setFaceAndVertexOwner() and init()
+ }
+
+
+
+ Polyhedron::Polyhedron(const Polyhedron &poly, int m_id) {
+ size_t n_faces = 0;
+
+ for (size_t i = 0; i < poly.faces.size(); ++i) {
+ const face_t &src = poly.faces[i];
+ if (src.manifold_id == m_id) n_faces++;
+ }
+
+ faces.reserve(n_faces);
+
+ for (size_t i = 0; i < poly.faces.size(); ++i) {
+ const face_t &src = poly.faces[i];
+ if (src.manifold_id == m_id) faces.push_back(src);
+ }
+
+ commonFaceInit(false); // calls setFaceAndVertexOwner() and init()
+ }
+
+
+
+ Polyhedron::Polyhedron(const std::vector<carve::geom3d::Vector> &_vertices,
+ int n_faces,
+ const std::vector<int> &face_indices) {
+ // The polyhedron is defined by a vector of vertices, which we
+ // want to copy, and a face index list, from which we need to
+ // generate a set of Faces.
+
+ vertices.clear();
+ vertices.resize(_vertices.size());
+ for (size_t i = 0; i < _vertices.size(); ++i) {
+ vertices[i].v = _vertices[i];
+ }
+
+ faces.reserve(n_faces);
+
+ std::vector<int>::const_iterator iter = face_indices.begin();
+ std::vector<const vertex_t *> v;
+ for (int i = 0; i < n_faces; ++i) {
+ int vertexCount = *iter++;
+
+ v.clear();
+
+ while (vertexCount--) {
+ CARVE_ASSERT(*iter >= 0);
+ CARVE_ASSERT((unsigned)*iter < vertices.size());
+ v.push_back(&vertices[*iter++]);
+ }
+ faces.push_back(face_t(v));
+ }
+
+ setFaceAndVertexOwner();
+
+ if (!init()) {
+ throw carve::exception("polyhedron creation failed");
+ }
+ }
+
+
+
+ Polyhedron::Polyhedron(std::vector<face_t> &_faces,
+ std::vector<vertex_t> &_vertices,
+ bool _recalc) {
+ faces.swap(_faces);
+ vertices.swap(_vertices);
+
+ setFaceAndVertexOwner();
+
+ if (_recalc) faceRecalc();
+
+ if (!init()) {
+ throw carve::exception("polyhedron creation failed");
+ }
+ }
+
+
+
+ Polyhedron::Polyhedron(std::vector<face_t> &_faces,
+ bool _recalc) {
+ faces.swap(_faces);
+ commonFaceInit(_recalc); // calls setFaceAndVertexOwner() and init()
+ }
+
+
+
+ Polyhedron::Polyhedron(std::list<face_t> &_faces,
+ bool _recalc) {
+ faces.reserve(_faces.size());
+ std::copy(_faces.begin(), _faces.end(), std::back_inserter(faces));
+ commonFaceInit(_recalc); // calls setFaceAndVertexOwner() and init()
+ }
+
+
+
+ void Polyhedron::collectFaceVertices(std::vector<face_t> &faces,
+ std::vector<vertex_t> &vertices,
+ std::unordered_map<const vertex_t *, const vertex_t *> &vmap) {
+ // Given a set of faces, copy all referenced vertices into a
+ // single vertex array and update the faces to point into that
+ // array. On exit, vmap contains a mapping from old pointer to
+ // new pointer.
+
+ vertices.clear();
+ vmap.clear();
+
+ for (size_t i = 0, il = faces.size(); i != il; ++i) {
+ face_t &f = faces[i];
+
+ for (size_t j = 0, jl = f.nVertices(); j != jl; ++j) {
+ vmap[f.vertex(j)] = NULL;
+ }
+ }
+
+ vertices.reserve(vmap.size());
+
+ for (std::unordered_map<const vertex_t *, const vertex_t *>::iterator i = vmap.begin(),
+ e = vmap.end();
+ i != e;
+ ++i) {
+ vertices.push_back(*(*i).first);
+ (*i).second = &vertices.back();
+ }
+
+ for (size_t i = 0, il = faces.size(); i != il; ++i) {
+ face_t &f = faces[i];
+
+ for (size_t j = 0, jl = f.nVertices(); j != jl; ++j) {
+ f.vertex(j) = vmap[f.vertex(j)];
+ }
+ }
+ }
+
+
+
+ void Polyhedron::collectFaceVertices(std::vector<face_t> &faces,
+ std::vector<vertex_t> &vertices) {
+ std::unordered_map<const vertex_t *, const vertex_t *> vmap;
+ collectFaceVertices(faces, vertices, vmap);
+ }
+
+
+
+ void Polyhedron::setFaceAndVertexOwner() {
+ for (size_t i = 0; i < vertices.size(); ++i) vertices[i].owner = this;
+ for (size_t i = 0; i < faces.size(); ++i) faces[i].owner = this;
+ }
+
+
+
+ void Polyhedron::commonFaceInit(bool _recalc) {
+ collectFaceVertices(faces, vertices);
+ setFaceAndVertexOwner();
+ if (_recalc) faceRecalc();
+
+ if (!init()) {
+ throw carve::exception("polyhedron creation failed");
+ }
+ }
+
+
+
+ Polyhedron::~Polyhedron() {
+ }
+
+
+
+ void Polyhedron::testVertexAgainstClosedManifolds(const carve::geom3d::Vector &v,
+ std::map<int, PointClass> &result,
+ bool ignore_orientation) const {
+
+ for (size_t i = 0; i < faces.size(); i++) {
+ if (!manifold_is_closed[faces[i].manifold_id]) continue; // skip open manifolds
+ if (faces[i].containsPoint(v)) {
+ result[faces[i].manifold_id] = POINT_ON;
+ }
+ }
+
+ double ray_len = aabb.extent.length() * 2;
+
+ std::vector<const face_t *> possible_faces;
+
+ std::vector<std::pair<const face_t *, carve::geom3d::Vector> > manifold_intersections;
+
+ boost::mt19937 rng;
+ boost::uniform_on_sphere<double> distrib(3);
+ boost::variate_generator<boost::mt19937 &, boost::uniform_on_sphere<double> > gen(rng, distrib);
+
+ for (;;) {
+ carve::geom3d::Vector ray_dir;
+ ray_dir = gen();
+
+ carve::geom3d::Vector v2 = v + ray_dir * ray_len;
+
+ bool failed = false;
+ carve::geom3d::LineSegment line(v, v2);
+ carve::geom3d::Vector intersection;
+
+ possible_faces.clear();
+ manifold_intersections.clear();
+ octree.findFacesNear(line, possible_faces);
+
+ for (unsigned i = 0; !failed && i < possible_faces.size(); i++) {
+ if (!manifold_is_closed[possible_faces[i]->manifold_id]) continue; // skip open manifolds
+ if (result.find(possible_faces[i]->manifold_id) != result.end()) continue; // already ON
+
+ switch (possible_faces[i]->lineSegmentIntersection(line, intersection)) {
+ case INTERSECT_FACE: {
+ manifold_intersections.push_back(std::make_pair(possible_faces[i], intersection));
+ break;
+ }
+ case INTERSECT_NONE: {
+ break;
+ }
+ default: {
+ failed = true;
+ break;
+ }
+ }
+ }
+
+ if (!failed) break;
+ }
+
+ std::vector<int> crossings(manifold_is_closed.size(), 0);
+
+ for (size_t i = 0; i < manifold_intersections.size(); ++i) {
+ const face_t *f = manifold_intersections[i].first;
+ crossings[f->manifold_id]++;
+ }
+
+ for (size_t i = 0; i < crossings.size(); ++i) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "crossing: " << i << " = " << crossings[i] << " is_negative = " << manifold_is_negative[i] << std::endl;
+#endif
+ if (!manifold_is_closed[i]) continue;
+ if (result.find(i) != result.end()) continue;
+ PointClass pc = (crossings[i] & 1) ? POINT_IN : POINT_OUT;
+ if (!ignore_orientation && manifold_is_negative[i]) pc = (PointClass)-pc;
+ result[i] = pc;
+ }
+ }
+
+
+
+ PointClass Polyhedron::containsVertex(const carve::geom3d::Vector &v,
+ const face_t **hit_face,
+ bool even_odd,
+ int manifold_id) const {
+ if (hit_face) *hit_face = NULL;
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{containsVertex " << v << "}" << std::endl;
+#endif
+
+ if (!aabb.containsPoint(v)) {
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT(aabb short circuit)}" << std::endl;
+#endif
+ // XXX: if the top level manifolds are negative, this should be POINT_IN.
+ // for the moment, this only works for a single manifold.
+ if (manifold_is_negative.size() == 1 && manifold_is_negative[0]) return POINT_IN;
+ return POINT_OUT;
+ }
+
+ for (size_t i = 0; i < faces.size(); i++) {
+ if (manifold_id != -1 && manifold_id != faces[i].manifold_id) continue;
+
+ // XXX: Do allow the tested vertex to be ON an open
+ // manifold. This was here originally because of the
+ // possibility of an open manifold contained within a closed
+ // manifold.
+
+ // if (!manifold_is_closed[faces[i].manifold_id]) continue;
+
+ if (faces[i].containsPoint(v)) {
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:ON(hits face " << &faces[i] << ")}" << std::endl;
+#endif
+ if (hit_face) *hit_face = &faces[i];
+ return POINT_ON;
+ }
+ }
+
+ double ray_len = aabb.extent.length() * 2;
+
+ std::vector<const face_t *> possible_faces;
+
+ std::vector<std::pair<const face_t *, carve::geom3d::Vector> > manifold_intersections;
+
+ for (;;) {
+ double a1 = random() / double(RAND_MAX) * M_TWOPI;
+ double a2 = random() / double(RAND_MAX) * M_TWOPI;
+
+ carve::geom3d::Vector ray_dir = carve::geom::VECTOR(sin(a1) * sin(a2), cos(a1) * sin(a2), cos(a2));
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{testing ray: " << ray_dir << "}" << std::endl;
+#endif
+
+ carve::geom3d::Vector v2 = v + ray_dir * ray_len;
+
+ bool failed = false;
+ carve::geom3d::LineSegment line(v, v2);
+ carve::geom3d::Vector intersection;
+
+ possible_faces.clear();
+ manifold_intersections.clear();
+ octree.findFacesNear(line, possible_faces);
+
+ for (unsigned i = 0; !failed && i < possible_faces.size(); i++) {
+ if (manifold_id != -1 && manifold_id != faces[i].manifold_id) continue;
+
+ if (!manifold_is_closed[possible_faces[i]->manifold_id]) continue;
+
+ switch (possible_faces[i]->lineSegmentIntersection(line, intersection)) {
+ case INTERSECT_FACE: {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersects face: " << possible_faces[i]
+ << " dp: " << dot(ray_dir, possible_faces[i]->plane_eqn.N) << "}" << std::endl;
+#endif
+
+ if (!even_odd && fabs(dot(ray_dir, possible_faces[i]->plane_eqn.N)) < EPSILON) {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{failing(small dot product)}" << std::endl;
+#endif
+
+ failed = true;
+ break;
+ }
+ manifold_intersections.push_back(std::make_pair(possible_faces[i], intersection));
+ break;
+ }
+ case INTERSECT_NONE: {
+ break;
+ }
+ default: {
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{failing(degenerate intersection)}" << std::endl;
+#endif
+ failed = true;
+ break;
+ }
+ }
+ }
+
+ if (!failed) {
+ if (even_odd) {
+ return (manifold_intersections.size() & 1) ? POINT_IN : POINT_OUT;
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersections ok [count:"
+ << manifold_intersections.size()
+ << "], sorting}"
+ << std::endl;
+#endif
+
+ carve::geom3d::sortInDirectionOfRay(ray_dir,
+ manifold_intersections.begin(),
+ manifold_intersections.end(),
+ carve::geom3d::vec_adapt_pair_second());
+
+ std::vector<int> crossings(manifold_is_closed.size(), 0);
+
+ for (size_t i = 0; i < manifold_intersections.size(); ++i) {
+ const face_t *f = manifold_intersections[i].first;
+ if (dot(ray_dir, f->plane_eqn.N) < 0.0) {
+ crossings[f->manifold_id]++;
+ } else {
+ crossings[f->manifold_id]--;
+ }
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ for (size_t i = 0; i < crossings.size(); ++i) {
+ std::cerr << "{manifold " << i << " crossing count: " << crossings[i] << "}" << std::endl;
+ }
+#endif
+
+ for (size_t i = 0; i < manifold_intersections.size(); ++i) {
+ const face_t *f = manifold_intersections[i].first;
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{intersection at "
+ << manifold_intersections[i].second
+ << " id: "
+ << f->manifold_id
+ << " count: "
+ << crossings[f->manifold_id]
+ << "}"
+ << std::endl;
+#endif
+
+ if (crossings[f->manifold_id] < 0) {
+ // inside this manifold.
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:IN}" << std::endl;
+#endif
+
+ return POINT_IN;
+ } else if (crossings[f->manifold_id] > 0) {
+ // outside this manifold, but it's an infinite manifold. (for instance, an inverted cube)
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT}" << std::endl;
+#endif
+
+ return POINT_OUT;
+ }
+ }
+
+#if defined(DEBUG_CONTAINS_VERTEX)
+ std::cerr << "{final:OUT(default)}" << std::endl;
+#endif
+
+ return POINT_OUT;
+ }
+ }
+ }
+
+
+
+ void Polyhedron::findEdgesNear(const carve::geom::aabb<3> &aabb,
+ std::vector<const edge_t *> &outEdges) const {
+ outEdges.clear();
+ octree.findEdgesNear(aabb, outEdges);
+ }
+
+
+
+ void Polyhedron::findEdgesNear(const carve::geom3d::LineSegment &line,
+ std::vector<const edge_t *> &outEdges) const {
+ outEdges.clear();
+ octree.findEdgesNear(line, outEdges);
+ }
+
+
+
+ void Polyhedron::findEdgesNear(const carve::geom3d::Vector &v,
+ std::vector<const edge_t *> &outEdges) const {
+ outEdges.clear();
+ octree.findEdgesNear(v, outEdges);
+ }
+
+
+
+ void Polyhedron::findEdgesNear(const face_t &face,
+ std::vector<const edge_t *> &edges) const {
+ edges.clear();
+ octree.findEdgesNear(face, edges);
+ }
+
+
+
+ void Polyhedron::findEdgesNear(const edge_t &edge,
+ std::vector<const edge_t *> &outEdges) const {
+ outEdges.clear();
+ octree.findEdgesNear(edge, outEdges);
+ }
+
+
+
+ void Polyhedron::findFacesNear(const carve::geom3d::LineSegment &line,
+ std::vector<const face_t *> &outFaces) const {
+ outFaces.clear();
+ octree.findFacesNear(line, outFaces);
+ }
+
+
+
+ void Polyhedron::findFacesNear(const carve::geom::aabb<3> &aabb,
+ std::vector<const face_t *> &outFaces) const {
+ outFaces.clear();
+ octree.findFacesNear(aabb, outFaces);
+ }
+
+
+
+ void Polyhedron::findFacesNear(const edge_t &edge,
+ std::vector<const face_t *> &outFaces) const {
+ outFaces.clear();
+ octree.findFacesNear(edge, outFaces);
+ }
+
+
+
+ void Polyhedron::transform(const carve::math::Matrix &xform) {
+ for (size_t i = 0; i < vertices.size(); i++) {
+ vertices[i].v = xform * vertices[i].v;
+ }
+ for (size_t i = 0; i < faces.size(); i++) {
+ faces[i].recalc();
+ }
+ init();
+ }
+
+
+
+ void Polyhedron::print(std::ostream &o) const {
+ o << "Polyhedron@" << this << " {" << std::endl;
+ for (std::vector<vertex_t >::const_iterator
+ i = vertices.begin(), e = vertices.end(); i != e; ++i) {
+ o << " V@" << &(*i) << " " << (*i).v << std::endl;
+ }
+ for (std::vector<edge_t >::const_iterator
+ i = edges.begin(), e = edges.end(); i != e; ++i) {
+ o << " E@" << &(*i) << " {" << std::endl;
+ o << " V@" << (*i).v1 << " - " << "V@" << (*i).v2 << std::endl;
+ const std::vector<const face_t *> &faces = connectivity.edge_to_face[edgeToIndex_fast(&(*i))];
+ for (size_t j = 0; j < (faces.size() & ~1U); j += 2) {
+ o << " fp: F@" << faces[j] << ", F@" << faces[j+1] << std::endl;
+ }
+ o << " }" << std::endl;
+ }
+ for (std::vector<face_t >::const_iterator
+ i = faces.begin(), e = faces.end(); i != e; ++i) {
+ o << " F@" << &(*i) << " {" << std::endl;
+ o << " vertices {" << std::endl;
+ for (face_t::const_vertex_iter_t j = (*i).vbegin(), je = (*i).vend(); j != je; ++j) {
+ o << " V@" << (*j) << std::endl;
+ }
+ o << " }" << std::endl;
+ o << " edges {" << std::endl;
+ for (face_t::const_edge_iter_t j = (*i).ebegin(), je = (*i).eend(); j != je; ++j) {
+ o << " E@" << (*j) << std::endl;
+ }
+ carve::geom::plane<3> p = (*i).plane_eqn;
+ o << " }" << std::endl;
+ o << " normal " << (*i).plane_eqn.N << std::endl;
+ o << " aabb " << (*i).aabb << std::endl;
+ o << " plane_eqn ";
+ carve::geom::operator<< <3>(o, p);
+ o << std::endl;
+ o << " }" << std::endl;
+ }
+
+ o << "}" << std::endl;
+ }
+
+
+
+ void Polyhedron::canonicalize() {
+ orderVertices();
+ for (size_t i = 0; i < faces.size(); i++) {
+ face_t &f = faces[i];
+ size_t j = std::distance(f.vbegin(),
+ std::min_element(f.vbegin(),
+ f.vend()));
+ if (j) {
+ {
+ std::vector<const vertex_t *> temp;
+ temp.reserve(f.nVertices());
+ std::copy(f.vbegin() + j, f.vend(), std::back_inserter(temp));
+ std::copy(f.vbegin(), f.vbegin() + j, std::back_inserter(temp));
+ std::copy(temp.begin(), temp.end(), f.vbegin());
+ }
+ {
+ std::vector<const edge_t *> temp;
+ temp.reserve(f.nEdges());
+ std::copy(f.ebegin() + j, f.eend(), std::back_inserter(temp));
+ std::copy(f.ebegin(), f.ebegin() + j, std::back_inserter(temp));
+ std::copy(temp.begin(), temp.end(), f.ebegin());
+ }
+ }
+ }
+
+ std::vector<face_t *> face_ptrs;
+ face_ptrs.reserve(faces.size());
+ for (size_t i = 0; i < faces.size(); ++i) face_ptrs.push_back(&faces[i]);
+ std::sort(face_ptrs.begin(), face_ptrs.end(), order_faces());
+ std::vector<face_t> sorted_faces;
+ sorted_faces.reserve(faces.size());
+ for (size_t i = 0; i < faces.size(); ++i) sorted_faces.push_back(*face_ptrs[i]);
+ std::swap(faces, sorted_faces);
+ }
+
+ }
+}
+
diff --git a/extern/carve/lib/polyline.cpp b/extern/carve/lib/polyline.cpp
new file mode 100644
index 00000000000..d9681c76a5c
--- /dev/null
+++ b/extern/carve/lib/polyline.cpp
@@ -0,0 +1,67 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/geom.hpp>
+#include <carve/vector.hpp>
+#include <carve/polyline.hpp>
+
+namespace carve {
+ namespace line {
+ carve::geom3d::AABB Polyline::aabb() const {
+ return carve::geom3d::AABB(vbegin(), vend(), vec_adapt_vertex_ptr());
+ }
+
+ PolylineSet::PolylineSet(const std::vector<carve::geom3d::Vector> &points) {
+ vertices.resize(points.size());
+ for (size_t i = 0; i < points.size(); ++i) vertices[i].v = points[i];
+ aabb.fit(points.begin(), points.end(), carve::geom3d::vec_adapt_ident());
+ }
+
+ void PolylineSet::sortVertices(const carve::geom3d::Vector &axis) {
+ std::vector<std::pair<double, size_t> > temp;
+ temp.reserve(vertices.size());
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ temp.push_back(std::make_pair(dot(axis, vertices[i].v), i));
+ }
+ std::sort(temp.begin(), temp.end());
+ std::vector<Vertex> vnew;
+ std::vector<int> revmap;
+ vnew.reserve(vertices.size());
+ revmap.resize(vertices.size());
+
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ vnew.push_back(vertices[temp[i].second]);
+ revmap[temp[i].second] = i;
+ }
+
+ for (line_iter i = lines.begin(); i != lines.end(); ++i) {
+ Polyline &l = *(*i);
+ for (size_t j = 0; j < l.edges.size(); ++j) {
+ PolylineEdge &e = *l.edges[j];
+ if (e.v1) e.v1 = &vnew[revmap[vertexToIndex_fast(e.v1)]];
+ if (e.v2) e.v2 = &vnew[revmap[vertexToIndex_fast(e.v2)]];
+ }
+ }
+ vertices.swap(vnew);
+ }
+
+ }
+}
diff --git a/extern/carve/lib/tag.cpp b/extern/carve/lib/tag.cpp
new file mode 100644
index 00000000000..449eb555346
--- /dev/null
+++ b/extern/carve/lib/tag.cpp
@@ -0,0 +1,24 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/tag.hpp>
+
+int carve::tagable::s_count = 0;
diff --git a/extern/carve/lib/timing.cpp b/extern/carve/lib/timing.cpp
new file mode 100644
index 00000000000..a98796cd8a7
--- /dev/null
+++ b/extern/carve/lib/timing.cpp
@@ -0,0 +1,436 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if CARVE_USE_TIMINGS
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/timing.hpp>
+
+#include <cstring>
+#include <list>
+#include <stack>
+#include <vector>
+#include <map>
+#include <iostream>
+#include <string>
+#include <algorithm>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <time.h>
+#include <sys/time.h>
+#endif
+
+#ifndef CARVE_USE_GLOBAL_NEW_DELETE
+#define CARVE_USE_GLOBAL_NEW_DELETE 0
+#endif
+
+namespace carve {
+ static uint64_t memoryCurr = 0;
+ static uint64_t memoryTotal = 0;
+ unsigned blkCntCurr[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+ unsigned blkCntTotal[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+ void addBlk(unsigned size) {
+ unsigned i = 0;
+ while (i < 31 && (1U<<i) < size) ++i;
+ blkCntCurr[i]++;
+ blkCntTotal[i]++;
+ }
+ void remBlk(unsigned size) {
+ unsigned i = 0;
+ while (i < 31 && (1<<i) < size) ++i;
+ blkCntCurr[i]--;
+ }
+}
+
+// lets provide a global new and delete as well
+
+#if CARVE_USE_GLOBAL_NEW_DELETE
+
+#if defined(__APPLE__)
+
+#include <stdlib.h>
+#include <malloc/malloc.h>
+
+void* carve_alloc(size_t size) {
+ void *p = malloc(size);
+ if (p == 0) throw std::bad_alloc(); // ANSI/ISO compliant behavior
+
+ unsigned sz = malloc_size(p);
+ carve::memoryCurr += sz;
+ carve::memoryTotal += sz;
+ carve::addBlk(sz);
+ return p;
+}
+
+void carve_free(void *p) {
+ unsigned sz = malloc_size(p);
+ carve::memoryCurr -= sz;
+ carve::remBlk(sz);
+ free(p);
+}
+
+#else
+
+void* carve_alloc(size_t size) {
+ void *p = malloc(size + 4);
+ if (p == 0) throw std::bad_alloc(); // ANSI/ISO compliant behavior
+
+ int *sizePtr = (int*)p;
+ *sizePtr = size;
+ ++sizePtr;
+ carve::memoryCurr += size;
+ carve::memoryTotal += size;
+ carve::addBlk(size);
+ return sizePtr;
+}
+
+void carve_free(void *p) {
+ // our memory block is actually a size of an int behind this pointer.
+ int *sizePtr = (int*)p;
+
+ --sizePtr;
+
+ carve::memoryCurr -= *sizePtr;
+ int size = *sizePtr;
+ carve::remBlk(size);
+ free(sizePtr);
+}
+
+#endif
+
+
+void* operator new (size_t size) {
+ return carve_alloc(size);
+}
+
+void* operator new[](size_t size) {
+ return carve_alloc(size);
+}
+
+
+void operator delete (void *p) {
+ carve_free(p);
+}
+
+void operator delete[](void *p) {
+ carve_free(p);
+}
+
+#endif
+
+namespace carve {
+
+
+
+#ifdef WIN32
+
+ typedef __int64 precise_time_t;
+
+ precise_time_t g_frequency;
+
+ void initTime() {
+ ::QueryPerformanceFrequency((LARGE_INTEGER*)&g_frequency);
+ }
+
+ void getTime(precise_time_t &t) {
+ ::QueryPerformanceCounter((LARGE_INTEGER*)&t);
+ }
+
+ double diffTime(precise_time_t from, precise_time_t to) {
+ return (double)(to - from) / (double)g_frequency;
+ }
+
+#else
+
+ typedef double precise_time_t;
+
+ void initTime() {
+ }
+
+ void getTime(precise_time_t &t) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ t = tv.tv_sec + tv.tv_usec / 1000000.0;
+ }
+
+ double diffTime(precise_time_t from, precise_time_t to) {
+ return to - from;
+ }
+
+#endif
+
+ struct Entry {
+ Entry(int _id) {
+ id = _id;
+ time = 0;
+ parent = NULL;
+ }
+ int id;
+ double time;
+ int64_t memoryDiff;
+ int64_t allocTotal;
+ int delta_blk_cnt_curr[32];
+ int delta_blk_cnt_total[32];
+ Entry *parent;
+ std::vector<Entry *> children;
+ };
+
+ struct Timer {
+ struct cmp {
+ bool operator()(const std::pair<int, double> &a, const std::pair<int, double> &b) const {
+ return b.second < a.second;
+ }
+ bool operator()(const Entry * const &a, const Entry * const &b) const {
+ return b->time < a->time;
+ }
+ };
+
+ Timer() {
+ initTime();
+ }
+
+ struct Snapshot {
+ precise_time_t time;
+ uint64_t memory_curr;
+ uint64_t memory_total;
+ unsigned blk_cnt_curr[32];
+ unsigned blk_cnt_total[32];
+ };
+
+ static void getSnapshot(Snapshot &snapshot) {
+ getTime(snapshot.time);
+ snapshot.memory_curr = carve::memoryCurr;
+ snapshot.memory_total = carve::memoryTotal;
+ std::memcpy(snapshot.blk_cnt_curr, carve::blkCntCurr, sizeof(carve::blkCntCurr));
+ std::memcpy(snapshot.blk_cnt_total, carve::blkCntTotal, sizeof(carve::blkCntTotal));
+ }
+
+ static void compareSnapshot(const Snapshot &from, const Snapshot &to, Entry *entry) {
+ entry->time = diffTime(from.time, to.time);
+ entry->memoryDiff = to.memory_curr - from.memory_curr;
+ entry->allocTotal = to.memory_total - from.memory_total;
+ for (int i = 0; i < 32; i++) {
+ entry->delta_blk_cnt_curr[i] = to.blk_cnt_curr[i] - from.blk_cnt_curr[i];
+ entry->delta_blk_cnt_total[i] = to.blk_cnt_total[i] - from.blk_cnt_total[i];
+ }
+ }
+
+ std::stack<std::pair<Entry*, Snapshot> > currentTimers;
+
+ void startTiming(int id) {
+ entries.push_back(Entry(id));
+ currentTimers.push(std::make_pair(&entries.back(), Snapshot()));
+ getSnapshot(currentTimers.top().second);
+ }
+
+ double endTiming() {
+ Snapshot end;
+ getSnapshot(end);
+
+ Entry *entry = currentTimers.top().first;
+ compareSnapshot(currentTimers.top().second, end, entry);
+
+ currentTimers.pop();
+ if (!currentTimers.empty()) {
+ entry->parent = currentTimers.top().first;
+ entry->parent->children.push_back(entry);
+ } else {
+ root_entries.push_back(entry);
+ }
+ //std::sort(entry->children.begin(), entry->children.end(), cmp());
+ return entry->time;
+ }
+
+ typedef std::list<Entry> EntryList;
+ EntryList entries;
+ std::vector<Entry *> root_entries;
+
+ std::map<int, std::string> names;
+
+ static std::string formatMemory(int64_t value) {
+
+ std::ostringstream result;
+
+ result << (value >= 0 ? "+" : "-");
+ if (value < 0) {
+ value = -value;
+ }
+
+ int power = 1;
+ while (value > pow(10.0, power)) {
+ power++;
+ }
+
+ for (power--; power >= 0; power--) {
+ int64_t base = pow(10.0, power);
+ int64_t amount = value / base;
+ result <<
+#if defined(_MSC_VER) && _MSC_VER < 1300
+ (long)
+#endif
+ amount;
+ if (power > 0 && (power % 3) == 0) {
+ result << ",";
+ }
+ value -= amount * base;
+ }
+
+ result << " bytes";
+
+ return result.str();
+ }
+
+ void printEntries(std::ostream &o, const std::vector<Entry *> &entries, const std::string &indent, double parent_time) {
+ if (parent_time <= 0.0) {
+ parent_time = 0.0;
+ for (size_t i = 0; i < entries.size(); ++i) {
+ parent_time += entries[i]->time;
+ }
+ }
+ double t_tot = 0.0;
+ for (size_t i = 0; i < entries.size(); ++i) {
+ const Entry *entry = entries[i];
+
+ std::ostringstream r;
+ r << indent;
+ std::string str = names[entry->id];
+ if (str.empty()) {
+ r << "(" << entry->id << ")";
+ } else {
+ r << str;
+ }
+ r << " ";
+ std::string pad(r.str().size(), ' ');
+ r << " - exectime: " << entry->time << "s (" << (entry->time * 100.0 / parent_time) << "%)" << std::endl;
+ if (entry->allocTotal || entry->memoryDiff) {
+ r << pad << " - alloc: " << formatMemory(entry->allocTotal) << " delta: " << formatMemory(entry->memoryDiff) << std::endl;
+ r << pad << " - alloc blks:";
+ for (int i = 0; i < 32; i++) { if (entry->delta_blk_cnt_total[i]) r << ' ' << ((1 << (i - 1)) + 1) << '-' << (1 << i) << ':' << entry->delta_blk_cnt_total[i]; }
+ r << std::endl;
+ r << pad << " - delta blks:";
+ for (int i = 0; i < 32; i++) { if (entry->delta_blk_cnt_curr[i]) r << ' ' << ((1 << (i - 1)) + 1) << '-' << (1 << i) << ':' << entry->delta_blk_cnt_curr[i]; }
+ r << std::endl;
+ }
+ o << r.str();
+ t_tot += entry->time;
+ if (entry->children.size()) printEntries(o, entry->children, indent + " ", entry->time);
+ }
+ if (t_tot < parent_time) {
+ o << indent << "*** unaccounted: " << (parent_time - t_tot) << "s (" << (100.0 - t_tot * 100.0 / parent_time) << "%)" << std::endl;
+ }
+ }
+
+ void print() {
+ std::map<int, double> totals;
+ std::cerr << "Timings: " << std::endl;
+ // print out all the entries.
+
+ //std::sort(root_entries.begin(), root_entries.end(), cmp());
+
+ printEntries(std::cerr, root_entries, " ", -1.0);
+
+ for (EntryList::const_iterator it = entries.begin(); it != entries.end(); ++it) {
+ totals[(*it).id] += (*it).time;
+ }
+
+ std::cerr << std::endl;
+ std::cerr << "Totals: " << std::endl;
+
+ std::vector<std::pair<int, double> > sorted_totals;
+ sorted_totals.reserve(totals.size());
+ for (std::map<int,double>::iterator it = totals.begin(); it != totals.end(); ++it) {
+ sorted_totals.push_back(*it);
+ }
+
+ std::sort(sorted_totals.begin(), sorted_totals.end(), cmp());
+
+ for (std::vector<std::pair<int,double> >::iterator it = sorted_totals.begin(); it != sorted_totals.end(); ++it) {
+ std::cerr << " ";
+ std::string str = names[it->first];
+ if (str.empty()) {
+ std::cerr << "(" << it->first << ")";
+ } else {
+ std::cerr << str;
+ }
+ std::cerr << " - " << it->second << "s " << std::endl;
+ }
+ }
+ void registerID(int id, const char *name) {
+ names[id] = name;
+ }
+ int registerID(const char *name) {
+ int id = names.size() + 1;
+ names[id] = name;
+ return id;
+ }
+
+ };
+
+ Timer timer;
+
+
+ TimingBlock::TimingBlock(int id) {
+#if CARVE_USE_TIMINGS
+ timer.startTiming(id);
+#endif
+ }
+
+ TimingBlock::TimingBlock(const TimingName &name) {
+#if CARVE_USE_TIMINGS
+ timer.startTiming(name.id);
+#endif
+ }
+
+
+ TimingBlock::~TimingBlock() {
+#if CARVE_USE_TIMINGS
+ timer.endTiming();
+#endif
+ }
+ void Timing::start(int id) {
+#if CARVE_USE_TIMINGS
+ timer.startTiming(id);
+#endif
+ }
+
+ double Timing::stop() {
+#if CARVE_USE_TIMINGS
+ return timer.endTiming();
+#endif
+ }
+
+ void Timing::printTimings() {
+ timer.print();
+ }
+
+ void Timing::registerID(int id, const char *name) {
+ timer.registerID(id, name);
+ }
+
+ TimingName::TimingName(const char *name) {
+ id = timer.registerID(name);
+ }
+
+}
+
+#endif
diff --git a/extern/carve/lib/triangulator.cpp b/extern/carve/lib/triangulator.cpp
new file mode 100644
index 00000000000..b36aecf98be
--- /dev/null
+++ b/extern/carve/lib/triangulator.cpp
@@ -0,0 +1,1211 @@
+// Begin License:
+// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com).
+// All rights reserved.
+//
+// This file is part of the Carve CSG Library (http://carve-csg.com/)
+//
+// This file may be used under the terms of the GNU General Public
+// License version 2.0 as published by the Free Software Foundation
+// and appearing in the file LICENSE.GPL2 included in the packaging of
+// this file.
+//
+// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE.
+// End:
+
+
+#if defined(HAVE_CONFIG_H)
+# include <carve_config.h>
+#endif
+
+#include <carve/csg.hpp>
+#include <carve/triangulator.hpp>
+
+#include <fstream>
+#include <sstream>
+
+#include <algorithm>
+
+
+namespace {
+ // private code related to hole patching.
+
+ class order_h_loops_2d {
+ order_h_loops_2d &operator=(const order_h_loops_2d &);
+
+ const std::vector<std::vector<carve::geom2d::P2> > &poly;
+ int axis;
+ public:
+
+ order_h_loops_2d(const std::vector<std::vector<carve::geom2d::P2> > &_poly, int _axis) :
+ poly(_poly), axis(_axis) {
+ }
+
+ bool operator()(const std::pair<size_t, size_t> &a,
+ const std::pair<size_t, size_t> &b) const {
+ return carve::triangulate::detail::axisOrdering(poly[a.first][a.second], poly[b.first][b.second], axis);
+ }
+ };
+
+ class heap_ordering_2d {
+ heap_ordering_2d &operator=(const heap_ordering_2d &);
+
+ const std::vector<std::vector<carve::geom2d::P2> > &poly;
+ const std::vector<std::pair<size_t, size_t> > &loop;
+ const carve::geom2d::P2 p;
+ int axis;
+
+ public:
+
+ heap_ordering_2d(const std::vector<std::vector<carve::geom2d::P2> > &_poly,
+ const std::vector<std::pair<size_t, size_t> > &_loop,
+ const carve::geom2d::P2 _p,
+ int _axis) : poly(_poly), loop(_loop), p(_p), axis(_axis) {
+ }
+
+ bool operator()(size_t a, size_t b) const {
+ double da = carve::geom::distance2(p, poly[loop[a].first][loop[a].second]);
+ double db = carve::geom::distance2(p, poly[loop[b].first][loop[b].second]);
+ if (da > db) return true;
+ if (da < db) return false;
+ return carve::triangulate::detail::axisOrdering(poly[loop[a].first][loop[a].second], poly[loop[b].first][loop[b].second], axis);
+ }
+ };
+
+ static inline void patchHoleIntoPolygon_2d(std::vector<std::pair<size_t, size_t> > &f_loop,
+ size_t f_loop_attach,
+ size_t h_loop,
+ size_t h_loop_attach,
+ size_t h_loop_size) {
+ f_loop.insert(f_loop.begin() + f_loop_attach + 1, h_loop_size + 2, std::make_pair(h_loop, 0));
+ size_t f = f_loop_attach + 1;
+
+ for (size_t h = h_loop_attach; h != h_loop_size; ++h) {
+ f_loop[f++].second = h;
+ }
+
+ for (size_t h = 0; h <= h_loop_attach; ++h) {
+ f_loop[f++].second = h;
+ }
+
+ f_loop[f] = f_loop[f_loop_attach];
+ }
+
+ static inline const carve::geom2d::P2 &pvert(const std::vector<std::vector<carve::geom2d::P2> > &poly, const std::pair<size_t, size_t> &idx) {
+ return poly[idx.first][idx.second];
+ }
+}
+
+
+namespace {
+ // private code related to triangulation.
+
+ using carve::triangulate::detail::vertex_info;
+
+ struct vertex_info_ordering {
+ bool operator()(const vertex_info *a, const vertex_info *b) const {
+ return a->score < b->score;
+ }
+ };
+
+ struct vertex_info_l2norm_inc_ordering {
+ const vertex_info *v;
+ vertex_info_l2norm_inc_ordering(const vertex_info *_v) : v(_v) {
+ }
+ bool operator()(const vertex_info *a, const vertex_info *b) const {
+ return carve::geom::distance2(v->p, a->p) > carve::geom::distance2(v->p, b->p);
+ }
+ };
+
+ class EarQueue {
+ std::vector<vertex_info *> queue;
+
+ void checkheap() {
+#ifdef __GNUC__
+ CARVE_ASSERT(std::__is_heap(queue.begin(), queue.end(), vertex_info_ordering()));
+#endif
+ }
+
+ public:
+ EarQueue() {
+ }
+
+ size_t size() const {
+ return queue.size();
+ }
+
+ void push(vertex_info *v) {
+#if defined(CARVE_DEBUG)
+ checkheap();
+#endif
+ queue.push_back(v);
+ std::push_heap(queue.begin(), queue.end(), vertex_info_ordering());
+ }
+
+ vertex_info *pop() {
+#if defined(CARVE_DEBUG)
+ checkheap();
+#endif
+ std::pop_heap(queue.begin(), queue.end(), vertex_info_ordering());
+ vertex_info *v = queue.back();
+ queue.pop_back();
+ return v;
+ }
+
+ void remove(vertex_info *v) {
+#if defined(CARVE_DEBUG)
+ checkheap();
+#endif
+ CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end());
+ double score = v->score;
+ if (v != queue[0]) {
+ v->score = queue[0]->score + 1;
+ std::make_heap(queue.begin(), queue.end(), vertex_info_ordering());
+ }
+ CARVE_ASSERT(v == queue[0]);
+ std::pop_heap(queue.begin(), queue.end(), vertex_info_ordering());
+ CARVE_ASSERT(queue.back() == v);
+ queue.pop_back();
+ v->score = score;
+ }
+
+ void changeScore(vertex_info *v, double score) {
+#if defined(CARVE_DEBUG)
+ checkheap();
+#endif
+ CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end());
+ if (v->score != score) {
+ v->score = score;
+ std::make_heap(queue.begin(), queue.end(), vertex_info_ordering());
+ }
+ }
+
+ // 39% of execution time
+ void updateVertex(vertex_info *v) {
+ double spre = v->score;
+ bool qpre = v->isCandidate();
+ v->recompute();
+ bool qpost = v->isCandidate();
+ double spost = v->score;
+
+ v->score = spre;
+
+ if (qpre) {
+ if (qpost) {
+ if (v->score != spre) {
+ changeScore(v, spost);
+ }
+ } else {
+ remove(v);
+ }
+ } else {
+ if (qpost) {
+ push(v);
+ }
+ }
+ }
+ };
+
+
+
+ int windingNumber(vertex_info *begin, const carve::geom2d::P2 &point) {
+ int wn = 0;
+
+ vertex_info *v = begin;
+ do {
+ if (v->p.y <= point.y) {
+ if (v->next->p.y > point.y && carve::geom2d::orient2d(v->p, v->next->p, point) > 0.0) {
+ ++wn;
+ }
+ } else {
+ if (v->next->p.y <= point.y && carve::geom2d::orient2d(v->p, v->next->p, point) < 0.0) {
+ --wn;
+ }
+ }
+ v = v->next;
+ } while (v != begin);
+
+ return wn;
+ }
+
+
+
+ bool internalToAngle(const vertex_info *a,
+ const vertex_info *b,
+ const vertex_info *c,
+ const carve::geom2d::P2 &p) {
+ return carve::geom2d::internalToAngle(a->p, b->p, c->p, p);
+ }
+
+
+
+ bool findDiagonal(vertex_info *begin, vertex_info *&v1, vertex_info *&v2) {
+ vertex_info *t;
+ std::vector<vertex_info *> heap;
+
+ v1 = begin;
+ do {
+ heap.clear();
+
+ for (v2 = v1->next->next; v2 != v1->prev; v2 = v2->next) {
+ if (!internalToAngle(v1->next, v1, v1->prev, v2->p) ||
+ !internalToAngle(v2->next, v2, v2->prev, v1->p)) continue;
+
+ heap.push_back(v2);
+ std::push_heap(heap.begin(), heap.end(), vertex_info_l2norm_inc_ordering(v1));
+ }
+
+ while (heap.size()) {
+ std::pop_heap(heap.begin(), heap.end(), vertex_info_l2norm_inc_ordering(v1));
+ v2 = heap.back(); heap.pop_back();
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "testing: " << v1 << " - " << v2 << std::endl;
+ std::cerr << " length = " << (v2->p - v1->p).length() << std::endl;
+ std::cerr << " pos: " << v1->p << " - " << v2->p << std::endl;
+#endif
+ // test whether v1-v2 is a valid diagonal.
+ double v_min_x = std::min(v1->p.x, v2->p.x);
+ double v_max_x = std::max(v1->p.x, v2->p.x);
+
+ bool intersected = false;
+
+ for (t = v1->next; !intersected && t != v1->prev; t = t->next) {
+ vertex_info *u = t->next;
+ if (t == v2 || u == v2) continue;
+
+ double l1 = carve::geom2d::orient2d(v1->p, v2->p, t->p);
+ double l2 = carve::geom2d::orient2d(v1->p, v2->p, u->p);
+
+ if ((l1 > 0.0 && l2 > 0.0) || (l1 < 0.0 && l2 < 0.0)) {
+ // both on the same side; no intersection
+ continue;
+ }
+
+ double dx13 = v1->p.x - t->p.x;
+ double dy13 = v1->p.y - t->p.y;
+ double dx43 = u->p.x - t->p.x;
+ double dy43 = u->p.y - t->p.y;
+ double dx21 = v2->p.x - v1->p.x;
+ double dy21 = v2->p.y - v1->p.y;
+ double ua_n = dx43 * dy13 - dy43 * dx13;
+ double ub_n = dx21 * dy13 - dy21 * dx13;
+ double u_d = dy43 * dx21 - dx43 * dy21;
+
+ if (carve::math::ZERO(u_d)) {
+ // parallel
+ if (carve::math::ZERO(ua_n)) {
+ // colinear
+ if (std::max(t->p.x, u->p.x) >= v_min_x && std::min(t->p.x, u->p.x) <= v_max_x) {
+ // colinear and intersecting
+ intersected = true;
+ }
+ }
+ } else {
+ // not parallel
+ double ua = ua_n / u_d;
+ double ub = ub_n / u_d;
+
+ if (0.0 <= ua && ua <= 1.0 && 0.0 <= ub && ub <= 1.0) {
+ intersected = true;
+ }
+ }
+#if defined(CARVE_DEBUG)
+ if (intersected) {
+ std::cerr << " failed on edge: " << t << " - " << u << std::endl;
+ std::cerr << " pos: " << t->p << " - " << u->p << std::endl;
+ }
+#endif
+ }
+
+ if (!intersected) {
+ // test whether midpoint winding == 1
+
+ carve::geom2d::P2 mid = (v1->p + v2->p) / 2;
+ if (windingNumber(begin, mid) == 1) {
+ // this diagonal is ok
+ return true;
+ }
+ }
+ }
+
+ // couldn't find a diagonal from v1 that was ok.
+ v1 = v1->next;
+ } while (v1 != begin);
+ return false;
+ }
+
+
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ void dumpPoly(const std::vector<carve::geom2d::P2> &points,
+ const std::vector<carve::triangulate::tri_idx> &result) {
+ static int step = 0;
+ std::ostringstream filename;
+ filename << "poly_" << step++ << ".svg";
+ std::cerr << "dumping to " << filename.str() << std::endl;
+ std::ofstream out(filename.str().c_str());
+
+ double minx = points[0].x, maxx = points[0].x;
+ double miny = points[0].y, maxy = points[0].y;
+
+ for (size_t i = 1; i < points.size(); ++i) {
+ minx = std::min(points[i].x, minx); maxx = std::max(points[i].x, maxx);
+ miny = std::min(points[i].y, miny); maxy = std::max(points[i].y, maxy);
+ }
+ double scale = 100 / std::max(maxx-minx, maxy-miny);
+
+ maxx *= scale; minx *= scale;
+ maxy *= scale; miny *= scale;
+
+ double width = maxx - minx + 10;
+ double height = maxy - miny + 10;
+
+ out << "\
+<?xml version=\"1.0\"?>\n\
+<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\
+<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" << width << "\" height=\"" << height << "\">\n";
+
+ out << "<polygon fill=\"rgb(0,0,0)\" stroke=\"blue\" stroke-width=\"0.1\" points=\"";
+ for (size_t i = 0; i < points.size(); ++i) {
+ if (i) out << ' ';
+ double x, y;
+ x = scale * (points[i].x) - minx + 5;
+ y = scale * (points[i].y) - miny + 5;
+ out << x << ',' << y;
+ }
+ out << "\" />" << std::endl;
+
+ for (size_t i = 0; i < result.size(); ++i) {
+ out << "<polygon fill=\"rgb(255,255,255)\" stroke=\"black\" stroke-width=\"0.1\" points=\"";
+ double x, y;
+ x = scale * (points[result[i].a].x) - minx + 5;
+ y = scale * (points[result[i].a].y) - miny + 5;
+ out << x << ',' << y << ' ';
+ x = scale * (points[result[i].b].x) - minx + 5;
+ y = scale * (points[result[i].b].y) - miny + 5;
+ out << x << ',' << y << ' ';
+ x = scale * (points[result[i].c].x) - minx + 5;
+ y = scale * (points[result[i].c].y) - miny + 5;
+ out << x << ',' << y;
+ out << "\" />" << std::endl;
+ }
+
+ out << "</svg>" << std::endl;
+ }
+#endif
+}
+
+
+
+double carve::triangulate::detail::vertex_info::triScore(const vertex_info *p, const vertex_info *v, const vertex_info *n) {
+
+ // different scoring functions.
+#if 0
+ bool convex = isLeft(p, v, n);
+ if (!convex) return -1e-5;
+
+ double a1 = carve::geom2d::atan2(p->p - v->p) - carve::geom2d::atan2(n->p - v->p);
+ double a2 = carve::geom2d::atan2(v->p - n->p) - carve::geom2d::atan2(p->p - n->p);
+ if (a1 < 0) a1 += M_PI * 2;
+ if (a2 < 0) a2 += M_PI * 2;
+
+ return std::min(a1, std::min(a2, M_PI - a1 - a2)) / (M_PI / 3);
+#endif
+
+#if 1
+ // range: 0 - 1
+ double a, b, c;
+
+ bool convex = isLeft(p, v, n);
+ if (!convex) return -1e-5;
+
+ a = (n->p - v->p).length();
+ b = (p->p - n->p).length();
+ c = (v->p - p->p).length();
+
+ if (a < 1e-10 || b < 1e-10 || c < 1e-10) return 0.0;
+
+ return std::max(std::min((a+b)/c, std::min((a+c)/b, (b+c)/a)) - 1.0, 0.0);
+#endif
+}
+
+
+
+double carve::triangulate::detail::vertex_info::calcScore() const {
+
+#if 0
+ // examine only this triangle.
+ double this_tri = triScore(prev, this, next);
+ return this_tri;
+#endif
+
+#if 1
+ // attempt to look ahead in the neighbourhood to attempt to clip ears that have good neighbours.
+ double this_tri = triScore(prev, this, next);
+ double next_tri = triScore(prev, next, next->next);
+ double prev_tri = triScore(prev->prev, prev, next);
+
+ return this_tri + std::max(next_tri, prev_tri) * .2;
+#endif
+
+#if 0
+ // attempt to penalise ears that will require producing a sliver triangle.
+ double score = triScore(prev, this, next);
+
+ double a1, a2;
+ a1 = carve::geom2d::atan2(prev->p - next->p);
+ a2 = carve::geom2d::atan2(next->next->p - next->p);
+ if (fabs(a1 - a2) < 1e-5) score -= .5;
+
+ a1 = carve::geom2d::atan2(next->p - prev->p);
+ a2 = carve::geom2d::atan2(prev->prev->p - prev->p);
+ if (fabs(a1 - a2) < 1e-5) score -= .5;
+
+ return score;
+#endif
+}
+
+
+
+bool carve::triangulate::detail::vertex_info::isClipable() const {
+ for (const vertex_info *v_test = next->next; v_test != prev; v_test = v_test->next) {
+ if (v_test->convex) {
+ continue;
+ }
+
+ if (v_test->p == prev->p ||
+ v_test->p == next->p) {
+ continue;
+ }
+
+ if (v_test->p == p) {
+ if (v_test->next->p == prev->p &&
+ v_test->prev->p == next->p) {
+ return false;
+ }
+ if (v_test->next->p == prev->p ||
+ v_test->prev->p == next->p) {
+ continue;
+ }
+ }
+
+ if (pointInTriangle(prev, this, next, v_test)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+size_t carve::triangulate::detail::removeDegeneracies(vertex_info *&begin, std::vector<carve::triangulate::tri_idx> &result) {
+ vertex_info *v = begin;
+ vertex_info *n;
+ size_t count = 0;
+ do {
+ bool remove = false;
+ if (v->p == v->next->p) {
+ remove = true;
+ } else if (v->p == v->next->next->p) {
+ if (v->next->p == v->next->next->next->p) {
+ // a 'z' in the loop: z (a) b a b c -> remove a-b-a -> z (a) a b c -> remove a-a-b (next loop) -> z a b c
+ // z --(a)-- b
+ // /
+ // /
+ // a -- b -- d
+ remove = true;
+ } else {
+ // a 'shard' in the loop: z (a) b a c d -> remove a-b-a -> z (a) a b c d -> remove a-a-b (next loop) -> z a b c d
+ // z --(a)-- b
+ // /
+ // /
+ // a -- c -- d
+ // n.b. can only do this if the shard is pointing out of the polygon. i.e. b is outside z-a-c
+ remove = !internalToAngle(v->next->next->next, v, v->prev, v->next->p);
+ }
+ }
+
+ if (remove) {
+ result.push_back(carve::triangulate::tri_idx(v->idx, v->next->idx, v->next->next->idx));
+ n = v->next;
+ if (n == begin) begin = n->next;
+ n->remove();
+ count++;
+ delete n;
+ continue;
+ }
+
+ v = v->next;
+ } while (v != begin);
+ return count;
+}
+
+
+
+bool carve::triangulate::detail::splitAndResume(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result) {
+ vertex_info *v1, *v2;
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ {
+ std::vector<carve::triangulate::tri_idx> dummy;
+ std::vector<carve::geom2d::P2> dummy_p;
+ vertex_info *v = begin;
+ do {
+ dummy_p.push_back(v->p);
+ v = v->next;
+ } while (v != begin);
+ std::cerr << "input to splitAndResume:" << std::endl;
+ dumpPoly(dummy_p, dummy);
+ }
+#endif
+
+
+ if (!findDiagonal(begin, v1, v2)) return false;
+
+ vertex_info *v1_copy = new vertex_info(*v1);
+ vertex_info *v2_copy = new vertex_info(*v2);
+
+ v1->next = v2;
+ v2->prev = v1;
+
+ v1_copy->next->prev = v1_copy;
+ v2_copy->prev->next = v2_copy;
+
+ v1_copy->prev = v2_copy;
+ v2_copy->next = v1_copy;
+
+ bool r1 = doTriangulate(v1, result);
+ bool r2 = doTriangulate(v1_copy, result);
+ return r1 && r2;
+}
+
+
+
+bool carve::triangulate::detail::doTriangulate(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result) {
+#if defined(CARVE_DEBUG)
+ std::cerr << "entering doTriangulate" << std::endl;
+#endif
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ {
+ std::vector<carve::triangulate::tri_idx> dummy;
+ std::vector<carve::geom2d::P2> dummy_p;
+ vertex_info *v = begin;
+ do {
+ dummy_p.push_back(v->p);
+ v = v->next;
+ } while (v != begin);
+ dumpPoly(dummy_p, dummy);
+ }
+#endif
+
+ EarQueue vq;
+
+ vertex_info *v = begin;
+ size_t remain = 0;
+ do {
+ if (v->isCandidate()) vq.push(v);
+ v = v->next;
+ remain++;
+ } while (v != begin);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "remain = " << remain << std::endl;
+#endif
+
+ while (vq.size()) {
+ vertex_info *v = vq.pop();
+ if (!v->isClipable()) {
+ v->failed = true;
+ continue;
+ }
+
+ continue_clipping:
+ vertex_info *n = v->next;
+ vertex_info *p = v->prev;
+
+ result.push_back(carve::triangulate::tri_idx(v->prev->idx, v->idx, v->next->idx));
+
+#if defined(CARVE_DEBUG)
+ {
+ std::vector<carve::geom2d::P2> temp;
+ temp.push_back(v->prev->p);
+ temp.push_back(v->p);
+ temp.push_back(v->next->p);
+ std::cerr << "clip " << v << " idx = " << v->idx << " score = " << v->score << " area = " << carve::geom2d::signedArea(temp) << " " << temp[0] << " " << temp[1] << " " << temp[2] << std::endl;
+ }
+#endif
+
+ v->remove();
+ remain--;
+ if (v == begin) begin = v->next;
+ delete v;
+
+ vq.updateVertex(n);
+ vq.updateVertex(p);
+
+ if (n->score < p->score) { std::swap(n, p); }
+
+ if (n->score > 0.25 && n->isCandidate() && n->isClipable()) {
+ vq.remove(n);
+ v = n;
+#if defined(CARVE_DEBUG)
+ std::cerr << " continue clipping (n), score = " << n->score << std::endl;
+#endif
+ goto continue_clipping;
+ }
+
+ if (p->score > 0.25 && p->isCandidate() && p->isClipable()) {
+ vq.remove(p);
+ v = p;
+#if defined(CARVE_DEBUG)
+ std::cerr << " continue clipping (p), score = " << n->score << std::endl;
+#endif
+ goto continue_clipping;
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "looking for new start point" << std::endl;
+ std::cerr << "remain = " << remain << std::endl;
+#endif
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "doTriangulate complete; remain=" << remain << std::endl;
+#endif
+
+ bool ret = true;
+
+ if (remain > 3) {
+ std::vector<carve::geom2d::P2> temp;
+ temp.reserve(remain);
+ vertex_info *v = begin;
+
+ do {
+ temp.push_back(v->p);
+ v = v->next;
+ } while (v != begin);
+
+ if (carve::geom2d::signedArea(temp) == 0) {
+ // XXX: this test will fail in cases where the boundary is
+ // twisted so that a negative area balances a positive area.
+#if defined(CARVE_DEBUG)
+ std::cerr << "skeleton remains. complete." << std::endl;
+#endif
+ goto done;
+ }
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "before removeDegeneracies: remain=" << remain << std::endl;
+#endif
+ remain -= removeDegeneracies(begin, result);
+#if defined(CARVE_DEBUG)
+ std::cerr << "after removeDegeneracies: remain=" << remain << std::endl;
+#endif
+ }
+
+ if (remain > 3) {
+ return splitAndResume(begin, result);
+ } else if (remain == 3) {
+ result.push_back(carve::triangulate::tri_idx(begin->idx, begin->next->idx, begin->next->next->idx));
+ ret = true;
+ } else {
+ ret = true;
+ }
+
+ done:
+ vertex_info *d = begin;
+ do {
+ vertex_info *n = d->next;
+ delete d;
+ d = n;
+ } while (d != begin);
+
+ return ret;
+}
+
+
+
+bool testCandidateAttachment(const std::vector<std::vector<carve::geom2d::P2> > &poly,
+ std::vector<std::pair<size_t, size_t> > &current_f_loop,
+ size_t curr,
+ carve::geom2d::P2 hole_min) {
+ const size_t SZ = current_f_loop.size();
+
+ if (!carve::geom2d::internalToAngle(pvert(poly, current_f_loop[(curr+1) % SZ]),
+ pvert(poly, current_f_loop[curr]),
+ pvert(poly, current_f_loop[(curr+SZ-1) % SZ]),
+ hole_min)) {
+ return false;
+ }
+
+ if (hole_min == pvert(poly, current_f_loop[curr])) {
+ return true;
+ }
+
+ carve::geom2d::LineSegment2 test(hole_min, pvert(poly, current_f_loop[curr]));
+
+ size_t v1 = current_f_loop.size() - 1;
+ size_t v2 = 0;
+ double v1_side = carve::geom2d::orient2d(test.v1, test.v2, pvert(poly, current_f_loop[v1]));
+ double v2_side = 0;
+
+ while (v2 != current_f_loop.size()) {
+ v2_side = carve::geom2d::orient2d(test.v1, test.v2, pvert(poly, current_f_loop[v2]));
+
+ if (v1_side != v2_side) {
+ // XXX: need to test vertices, not indices, because they may
+ // be duplicated.
+ if (pvert(poly, current_f_loop[v1]) != pvert(poly, current_f_loop[curr]) &&
+ pvert(poly, current_f_loop[v2]) != pvert(poly, current_f_loop[curr])) {
+ carve::geom2d::LineSegment2 test2(pvert(poly, current_f_loop[v1]), pvert(poly, current_f_loop[v2]));
+ if (carve::geom2d::lineSegmentIntersection_simple(test, test2)) {
+ // intersection; failed.
+ return false;
+ }
+ }
+ }
+
+ v1 = v2;
+ v1_side = v2_side;
+ ++v2;
+ }
+ return true;
+}
+
+
+
+void
+carve::triangulate::incorporateHolesIntoPolygon(
+ const std::vector<std::vector<carve::geom2d::P2> > &poly,
+ std::vector<std::pair<size_t, size_t> > &result,
+ size_t poly_loop,
+ const std::vector<size_t> &hole_loops) {
+ typedef std::vector<carve::geom2d::P2> loop_t;
+
+ size_t N = poly[poly_loop].size();
+
+ // work out how much space to reserve for the patched in holes.
+ for (size_t i = 0; i < hole_loops.size(); i++) {
+ N += 2 + poly[hole_loops[i]].size();
+ }
+
+ // this is the vector that we will build the result in.
+ result.clear();
+ result.reserve(N);
+
+ // this is a heap of result indices that defines the vertex test order.
+ std::vector<size_t> f_loop_heap;
+ f_loop_heap.reserve(N);
+
+ // add the poly loop to result.
+ for (size_t i = 0; i < poly[poly_loop].size(); ++i) {
+ result.push_back(std::make_pair((size_t)poly_loop, i));
+ }
+
+ if (hole_loops.size() == 0) {
+ return;
+ }
+
+ std::vector<std::pair<size_t, size_t> > h_loop_min_vertex;
+
+ h_loop_min_vertex.reserve(hole_loops.size());
+
+ // find the major axis for the holes - this is the axis that we
+ // will sort on for finding vertices on the polygon to join
+ // holes up to.
+ //
+ // it might also be nice to also look for whether it is better
+ // to sort ascending or descending.
+ //
+ // another trick that could be used is to modify the projection
+ // by 90 degree rotations or flipping about an axis. just as
+ // long as we keep the carve::geom3d::Vector pointers for the
+ // real data in sync, everything should be ok. then we wouldn't
+ // need to accomodate axes or sort order in the main loop.
+
+ // find the bounding box of all the holes.
+ carve::geom2d::P2 h_min, h_max;
+ h_min = h_max = poly[hole_loops[0]][0];
+ for (size_t i = 0; i < hole_loops.size(); ++i) {
+ const loop_t &hole = poly[hole_loops[i]];
+ for (size_t j = 0; j < hole.size(); ++j) {
+ assign_op(h_min, h_min, hole[j], carve::util::min_functor());
+ assign_op(h_max, h_max, hole[j], carve::util::max_functor());
+ }
+ }
+ // choose the axis for which the bbox is largest.
+ int axis = (h_max.x - h_min.x) > (h_max.y - h_min.y) ? 0 : 1;
+
+ // for each hole, find the minimum vertex in the chosen axis.
+ for (size_t i = 0; i < hole_loops.size(); ++i) {
+ const loop_t &hole = poly[hole_loops[i]];
+ size_t best, curr;
+ best = 0;
+ for (curr = 1; curr != hole.size(); ++curr) {
+ if (detail::axisOrdering(hole[curr], hole[best], axis)) {
+ best = curr;
+ }
+ }
+ h_loop_min_vertex.push_back(std::make_pair(hole_loops[i], best));
+ }
+
+ // sort the holes by the minimum vertex.
+ std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis));
+
+ // now, for each hole, find a vertex in the current polygon loop that it can be joined to.
+ for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) {
+ // the index of the vertex in the hole to connect.
+ size_t hole_i = h_loop_min_vertex[i].first;
+ size_t hole_i_connect = h_loop_min_vertex[i].second;
+
+ carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect];
+
+ f_loop_heap.clear();
+ // we order polygon loop vertices that may be able to be connected
+ // to the hole vertex by their distance to the hole vertex
+ heap_ordering_2d _heap_ordering(poly, result, hole_min, axis);
+
+ const size_t SZ = result.size();
+ for (size_t j = 0; j < SZ; ++j) {
+ // it is guaranteed that there exists a polygon vertex with
+ // coord < the min hole coord chosen, which can be joined to
+ // the min hole coord without crossing the polygon
+ // boundary. also, because we merge holes in ascending
+ // order, it is also true that this join can never cross
+ // another hole (and that doesn't need to be tested for).
+ if (pvert(poly, result[j]).v[axis] <= hole_min.v[axis]) {
+ f_loop_heap.push_back(j);
+ std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
+ }
+ }
+
+ // we are going to test each potential (according to the
+ // previous test) polygon vertex as a candidate join. we order
+ // by closeness to the hole vertex, so that the join we make
+ // is as small as possible. to test, we need to check the
+ // joining line segment does not cross any other line segment
+ // in the current polygon loop (excluding those that have the
+ // vertex that we are attempting to join with as an endpoint).
+ size_t attachment_point = result.size();
+
+ while (f_loop_heap.size()) {
+ std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
+ size_t curr = f_loop_heap.back();
+ f_loop_heap.pop_back();
+ // test the candidate join from result[curr] to hole_min
+
+ if (!testCandidateAttachment(poly, result, curr, hole_min)) {
+ continue;
+ }
+
+ attachment_point = curr;
+ break;
+ }
+
+ if (attachment_point == result.size()) {
+ CARVE_FAIL("didn't manage to link up hole!");
+ }
+
+ patchHoleIntoPolygon_2d(result, attachment_point, hole_i, hole_i_connect, poly[hole_i].size());
+ }
+}
+
+
+
+std::vector<std::pair<size_t, size_t> >
+carve::triangulate::incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly) {
+#if 1
+ std::vector<std::pair<size_t, size_t> > result;
+ std::vector<size_t> hole_indices;
+ hole_indices.reserve(poly.size() - 1);
+ for (size_t i = 1; i < poly.size(); ++i) {
+ hole_indices.push_back(i);
+ }
+
+ incorporateHolesIntoPolygon(poly, result, 0, hole_indices);
+
+ return result;
+
+#else
+ typedef std::vector<carve::geom2d::P2> loop_t;
+ size_t N = poly[0].size();
+ //
+ // work out how much space to reserve for the patched in holes.
+ for (size_t i = 0; i < poly.size(); i++) {
+ N += 2 + poly[i].size();
+ }
+
+ // this is the vector that we will build the result in.
+ std::vector<std::pair<size_t, size_t> > current_f_loop;
+ current_f_loop.reserve(N);
+
+ // this is a heap of current_f_loop indices that defines the vertex test order.
+ std::vector<size_t> f_loop_heap;
+ f_loop_heap.reserve(N);
+
+ // add the poly loop to current_f_loop.
+ for (size_t i = 0; i < poly[0].size(); ++i) {
+ current_f_loop.push_back(std::make_pair((size_t)0, i));
+ }
+
+ if (poly.size() == 1) {
+ return current_f_loop;
+ }
+
+ std::vector<std::pair<size_t, size_t> > h_loop_min_vertex;
+
+ h_loop_min_vertex.reserve(poly.size() - 1);
+
+ // find the major axis for the holes - this is the axis that we
+ // will sort on for finding vertices on the polygon to join
+ // holes up to.
+ //
+ // it might also be nice to also look for whether it is better
+ // to sort ascending or descending.
+ //
+ // another trick that could be used is to modify the projection
+ // by 90 degree rotations or flipping about an axis. just as
+ // long as we keep the carve::geom3d::Vector pointers for the
+ // real data in sync, everything should be ok. then we wouldn't
+ // need to accomodate axes or sort order in the main loop.
+
+ // find the bounding box of all the holes.
+ double min_x, min_y, max_x, max_y;
+ min_x = max_x = poly[1][0].x;
+ min_y = max_y = poly[1][0].y;
+ for (size_t i = 1; i < poly.size(); ++i) {
+ const loop_t &hole = poly[i];
+ for (size_t j = 0; j < hole.size(); ++j) {
+ min_x = std::min(min_x, hole[j].x);
+ min_y = std::min(min_y, hole[j].y);
+ max_x = std::max(max_x, hole[j].x);
+ max_y = std::max(max_y, hole[j].y);
+ }
+ }
+
+ // choose the axis for which the bbox is largest.
+ int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1;
+
+ // for each hole, find the minimum vertex in the chosen axis.
+ for (size_t i = 1; i < poly.size(); ++i) {
+ const loop_t &hole = poly[i];
+ size_t best, curr;
+ best = 0;
+ for (curr = 1; curr != hole.size(); ++curr) {
+ if (detail::axisOrdering(hole[curr], hole[best], axis)) {
+ best = curr;
+ }
+ }
+ h_loop_min_vertex.push_back(std::make_pair(i, best));
+ }
+
+ // sort the holes by the minimum vertex.
+ std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis));
+
+ // now, for each hole, find a vertex in the current polygon loop that it can be joined to.
+ for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) {
+ // the index of the vertex in the hole to connect.
+ size_t hole_i = h_loop_min_vertex[i].first;
+ size_t hole_i_connect = h_loop_min_vertex[i].second;
+
+ carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect];
+
+ f_loop_heap.clear();
+ // we order polygon loop vertices that may be able to be connected
+ // to the hole vertex by their distance to the hole vertex
+ heap_ordering_2d _heap_ordering(poly, current_f_loop, hole_min, axis);
+
+ const size_t SZ = current_f_loop.size();
+ for (size_t j = 0; j < SZ; ++j) {
+ // it is guaranteed that there exists a polygon vertex with
+ // coord < the min hole coord chosen, which can be joined to
+ // the min hole coord without crossing the polygon
+ // boundary. also, because we merge holes in ascending
+ // order, it is also true that this join can never cross
+ // another hole (and that doesn't need to be tested for).
+ if (pvert(poly, current_f_loop[j]).v[axis] <= hole_min.v[axis]) {
+ f_loop_heap.push_back(j);
+ std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
+ }
+ }
+
+ // we are going to test each potential (according to the
+ // previous test) polygon vertex as a candidate join. we order
+ // by closeness to the hole vertex, so that the join we make
+ // is as small as possible. to test, we need to check the
+ // joining line segment does not cross any other line segment
+ // in the current polygon loop (excluding those that have the
+ // vertex that we are attempting to join with as an endpoint).
+ size_t attachment_point = current_f_loop.size();
+
+ while (f_loop_heap.size()) {
+ std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
+ size_t curr = f_loop_heap.back();
+ f_loop_heap.pop_back();
+ // test the candidate join from current_f_loop[curr] to hole_min
+
+ if (!testCandidateAttachment(poly, current_f_loop, curr, hole_min)) {
+ continue;
+ }
+
+ attachment_point = curr;
+ break;
+ }
+
+ if (attachment_point == current_f_loop.size()) {
+ CARVE_FAIL("didn't manage to link up hole!");
+ }
+
+ patchHoleIntoPolygon_2d(current_f_loop, attachment_point, hole_i, hole_i_connect, poly[hole_i].size());
+ }
+
+ return current_f_loop;
+#endif
+}
+
+
+
+std::vector<std::vector<std::pair<size_t, size_t> > >
+carve::triangulate::mergePolygonsAndHoles(const std::vector<std::vector<carve::geom2d::P2> > &poly) {
+ std::vector<size_t> poly_indices, hole_indices;
+
+ poly_indices.reserve(poly.size());
+ hole_indices.reserve(poly.size());
+
+ for (size_t i = 0; i < poly.size(); ++i) {
+ if (carve::geom2d::signedArea(poly[i]) < 0) {
+ poly_indices.push_back(i);
+ } else {
+ hole_indices.push_back(i);
+ }
+ }
+
+ std::vector<std::vector<std::pair<size_t, size_t> > > result;
+ result.resize(poly_indices.size());
+
+ if (hole_indices.size() == 0) {
+ for (size_t i = 0; i < poly.size(); ++i) {
+ result[i].resize(poly[i].size());
+ for (size_t j = 0; j < poly[i].size(); ++j) {
+ result[i].push_back(std::make_pair(i, j));
+ }
+ }
+ return result;
+ }
+
+ if (poly_indices.size() == 1) {
+ incorporateHolesIntoPolygon(poly, result[0], poly_indices[0], hole_indices);
+
+ return result;
+ }
+
+ throw carve::exception("not implemented");
+}
+
+
+
+void carve::triangulate::triangulate(const std::vector<carve::geom2d::P2> &poly,
+ std::vector<carve::triangulate::tri_idx> &result) {
+ std::vector<detail::vertex_info *> vinfo;
+ const size_t N = poly.size();
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "TRIANGULATION BEGINS" << std::endl;
+#endif
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ dumpPoly(poly, result);
+#endif
+
+ result.clear();
+ if (N < 3) {
+ return;
+ }
+
+ result.reserve(poly.size() - 2);
+
+ if (N == 3) {
+ result.push_back(tri_idx(0, 1, 2));
+ return;
+ }
+
+ vinfo.resize(N);
+
+ vinfo[0] = new detail::vertex_info(poly[0], 0);
+ for (size_t i = 1; i < N-1; ++i) {
+ vinfo[i] = new detail::vertex_info(poly[i], i);
+ vinfo[i]->prev = vinfo[i-1];
+ vinfo[i-1]->next = vinfo[i];
+ }
+ vinfo[N-1] = new detail::vertex_info(poly[N-1], N-1);
+ vinfo[N-1]->prev = vinfo[N-2];
+ vinfo[N-1]->next = vinfo[0];
+ vinfo[0]->prev = vinfo[N-1];
+ vinfo[N-2]->next = vinfo[N-1];
+
+ for (size_t i = 0; i < N; ++i) {
+ vinfo[i]->recompute();
+ }
+
+ detail::vertex_info *begin = vinfo[0];
+
+ removeDegeneracies(begin, result);
+ doTriangulate(begin, result);
+
+#if defined(CARVE_DEBUG)
+ std::cerr << "TRIANGULATION ENDS" << std::endl;
+#endif
+
+#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
+ dumpPoly(poly, result);
+#endif
+}
+
+
+
+void carve::triangulate::detail::tri_pair_t::flip(vert_edge_t &old_edge,
+ vert_edge_t &new_edge,
+ vert_edge_t perim[4]) {
+ unsigned ai, bi;
+ unsigned cross_ai, cross_bi;
+
+ findSharedEdge(ai, bi);
+ old_edge = ordered_vert_edge_t(a->v[ai], b->v[bi]);
+
+ cross_ai = P(ai);
+ cross_bi = P(bi);
+ new_edge = ordered_vert_edge_t(a->v[cross_ai], b->v[cross_bi]);
+
+ score = -score;
+
+ a->v[N(ai)] = b->v[cross_bi];
+ b->v[N(bi)] = a->v[cross_ai];
+
+ perim[0] = ordered_vert_edge_t(a->v[P(ai)], a->v[ai]);
+ perim[1] = ordered_vert_edge_t(a->v[N(ai)], a->v[ai]); // this edge was a b-edge
+
+ perim[2] = ordered_vert_edge_t(b->v[P(bi)], b->v[bi]);
+ perim[3] = ordered_vert_edge_t(b->v[N(bi)], b->v[bi]); // this edge was an a-edge
+}
+
+
+
+void carve::triangulate::detail::tri_pairs_t::insert(unsigned a, unsigned b, carve::triangulate::tri_idx *t) {
+ tri_pair_t *tp;
+ if (a < b) {
+ tp = storage[vert_edge_t(a,b)];
+ if (!tp) {
+ tp = storage[vert_edge_t(a,b)] = new tri_pair_t;
+ }
+ tp->a = t;
+ } else {
+ tp = storage[vert_edge_t(b,a)];
+ if (!tp) {
+ tp = storage[vert_edge_t(b,a)] = new tri_pair_t;
+ }
+ tp->b = t;
+ }
+}