diff options
Diffstat (limited to 'extern/carve/lib')
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> > ¤t_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; + } +} |