2012-01-16 16:46:00 +00:00
|
|
|
// 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>
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
#include <memory>
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
carve::mesh::MeshSet<3>::vertex_t *v = *beg;
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
template<typename iter_t>
|
|
|
|
void orderEdgeIntersectionVertices(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<std::pair<double, double>, carve::mesh::MeshSet<3>::vertex_t *> > DVVector;
|
|
|
|
DVVector ordered_vertices;
|
|
|
|
|
|
|
|
ordered_vertices.reserve(std::distance(beg, end));
|
|
|
|
|
|
|
|
for (; beg != end; ++beg) {
|
|
|
|
carve::mesh::MeshSet<3>::vertex_t *v = (*beg).first;
|
|
|
|
double ovec = 0.0;
|
|
|
|
for (carve::csg::detail::EdgeIntInfo::mapped_type::const_iterator j = (*beg).second.begin(); j != (*beg).second.end(); ++j) {
|
|
|
|
ovec += (*j).second;
|
|
|
|
}
|
|
|
|
ordered_vertices.push_back(std::make_pair(std::make_pair(carve::geom::dot(v->v - base, dir), -ovec), 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::Hooks::intersectionVertex(const meshset_t::vertex_t *vertex,
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::Hooks::processOutputFace(std::vector<meshset_t::face_t *> &faces,
|
|
|
|
const meshset_t::face_t *orig_face,
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::Hooks::resultFace(const meshset_t::face_t *new_face,
|
|
|
|
const meshset_t::face_t *orig_face,
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *i_pt = ((*j).second);
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::vector<meshset_t::vertex_t *> vertices;
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::vector<meshset_t::vertex_t *> out;
|
2012-01-16 16:46:00 +00:00
|
|
|
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();
|
2012-07-04 16:07:01 +00:00
|
|
|
const meshset_t::vertex_t *o = (*t);
|
2012-01-16 16:46:00 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
static void recordEdgeIntersectionInfo(carve::mesh::MeshSet<3>::vertex_t *intersection,
|
|
|
|
carve::mesh::MeshSet<3>::edge_t *edge,
|
|
|
|
const carve::csg::detail::VFSMap::mapped_type &intersected_faces,
|
|
|
|
carve::csg::detail::Data &data) {
|
|
|
|
carve::mesh::MeshSet<3>::vertex_t::vector_t edge_dir = edge->v2()->v - edge->v1()->v;
|
|
|
|
carve::csg::detail::EdgeIntInfo::mapped_type &eint_info = data.emap[edge][intersection];
|
|
|
|
|
|
|
|
for (carve::csg::detail::VFSMap::mapped_type::const_iterator i = intersected_faces.begin(); i != intersected_faces.end(); ++i) {
|
|
|
|
carve::mesh::MeshSet<3>::vertex_t::vector_t normal = (*i)->plane.N;
|
|
|
|
eint_info.insert(std::make_pair((*i), carve::geom::dot(edge_dir, normal)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *i_pt = ((*i).first);
|
2012-01-16 16:46:00 +00:00
|
|
|
detail::VFSMap::mapped_type &face_set = (data.fmap_rev[i_pt]);
|
2012-07-04 16:07:01 +00:00
|
|
|
detail::VFSMap::mapped_type src_face_set;
|
|
|
|
detail::VFSMap::mapped_type tgt_face_set;
|
2012-01-16 16:46:00 +00:00
|
|
|
// 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);
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
src_face_set.clear();
|
|
|
|
tgt_face_set.clear();
|
|
|
|
// work out the faces involved.
|
|
|
|
facesForObject(i_src, data.vert_to_edges, src_face_set);
|
|
|
|
facesForObject(i_tgt, data.vert_to_edges, tgt_face_set);
|
|
|
|
// this updates fmap_rev.
|
|
|
|
std::copy(src_face_set.begin(), src_face_set.end(), set_inserter(face_set));
|
|
|
|
std::copy(tgt_face_set.begin(), tgt_face_set.end(), set_inserter(face_set));
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
// 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.
|
2012-07-04 16:07:01 +00:00
|
|
|
if (i_src.obtype == IObj::OBTYPE_EDGE) recordEdgeIntersectionInfo(i_pt, i_src.edge, tgt_face_set, data);
|
|
|
|
if (i_tgt.obtype == IObj::OBTYPE_EDGE) recordEdgeIntersectionInfo(i_pt, i_tgt.edge, src_face_set, data);
|
2012-01-16 16:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *f = (*k);
|
2012-01-16 16:46:00 +00:00
|
|
|
data.fmap[f].insert(i_pt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::_generateVertexVertexIntersections(meshset_t::vertex_t *va,
|
|
|
|
meshset_t::edge_t *eb) {
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateVertexVertexIntersections(meshset_t::face_t *a,
|
|
|
|
const std::vector<meshset_t::face_t *> &b) {
|
|
|
|
meshset_t::edge_t *ea, *eb;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
ea = a->edge;
|
|
|
|
do {
|
|
|
|
for (size_t i = 0; i < b.size(); ++i) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *t = b[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
eb = t->edge;
|
|
|
|
do {
|
|
|
|
_generateVertexVertexIntersections(ea->v1(), eb);
|
|
|
|
eb = eb->next;
|
|
|
|
} while (eb != t->edge);
|
|
|
|
}
|
|
|
|
ea = ea->next;
|
|
|
|
} while (ea != a->edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::_generateVertexEdgeIntersections(meshset_t::vertex_t *va,
|
|
|
|
meshset_t::edge_t *eb) {
|
2012-01-16 16:46:00 +00:00
|
|
|
if (intersections.intersects(va, eb)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-10 11:00:02 +00:00
|
|
|
carve::geom::aabb<3> eb_aabb;
|
|
|
|
eb_aabb.fit(eb->v1()->v, eb->v2()->v);
|
|
|
|
if (eb_aabb.maxAxisSeparation(va->v) > carve::EPSILON) {
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateVertexEdgeIntersections(meshset_t::face_t *a,
|
|
|
|
const std::vector<meshset_t::face_t *> &b) {
|
|
|
|
meshset_t::edge_t *ea, *eb;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
ea = a->edge;
|
|
|
|
do {
|
|
|
|
for (size_t i = 0; i < b.size(); ++i) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *t = b[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
eb = t->edge;
|
|
|
|
do {
|
|
|
|
_generateVertexEdgeIntersections(ea->v1(), eb);
|
|
|
|
eb = eb->next;
|
|
|
|
} while (eb != t->edge);
|
|
|
|
}
|
|
|
|
ea = ea->next;
|
|
|
|
} while (ea != a->edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::_generateEdgeEdgeIntersections(meshset_t::edge_t *ea,
|
|
|
|
meshset_t::edge_t *eb) {
|
2012-01-16 16:46:00 +00:00
|
|
|
if (intersections.intersects(ea, eb)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *v1 = ea->v1(), *v2 = ea->v2();
|
|
|
|
meshset_t::vertex_t *v3 = eb->v1(), *v4 = eb->v2();
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t::vector_t p1, p2;
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *p = vertex_pool.get((p1 + p2) / 2.0);
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateEdgeEdgeIntersections(meshset_t::face_t *a,
|
|
|
|
const std::vector<meshset_t::face_t *> &b) {
|
|
|
|
meshset_t::edge_t *ea, *eb;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
ea = a->edge;
|
|
|
|
do {
|
|
|
|
for (size_t i = 0; i < b.size(); ++i) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *t = b[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
eb = t->edge;
|
|
|
|
do {
|
|
|
|
_generateEdgeEdgeIntersections(ea, eb);
|
|
|
|
eb = eb->next;
|
|
|
|
} while (eb != t->edge);
|
|
|
|
}
|
|
|
|
ea = ea->next;
|
|
|
|
} while (ea != a->edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::_generateVertexFaceIntersections(meshset_t::face_t *fa,
|
|
|
|
meshset_t::edge_t *eb) {
|
2012-01-16 16:46:00 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateVertexFaceIntersections(meshset_t::face_t *a,
|
|
|
|
const std::vector<meshset_t::face_t *> &b) {
|
|
|
|
meshset_t::edge_t *eb;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < b.size(); ++i) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *t = b[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
eb = t->edge;
|
|
|
|
do {
|
|
|
|
_generateVertexFaceIntersections(a, eb);
|
|
|
|
eb = eb->next;
|
|
|
|
} while (eb != t->edge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::_generateEdgeFaceIntersections(meshset_t::face_t *fa,
|
|
|
|
meshset_t::edge_t *eb) {
|
2012-01-16 16:46:00 +00:00
|
|
|
if (intersections.intersects(eb, fa)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t::vector_t _p;
|
2012-01-16 16:46:00 +00:00
|
|
|
if (fa->simpleLineSegmentIntersection(carve::geom3d::LineSegment(eb->v1()->v, eb->v2()->v), _p)) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *p = vertex_pool.get(_p);
|
2012-01-16 16:46:00 +00:00
|
|
|
intersections.record(eb, fa, p);
|
|
|
|
if (eb->rev) intersections.record(eb->rev, fa, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateEdgeFaceIntersections(meshset_t::face_t *a,
|
|
|
|
const std::vector<meshset_t::face_t *> &b) {
|
|
|
|
meshset_t::edge_t *eb;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < b.size(); ++i) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *t = b[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
eb = t->edge;
|
|
|
|
do {
|
|
|
|
_generateEdgeFaceIntersections(a, eb);
|
|
|
|
eb = eb->next;
|
|
|
|
} while (eb != t->edge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateIntersectionCandidates(meshset_t *a,
|
2012-01-16 16:46:00 +00:00
|
|
|
const face_rtree_t *a_node,
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t *b,
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *fa = a_node->data[i];
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *fb = b_node->data[j];
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::generateIntersections(meshset_t *a,
|
2012-01-16 16:46:00 +00:00
|
|
|
const face_rtree_t *a_rtree,
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t *b,
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *f = (*i).first;
|
|
|
|
meshset_t::edge_t *e = f->edge;
|
2012-01-16 16:46:00 +00:00
|
|
|
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);
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
for (detail::EIntMap::const_iterator i = data.emap.begin(), ei = data.emap.end(); i != ei; ++i) {
|
|
|
|
meshset_t::edge_t *edge = (*i).first;
|
|
|
|
const detail::EIntMap::mapped_type &int_info = (*i).second;
|
|
|
|
std::vector<meshset_t::vertex_t *> &verts = data.divided_edges[edge];
|
|
|
|
orderEdgeIntersectionVertices(int_info.begin(), int_info.end(),
|
|
|
|
edge->v2()->v - edge->v1()->v, edge->v1()->v,
|
|
|
|
verts);
|
2012-01-16 16:46:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *face_a = (*i).first;
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *face_b = (*k);
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::face_t *face_b = (*j);
|
2012-01-16 16:46:00 +00:00
|
|
|
const detail::FVSMap::mapped_type &face_b_intersections = (data.fmap[face_b]);
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::vector<meshset_t::vertex_t *> vertices;
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
2012-07-04 16:07:01 +00:00
|
|
|
for (std::vector<meshset_t::vertex_t *>::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
|
2012-01-16 16:46:00 +00:00
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *v1 = vertices[0];
|
|
|
|
meshset_t::vertex_t *v2 = vertices[1];
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
2012-07-04 16:07:01 +00:00
|
|
|
std::vector<meshset_t::vertex_t *> ordered;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
// 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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t::vertex_t *v1 = ordered[k];
|
|
|
|
meshset_t::vertex_t *v2 = ordered[k + 1];
|
2012-01-16 16:46:00 +00:00
|
|
|
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());
|
2012-07-04 16:07:01 +00:00
|
|
|
std::map<const meshset_t::vertex_t *, size_t> vmap;
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::calc(meshset_t *a,
|
2012-01-16 16:46:00 +00:00
|
|
|
const face_rtree_t *a_rtree,
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t *b,
|
2012-01-16 16:46:00 +00:00
|
|
|
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)
|
|
|
|
{
|
2012-07-04 16:07:01 +00:00
|
|
|
std::auto_ptr<carve::mesh::MeshSet<3> > poly(faceLoopsToPolyhedron(a_face_loops));
|
|
|
|
writePLY("/tmp/a_split.ply", poly.get(), false);
|
2012-01-16 16:46:00 +00:00
|
|
|
}
|
|
|
|
{
|
2012-07-04 16:07:01 +00:00
|
|
|
std::auto_ptr<carve::mesh::MeshSet<3> > poly(faceLoopsToPolyhedron(b_face_loops));
|
|
|
|
writePLY("/tmp/b_split.ply", poly.get(), false);
|
2012-01-16 16:46:00 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
checkFaceLoopIntegrity(a_face_loops);
|
|
|
|
checkFaceLoopIntegrity(b_face_loops);
|
|
|
|
|
|
|
|
#if defined(CARVE_DEBUG)
|
|
|
|
std::cerr << "classify" << std::endl;
|
|
|
|
#endif
|
|
|
|
// initialize some classification information.
|
2012-07-04 16:07:01 +00:00
|
|
|
for (std::vector<meshset_t::vertex_t>::iterator
|
2012-01-16 16:46:00 +00:00
|
|
|
i = a->vertex_storage.begin(), e = a->vertex_storage.end(); i != e; ++i) {
|
|
|
|
vclass[map_vertex(data.vmap, &(*i))].cls[0] = POINT_ON;
|
|
|
|
}
|
2012-07-04 16:07:01 +00:00
|
|
|
for (std::vector<meshset_t::vertex_t>::iterator
|
2012-01-16 16:46:00 +00:00
|
|
|
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
|
|
|
|
*/
|
2012-07-04 16:07:01 +00:00
|
|
|
carve::mesh::MeshSet<3> *carve::csg::CSG::compute(meshset_t *a,
|
|
|
|
meshset_t *b,
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::auto_ptr<face_rtree_t> a_rtree(face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4));
|
|
|
|
std::auto_ptr<face_rtree_t> b_rtree(face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4));
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
static carve::TimingName FUNC_NAME("CSG::compute - calc()");
|
|
|
|
carve::TimingBlock block(FUNC_NAME);
|
2012-07-04 16:07:01 +00:00
|
|
|
calc(a, a_rtree.get(), b, b_rtree.get(), vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
|
2012-01-16 16:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2012-07-04 16:07:01 +00:00
|
|
|
a_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
a_loops_grouped,
|
|
|
|
a_edge_map,
|
|
|
|
b,
|
2012-07-04 16:07:01 +00:00
|
|
|
b_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
b_loops_grouped,
|
|
|
|
b_edge_map,
|
|
|
|
collector);
|
|
|
|
break;
|
|
|
|
case CLASSIFY_NORMAL:
|
|
|
|
classifyFaceGroups(shared_edges,
|
|
|
|
vclass,
|
|
|
|
a,
|
2012-07-04 16:07:01 +00:00
|
|
|
a_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
a_loops_grouped,
|
|
|
|
a_edge_map,
|
|
|
|
b,
|
2012-07-04 16:07:01 +00:00
|
|
|
b_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
b_loops_grouped,
|
|
|
|
b_edge_map,
|
|
|
|
collector);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t *result = collector.done(hooks);
|
2012-01-16 16:46:00 +00:00
|
|
|
if (result != NULL && shared_edges_ptr != NULL) {
|
2012-07-04 16:07:01 +00:00
|
|
|
std::list<meshset_t *> result_list;
|
2012-01-16 16:46:00 +00:00
|
|
|
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
|
|
|
|
*/
|
2012-07-04 16:07:01 +00:00
|
|
|
carve::mesh::MeshSet<3> *carve::csg::CSG::compute(meshset_t *a,
|
|
|
|
meshset_t *b,
|
2012-01-16 16:46:00 +00:00
|
|
|
carve::csg::CSG::OP op,
|
|
|
|
carve::csg::V2Set *shared_edges,
|
|
|
|
CLASSIFY_TYPE classify_type) {
|
|
|
|
Collector *coll = makeCollector(op, a, b);
|
|
|
|
if (!coll) return NULL;
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
meshset_t *result = compute(a, b, *coll, shared_edges, classify_type);
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
delete coll;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param closed
|
|
|
|
* @param open
|
|
|
|
* @param FaceClass
|
|
|
|
* @param result
|
|
|
|
* @param hooks
|
|
|
|
* @param shared_edges_ptr
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
2012-07-04 16:07:01 +00:00
|
|
|
bool carve::csg::CSG::sliceAndClassify(meshset_t *closed,
|
|
|
|
meshset_t *open,
|
|
|
|
std::list<std::pair<FaceClass, meshset_t *> > &result,
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::auto_ptr<face_rtree_t> closed_rtree(face_rtree_t::construct_STR(closed->faceBegin(), closed->faceEnd(), 4, 4));
|
|
|
|
std::auto_ptr<face_rtree_t> open_rtree(face_rtree_t::construct_STR(open->faceBegin(), open->faceEnd(), 4, 4));
|
2012-01-16 16:46:00 +00:00
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
calc(closed, closed_rtree.get(), open, open_rtree.get(), vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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,
|
2012-07-04 16:07:01 +00:00
|
|
|
closed_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
a_loops_grouped,
|
|
|
|
a_edge_map,
|
|
|
|
open,
|
2012-07-04 16:07:01 +00:00
|
|
|
open_rtree.get(),
|
2012-01-16 16:46:00 +00:00
|
|
|
b_loops_grouped,
|
|
|
|
b_edge_map,
|
|
|
|
result);
|
|
|
|
|
|
|
|
if (shared_edges_ptr != NULL) {
|
2012-07-04 16:07:01 +00:00
|
|
|
std::list<meshset_t *> result_list;
|
|
|
|
for (std::list<std::pair<FaceClass, meshset_t *> >::iterator it = result.begin(); it != result.end(); it++) {
|
2012-01-16 16:46:00 +00:00
|
|
|
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
|
|
|
|
*/
|
2012-07-04 16:07:01 +00:00
|
|
|
void carve::csg::CSG::slice(meshset_t *a,
|
|
|
|
meshset_t *b,
|
|
|
|
std::list<meshset_t *> &a_sliced,
|
|
|
|
std::list<meshset_t *> &b_sliced,
|
2012-01-16 16:46:00 +00:00
|
|
|
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;
|
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
std::auto_ptr<face_rtree_t> a_rtree(face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4));
|
|
|
|
std::auto_ptr<face_rtree_t> b_rtree(face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4));
|
2012-01-16 16:46:00 +00:00
|
|
|
|
2012-07-04 16:07:01 +00:00
|
|
|
calc(a, a_rtree.get(), b, b_rtree.get(), vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count);
|
2012-01-16 16:46:00 +00:00
|
|
|
|
|
|
|
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) {
|
2012-07-04 16:07:01 +00:00
|
|
|
std::list<meshset_t *> result_list;
|
2012-01-16 16:46:00 +00:00
|
|
|
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();
|
|
|
|
}
|