diff --git a/.gitignore b/.gitignore
index d66b9dcf9..52c5e413c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,5 @@ examples/LoadFileExample/dump_mesh_debug.txt
examples/CreateIfcWallAndWriteFile/example.ifc
examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user
examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user
+.vs/IfcPlusPlus/v17/DocumentLayout.json
+examples/SimpleViewerExampleQt/.vs/SimpleViewerExampleQt/v17/DocumentLayout.json
diff --git a/IfcPlusPlus/CMakeLists.txt b/IfcPlusPlus/CMakeLists.txt
index 976d18b2c..fce8b6413 100644
--- a/IfcPlusPlus/CMakeLists.txt
+++ b/IfcPlusPlus/CMakeLists.txt
@@ -37,6 +37,7 @@ set(IFCPP_SOURCE_FILES
src/ifcpp/writer/WriterUtil.cpp
src/ifcpp/geometry/MeshOps.cpp
src/ifcpp/geometry/GeometryInputData.cpp
+ src/ifcpp/geometry/MeshSimplifier.cpp
src/external/Carve/src/lib/aabb.cpp
src/external/Carve/src/lib/carve.cpp
src/external/Carve/src/lib/convex_hull.cpp
diff --git a/IfcPlusPlus/IfcPlusPlus.vcxproj b/IfcPlusPlus/IfcPlusPlus.vcxproj
index 6d7707b19..442001b82 100644
--- a/IfcPlusPlus/IfcPlusPlus.vcxproj
+++ b/IfcPlusPlus/IfcPlusPlus.vcxproj
@@ -184,7 +184,7 @@
ProgramDatabase
$(IntDir)/%(RelativeDir)/
- stdcpp17
+ stdcpp20
true
@@ -265,7 +265,7 @@
$(IntDir)/%(RelativeDir)/
Default
true
- stdcpp17
+ stdcpp20
4267;4244
@@ -338,8 +338,10 @@
+
+
/bigobj %(AdditionalOptions)
diff --git a/IfcPlusPlus/IfcPlusPlus.vcxproj.filters b/IfcPlusPlus/IfcPlusPlus.vcxproj.filters
index a51d6e1b5..bedd2152a 100644
--- a/IfcPlusPlus/IfcPlusPlus.vcxproj.filters
+++ b/IfcPlusPlus/IfcPlusPlus.vcxproj.filters
@@ -290,5 +290,11 @@
Quelldateien
+
+ Quelldateien
+
+
+ Quelldateien
+
\ No newline at end of file
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/aabb.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/aabb.hpp
index a950f6856..f79a7fb0c 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/aabb.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/aabb.hpp
@@ -71,6 +71,7 @@ struct aabb {
void expand(double pad);
bool completelyContains(const aabb& other) const;
+ bool completelyContains(const aabb& other, double eps) const;
bool containsPoint(const vector_t& v) const;
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/aabb_impl.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/aabb_impl.hpp
index bfc72dff0..c1e1a460a 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/aabb_impl.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/aabb_impl.hpp
@@ -181,6 +181,42 @@ namespace carve {
return true;
}
+ /*
+ * This function checks if the other aabb is completely contained in this aabb
+ * @param other the other aabb
+ * @param eps the epsilon value. If positive, it allows the other aabb to be slightly bigger than this aabb
+ * If negative, the other aabb must be strictly smaller than this aabb
+ */
+ template
+ bool aabb::completelyContains(const aabb& other, double eps) const
+ {
+ // pos ext eps
+ // ----------|------------|---|
+ //
+ // ----------|-----------------| other is bigger than this+eps -> false
+
+ // ----------|--------------| other is smaller in all dimensions -> true
+
+ for (unsigned i = 0; i < ndim; ++i)
+ {
+ // if max point of other is greater than max point of this, return false
+ double max = pos.v[i] + extent[i];
+ double maxOther = other.pos.v[i] + other.extent[i];
+ if (maxOther > max + eps)
+ {
+ return false;
+ }
+
+ double min = pos.v[i] - extent[i];
+ double minOther = other.pos.v[i] - other.extent[i];
+ if (minOther < min - eps)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
template
bool aabb::containsPoint(const vector_t& v) const {
for( unsigned i = 0; i < ndim; ++i ) {
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/carve.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/carve.hpp
index da7ded561..ac08ea36e 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/carve.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/carve.hpp
@@ -264,3 +264,8 @@ namespace carve {
MACRO_BEGIN throw carve::exception() << __FILE__ << ":" << __LINE__ << " " \
<< #x; \
MACRO_END
+
+void printToDebugLogOn(bool on);
+bool IsPrintToDebugLogOn();
+void printToDebugLog(const char* funcName, std::string details);
+void clearDebugLogLinux();
\ No newline at end of file
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/geom3d.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/geom3d.hpp
index d419b8066..597c5e1ff 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/geom3d.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/geom3d.hpp
@@ -26,7 +26,7 @@
#include
#include
-
+#include
#include
#include
@@ -299,6 +299,13 @@ namespace carve {
double d3 = carve::geom::dotcross(direction, b, base);
#endif
+ if (isnan(d1) || isnan(d2) || isnan(d3))
+ {
+ printToDebugLog( __FUNCTION__, " d1=" +std::to_string( d1) + ", d2=" + std::to_string(d2) +
+ ", d3=" + std::to_string(d3 ) );
+ }
+
+
// CASE: a and b are coplanar wrt. direction.
if( d1 == 0.0 ) {
// a and b point in the same direction.
@@ -325,7 +332,7 @@ namespace carve {
return -1;
}
if( d2 > 0.0 && d3 < 0.0 ) {
- return +1;
+ return 1;
}
// both a and b are to one side of plane(direction, base) -
@@ -342,7 +349,7 @@ namespace carve {
return dot(a, base) > 0.0 ? +1 : -1;
}
else {
- return dot(a, base) > 0.0 ? -1 : +1;
+ return dot(a, base) > 0.0 ? -1 : 1;
}
}
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/geom_impl.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/geom_impl.hpp
index 61d82d88b..81a658df4 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/geom_impl.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/geom_impl.hpp
@@ -44,7 +44,7 @@ namespace carve {
#if defined(CARVE_DEBUG)
CARVE_ASSERT(length() > 0.0);
#endif
- if( length2() == 0.0 )
+ if( length2() < 1e-16 )
{
return *this;
}
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/mesh.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/mesh.hpp
index d72c2f692..5f4dd511d 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/mesh.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/mesh.hpp
@@ -35,6 +35,10 @@
#include
+#if defined _DEBUG || defined _DEBUG_RELEASE
+static long globalNumMeshSets = 0;
+#endif
+
namespace carve {
namespace poly {
class Polyhedron;
@@ -339,7 +343,7 @@ namespace carve {
aabb_t getAABB() const;
bool recalc(double CARVE_EPSILON);
- carve::geom::vector computeNormal(double CARVE_EPSILON);
+ carve::geom::vector computeNormal(double CARVE_EPSILON, bool calledRecursive = false);
void clearEdges();
@@ -630,6 +634,8 @@ namespace carve {
bool is_inner_mesh = false; // completely inside other mesh
meshset_t* meshset = nullptr;
+ double m_volume = std::numeric_limits::quiet_NaN();
+ void resetVolume() { m_volume = std::numeric_limits::quiet_NaN(); }
protected:
Mesh(std::vector& _faces, std::vector& _open_edges, std::vector& _closed_edges, bool _is_negative, bool _is_inner_mesh);
@@ -648,24 +654,30 @@ namespace carve {
bool isNegative() const { return is_negative; }
- double volume() const
+ double volume()
{
- if( is_negative || !faces.size() ) {
+ if (is_negative || !faces.size()) {
return 0.0;
}
+ if (!std::isnan(m_volume))
+ {
+ return m_volume;
+ }
+
double vol = 0.0;
typename vertex_t::vector_t origin = faces[0]->edge->vert->v;
- for( size_t f = 0; f < faces.size(); ++f )
+ for (size_t f = 0; f < faces.size(); ++f)
{
face_t* face = faces[f];
edge_t* e1 = face->edge;
- for( edge_t* e2 = e1->next; e2->next != e1; e2 = e2->next )
+ for (edge_t* e2 = e1->next; e2->next != e1; e2 = e2->next)
{
vol += carve::geom3d::tetrahedronVolume(e1->vert->v, e2->vert->v, e2->next->vert->v, origin);
}
}
+ m_volume = vol;
return vol;
}
@@ -728,6 +740,7 @@ namespace carve {
std::vector vertex_storage;
std::vector meshes;
+ //bool m_inner_outer_meshes_classified = false;
public:
template
@@ -827,17 +840,14 @@ namespace carve {
MeshSet(const std::vector& points, size_t n_faces, const std::vector& face_indices, double CARVE_EPSILON, const MeshOptions& opts = MeshOptions());
- // Construct a mesh set from a set of disconnected faces. Takes
- // possession of the face pointers.
+ // Construct a mesh set from a set of disconnected faces. Takes possession of the face pointers.
MeshSet(std::vector& faces, const MeshOptions& opts = MeshOptions());
MeshSet(std::list& faces, const MeshOptions& opts = MeshOptions());
- MeshSet(std::vector& _vertex_storage,
- std::vector& _meshes);
+ MeshSet(std::vector& _vertex_storage, std::vector& _meshes);
- // This constructor consolidates and rewrites vertex pointers in
- // each mesh, repointing them to local storage.
+ // This constructor consolidates and rewrites vertex pointers in each mesh, repointing them to local storage.
MeshSet(std::vector& _meshes);
MeshSet* clone() const;
@@ -864,6 +874,38 @@ namespace carve {
void canonicalize();
void separateMeshes();
+
+ //void classifyInnerOuterMeshes(double eps);
+
+ //double volume(double eps)
+ //{
+ // if (!m_inner_outer_meshes_classified)
+ // {
+ // classifyInnerOuterMeshes(eps);
+ // }
+
+ // double vol = 0;
+ // for (size_t ii = 0; ii < meshes.size(); ++ii)
+ // {
+ // carve::mesh::Mesh* mesh = meshes[ii];
+ // double meshVolume = mesh->volume();
+ // if (meshVolume < 0.0)
+ // {
+ // mesh->invert();
+ // meshVolume = -meshVolume;
+ // }
+
+ // if (mesh->is_inner_mesh)
+ // {
+ // vol -= meshVolume;
+ // }
+ // else
+ // {
+ // vol += meshVolume;
+ // }
+ // }
+ // return vol;
+ //}
};
carve::PointClass classifyPoint( const carve::mesh::MeshSet<3>* meshset, const carve::geom::RTreeNode<3, carve::mesh::Face<3>*>* face_rtree,
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/mesh_impl.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/mesh_impl.hpp
index b6976c22a..56c01b97e 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/mesh_impl.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/mesh_impl.hpp
@@ -30,9 +30,9 @@
#include
#include
+#include
#include
#include
-#include
namespace carve {
namespace mesh {
@@ -304,9 +304,11 @@ typename Face::aabb_t Face::getAABB() const {
return aabb;
}
+bool getAdjacentFaceNormal(Face<3>* face, carve::geom::vector<3>& result, double CARVE_EPSILON, bool calledRecursive);
+
template
-carve::geom::vector Face::computeNormal(double CARVE_EPSILON) {
- vector_t polygon_normal = carve::geom::VECTOR(0, 0, 0);
+carve::geom::vector Face::computeNormal(double CARVE_EPSILON, bool calledRecursive) {
+ vector_t polygon_normal = carve::geom::VECTOR(0, 0, 1);
edge_t* edgePtr = edge;
if (n_edges < 2)
@@ -324,18 +326,28 @@ carve::geom::vector Face::computeNormal(double CARVE_EPSILON) {
vector_t AB(B - A);
vector_t AC(C - A);
vector_t crossProduct = cross(AB, AC);
+
+ if (crossProduct.length2() < 1e-16)
+ {
+ // degenerate face. Try adjacent faces
+ bool foundAdjacentNormal = getAdjacentFaceNormal(this, polygon_normal, CARVE_EPSILON, calledRecursive);
+ if( foundAdjacentNormal )
+ {
+ return polygon_normal;
+ }
+ }
crossProduct.normalize();
polygon_normal = crossProduct;
}
else
- {
+ {
// find triangle with largest area
edge_t* longestEdge = nullptr;
double longestEdgeLength = 0;
double largestArea = 0;
for (size_t i_edge = 0; i_edge < n_edges; ++i_edge)
- {
+ {
if (!edgePtr)
{
continue;
@@ -368,6 +380,7 @@ carve::geom::vector Face::computeNormal(double CARVE_EPSILON) {
edge_t* edge1 = longestEdge->next;
const vector_t& pA = longestEdge->v1()->v; // vert
const vector_t& B = longestEdge->v2()->v; // next->vert
+ bool normalFound = false;
for (size_t i_edge = 0; i_edge < n_edges - 1; ++i_edge)
{
@@ -376,16 +389,30 @@ carve::geom::vector Face::computeNormal(double CARVE_EPSILON) {
vector_t AB(B - pA);
vector_t AC(C - pA);
vector_t crossProduct = cross(AB, AC);
- double area = crossProduct.length() * 0.5;
- if (std::abs(area) > largestArea)
- {
- largestArea = area;
- crossProduct.normalize();
- polygon_normal = crossProduct;
- }
+ if (crossProduct.length2() > 1e-16)
+ {
+ double area = crossProduct.length() * 0.5;
+ if (std::abs(area) > largestArea)
+ {
+ largestArea = area;
+ crossProduct.normalize();
+ polygon_normal = crossProduct;
+ normalFound = true;
+ }
+ }
edge1 = edge1->next;
- }
+ }
+
+ if (!normalFound)
+ {
+ // degenerate face. Try adjacent faces
+ bool foundAdjacentNormal = getAdjacentFaceNormal(this, polygon_normal, CARVE_EPSILON, calledRecursive);
+ if (foundAdjacentNormal)
+ {
+ return polygon_normal;
+ }
+ }
#ifdef _DEBUG
if (edgePtr != edge)
@@ -458,6 +485,8 @@ carve::geom::vector Face::computeNormal(double CARVE_EPSILON) {
}
#endif
+
+
return polygon_normal;
}
@@ -1045,7 +1074,16 @@ void MeshSet::_init_from_faces(iter_t begin, iter_t end, const MeshOptions
}
template
-MeshSet::MeshSet(const std::vector::vertex_t::vector_t>& points, size_t n_faces, const std::vector& face_indices, double CARVE_EPSILON, const MeshOptions& opts)
+MeshSet::MeshSet() {
+#if defined _DEBUG || defined _DEBUG_RELEASE
+ ++globalNumMeshSets;
+#endif
+}
+
+template
+MeshSet::MeshSet(const std::vector::vertex_t::vector_t>& points, size_t n_faces,
+ const std::vector& face_indices, double CARVE_EPSILON, const MeshOptions& opts)
+ : MeshSet()
{
vertex_storage.reserve(points.size());
std::vector faces;
@@ -1079,19 +1117,26 @@ MeshSet::MeshSet(const std::vector::vertex_t::vecto
}
}
+
+
template
-MeshSet::MeshSet(std::vector& faces, const MeshOptions& opts) {
+MeshSet::MeshSet(std::vector& faces, const MeshOptions& opts)
+ : MeshSet()
+{
_init_from_faces(faces.begin(), faces.end(), opts);
}
template
-MeshSet::MeshSet(std::list& faces, const MeshOptions& opts) {
+MeshSet::MeshSet(std::list& faces, const MeshOptions& opts)
+ : MeshSet()
+{
_init_from_faces(faces.begin(), faces.end(), opts);
}
template
-MeshSet::MeshSet(std::vector& _vertex_storage,
- std::vector& _meshes) {
+MeshSet::MeshSet(std::vector& _vertex_storage, std::vector& _meshes)
+ : MeshSet()
+{
vertex_storage.swap(_vertex_storage);
meshes.swap(_meshes);
@@ -1101,44 +1146,46 @@ MeshSet::MeshSet(std::vector& _vertex_storage,
}
template
-MeshSet::MeshSet(std::vector::mesh_t*>& _meshes) {
- meshes.swap(_meshes);
- std::unordered_map vert_idx;
-
- for (size_t m = 0; m < meshes.size(); ++m) {
- mesh_t* mesh = meshes[m];
- CARVE_ASSERT(mesh->meshset == nullptr);
- mesh->meshset = this;
- for (size_t f = 0; f < mesh->faces.size(); ++f) {
- face_t* face = mesh->faces[f];
- edge_t* edge = face->edge;
- do {
- vert_idx[edge->vert] = 0;
- edge = edge->next;
- } while (edge != face->edge);
+MeshSet::MeshSet(std::vector::mesh_t*>& _meshes)
+ : MeshSet()
+{
+ meshes.swap(_meshes);
+ std::unordered_map vert_idx;
+
+ for (size_t m = 0; m < meshes.size(); ++m) {
+ mesh_t* mesh = meshes[m];
+ CARVE_ASSERT(mesh->meshset == nullptr);
+ mesh->meshset = this;
+ for (size_t f = 0; f < mesh->faces.size(); ++f) {
+ face_t* face = mesh->faces[f];
+ edge_t* edge = face->edge;
+ do {
+ vert_idx[edge->vert] = 0;
+ edge = edge->next;
+ } while (edge != face->edge);
+ }
}
- }
- vertex_storage.reserve(vert_idx.size());
- for (typename std::unordered_map::iterator i =
- vert_idx.begin();
- i != vert_idx.end(); ++i) {
- (*i).second = vertex_storage.size();
- vertex_storage.push_back(*(*i).first);
- }
+ vertex_storage.reserve(vert_idx.size());
+ for (typename std::unordered_map::iterator i =
+ vert_idx.begin();
+ i != vert_idx.end(); ++i) {
+ (*i).second = vertex_storage.size();
+ vertex_storage.push_back(*(*i).first);
+ }
- for (size_t m = 0; m < meshes.size(); ++m) {
- mesh_t* mesh = meshes[m];
- for (size_t f = 0; f < mesh->faces.size(); ++f) {
- face_t* face = mesh->faces[f];
- edge_t* edge = face->edge;
- do {
- size_t i = vert_idx[edge->vert];
- edge->vert = &vertex_storage[i];
- edge = edge->next;
- } while (edge != face->edge);
+ for (size_t m = 0; m < meshes.size(); ++m) {
+ mesh_t* mesh = meshes[m];
+ for (size_t f = 0; f < mesh->faces.size(); ++f) {
+ face_t* face = mesh->faces[f];
+ edge_t* edge = face->edge;
+ do {
+ size_t i = vert_idx[edge->vert];
+ edge->vert = &vertex_storage[i];
+ edge = edge->next;
+ } while (edge != face->edge);
+ }
}
- }
}
template
@@ -1159,6 +1206,10 @@ MeshSet::~MeshSet() {
for (size_t i = 0; i < meshes.size(); ++i) {
delete meshes[i];
}
+
+#if defined _DEBUG || defined _DEBUG_RELEASE
+ --globalNumMeshSets;
+#endif
}
template
diff --git a/IfcPlusPlus/src/external/Carve/src/include/carve/rtree.hpp b/IfcPlusPlus/src/external/Carve/src/include/carve/rtree.hpp
index 95c6c08ed..e5f094591 100644
--- a/IfcPlusPlus/src/external/Carve/src/include/carve/rtree.hpp
+++ b/IfcPlusPlus/src/external/Carve/src/include/carve/rtree.hpp
@@ -259,6 +259,12 @@ namespace carve {
{
const size_t N = std::distance(begin, end);
+ if (N == 0)
+ {
+ printToDebugLog(__FUNCTION__, "N == 0");
+ return;
+ }
+
size_t dim = ndim;
double r_best = N + 1;
diff --git a/IfcPlusPlus/src/external/Carve/src/lib/carve.cpp b/IfcPlusPlus/src/external/Carve/src/lib/carve.cpp
index 1f4da74b7..5eb6fd10d 100644
--- a/IfcPlusPlus/src/external/Carve/src/lib/carve.cpp
+++ b/IfcPlusPlus/src/external/Carve/src/lib/carve.cpp
@@ -28,9 +28,41 @@
#include
-#define DEF_EPSILON 1.4901161193847656e-08
-
-namespace carve {
+//#define DEF_EPSILON 1.4901161193847656e-08
+//namespace carve {
//double CARVE_EPSILON_INTERNAL[24] = { DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON, DEF_EPSILON };
//double CARVE_EPSILON2_INTERNAL[24] = { DEF_EPSILON*DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON, DEF_EPSILON * DEF_EPSILON };
+//}
+
+#include
+#include
+#include
+static std::mutex mutex_debug_log;
+static std::string fileNameLinuxDebugLog = "debugLogLinux.txt";
+static bool LinuxDebugOn = false;
+bool IsPrintToDebugLogOn() { return LinuxDebugOn; }
+void printToDebugLogOn(bool on) { LinuxDebugOn = on; }
+void printToDebugLog(const char* funcName, std::string details)
+{
+ return;
+ if (LinuxDebugOn)
+ {
+ std::cout << funcName << " " << details << std::endl;
+ }
+ return;
+
+
+ std::lock_guard lock(mutex_debug_log);
+ std::ofstream debugLog;
+ debugLog.open(fileNameLinuxDebugLog, std::ios::app);
+ debugLog << funcName << " " << details << std::endl;
+ debugLog.close();
+}
+
+void clearDebugLogLinux()
+{
+ return;
+ std::ofstream debugLog;
+ debugLog.open(fileNameLinuxDebugLog, std::ios::trunc);
+ debugLog.close();
}
diff --git a/IfcPlusPlus/src/external/Carve/src/lib/mesh.cpp b/IfcPlusPlus/src/external/Carve/src/lib/mesh.cpp
index 288a18811..67f80c99a 100644
--- a/IfcPlusPlus/src/external/Carve/src/lib/mesh.cpp
+++ b/IfcPlusPlus/src/external/Carve/src/lib/mesh.cpp
@@ -25,7 +25,8 @@
#if defined(HAVE_CONFIG_H)
#include
#endif
-
+#include
+#include
#include
#include
#include
@@ -265,15 +266,12 @@ namespace carve {
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);
+ int v = carve::geom3d::compareAngles(edge_dir, base_dir, a.face_dir, b.face_dir);
#if defined(CARVE_DEBUG)
{
- double da =
- carve::geom3d::antiClockwiseAngle(base_dir, a.face_dir, edge_dir);
- double db =
- carve::geom3d::antiClockwiseAngle(base_dir, b.face_dir, edge_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 v_cmp = 0;
if( da < db )
v_cmp = -1;
@@ -288,6 +286,19 @@ namespace carve {
}
#endif
+ // strict weak ordering
+ // a is equivalent to b: a < b false
+ // a is equivalent to b b < a false
+
+ if( a.edge == b.edge ) {
+ if (a.group_id != b.group_id) {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE) ||defined(CARVE_DEBUG)
+ std::cout << "a.group_id != b.group_id" << std::endl;
+#endif
+ }
+ return false;
+ }
+
if( v < 0 ) {
return true;
}
@@ -384,7 +395,36 @@ namespace carve {
continue;
}
- std::sort(result[i].begin(), result[i].end(), EdgeOrderData::Cmp(sort_dir, result[i][0].face_dir));
+ try
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ std::vector resultI = result[i];
+ carve::geom::vector<3> firstDir = resultI[0].face_dir;
+
+ EdgeOrderData::Cmp testComparator(sort_dir, firstDir);
+
+ for (size_t ii = 0; ii < resultI.size(); ++ii)
+ {
+ EdgeOrderData edgeOrder = resultI[ii];
+
+ for (size_t jj = 0; jj < resultI.size(); ++jj)
+ {
+ EdgeOrderData edgeOrderJ = resultI[jj];
+ bool compareResult = testComparator.operator()(edgeOrder, edgeOrderJ);
+ //testComparator.operator()(edgeOrderJ, edgeOrder);
+ }
+
+ }
+#endif
+ std::sort(result[i].begin(), result[i].end(), EdgeOrderData::Cmp(sort_dir, result[i][0].face_dir));
+ }
+ catch (...)
+ {
+ if (IsPrintToDebugLogOn())
+ {
+ printToDebugLog(__FUNCTION__, "EdgeOrderData::Cmp failed " );
+ }
+ }
}
}
@@ -1237,7 +1277,7 @@ carve::PointClass carve::mesh::classifyPoint(const carve::mesh::MeshSet<3>* mesh
double a1 = 0.1;
double a2 = 0.2;
- for(size_t numIntersectionRuns = 1; numIntersectionRuns < 10000; ++numIntersectionRuns )
+ for(size_t numIntersectionRuns = 1; numIntersectionRuns < 40; ++numIntersectionRuns )
{
#ifdef _DEBUG
if (numIntersectionRuns > 0)
@@ -1276,7 +1316,7 @@ carve::PointClass carve::mesh::classifyPoint(const carve::mesh::MeshSet<3>* mesh
manifold_intersections.clear();
face_rtree->search(line, std::back_inserter(near_faces), eps);
- if (numIntersectionRuns > 10000)
+ if (numIntersectionRuns > 20)
{
size_t edgeCount = 0;
for (unsigned int i = 0; !failed && i < near_faces.size(); i++)
@@ -1410,3 +1450,212 @@ carve::PointClass carve::mesh::classifyPoint(const carve::mesh::MeshSet<3>* mesh
}
return POINT_UNK;
}
+
+bool carve::mesh::getAdjacentFaceNormal(carve::mesh::Face<3>* face, carve::geom::vector<3>& result, double CARVE_EPSILON, bool calledRecursive)
+{
+ if (face->edge)
+ {
+ // TODO: make this more general:
+ // for( size_t ii = 0; ii < face->n_edges; ++ii )
+
+ carve::mesh::Edge<3>* edgeRevPtr = face->edge->rev;
+ if (edgeRevPtr)
+ {
+ if (edgeRevPtr->face)
+ {
+ carve::geom::vector<3> face1normal = edgeRevPtr->face->plane.N;
+ if (!calledRecursive)
+ {
+ face1normal = edgeRevPtr->face->computeNormal(CARVE_EPSILON, true);
+ }
+
+ if (face->edge->next)
+ {
+ carve::geom::vector<3> face2normal;
+ bool face2normalFound = false;
+ carve::mesh::Edge<3>* edgeNextRevPtr = face->edge->next->rev;
+ if (edgeNextRevPtr)
+ {
+ if (edgeNextRevPtr->face)
+ {
+ face2normal = edgeNextRevPtr->face->plane.N;
+ if (!calledRecursive)
+ {
+ face2normal = edgeNextRevPtr->face->computeNormal(CARVE_EPSILON, true);
+ }
+ face2normalFound = true;
+ double dotProduct = dot(face1normal, face2normal);
+ if (std::abs(dotProduct - 1.0) < CARVE_EPSILON * 100)
+ {
+ result = face2normal;
+ face->plane.N = result;
+ return true;
+ }
+ }
+ }
+
+ if (face->edge->next->next)
+ {
+ carve::mesh::Edge<3>* edgeNextNextRevPtr = face->edge->next->next->rev;
+ if (edgeNextNextRevPtr)
+ {
+ if (edgeNextNextRevPtr->face)
+ {
+ carve::geom::vector<3> face3normal = edgeNextNextRevPtr->face->plane.N;
+ if (!calledRecursive)
+ {
+ face3normal = edgeNextNextRevPtr->face->computeNormal(CARVE_EPSILON, true);
+ }
+ // try face 1 and face 3
+ double dotProduct = dot(face1normal, face3normal);
+ if (std::abs(dotProduct - 1.0) < CARVE_EPSILON * 100)
+ {
+ result = face3normal;
+ face->plane.N = result;
+ return true;
+ }
+
+ if (face2normalFound)
+ {
+ // try face 2 and face 3
+ double dotProduct = dot(face2normal, face3normal);
+ if (std::abs(dotProduct - 1.0) < CARVE_EPSILON * 100)
+ {
+ result = face2normal;
+ face->plane.N = result;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//template
+/*
+void carve::mesh::MeshSet<3>::classifyInnerOuterMeshes(double eps)
+{
+ if (m_inner_outer_meshes_classified)
+ {
+ return;
+ }
+ m_inner_outer_meshes_classified = true;
+ size_t iiOuterMesh = 0;
+ double maxVolume = 0;
+ for (size_t ii = 0; ii < meshes.size(); ++ii)
+ {
+ carve::mesh::Mesh<3>* mesh0 = meshes[ii];
+ if (mesh0->is_negative)
+ {
+ mesh0->invert();
+ }
+ double vol = mesh0->volume();
+ if (vol > maxVolume)
+ {
+ maxVolume = vol;
+ iiOuterMesh = ii;
+ }
+ }
+
+ if (meshes.size() > iiOuterMesh)
+ {
+ carve::mesh::Mesh<3>* outerMesh = meshes[iiOuterMesh];
+
+ //int manifold_id = iiOuterMesh;
+ //carve::poly::Polyhedron* polyPointer = carve::poly::polyhedronFromMesh(outerMesh, manifold_id, eps);
+
+ carve::input::PolyInputCache3D outerMeshAsPolyInput(eps);
+ carve::poly::polyhedronFromMesh(outerMesh, outerMeshAsPolyInput);
+ shared_ptr& polyData = outerMeshAsPolyInput.m_poly_data;
+
+ std::map mesh_input_options;
+ shared_ptr polyPointer(polyData->create(mesh_input_options, eps));
+
+ for (size_t ii = 0; ii < meshes.size(); ++ii)
+ {
+ carve::mesh::Mesh<3>* mesh = meshes[ii];
+
+ if (ii == iiOuterMesh)
+ {
+ continue;
+ }
+ carve::mesh::Mesh<3>* meshToCheckInside = meshes[ii];
+ size_t numPointsInside = 0;
+ size_t numPointsOutside = 0;
+ size_t numPointsOn = 0;
+
+ for (size_t jj = 0; jj < meshToCheckInside->faces.size(); ++jj)
+ {
+ carve::mesh::Face<3>* face = meshToCheckInside->faces[jj];
+ carve::mesh::Edge<3>* edge = face->edge;
+
+ for (size_t kk = 0; kk < face->n_edges; ++kk)
+ {
+ carve::mesh::Vertex<3>* vert = edge->vert;
+
+ int m_id = 0;
+ std::map result;
+ polyPointer->testVertexAgainstClosedManifolds(vert->v, result, true, eps);
+ std::set inside;
+ for (std::map::iterator j = result.begin(); j != result.end(); ++j)
+ {
+ //if ((*j).first == m_id)
+ //{
+ // continue;
+ //}
+
+ carve::PointClass pointInOrOut = (*j).second;
+
+ if (pointInOrOut == carve::POINT_IN)
+ {
+ inside.insert((*j).first);
+ ++numPointsInside;
+ }
+ else if (pointInOrOut == carve::POINT_ON)
+ {
+ ++numPointsOn;
+ }
+ else if (pointInOrOut == carve::POINT_OUT)
+ {
+ ++numPointsOutside;
+ // not inside, nothing to check further
+ break;
+ }
+ else
+ {
+#ifdef _DEBUG
+ std::cout << "Point not in or out: " << pointInOrOut << std::endl;
+#endif
+ }
+ }
+
+ if (numPointsOutside > 0)
+ {
+ break;
+ }
+ }
+
+ if (numPointsOutside > 0)
+ {
+ break;
+ }
+ }
+
+ if (numPointsOutside == 0 && numPointsInside > 0)
+ {
+ meshToCheckInside->is_inner_mesh = true;
+ //innerMeshes.push_back(meshToCheckInside);
+ }
+ else
+ {
+ meshToCheckInside->is_inner_mesh = false;
+ }
+ }
+ }
+}
+*/
\ No newline at end of file
diff --git a/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.cpp b/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.cpp
new file mode 100644
index 000000000..9b65a54e4
--- /dev/null
+++ b/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.cpp
@@ -0,0 +1,923 @@
+/* -*-c++-*- IfcQuery www.ifcquery.com
+*
+MIT License
+
+Copyright (c) 2017 Fabian Gerold
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+//#define CSG_OCC // define CSG_OCC to enable OCC fall back if Carve fails
+#include "Carve2OpenCascade.h"
+
+#include "CSG_Adapter.h"
+#include "MeshNormalizer.h"
+#include "MeshOps.h"
+#include "MeshFlattener.h"
+#include "GeometryInputData.h"
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+static int csg_compute_count = 0;
+#define CSG_DEBUG
+#endif
+
+void CSG_Adapter::mergeMeshesToMeshset(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params)
+{
+ for (size_t ii = 0; ii < meshes.size(); ++ii)
+ {
+ carve::mesh::Mesh<3>* meshToMerge = meshes[ii];
+ if (meshToMerge->is_negative)
+ {
+ meshToMerge->invert();
+ }
+
+ PolyInputCache3D polyInput1(params.epsMergePoints);
+ MeshOps::polyhedronFromMesh(meshToMerge, polyInput1);
+
+ std::map mesh_input_options;
+ shared_ptr > currentMeshAsMeshset(polyInput1.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints));
+
+ bool isClosed = currentMeshAsMeshset->isClosed();
+
+ if (!result)
+ {
+ result = currentMeshAsMeshset;
+ continue;
+ }
+
+ GeomProcessingParams paramsCurrentMesh(params);
+ paramsCurrentMesh.debugDump = false;
+ MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
+ MeshOps::simplifyMeshSet(currentMeshAsMeshset, infoResult, paramsCurrentMesh);
+
+ MeshOps::checkMeshSetNonNegativeAndClosed(result, params);
+
+#ifdef CSG_DEBUG
+ glm::vec4 color(0.2, 0.2, 0.2, 1.);
+ if (params.debugDump)
+ {
+ GeomDebugDump::moveOffset(0.5);
+ GeomDebugDump::dumpMeshset(result.get(), color, true, false);
+ GeomDebugDump::dumpMeshset(currentMeshAsMeshset.get(), color, true, false);
+ }
+#endif
+
+ carve::csg::CSG csg(params.epsMergePoints);
+ shared_ptr > resultMerge(csg.compute(result.get(), currentMeshAsMeshset.get(), carve::csg::CSG::UNION, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
+ if (resultMerge)
+ {
+#ifdef CSG_DEBUG
+ if (params.debugDump)
+ {
+ GeomDebugDump::moveOffset(0.5);
+ GeomDebugDump::dumpMeshset(resultMerge.get(), color, true, false);
+ }
+#endif
+ GeomProcessingParams paramsResult(params);
+ paramsResult.debugDump = false;
+ MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
+ MeshOps::simplifyMeshSet(resultMerge, infoResult, paramsResult);
+
+ if (infoResult.meshSetValid)
+ {
+ result = resultMerge;
+ }
+ }
+ }
+}
+
+void CSG_Adapter::assignResultOnFail(const shared_ptr >& op1, const shared_ptr >& op2, const carve::csg::CSG::OP operation, shared_ptr >& result)
+{
+ if (operation == carve::csg::CSG::A_MINUS_B)
+ {
+ result = op1;
+ }
+ else if (operation == carve::csg::CSG::B_MINUS_A)
+ {
+ result = op2;
+ }
+ else if (operation == carve::csg::CSG::UNION)
+ {
+ result = op1;
+ }
+}
+
+
+bool CSG_Adapter::checkBoundingBoxIntersection(const carve::geom::aabb<3>& bbox1, const carve::geom::aabb<3>& bbox2, const carve::csg::CSG::OP operation, double eps)
+{
+ if (operation == carve::csg::CSG::UNION)
+ {
+ // union operation needs to be done also when there is no intersection
+ return true;
+ }
+
+ bool bbox_direct_intersects = bbox1.intersects(bbox2, eps);
+ if (bbox_direct_intersects)
+ {
+ return true;
+ }
+
+ double deltBbox = bbox1.maxAxisSeparation(bbox2);
+ if (deltBbox < 0)
+ {
+ return true;
+ }
+
+ if (deltBbox > eps)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool checkResultByBBoxAndVolume(const shared_ptr >& op1, const shared_ptr >& op2,
+ const shared_ptr >& result, GeomProcessingParams& paramsScaled, double epsDefault, double expectedMinVolume)
+{
+ carve::geom::aabb<3> bboxOperandA = op1->getAABB();
+ carve::geom::aabb<3> bboxOperandB = op2->getAABB();
+ carve::geom::aabb<3> bboxResult = result->getAABB();
+ bool resultBboxWrong = false;
+#ifdef CSG_DEBUG
+ if ((paramsScaled.debugDump || csg_compute_count > 14) && false)
+ {
+ glm::vec4 color(0.5, 0.5, 0.7, 1.0);
+ GeomDebugDump::moveOffset(0.3);
+ GeomDebugDump::dumpBBox(bboxOperandA, color, false);
+ GeomDebugDump::dumpBBox(bboxResult, color, false);
+ }
+#endif
+
+ if (!bboxResult.intersects(bboxOperandA, epsDefault))
+ {
+ // result is outside of operandB, should not happen
+ resultBboxWrong = true;
+ }
+
+ vec3 minResult = bboxResult.pos - bboxResult.extent;
+ vec3 maxResult = bboxResult.pos + bboxResult.extent;
+
+ vec3 minOp1 = bboxOperandA.pos - bboxOperandA.extent;
+ vec3 maxOp1 = bboxOperandA.pos + bboxOperandA.extent;
+ vec3 minOp2 = bboxOperandB.pos - bboxOperandB.extent;
+ vec3 maxOp2 = bboxOperandB.pos + bboxOperandB.extent;
+
+ for (size_t dim = 0; dim < 3; ++dim)
+ {
+ double minOp1val = minOp1.v[dim];
+ double minOp2val = minOp2.v[dim];
+ if (minOp1val < minOp2val)
+ {
+ // minX of result should be same as minX of op1
+ if (std::abs(minResult.v[dim] - minOp1.v[dim]) > epsDefault)
+ {
+ resultBboxWrong = true;
+ }
+ }
+ else
+ {
+ double maxOp1val = maxOp1.v[dim];
+ double maxOp2val = maxOp2.v[dim];
+ if (maxOp1val > maxOp2val)
+ {
+ // maxX of result should be same as maxX of op1
+ if (std::abs(maxResult.v[dim] - maxOp1.v[dim]) > epsDefault)
+ {
+ resultBboxWrong = true;
+ }
+ }
+ }
+ }
+
+ // volume of result mesh: volumeOp1-volumeOp2 >= volumeResult >= volumeOp1
+ double volumeResult = 0;
+ if (result)
+ {
+ volumeResult = MeshOps::computeMeshsetVolume(result.get());
+ }
+
+ if (volumeResult < expectedMinVolume)
+ {
+#ifdef CSG_DEBUG
+ if (volumeResult < 0.0002)
+ {
+
+ if (paramsScaled.debugDump)
+ {
+ glm::vec4 color(0.2, 0.2, 0.2, 1.);
+ GeomDebugDump::dumpMeshset(result, color, true, true);
+ }
+ }
+#endif
+ // result volume can not be less than volumeOp1 minus volumeOp2
+ resultBboxWrong = true;
+ }
+
+ return !resultBboxWrong;
+}
+
+bool CSG_Adapter::computeCSG_Carve(const shared_ptr >& inputA, const shared_ptr >& inputB,
+ const carve::csg::CSG::OP operation, shared_ptr >& result,
+ GeomProcessingParams& params, CsgOperationParams& csgParams)
+{
+ if (!inputA || !inputB)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ if (inputA->vertex_storage.size() > 4000)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ if (inputB->vertex_storage.size() > 4000)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ carve::geom::aabb<3> bboxInputA = inputA->getAABB();
+ carve::geom::aabb<3> bboxInputB = inputB->getAABB();
+ CarveMeshNormalizer normMesh(bboxInputA, bboxInputB, csgParams.normalizeCoords);
+ normMesh.m_disableNormalizeAll = false;
+
+ int tag = -1;
+ if (params.ifc_entity)
+ {
+ tag = params.ifc_entity->m_tag;
+ }
+
+#ifdef _DO_CSG_PROFILING
+ // ScopedTimeMeasure() : in destructor, measure end time and save in map 100 most time consuming tags
+ ScopedTimeMeasure measure(&(params.generalSettings->m_mapCsgTimeTag), tag, 10);
+#endif
+
+ printToDebugLog(__FUNC__, "element tag " + std::to_string(tag));
+ GeomProcessingParams paramsUnscaled(params);
+ shared_ptr geomSettingsLocal(new GeometrySettings(params.generalSettings));
+ double scale = normMesh.getScale();
+ double epsDefault = geomSettingsLocal->getEpsilonMergePoints() * csgParams.epsilonFactor;
+ if (!csgParams.normalizeCoords)
+ {
+ scale = 1.0;
+
+ // if scale < 1.0, then epsilon needs to be smaller too
+ double epsMinFaceAreaDefault = geomSettingsLocal->getMinTriangleArea();
+ double epsMinFaceArea = epsMinFaceAreaDefault * (scale * scale);
+ geomSettingsLocal->setEpsilonMergePoints(epsDefault * scale);
+ geomSettingsLocal->setMinTriangleArea(epsMinFaceArea);
+ }
+ else
+ {
+ epsDefault = EPS_RANDOM_FACTOR * EPS_M8 * csgParams.epsilonFactor;
+ geomSettingsLocal->setEpsilonMergePoints(epsDefault);
+ geomSettingsLocal->setMinTriangleArea(epsDefault * 0.00001); // TODO: try other values
+ }
+ GeomProcessingParams paramsScaled(geomSettingsLocal, false);
+
+ bool intersecting = checkBoundingBoxIntersection(bboxInputA, bboxInputB, operation, epsDefault);
+ if (!intersecting)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return true;
+ }
+
+ size_t numDegenerateFacesA = MeshOps::countDegeneratedFaces(inputA.get());
+ size_t numDegenerateFacesB = MeshOps::countDegeneratedFaces(inputB.get());
+ if (numDegenerateFacesA > 0)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return true;
+ }
+ if (numDegenerateFacesB > 0)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return true;
+ }
+
+ if (inputA == inputB)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return true;
+ }
+
+ MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
+ MeshSetInfo infoInputA(params.callbackFunc, params.ifc_entity);
+ MeshSetInfo infoInputB(params.callbackFunc, params.ifc_entity);
+ paramsScaled.allowFinEdges = csgParams.allowFinEdgesInResult;
+ paramsUnscaled.allowFinEdges = csgParams.allowFinEdgesInResult;
+ MeshOps::checkMeshSetValidAndClosed(inputA, infoInputA, paramsScaled);
+ MeshOps::checkMeshSetValidAndClosed(inputB, infoInputB, paramsScaled);
+
+ shared_ptr > op1(inputA->clone());
+ shared_ptr > op2(inputB->clone());
+
+ std::stringstream strs_err;
+ try
+ {
+ // normalize first, so that EPS values match the size of different meshes
+ normMesh.normalizeMesh(op1, "op1", epsDefault);
+ normMesh.normalizeMesh(op2, "op2", epsDefault);
+
+ if (csgParams.flattenFacePlanes )
+ {
+ MeshFlattener flat;
+ flat.flattenFacePlanes(op1, op2, paramsScaled);
+ }
+
+ paramsScaled.triangulateResult = true;
+ paramsScaled.shouldBeClosedManifold = true;
+ paramsScaled.allowDegenerateEdges = csgParams.allowDegenerateEdges;
+
+ MeshSetInfo infoOp1(params.callbackFunc, params.ifc_entity);
+ MeshSetInfo infoOp2(params.callbackFunc, params.ifc_entity);
+ MeshOps::simplifyMeshSet(op1, infoOp1, paramsScaled);
+ MeshOps::simplifyMeshSet(op2, infoOp2, paramsScaled);
+
+ double volumeOp1 = MeshOps::computeMeshsetVolume(op1.get());
+ double volumeOp2 = MeshOps::computeMeshsetVolume(op2.get());
+ double expectedMinVolume = (volumeOp1 - volumeOp2) * 0.99; // leave 1% margin for mesh inaccuracies
+
+ if (infoOp1.finEdges.size() > 0)
+ {
+ paramsScaled.allowFinEdges = true;
+ paramsUnscaled.allowFinEdges = true;
+ }
+
+ if (infoOp2.finEdges.size() > 0)
+ {
+ paramsScaled.allowFinEdges = true;
+ paramsUnscaled.allowFinEdges = true;
+ }
+
+ if (infoInputA.zeroAreaFaces.size() > 0)
+ {
+ paramsScaled.allowZeroAreaFaces = true; // temp
+ paramsUnscaled.allowZeroAreaFaces = true; // temp
+ }
+
+#ifdef CSG_DEBUG
+ ++csg_compute_count;
+ GeomDebugDump::DumpSettingsStruct dumpColorSettings;
+ GeomDebugDump::DumpData::instance().maxDumpCount = 1000;
+ bool operandA_dumped = false;
+ bool operandB_dumped = false;
+ bool dump_result_mesh = false;
+
+ {
+ if (232535 == tag || !infoInputA.meshSetValid)
+ {
+ //epsDefault *= 10;
+ paramsScaled.debugDump = true;
+
+ std::vector* > vecDegenerateEdgeFaces;
+ for (auto e : infoOp1.degenerateEdges)
+ {
+ size_t n_edges = e->face->n_edges;
+ size_t n_edges_reverseFace = e->rev->face->n_edges;
+ vecDegenerateEdgeFaces.push_back(e->face);
+ }
+ GeomDebugDump::dumpFaces(vecDegenerateEdgeFaces, dumpColorSettings.colorMesh, true);
+
+ dump_result_mesh = true;
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+
+ if (infoInputA.finEdges.size() > 0 || infoInputA.finFaces.size() > 0)
+ {
+ for (auto mesh : inputA->meshes)
+ {
+ const std::vector* >& vecFaces = mesh->faces;
+ GeomDebugDump::dumpFacePolygons(vecFaces, dumpColorSettings.colorMesh, false);
+ }
+ std::vector* > vecFinFaces;
+ std::vector* > vecFinEdges;
+ std::copy(infoInputA.finFaces.begin(), infoInputA.finFaces.end(), std::back_inserter(vecFinFaces));
+ std::copy(infoInputA.finEdges.begin(), infoInputA.finEdges.end(), std::back_inserter(vecFinEdges));
+
+ GeomDebugDump::dumpEdges(vecFinEdges);
+ GeomDebugDump::dumpFaces(vecFinFaces, dumpColorSettings.colorMesh, true);
+ GeomDebugDump::moveOffset(1.5);
+ }
+
+
+ MeshSetInfo infoMesh1_debug(params.callbackFunc, params.ifc_entity);
+ bool operand1Valid_debug = MeshOps::checkMeshSetValidAndClosed(op1, infoMesh1_debug, paramsScaled);
+ if (infoMesh1_debug.finEdges.size() > 0 || infoMesh1_debug.finFaces.size() > 0)
+ {
+ for (auto mesh : op1->meshes)
+ {
+ const std::vector* >& vecFaces = mesh->faces;
+ GeomDebugDump::dumpFacePolygons(vecFaces, dumpColorSettings.colorMesh, false);
+ }
+ std::vector* > vecFinFaces;
+ std::vector* > vecFinEdges;
+ std::copy(infoMesh1_debug.finFaces.begin(), infoMesh1_debug.finFaces.end(), std::back_inserter(vecFinFaces));
+ std::copy(infoMesh1_debug.finEdges.begin(), infoMesh1_debug.finEdges.end(), std::back_inserter(vecFinEdges));
+
+ GeomDebugDump::dumpEdges(vecFinEdges);
+ GeomDebugDump::dumpFaces(vecFinFaces, dumpColorSettings.colorMesh, true);
+ GeomDebugDump::moveOffset(0.5);
+ dump_result_mesh = true;
+ }
+ }
+
+ dumpColorSettings.eps = epsDefault;
+ CarveMeshNormalizer normMesh_scaleMeshDump(normMesh);
+ if (!csgParams.normalizeCoords)
+ {
+ normMesh_scaleMeshDump.m_normalizeCoordsInsteadOfEpsilon = true;
+ paramsScaled.normalizer = &normMesh_scaleMeshDump;
+ paramsUnscaled.normalizer = &normMesh_scaleMeshDump;
+ }
+
+ if (infoInputA.zeroAreaFaces.size() > 0)
+ {
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+#endif
+
+ if (!infoOp1.meshSetValid)
+ {
+ op1 = shared_ptr >(inputA->clone());
+ MeshSetInfo infoMesh1copy(params.callbackFunc, params.ifc_entity);
+ MeshOps::checkMeshSetValidAndClosed(op1, infoMesh1copy, paramsUnscaled);
+
+ normMesh.normalizeMesh(op1, "op1_copy", epsDefault);
+ MeshOps::checkMeshSetValidAndClosed(op1, infoOp1, paramsScaled);
+ if (!infoOp1.meshSetValid && infoInputA.meshSetValid)
+ {
+ // normalizing changed the validity, should not happen
+#ifdef CSG_DEBUG
+ //dumpOperands(op1, op2, result, tag, op1_dumped, op2_dumped, dumpColorSettings, paramsScaled);
+ double vol1 = MeshOps::computeMeshsetVolume(op1.get());
+#endif
+ }
+ }
+
+ if (!infoOp2.meshSetValid)
+ {
+ op2 = shared_ptr >(inputB->clone());
+ MeshSetInfo infoMesh2copy(params.callbackFunc, params.ifc_entity);
+ bool operand2copy_valid = MeshOps::checkMeshSetValidAndClosed(op2, infoMesh2copy, paramsUnscaled);
+
+ normMesh.normalizeMesh(op2, "op2_copy", epsDefault);
+ MeshOps::checkMeshSetValidAndClosed(op2, infoOp2, paramsScaled);
+ if (!infoOp2.meshSetValid && infoInputB.meshSetValid)
+ {
+ // normalizing changed the validity, should not happen
+#ifdef CSG_DEBUG
+
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ double vol2 = MeshOps::computeMeshsetVolume(op2.get());
+#endif
+ }
+ }
+
+ if (!infoOp1.meshSetValid || !infoOp2.meshSetValid)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ size_t numFacesOp1 = MeshOps::countFaces(op1.get());
+ if (op1->vertex_storage.size() < 4 || numFacesOp1 < 4)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ size_t numFacesOp2 = MeshOps::countFaces(op2.get());
+ if (op2->vertex_storage.size() < 4 || numFacesOp2 < 4)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+
+ //paramsScaled.allowDegenerateEdges = true;
+ if (infoOp1.degenerateEdges.size() > 0) { paramsScaled.allowDegenerateEdges = true; }
+ if (infoOp2.degenerateEdges.size() > 0) { paramsScaled.allowDegenerateEdges = true; }
+
+ ////////////////////// compute carve csg operation /////////////////////////////////////////////
+ bool boolOpDone = false;
+ if (op1->meshes.size() > 1 && operation == carve::csg::CSG::A_MINUS_B)
+ {
+ handleInnerOuterMeshesInOperands(op1, op2, result, paramsScaled, boolOpDone, epsDefault);
+ }
+
+ if (!boolOpDone)
+ {
+ carve::csg::CSG csg(epsDefault);
+ result = shared_ptr >(csg.compute(op1.get(), op2.get(), operation, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
+ }
+
+ paramsScaled.allowFinEdges = csgParams.allowFinEdgesInResult;
+ MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
+
+ if (!csgParams.allowFinFacesInResult && infoResult.finFaces.size() > 0)
+ {
+ infoResult.meshSetValid = false;
+ }
+
+ if (!infoResult.meshSetValid || (infoResult.meshSetValid && infoResult.degenerateEdges.size() > 0))
+ {
+ bool checkBBoxIntersection = false;
+ if (result)
+ {
+ if (result->meshes.size() == 0)
+ {
+ checkBBoxIntersection = true;
+ }
+ }
+ else
+ {
+ checkBBoxIntersection = true;
+ }
+
+ if (checkBBoxIntersection)
+ {
+ carve::geom::aabb<3> bboxOperand1 = op1->getAABB();
+ carve::geom::aabb<3> bboxOperand2 = op2->getAABB();
+ if (bboxOperand2.completelyContains(bboxOperand1, epsDefault))
+ {
+ // result is empty, but valid
+ // TODO: check if it is necessary to check all points of op1 if they are inside op2
+ result = shared_ptr >();
+ return true;
+ }
+ }
+
+ if(result)
+ {
+#ifdef CSG_DEBUG
+ if ( paramsScaled.debugDump )//|| csg_compute_count > 14)
+ {
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ double volume_result = MeshOps::computeMeshsetVolume(result.get());
+ }
+#endif
+
+ MeshOps::simplifyMeshSet(result, infoResult, paramsScaled);
+ //MeshOps::removeDegenerateMeshes(result, paramsScaled, true);
+ MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
+ }
+ }
+
+#ifdef CSG_DEBUG
+ if (!infoResult.meshSetValid || dump_result_mesh)
+ {
+ //if (paramsScaled.debugDump)
+ if( csg_compute_count > 22)
+ {
+ GeomDebugDump::clearMeshsetDump();
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+ }
+#endif
+ if (infoResult.meshSetValid)
+ {
+ if (operation == carve::csg::CSG::A_MINUS_B)
+ {
+ bool resultOkByBBox = checkResultByBBoxAndVolume(op1, op2, result, paramsScaled, epsDefault, expectedMinVolume);
+ if (!resultOkByBBox)
+ {
+ // result is smaller than operandA, should not happen
+ infoResult.meshSetValid = false;
+ }
+ }
+ }
+
+ if (!infoResult.meshSetValid || (infoResult.meshSetValid && infoResult.degenerateEdges.size() > 0))
+ {
+ // no success so far. Try again with CLASSIFY_NORMAL
+ carve::csg::CSG csg(epsDefault);
+ shared_ptr > resultClassifyNormal(csg.compute(op1.get(), op2.get(), operation, nullptr, carve::csg::CSG::CLASSIFY_NORMAL));
+
+ MeshSetInfo infoResultClassifyNormal;
+ MeshOps::checkMeshSetValidAndClosed(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
+
+ if (!infoResultClassifyNormal.meshSetValid || (infoResultClassifyNormal.meshSetValid && infoResultClassifyNormal.degenerateEdges.size() > 0))
+ {
+#ifdef CSG_DEBUG
+ //if (paramsScaled.debugDump)
+ {
+ dumpOperands(op1, op2, resultClassifyNormal, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+#endif
+
+ MeshOps::simplifyMeshSet(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
+ MeshOps::checkMeshSetValidAndClosed(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
+
+ if (MeshOps::isBetterForBoolOp(infoResultClassifyNormal, infoResult, false))
+ {
+ result = resultClassifyNormal;
+ }
+ }
+ }
+
+ if (infoResult.meshSetValid)
+ {
+ if (operation == carve::csg::CSG::A_MINUS_B)
+ {
+ bool resultOkByBBox = checkResultByBBoxAndVolume(op1, op2, result, paramsScaled, epsDefault, expectedMinVolume);
+ if (!resultOkByBBox)
+ {
+ // result is smaller than operandA, should not happen
+ infoResult.meshSetValid = false;
+ }
+ }
+ }
+
+#ifdef CSG_OCC
+ if (!infoResult.meshSetValid)
+ {
+ // OCC
+ TopoDS_Shape operandA_OCC, operandB_OCC, resultOCC;
+ Carve2OpenCascade::convertCarve2OCC(op1, operandA_OCC, epsDefault);
+
+ Carve2OpenCascade::convertCarve2OCC(op2, operandB_OCC, epsDefault);
+ Carve2OpenCascade::boolOpOCC(operandA_OCC, operandB_OCC, operation, resultOCC);
+ Carve2OpenCascade::convertOCC2Carve(resultOCC, result);
+
+ {
+ shared_ptr > opAback;
+ Carve2OpenCascade::convertOCC2Carve(operandA_OCC, opAback);
+
+ shared_ptr > opBback;
+ Carve2OpenCascade::convertOCC2Carve(operandB_OCC, opBback);
+
+
+
+ glm::vec4 color(0.5, 0.5, 0.5, 1);
+ GeomDebugDump::dumpMeshset(opAback, color, false, false);
+ GeomDebugDump::dumpMeshset(opBback, color, false, true);
+
+ GeomDebugDump::dumpMeshset(result, color, false, true);
+ }
+
+ paramsScaled.allowFinEdges = true;
+ MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
+
+ if (!infoResult.meshSetValid || (infoResult.meshSetValid && infoResult.degenerateEdges.size() > 0))
+ {
+#ifdef CSG_DEBUG
+ //if ( paramsScaled.debugDump || csg_compute_count > 14)
+ {
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ double volume_result = MeshOps::computeMeshsetVolume(result.get());
+ }
+#endif
+
+ MeshOps::simplifyMeshSet(result, infoResult, paramsScaled);
+ MeshOps::removeDegenerateMeshes(result, paramsScaled, true);
+ MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
+ }
+ }
+
+ if (infoResult.meshSetValid)
+ {
+ if (operation == carve::csg::CSG::A_MINUS_B)
+ {
+ bool resultOkByBBox = checkResultByBBoxAndVolume(op1, op2, result, paramsScaled, epsDefault, expectedMinVolume);
+ if (!resultOkByBBox)
+ {
+ // result is smaller than operandA, should not happen
+ infoResult.meshSetValid = false;
+ }
+ }
+ }
+#endif
+
+
+ if (!infoResult.meshSetValid)
+ {
+ strs_err << "csg operation failed" << std::endl;
+
+ // TODO: keep invalid shape as ItemShapeData::m_meshset_invalid_after_csg
+ // if no further boolean operation is performed, it could still be used for visualization
+
+#ifdef CSG_DEBUG
+ if (paramsScaled.debugDump)
+ {
+ dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+#endif
+ }
+ }
+ catch (carve::exception& ce)
+ {
+ strs_err << "csg operation failed" << ce.str().c_str();
+ }
+ catch (const std::out_of_range& oor)
+ {
+ strs_err << "csg operation failed" << oor.what();
+ }
+ catch (std::exception& e)
+ {
+ strs_err << "csg operation failed" << e.what();
+ }
+ catch (...)
+ {
+ strs_err << "csg operation failed" << std::endl;
+ }
+
+ if (strs_err.tellp() > 0)
+ {
+ if (!infoResult.meshSetValid)
+ {
+ assignResultOnFail(inputA, inputB, operation, result);
+ return false;
+ }
+ }
+
+#ifdef CSG_DEBUG
+ MeshSetInfo infoMesh_beforeDeNormalize(params.callbackFunc, params.ifc_entity);
+ bool result_meshset_ok_beforeDeNormalize = MeshOps::checkMeshSetValidAndClosed(result, infoMesh_beforeDeNormalize, paramsScaled);
+#endif
+
+ normMesh.deNormalizeMesh(result, "", epsDefault);
+
+#ifdef CSG_DEBUG
+ {
+ // de-normalized:
+ MeshSetInfo infoMesh1(params.callbackFunc, params.ifc_entity);
+ bool result_valid_2 = MeshOps::checkMeshSetValidAndClosed(result, infoMesh1, paramsUnscaled);
+
+ if (!result_valid_2)
+ {
+ std::cout << "!result" << std::endl;
+ }
+ }
+#endif
+
+ return infoResult.meshSetValid;
+}
+#define _ORDER_CSG_BY_VOLUME
+
+void CSG_Adapter::computeCSG(shared_ptr >& op1, const std::vector > >& operands2, const carve::csg::CSG::OP operation, GeomProcessingParams& params)
+{
+ if (!op1 || operands2.size() == 0)
+ {
+ return;
+ }
+
+ bool success = false;
+ std::multimap > > mapVolumeMeshes;
+ for (const shared_ptr >&meshset2 : operands2)
+ {
+#ifdef _ORDER_CSG_BY_VOLUME
+ double volume = MeshOps::computeMeshsetVolume(meshset2.get());
+ mapVolumeMeshes.insert({ volume, meshset2 });
+ }
+
+ for (auto it = mapVolumeMeshes.rbegin(); it != mapVolumeMeshes.rend(); ++it )
+ {
+ const shared_ptr >& meshset2 = it->second;
+#endif
+ std::vector vecCsgParams =
+ {
+ // epsFactor, normalizeCoords, allowDegenEdges, allowFinFacesInResult, allowFinEdgesInResult, flattenFacePlanes
+ {1.0, true, false, false, false, false },
+ {1.0, true, false, false, false, true }, // one variant with flattenFacePlanes
+ {1.0, true, true, false, false, false },
+ {15.3, true, true, false, false, false }, // one variant with bigger epsilon
+ {0.11, true, true, false, false, false }, // one variant with smaller epsilon
+ {1.0, true, true, true, true, false },
+ {1.0, false, true, false, false, false } // one variant without normalizing
+ };
+
+ for (size_t ii = 0; ii < vecCsgParams.size(); ++ii)
+ {
+ shared_ptr > result;
+ CsgOperationParams& csgParams = vecCsgParams[ii];
+ success = computeCSG_Carve(op1, meshset2, operation, result, params, csgParams);
+
+ if (success)
+ {
+ if (operation == carve::csg::CSG::A_MINUS_B || operation == carve::csg::CSG::UNION)
+ {
+ op1 = result;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void CSG_Adapter::handleInnerOuterMeshesInOperands(shared_ptr >& op1, shared_ptr >& op2, shared_ptr >& result,
+ GeomProcessingParams& params, bool& boolOpDone, double eps)
+{
+ // if op1 consists of > 1 meshes, and one is completely inside the other, then all inner meshes need to be merged with op2, then op2 subtracted from the op1 outer mesh
+ // if op1 consists of > 1 meshes, and they are apart from each other, then regular csg.compute works fine
+
+ std::vector*> innerMeshes;
+ std::vector*> outerMeshes;
+
+ for (size_t iiMesh = 0; iiMesh < op1->meshes.size(); ++iiMesh)
+ {
+ carve::mesh::Mesh<3>* mesh1 = op1->meshes[iiMesh];
+
+ if (mesh1->is_inner_mesh && iiMesh > 0)
+ {
+ innerMeshes.push_back(mesh1);
+ }
+ else
+ {
+ outerMeshes.push_back(mesh1);
+ }
+ }
+
+ shared_ptr > resultClassified;
+ if (outerMeshes.size() > 1)
+ {
+ // check if some outer meshes are in fact inner meshes
+ if (innerMeshes.size() == 0)
+ {
+ MeshOps::classifyMeshesInside(outerMeshes, resultClassified, params);
+
+ innerMeshes.clear();
+ outerMeshes.clear();
+ if (resultClassified)
+ {
+ for (size_t iiMesh = 0; iiMesh < resultClassified->meshes.size(); ++iiMesh)
+ {
+ carve::mesh::Mesh<3>* mesh1 = resultClassified->meshes[iiMesh];
+
+ if (mesh1->is_inner_mesh && iiMesh > 0)
+ {
+ innerMeshes.push_back(mesh1);
+ }
+ else
+ {
+ outerMeshes.push_back(mesh1);
+ }
+ }
+ }
+ }
+ }
+
+ shared_ptr > op1OuterMeshset;
+ mergeMeshesToMeshset(outerMeshes, op1OuterMeshset, params);
+
+ shared_ptr > op2Meshset(op2->clone());
+ mergeMeshesToMeshset(innerMeshes, op2Meshset, params);
+
+#ifdef CSG_DEBUG
+ if (csg_compute_count >= 9)
+ {
+ GeomDebugDump::moveOffset(0.4);
+ //operandA_dumped = false;
+ //operandB_dumped = false;
+ //dumpOperands(op1OuterMeshset, op2Meshset, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
+ }
+ GeomDebugDump::DumpSettingsStruct dumpColorSettings;
+ if (csg_compute_count == 12 && false)
+ {
+ GeomDebugDump::moveOffset(0.2);
+ dumpWithLabel("computeCSG::op1", op1OuterMeshset, dumpColorSettings, params, true, false);
+ dumpWithLabel("computeCSG::op2", op2, dumpColorSettings, params, false, false);
+ }
+#endif
+
+ MeshSetInfo infoOuterMesh(params.callbackFunc, params.ifc_entity);
+ MeshSetInfo infoInnerMesh(params.callbackFunc, params.ifc_entity);
+ bool outerMesh_valid = MeshOps::checkMeshSetValidAndClosed(op1OuterMeshset, infoOuterMesh, params);
+ bool innerMesh_valid = MeshOps::checkMeshSetValidAndClosed(op2Meshset, infoInnerMesh, params);
+
+ if (outerMesh_valid && innerMesh_valid)
+ {
+ carve::csg::CSG csg(eps);
+ shared_ptr > resultMerge(csg.compute(op1OuterMeshset.get(), op2Meshset.get(), carve::csg::CSG::A_MINUS_B, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
+ if (resultMerge)
+ {
+ result = resultMerge;
+ MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
+ params.allowDegenerateEdges = true;
+ bool resultValid = MeshOps::checkMeshSetValidAndClosed(result, infoResult, params);
+ if (resultValid)
+ {
+ boolOpDone = true;
+ }
+ }
+ }
+}
+
diff --git a/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.h b/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.h
index 2064af770..8be12c3e0 100644
--- a/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.h
+++ b/IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.h
@@ -17,739 +17,41 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU
#pragma once
-#include
-#include
#include
#include
#include
-#include
#include "IncludeCarveHeaders.h"
-#include "MeshNormalizer.h"
-#include "MeshOps.h"
-#include "GeometryInputData.h"
-
-#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
-static int csg_compute_count = 0;
-//#define CSG_DEBUG
-#endif
class CSG_Adapter
{
public:
- static void mergeMeshesToMeshset(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params)
- {
- for (size_t ii = 0; ii < meshes.size(); ++ii)
- {
- carve::mesh::Mesh<3>* meshToMerge = meshes[ii];
- if (meshToMerge->is_negative)
- {
- meshToMerge->invert();
- }
-
- PolyInputCache3D polyInput1(params.epsMergePoints);
- MeshOps::polyhedronFromMesh(meshToMerge, polyInput1);
-
- std::map mesh_input_options;
- shared_ptr > currentMeshAsMeshset(polyInput1.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints));
-
- bool isClosed = currentMeshAsMeshset->isClosed();
-
- if (!result)
- {
- result = currentMeshAsMeshset;
- continue;
- }
+ static void computeCSG(shared_ptr >& op1, const std::vector > >& operands2,
+ const carve::csg::CSG::OP operation, GeomProcessingParams& params);
- GeomProcessingParams paramsCurrentMesh(params);
- paramsCurrentMesh.debugDump = false;
- MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
- MeshOps::simplifyMeshSet(currentMeshAsMeshset, infoResult, paramsCurrentMesh);
- MeshOps::checkMeshSetNonNegativeAndClosed(result, params);
+ static void mergeMeshesToMeshset(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params);
-#ifdef CSG_DEBUG
- glm::vec4 color(0.2, 0.2, 0.2, 1.);
- if (params.debugDump)
- {
- GeomDebugDump::moveOffset(0.5);
- GeomDebugDump::dumpMeshset(result.get(), color, true, false);
- GeomDebugDump::dumpMeshset(currentMeshAsMeshset.get(), color, true, false);
- }
-#endif
+ static void assignResultOnFail(const shared_ptr >& op1, const shared_ptr >& op2, const carve::csg::CSG::OP operation, shared_ptr >& result);
- carve::csg::CSG csg(params.epsMergePoints);
- shared_ptr > resultMerge(csg.compute(result.get(), currentMeshAsMeshset.get(), carve::csg::CSG::UNION, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
- if (resultMerge)
- {
-#ifdef CSG_DEBUG
- if (params.debugDump)
- {
- GeomDebugDump::moveOffset(0.5);
- GeomDebugDump::dumpMeshset(resultMerge.get(), color, true, false);
- }
-#endif
- GeomProcessingParams paramsResult(params);
- paramsResult.debugDump = false;
- MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
- MeshOps::simplifyMeshSet(resultMerge, infoResult, paramsResult);
-
- if (infoResult.meshSetValid)
- {
- result = resultMerge;
- }
- }
- }
- }
+ static bool checkBoundingBoxIntersection(const carve::geom::aabb<3>& bbox1, const carve::geom::aabb<3>& bbox2, const carve::csg::CSG::OP operation, double eps);
- static void assignResultOnFail(const shared_ptr >& op1, const shared_ptr >& op2, const carve::csg::CSG::OP operation, shared_ptr >& result)
+ struct CsgOperationParams
{
- if( operation == carve::csg::CSG::A_MINUS_B )
- {
- result = op1;
- }
- else if( operation == carve::csg::CSG::B_MINUS_A )
- {
- result = op2;
- }
- else if( operation == carve::csg::CSG::UNION )
- {
- result = op1;
- }
- }
+ double epsilonFactor = 1.0;
+ bool normalizeCoords = true;
+ bool allowDegenerateEdges = false;
+ bool allowFinFacesInResult = false;
+ bool allowFinEdgesInResult = false;
+ bool flattenFacePlanes = false;
+ };
+ static bool computeCSG_Carve(const shared_ptr >& inputA, const shared_ptr >& inputB, const carve::csg::CSG::OP operation, shared_ptr >& result,
+ GeomProcessingParams& params, CsgOperationParams& csgParams);
- static bool checkBoundinbBoxIntersection(const carve::geom::aabb<3>& bbox1, const carve::geom::aabb<3>& bbox2, const carve::csg::CSG::OP operation, double eps)
- {
- if( operation == carve::csg::CSG::UNION )
- {
- // union operation needs to be done also when there is no intersection
- return true;
- }
-
- bool bbox_direct_intersects = bbox1.intersects(bbox2, eps);
- if( bbox_direct_intersects )
- {
- return true;
- }
-
- double deltBbox = bbox1.maxAxisSeparation(bbox2);
- if (deltBbox < 0)
- {
- return true;
- }
-
- if (deltBbox > eps)
- {
- return false;
- }
-
- return true;
- }
-
- static bool computeCSG_Carve(const shared_ptr >& inputA, const shared_ptr >& inputB, const carve::csg::CSG::OP operation, shared_ptr >& result,
- GeomProcessingParams& params, bool normalizeCoords, bool allowDegenerateEdges)
- {
- if( !inputA || !inputB )
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- if( inputA->vertex_storage.size() > 4000 )
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- if( inputB->vertex_storage.size() > 4000 )
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- carve::geom::aabb<3> bboxA = inputA->getAABB();
- carve::geom::aabb<3> bboxB = inputB->getAABB();
- CarveMeshNormalizer normMesh(bboxA, bboxB, normalizeCoords);
- normMesh.m_disableNormalizeAll = false;
-
- int tag = -1;
- if (params.ifc_entity)
- {
- tag = params.ifc_entity->m_tag;
- }
-
- GeomProcessingParams paramsUnscaled(params);
- shared_ptr geomSettingsLocal(new GeometrySettings(params.generalSettings));
- double scale = normMesh.getScale();
- double epsDefault = geomSettingsLocal->getEpsilonMergePoints();
- if (!normalizeCoords)
- {
- scale = 1.0;
-
- // if scale < 1.0, then epsilon needs to be smaller too
- double epsMinFaceAreaDefault = geomSettingsLocal->getMinTriangleArea();
- double epsMinFaceArea = epsMinFaceAreaDefault * (scale * scale);
- geomSettingsLocal->setEpsilonMergePoints(epsDefault * scale);
- geomSettingsLocal->setMinTriangleArea(epsMinFaceArea);
- }
- else
- {
- epsDefault = 1.5 * EPS_M8;
- geomSettingsLocal->setEpsilonMergePoints(epsDefault);
- geomSettingsLocal->setMinTriangleArea(epsDefault*0.00001);
- }
- GeomProcessingParams paramsScaled(geomSettingsLocal, false);
-
- bool intersecting = checkBoundinbBoxIntersection(bboxA, bboxB, operation, epsDefault);
- if (!intersecting)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return true;
- }
-
- size_t numDegenerateFacesA = MeshOps::countDegeneratedFaces(inputA.get());
- size_t numDegenerateFacesB = MeshOps::countDegeneratedFaces(inputB.get());
- if (numDegenerateFacesA > 0)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return true;
- }
- if (numDegenerateFacesB > 0)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return true;
- }
-
- if( inputA == inputB)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return true;
- }
-
- MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
- MeshSetInfo infoInputA(params.callbackFunc, params.ifc_entity);
- MeshSetInfo infoInputB(params.callbackFunc, params.ifc_entity);
- paramsScaled.allowFinEdges = false;
- paramsUnscaled.allowFinEdges = false;
- MeshOps::checkMeshSetValidAndClosed(inputA, infoInputA, paramsScaled);
- MeshOps::checkMeshSetValidAndClosed(inputB, infoInputB, paramsScaled);
-
- shared_ptr > op1(inputA->clone());
- shared_ptr > op2(inputB->clone());
- std::stringstream strs_err;
- try
- {
- // normalize first, so that EPS values match the size of different meshes
- normMesh.normalizeMesh(op1, "op1", epsDefault);
- normMesh.normalizeMesh(op2, "op2", epsDefault);
-
- MeshOps::flattenFacePlanes(op1, op2, paramsScaled);
-
- paramsScaled.triangulateResult = true;
- paramsScaled.shouldBeClosedManifold = true;
- paramsScaled.allowDegenerateEdges = allowDegenerateEdges;
-
- MeshSetInfo infoOp1(params.callbackFunc, params.ifc_entity);
- MeshSetInfo infoOp2(params.callbackFunc, params.ifc_entity);
- MeshOps::simplifyMeshSet(op1, infoOp1, paramsScaled);
- MeshOps::simplifyMeshSet(op2, infoOp2, paramsScaled);
-
- double volumeOp1 = MeshOps::computeMeshsetVolume(op1.get());
- double volumeOp2 = MeshOps::computeMeshsetVolume(op2.get());
-
- if (infoOp1.finEdges.size() > 0)
- {
- paramsScaled.allowFinEdges = true;
- paramsUnscaled.allowFinEdges = true;
- }
-
- if (infoOp2.finEdges.size() > 0)
- {
- paramsScaled.allowFinEdges = true;
- paramsUnscaled.allowFinEdges = true;
- }
-
- if (infoInputA.zeroAreaFaces.size() > 0)
- {
- paramsScaled.allowZeroAreaFaces = true; // temp
- paramsUnscaled.allowZeroAreaFaces = true; // temp
- }
-
-#ifdef CSG_DEBUG
- ++csg_compute_count;
- GeomDebugDump::DumpSettingsStruct dumpColorSettings;
- GeomDebugDump::DumpData::instance().maxDumpCount = 1000;
- bool operandA_dumped = false;
- bool operandB_dumped = false;
- bool dump_result_mesh = false;
-
- if (csg_compute_count > 0 )
- {
- if (14700322 == tag || !infoInputA.meshSetValid)
- {
- paramsScaled.debugDump = true;
- MeshSetInfo infoMeshOp1;
- MeshOps::checkMeshSetValidAndClosed(op1, infoMeshOp1, params);
-
- //MeshOps::simplifyMeshSet(op1, infoOp1, params);
- GeomDebugDump::dumpMeshset(op1, dumpColorSettings.colorMesh, false, true);
- GeomDebugDump::dumpMeshsetOpenEdges(op1, dumpColorSettings.colorMesh, false, false);
- std::vector* > vecDegenerateEdgeFaces;
- for (auto e : infoOp1.degenerateEdges)
- {
- size_t n_edges = e->face->n_edges;
- size_t n_edges_reverseFace = e->rev->face->n_edges;
- vecDegenerateEdgeFaces.push_back(e->face);
- }
- GeomDebugDump::dumpFaces(vecDegenerateEdgeFaces, dumpColorSettings.colorMesh, true);
-
- dump_result_mesh = true;
- //paramsScaled.debugDump = true;
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
-
- if (infoInputA.finEdges.size() > 0 || infoInputA.finFaces.size() > 0)
- {
- for (auto mesh : inputA->meshes)
- {
- const std::vector* >& vecFaces = mesh->faces;
- GeomDebugDump::dumpFacePolygons(vecFaces, dumpColorSettings.colorMesh, false);
- }
- std::vector* > vecFinFaces;
- std::vector* > vecFinEdges;
- std::copy(infoInputA.finFaces.begin(), infoInputA.finFaces.end(), std::back_inserter(vecFinFaces));
- std::copy(infoInputA.finEdges.begin(), infoInputA.finEdges.end(), std::back_inserter(vecFinEdges));
-
- GeomDebugDump::dumpEdges(vecFinEdges);
- GeomDebugDump::dumpFaces(vecFinFaces, dumpColorSettings.colorMesh, true);
- GeomDebugDump::moveOffset(1.5);
- }
-
-
- MeshSetInfo infoMesh1_debug(params.callbackFunc, params.ifc_entity);
- bool operand1Valid_debug = MeshOps::checkMeshSetValidAndClosed(op1, infoMesh1_debug, paramsScaled);
- if (infoMesh1_debug.finEdges.size() > 0 || infoMesh1_debug.finFaces.size() > 0)
- {
- for (auto mesh : op1->meshes)
- {
- const std::vector* >& vecFaces = mesh->faces;
- GeomDebugDump::dumpFacePolygons(vecFaces, dumpColorSettings.colorMesh, false);
- }
- std::vector* > vecFinFaces;
- std::vector* > vecFinEdges;
- std::copy(infoMesh1_debug.finFaces.begin(), infoMesh1_debug.finFaces.end(), std::back_inserter(vecFinFaces));
- std::copy(infoMesh1_debug.finEdges.begin(), infoMesh1_debug.finEdges.end(), std::back_inserter(vecFinEdges));
-
- GeomDebugDump::dumpEdges(vecFinEdges);
- GeomDebugDump::dumpFaces(vecFinFaces, dumpColorSettings.colorMesh, true);
- GeomDebugDump::moveOffset(0.5);
- dump_result_mesh = true;
- }
- }
-
- dumpColorSettings.eps = epsDefault;
- CarveMeshNormalizer normMesh_scaleMeshDump(normMesh);
- if (!normalizeCoords)
- {
- normMesh_scaleMeshDump.m_normalizeCoordsInsteadOfEpsilon = true;
- paramsScaled.normalizer = &normMesh_scaleMeshDump;
- paramsUnscaled.normalizer = &normMesh_scaleMeshDump;
- }
-
- if (infoInputA.zeroAreaFaces.size() > 0)
- {
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
-#endif
-
- if (!infoOp1.meshSetValid)
- {
- op1 = shared_ptr >(inputA->clone());
- MeshSetInfo infoMesh1copy(params.callbackFunc, params.ifc_entity);
- MeshOps::checkMeshSetValidAndClosed(op1, infoMesh1copy, paramsUnscaled);
-
- normMesh.normalizeMesh(op1, "op1_copy", epsDefault);
- MeshOps::checkMeshSetValidAndClosed(op1, infoOp1, paramsScaled);
- if (!infoOp1.meshSetValid && infoInputA.meshSetValid)
- {
- // normalizing changed the validity, should not happen
-#ifdef CSG_DEBUG
- //dumpOperands(op1, op2, result, tag, op1_dumped, op2_dumped, dumpColorSettings, paramsScaled);
- double vol1 = MeshOps::computeMeshsetVolume(op1.get());
-#endif
- }
- }
-
- if (!infoOp2.meshSetValid)
- {
- op2 = shared_ptr >(inputB->clone());
- MeshSetInfo infoMesh2copy(params.callbackFunc, params.ifc_entity);
- bool operand2copy_valid = MeshOps::checkMeshSetValidAndClosed(op2, infoMesh2copy, paramsUnscaled);
-
- normMesh.normalizeMesh(op2, "op2_copy", epsDefault);
- MeshOps::checkMeshSetValidAndClosed(op2, infoOp2, paramsScaled);
- if (!infoOp2.meshSetValid && infoInputB.meshSetValid)
- {
- // normalizing changed the validity, should not happen
-#ifdef CSG_DEBUG
-
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- double vol2 = MeshOps::computeMeshsetVolume(op2.get());
-#endif
- }
- }
-
- if( !infoOp1.meshSetValid || !infoOp2.meshSetValid)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- size_t numFacesOp1 = MeshOps::countFaces(op1.get());
- if (op1->vertex_storage.size() < 4 || numFacesOp1 < 4)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- size_t numFacesOp2 = MeshOps::countFaces(op2.get());
- if (op2->vertex_storage.size() < 4 || numFacesOp2 < 4)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
-
- //paramsScaled.allowDegenerateEdges = true;
- if (infoOp1.degenerateEdges.size() > 0) { paramsScaled.allowDegenerateEdges = true; }
- if (infoOp2.degenerateEdges.size() > 0) { paramsScaled.allowDegenerateEdges = true; }
-
- ////////////////////// compute carve csg operation /////////////////////////////////////////////
- bool boolOpDone = false;
- if (op1->meshes.size() > 1 && operation == carve::csg::CSG::A_MINUS_B )
- {
- handleInnerOuterMeshesInOperands(op1, op2, result, paramsScaled, boolOpDone, epsDefault);
- }
-
- if( !boolOpDone )
- {
- carve::csg::CSG csg(epsDefault);
- result = shared_ptr >(csg.compute(op1.get(), op2.get(), operation, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
- }
-
- paramsScaled.allowFinEdges = true;
- MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
-
- if (!infoResult.meshSetValid || (infoResult.meshSetValid && infoResult.degenerateEdges.size() > 0 ))
- {
-#ifdef CSG_DEBUG
- //if ( paramsScaled.debugDump || csg_compute_count > 14)
- {
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- double volume_result = MeshOps::computeMeshsetVolume(result.get());
- }
-#endif
-
- MeshOps::simplifyMeshSet(result, infoResult, paramsScaled);
- MeshOps::removeDegenerateMeshes(result, paramsScaled, true);
- MeshOps::checkMeshSetValidAndClosed(result, infoResult, paramsScaled);
- }
-
-#ifdef CSG_DEBUG
- if (!infoResult.meshSetValid|| dump_result_mesh)
- {
- if (paramsScaled.debugDump)
- {
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
- }
-#endif
-
- if (!infoResult.meshSetValid || (infoResult.meshSetValid && infoResult.degenerateEdges.size() > 0))
- {
- carve::csg::CSG csg(epsDefault);
- shared_ptr > resultClassifyNormal(csg.compute(op1.get(), op2.get(), operation, nullptr, carve::csg::CSG::CLASSIFY_NORMAL));
-
- MeshSetInfo infoResultClassifyNormal;
- MeshOps::checkMeshSetValidAndClosed(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
-
- if (!infoResultClassifyNormal.meshSetValid || (infoResultClassifyNormal.meshSetValid && infoResultClassifyNormal.degenerateEdges.size() > 0))
- {
-#ifdef CSG_DEBUG
- if (paramsScaled.debugDump)
- {
- dumpOperands(op1, op2, resultClassifyNormal, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
-#endif
-
- MeshOps::simplifyMeshSet(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
- MeshOps::checkMeshSetValidAndClosed(resultClassifyNormal, infoResultClassifyNormal, paramsScaled);
-
- if (MeshOps::isBetterForBoolOp(infoResultClassifyNormal, infoResult, false))
- {
- result = resultClassifyNormal;
- }
- }
- }
-
-
- if (operation == carve::csg::CSG::A_MINUS_B)
- {
- if (op1->meshes.size() == 1)
- {
- // volume of result mesh: volumeOp1-volumeOp2 >= volumeResult >= volumeOp1
- double volumeResult = MeshOps::computeMeshsetVolume(result.get());
- double expectedMinVolume = volumeOp1 - volumeOp2;
- double deltaExpectedVolume = volumeResult - expectedMinVolume*0.99; // leave 1% margin for mesh inaccuracies
- if (deltaExpectedVolume < 0)
- {
-#ifdef CSG_DEBUG
- if (volumeResult < 0.0002)
- {
- //std::cout << "empty?" << std::endl;
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
-#endif
-
- result = op1;
- }
- }
- }
-
- if( !infoResult.meshSetValid)
- {
- strs_err << "csg operation failed" << std::endl;
-
-#ifdef CSG_DEBUG
- if (paramsScaled.debugDump)
- {
- dumpOperands(op1, op2, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
-#endif
- }
- }
- catch( carve::exception& ce )
- {
- strs_err << "csg operation failed" << ce.str().c_str();
- }
- catch( const std::out_of_range& oor )
- {
- strs_err << "csg operation failed" << oor.what();
- }
- catch( std::exception& e )
- {
- strs_err << "csg operation failed" << e.what();
- }
- catch( ... )
- {
- strs_err << "csg operation failed" << std::endl;
- }
-
- if( strs_err.tellp() > 0 )
- {
- if( !infoResult.meshSetValid)
- {
- assignResultOnFail(inputA, inputB, operation, result);
- return false;
- }
- }
-
-#ifdef CSG_DEBUG
- MeshSetInfo infoMesh_beforeDeNormalize(params.callbackFunc, params.ifc_entity);
- bool result_meshset_ok_beforeDeNormalize = MeshOps::checkMeshSetValidAndClosed(result, infoMesh_beforeDeNormalize, paramsScaled);
-#endif
-
- normMesh.deNormalizeMesh(result, "", epsDefault);
-
-#ifdef CSG_DEBUG
- {
- // de-normalized:
- MeshSetInfo infoMesh1(params.callbackFunc, params.ifc_entity);
- bool result_valid_2 = MeshOps::checkMeshSetValidAndClosed(result, infoMesh1, paramsUnscaled);
-
- if (!result_valid_2)
- {
- std::cout << "!result" << std::endl;
- }
- }
-#endif
-
- return infoResult.meshSetValid;
- }
-
- static void computeCSG(shared_ptr >& op1, const std::vector > >& operands2, const carve::csg::CSG::OP operation, GeomProcessingParams& params)
- {
- if( !op1 || operands2.size() == 0 )
- {
- return;
- }
-
- bool success = false;
- std::multimap > > mapVolumeMeshes;
- for (const shared_ptr >&meshset2 : operands2)
- {
-#ifdef _ORDER_CSG_BY_VOLUME
- double volume = MeshOps::computeMeshsetVolume(meshset2.get());
- mapVolumeMeshes.insert({ volume, meshset2 });
- }
-
- for (auto it : mapVolumeMeshes)
- {
- const shared_ptr >& meshset2 = it.second;
-#endif
- bool normalizeCoords = true;
- bool allowDegenerateEdges = false;
- shared_ptr > result;
- success = computeCSG_Carve(op1, meshset2, operation, result, params, normalizeCoords, allowDegenerateEdges);
-
- if( success )
- {
- if( operation == carve::csg::CSG::A_MINUS_B || operation == carve::csg::CSG::UNION )
- {
- op1 = result;
- }
-
- continue;
- }
-
- allowDegenerateEdges = true;
- success = computeCSG_Carve(op1, meshset2, operation, result, params, normalizeCoords, allowDegenerateEdges);
- if( success )
- {
- if (operation == carve::csg::CSG::A_MINUS_B || operation == carve::csg::CSG::UNION)
- {
- op1 = result;
- }
- continue;
- }
-
- normalizeCoords = false;
- allowDegenerateEdges = false;
- success = computeCSG_Carve(op1, meshset2, operation, result, params, normalizeCoords, allowDegenerateEdges);
- if (success)
- {
- if (operation == carve::csg::CSG::A_MINUS_B || operation == carve::csg::CSG::UNION)
- {
-#ifdef CSG_DEBUG
- std::cout << "csg success with !normalizeCoords" << std::endl;
-#endif
- op1 = result;
- }
- continue;
- }
-
- allowDegenerateEdges = true;
- success = computeCSG_Carve(op1, meshset2, operation, result, params, normalizeCoords, allowDegenerateEdges);
- if (success)
- {
- if (operation == carve::csg::CSG::A_MINUS_B || operation == carve::csg::CSG::UNION)
- {
-#ifdef CSG_DEBUG
- std::cout << "csg success with !normalizeCoords" << std::endl;
-#endif
- op1 = result;
- }
- continue;
- }
-
- }
- }
+ static bool computeCSG_OCC(const shared_ptr >& inputA, const shared_ptr >& inputB, const carve::csg::CSG::OP operation, shared_ptr >& result,
+ GeomProcessingParams& params, CsgOperationParams& csgParams);
static void handleInnerOuterMeshesInOperands(shared_ptr >& op1, shared_ptr >& op2, shared_ptr >& result,
- GeomProcessingParams& params, bool& boolOpDone, double eps)
- {
- // if op1 consists of > 1 meshes, and one is completely inside the other, then all inner meshes need to be merged with op2, then op2 subtracted from the op1 outer mesh
- // if op1 consists of > 1 meshes, and they are apart from each other, then regular csg.compute works fine
-
- std::vector*> innerMeshes;
- std::vector*> outerMeshes;
-
- for (size_t iiMesh = 0; iiMesh < op1->meshes.size(); ++iiMesh)
- {
- carve::mesh::Mesh<3>* mesh1 = op1->meshes[iiMesh];
-
- if (mesh1->is_inner_mesh && iiMesh > 0)
- {
- innerMeshes.push_back(mesh1);
- }
- else
- {
- outerMeshes.push_back(mesh1);
- }
- }
-
- shared_ptr > resultClassified;
- if (outerMeshes.size() > 1)
- {
- // check if some outer meshes are in fact inner meshes
- if (innerMeshes.size() == 0)
- {
- MeshOps::classifyMeshesInside(outerMeshes, resultClassified, params);
-
- innerMeshes.clear();
- outerMeshes.clear();
- if (resultClassified)
- {
- for (size_t iiMesh = 0; iiMesh < resultClassified->meshes.size(); ++iiMesh)
- {
- carve::mesh::Mesh<3>* mesh1 = resultClassified->meshes[iiMesh];
-
- if (mesh1->is_inner_mesh && iiMesh > 0)
- {
- innerMeshes.push_back(mesh1);
- }
- else
- {
- outerMeshes.push_back(mesh1);
- }
- }
- }
- }
- }
-
- shared_ptr > op1OuterMeshset;
- mergeMeshesToMeshset(outerMeshes, op1OuterMeshset, params);
-
- shared_ptr > op2Meshset(op2->clone());
- mergeMeshesToMeshset(innerMeshes, op2Meshset, params);
-
-#ifdef CSG_DEBUG
- if (csg_compute_count >= 9)
- {
- GeomDebugDump::moveOffset(0.4);
- //operandA_dumped = false;
- //operandB_dumped = false;
- //dumpOperands(op1OuterMeshset, op2Meshset, result, tag, operandA_dumped, operandB_dumped, dumpColorSettings, paramsScaled);
- }
- GeomDebugDump::DumpSettingsStruct dumpColorSettings;
- if (csg_compute_count == 12 && false)
- {
- GeomDebugDump::moveOffset(0.2);
- dumpWithLabel("computeCSG::op1", op1OuterMeshset, dumpColorSettings, params, true, false);
- dumpWithLabel("computeCSG::op2", op2, dumpColorSettings, params, false, false);
- }
-#endif
-
- MeshSetInfo infoOuterMesh(params.callbackFunc, params.ifc_entity);
- MeshSetInfo infoInnerMesh(params.callbackFunc, params.ifc_entity);
- bool outerMesh_valid = MeshOps::checkMeshSetValidAndClosed(op1OuterMeshset, infoOuterMesh, params);
- bool innerMesh_valid = MeshOps::checkMeshSetValidAndClosed(op2Meshset, infoInnerMesh, params);
-
- if (outerMesh_valid && innerMesh_valid)
- {
- carve::csg::CSG csg(eps);
- shared_ptr > resultMerge(csg.compute(op1OuterMeshset.get(), op2Meshset.get(), carve::csg::CSG::A_MINUS_B, nullptr, carve::csg::CSG::CLASSIFY_EDGE));
- if (resultMerge)
- {
- result = resultMerge;
- MeshSetInfo infoResult(params.callbackFunc, params.ifc_entity);
- params.allowDegenerateEdges = true;
- bool resultValid = MeshOps::checkMeshSetValidAndClosed(result, infoResult, params);
- if (resultValid)
- {
- boolOpDone = true;
- }
- }
- }
- }
+ GeomProcessingParams& params, bool& boolOpDone, double eps);
};
diff --git a/IfcPlusPlus/src/ifcpp/geometry/Carve2OpenCascade.h b/IfcPlusPlus/src/ifcpp/geometry/Carve2OpenCascade.h
new file mode 100644
index 000000000..8211d6e9c
--- /dev/null
+++ b/IfcPlusPlus/src/ifcpp/geometry/Carve2OpenCascade.h
@@ -0,0 +1,504 @@
+#pragma once
+
+#ifdef CSG_OCC
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class Carve2OpenCascade
+{
+public:
+ static void convertOCC2Carve(const TopoDS_Shape& input, shared_ptr >& result)
+ {
+ if (input.IsNull())
+ {
+ return;
+ }
+ double eps = 1.1e-6;
+ PolyInputCache3D poly(eps);
+
+ TopAbs_ShapeEnum shape_type = input.ShapeType();
+
+ if (shape_type == TopAbs_SOLID || shape_type == TopAbs_COMPOUND)
+ {
+ //shared_ptr upperLoop = make_shared