From edca2c9b1ac64b4e049a6fb30c6cca984749f224 Mon Sep 17 00:00:00 2001 From: ifcquery Date: Wed, 20 Mar 2024 17:01:38 +0700 Subject: [PATCH] improve geometry processing --- .gitignore | 2 + IfcPlusPlus/CMakeLists.txt | 1 + IfcPlusPlus/IfcPlusPlus.vcxproj | 6 +- IfcPlusPlus/IfcPlusPlus.vcxproj.filters | 6 + .../external/Carve/src/include/carve/aabb.hpp | 1 + .../Carve/src/include/carve/aabb_impl.hpp | 36 + .../Carve/src/include/carve/carve.hpp | 5 + .../Carve/src/include/carve/geom3d.hpp | 13 +- .../Carve/src/include/carve/geom_impl.hpp | 2 +- .../external/Carve/src/include/carve/mesh.hpp | 64 +- .../Carve/src/include/carve/mesh_impl.hpp | 155 +- .../Carve/src/include/carve/rtree.hpp | 6 + .../src/external/Carve/src/lib/carve.cpp | 38 +- .../src/external/Carve/src/lib/mesh.cpp | 269 +- .../src/ifcpp/geometry/CSG_Adapter.cpp | 923 ++++ IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.h | 734 +--- .../src/ifcpp/geometry/Carve2OpenCascade.h | 504 +++ IfcPlusPlus/src/ifcpp/geometry/ConverterOSG.h | 1 + .../src/ifcpp/geometry/CurveConverter.h | 95 +- .../src/ifcpp/geometry/EdgeLoopFinder.h | 967 +++++ .../src/ifcpp/geometry/FaceConverter.h | 71 +- .../src/ifcpp/geometry/GeomDebugDump.h | 35 +- IfcPlusPlus/src/ifcpp/geometry/GeomUtils.h | 236 +- .../src/ifcpp/geometry/GeometryConverter.h | 246 +- .../src/ifcpp/geometry/GeometryException.h | 92 +- .../src/ifcpp/geometry/GeometryInputData.cpp | 55 +- .../src/ifcpp/geometry/GeometryInputData.h | 384 +- .../src/ifcpp/geometry/GeometrySettings.h | 4 +- .../src/ifcpp/geometry/MeshFlattener.h | 947 +++++ .../src/ifcpp/geometry/MeshOps - Copy.cpp | 3734 +++++++++++++++++ IfcPlusPlus/src/ifcpp/geometry/MeshOps.cpp | 839 ++-- IfcPlusPlus/src/ifcpp/geometry/MeshOps.h | 7 +- .../src/ifcpp/geometry/MeshSimplifier.cpp | 1268 ++++++ .../src/ifcpp/geometry/MeshSimplifier.h | 1240 +----- .../src/ifcpp/geometry/PlacementConverter.h | 19 + .../ifcpp/geometry/RepresentationConverter.h | 82 +- .../src/ifcpp/geometry/SceneGraphUtils.h | 1508 +++---- .../src/ifcpp/geometry/SolidModelConverter.h | 92 +- .../src/ifcpp/geometry/StylesConverter.h | 1699 ++++---- .../src/ifcpp/model/AttributeObject.cpp | 18 +- IfcPlusPlus/src/ifcpp/model/AttributeObject.h | 202 +- IfcPlusPlus/src/ifcpp/model/BasicTypes.h | 174 +- .../src/ifcpp/model/BuildingException.h | 152 +- IfcPlusPlus/src/ifcpp/model/BuildingGuid.h | 82 +- IfcPlusPlus/src/ifcpp/model/BuildingModel.cpp | 1508 +++---- IfcPlusPlus/src/ifcpp/model/BuildingModel.h | 216 +- IfcPlusPlus/src/ifcpp/model/BuildingObject.h | 154 +- IfcPlusPlus/src/ifcpp/model/GlobalDefines.h | 82 +- IfcPlusPlus/src/ifcpp/model/StatusCallback.h | 416 +- IfcPlusPlus/src/ifcpp/model/UnitConverter.cpp | 990 ++--- IfcPlusPlus/src/ifcpp/model/UnitConverter.h | 176 +- .../src/ifcpp/model/UnknownEntityException.h | 60 +- IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.cpp | 2 +- IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.h | 80 +- IfcPlusPlus/src/ifcpp/reader/ReaderUtil.cpp | 3094 +++++++------- IfcPlusPlus/src/ifcpp/reader/ReaderUtil.h | 1530 +++---- IfcPlusPlus/src/ifcpp/writer/WriterSTEP.cpp | 216 +- IfcPlusPlus/src/ifcpp/writer/WriterSTEP.h | 62 +- IfcPlusPlus/src/ifcpp/writer/WriterUtil.cpp | 624 +-- IfcPlusPlus/src/ifcpp/writer/WriterUtil.h | 470 +-- .../Resources/styles.css | 4 +- .../SimpleViewerExampleQt.sln | 10 +- .../SimpleViewerExampleQt.vcxproj | 162 +- .../SimpleViewerExampleQt.vcxproj.user | 21 +- .../src/gui/OpenFileWidget.cpp | 31 +- .../src/gui/OpenFileWidget.h | 1 + 66 files changed, 17116 insertions(+), 9807 deletions(-) create mode 100644 IfcPlusPlus/src/ifcpp/geometry/CSG_Adapter.cpp create mode 100644 IfcPlusPlus/src/ifcpp/geometry/Carve2OpenCascade.h create mode 100644 IfcPlusPlus/src/ifcpp/geometry/EdgeLoopFinder.h create mode 100644 IfcPlusPlus/src/ifcpp/geometry/MeshFlattener.h create mode 100644 IfcPlusPlus/src/ifcpp/geometry/MeshOps - Copy.cpp create mode 100644 IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.cpp 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(); + //shared_ptr lowerLoop = make_shared(); + + + //shared_ptr shell = make_shared(); + //ifcBrep->m_Outer = shell; + + //std::unordered_map > mapIfcPoints; + + Standard_Real linear_tolerance = 0.01;// 0.06 * 0.001; // for [m] + Standard_Real angular_tolerance = 0.5; + bool is_relative = false; + BRepMesh_IncrementalMesh incremental_mesh(input, linear_tolerance, is_relative, angular_tolerance); + + TopExp_Explorer shape_explorer(input, TopAbs_FACE); + for (; shape_explorer.More(); shape_explorer.Next()) + { + const TopoDS_Face& face = TopoDS::Face(shape_explorer.Current()); + TopLoc_Location L = TopLoc_Location(); + const Handle(Poly_Triangulation)& poly_triangulation = BRep_Tool::Triangulation(face, L); + + if (poly_triangulation.IsNull()) + { + continue; + } + + const gp_Trsf& face_trsf = L.Transformation(); + + + //! Returns the number of nodes for this triangulation. + Standard_Integer numNodes = poly_triangulation->NbNodes(); + + //! Returns the number of triangles for this triangulation. + Standard_Integer numTriangles = poly_triangulation->NbTriangles(); + + //size_t offset_vertex_storage = vertices_tri.size(); + + //! Returns a node at the given index. + //! @param[in] theIndex node index within [1, NbNodes()] range + //! @return 3D point coordinates + for (size_t ii = 1; ii <= numTriangles; ++ii) + { + const Poly_Triangle& triangle = poly_triangulation->Triangle(ii); + + Standard_Integer theN1, theN2, theN3; + triangle.Get(theN1, theN2, theN3); + gp_Pnt point0 = poly_triangulation->Node(theN1); + gp_Pnt point1 = poly_triangulation->Node(theN2); + gp_Pnt point2 = poly_triangulation->Node(theN3); + + vec3 p0 = carve::geom::VECTOR(point0.X(), point0.Y(), point0.Z()); + vec3 p1 = carve::geom::VECTOR(point1.X(), point1.Y(), point1.Z()); + vec3 p2 = carve::geom::VECTOR(point2.X(), point2.Y(), point2.Z()); + + int idx0 = poly.addPoint(p0); + int idx1 = poly.addPoint(p1); + int idx2 = poly.addPoint(p2); + poly.m_poly_data->addFace(idx0, idx1, idx2); + + //shared_ptr ifcPoint0, ifcPoint1, ifcPoint2; + + //getOrCreateIfcPoint(mapIfcPoints, idx0, p0, ifcPoint0); + //getOrCreateIfcPoint(mapIfcPoints, idx1, p1, ifcPoint1); + //getOrCreateIfcPoint(mapIfcPoints, idx2, p2, ifcPoint2); + + //shared_ptr faceBound = make_shared(); + + //shared_ptr polyLoop = make_shared(); + //polyLoop->m_Polygon.push_back(ifcPoint0); + //polyLoop->m_Polygon.push_back(ifcPoint1); + //polyLoop->m_Polygon.push_back(ifcPoint2); + + //faceBound->m_Bound = polyLoop; + //faceBound->m_Orientation = make_shared(true); + + //shared_ptr ifcFace = make_shared(); + + //ifcFace->m_Bounds.push_back(faceBound); + //shell->m_CfsFaces.push_back(ifcFace); + } + } + + //m_ifcGeometricItem = ifcBrep; + } + else if (shape_type == TopAbs_WIRE || shape_type == TopAbs_EDGE || shape_type == TopAbs_VERTEX) + { + TopExp_Explorer Ex; + for (Ex.Init(input, TopAbs_EDGE); Ex.More(); Ex.Next()) + { + TopoDS_Edge edge = TopoDS::Edge(Ex.Current()); + //getEdgePoints(edge, vertices_lines, render_options); + } + } + + + if (input.ShapeType() == TopAbs_COMPOUND) + { + for (TopoDS_Iterator iter(input); iter.More(); iter.Next()) + { + } + + + //Standard_Real aDeflection = 0.1; + //BRepMesh_IncrementalMesh(input, 1); + //Standard_Integer aIndex = 1, nbNodes = 0; + //TColgp_SequenceOfPnt aPoints, aPoints1; + //for (TopExp_Explorer aExpFace(input, TopAbs_FACE); aExpFace.More(); aExpFace.Next()) + //{ + // TopoDS_Face aFace = TopoDS::Face(aExpFace.Current()); + // TopAbs_Orientation faceOrientation = aFace.Orientation(); + + + // TopLoc_Location aLocation; + // Handle(Poly_Triangulation) aTr = BRep_Tool::Triangulation(aFace, aLocation); + + + // if (!aTr.IsNull()) + // { + // const TColgp_Array1OfPnt& aNodes = aTr->Nodes(); + // const Poly_Array1OfTriangle& triangles = aTr->Triangles(); + // const TColgp_Array1OfPnt2d& uvNodes = aTr->UVNodes(); + + + // TColgp_Array1OfPnt aPoints(1, aNodes.Length()); + // for (Standard_Integer i = 1; i < aNodes.Length() + 1; i++) + // aPoints(i) = aNodes(i).Transformed(aLocation); + + + // Standard_Integer nnn = aTr->NbTriangles(); + // Standard_Integer nt, n1, n2, n3; + + + // for (nt = 1; nt < nnn + 1; nt++) + // { + // triangles(nt).Get(n1, n2, n3); + // gp_Pnt aPnt1 = aPoints(n1); + // gp_Pnt aPnt2 = aPoints(n2); + // gp_Pnt aPnt3 = aPoints(n3); + + + // gp_Pnt2d uv1 = uvNodes(n1); + // gp_Pnt2d uv2 = uvNodes(n2); + // gp_Pnt2d uv3 = uvNodes(n3); + + + // vec3 p1, p2, p3; + // if (faceOrientation == TopAbs_Orientation::TopAbs_FORWARD) + // { + // p1 = carve::geom::VECTOR(aPnt1.X(), aPnt1.Y(), aPnt1.Z()); + // p2 = carve::geom::VECTOR(aPnt2.X(), aPnt2.Y(), aPnt2.Z()); + // p3 = carve::geom::VECTOR(aPnt3.X(), aPnt3.Y(), aPnt3.Z()); + // } + // else + // { + // p1 = carve::geom::VECTOR(aPnt3.X(), aPnt3.Y(), aPnt3.Z()); + // p2 = carve::geom::VECTOR(aPnt2.X(), aPnt2.Y(), aPnt2.Z()); + // p3 = carve::geom::VECTOR(aPnt1.X(), aPnt1.Y(), aPnt1.Z()); + // } + + + + // vertices.push_back(p1); + // vertices.push_back(p2); + // vertices.push_back(p3); + + + // QVector3D dir1 = p2 - p1; + // QVector3D dir2 = p3 - p1; + // QVector3D normal = QVector3D::crossProduct(dir1, dir2); + + + // normals.push_back(normal); + // normals.push_back(normal); + // normals.push_back(normal); + + + // uvs2.push_back(QVector2D(uv1.X(), uv1.Y())); + // uvs2.push_back(QVector2D(uv2.X(), uv2.Y())); + // uvs2.push_back(QVector2D(uv3.X(), uv3.Y())); + + + // indices.push_back(idxCounter++); + // indices.push_back(idxCounter++); + // indices.push_back(idxCounter++); + // } + // } + //} + + // resultShapes.push_back(iter.Value()); + } + + std::map mesh_input_options; + shared_ptr > meshset(poly.m_poly_data->createMesh(mesh_input_options, eps)); + + result = meshset; + } + + static void createSolid(const TopTools_ListOfShape& face_list, TopoDS_Shape& result, double epsilon, BuildingEntity* entity) + { + bool valid_shell = false; + + TopTools_ListIteratorOfListOfShape face_iterator; + + BRepOffsetAPI_Sewing builder; + builder.SetTolerance(epsilon); + builder.SetMaxTolerance(epsilon * 10); + builder.SetMinTolerance(epsilon); + for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) + { + builder.Add(face_iterator.Value()); + } + + try + { + builder.Perform(); + result = builder.SewedShape(); + valid_shell = BRepCheck_Analyzer(result).IsValid() != 0; + } + catch (Standard_Failure sf) + { + } + catch (...) + { + } + + if (valid_shell) + { + TopAbs_ShapeEnum shapeType = result.ShapeType(); + if (shapeType == TopAbs_SOLID) + { + return; + } + + for (TopExp_Explorer exp(result, TopAbs_SHELL); exp.More(); exp.Next()) + { + TopoDS_Shape result_shape = exp.Current(); + } + + TopoDS_Shape complete_shape; + for (TopExp_Explorer exp(result, TopAbs_SHELL); exp.More(); exp.Next()) + { + TopoDS_Shape result_shape = exp.Current(); + + try + { + ShapeFix_Solid solid; + solid.LimitTolerance(epsilon); + TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current())); + if (!solid_shape.IsNull()) + { + try + { + BRepClass3d_SolidClassifier classifier(solid_shape); + result_shape = solid_shape; + classifier.PerformInfinitePoint(epsilon); + if (classifier.State() == TopAbs_IN) + { + result.Reverse(); + } + } + catch (Standard_Failure sf) + { + //messageCallback(sf.GetMessageString(), StatusCallback::MESSAGE_TYPE_MINOR_WARNING, "", entity); + } + catch (...) + { + } + } + } + catch (Standard_Failure sf) + { + } + catch (...) + { + } + + if (complete_shape.IsNull()) + { + complete_shape = result_shape; + } + else + { + BRep_Builder brep_builder; + if (complete_shape.ShapeType() != TopAbs_COMPOUND) + { + TopoDS_Compound compound; + brep_builder.MakeCompound(compound); + brep_builder.Add(compound, complete_shape); + complete_shape = compound; + + //messageCallback("Failed to connect faces", StatusCallback::MESSAGE_TYPE_MINOR_WARNING, "", entity); + } + brep_builder.Add(complete_shape, result_shape); + } + } + + if (!complete_shape.IsNull()) + { + result = complete_shape; + } + } + else + { + //messageCallback("Failed to connect faces", StatusCallback::MESSAGE_TYPE_MINOR_WARNING, "", entity); + } + } + + static void convertCarve2OCC(const shared_ptr >& input, TopoDS_Shape& result, double epsilon) + { + for (const carve::mesh::Mesh<3>*mesh : input->meshes) + { + bool closedShell = mesh->isClosed(); + BRepBuilderAPI_MakeShell makeOpenShell; + + TopTools_ListOfShape list_of_shapes; + + for (const carve::mesh::Face<3>*face : mesh->faces) + { + + + TopoDS_Vertex previousVertex; + BRepBuilderAPI_MakeWire mk_wire; + + carve::mesh::Edge<3>* edge = face->edge; + for (size_t ii = 0; ii < face->n_edges; ++ii) + { + vec3& point1 = edge->v1()->v; + vec3& point2 = edge->v2()->v; + if (previousVertex.IsNull()) + { + previousVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(point1.x, point1.y, point1.z)); + } + + try + { + TopoDS_Vertex currentVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(point2.x, point2.y, point2.z)); + mk_wire.Add(BRepBuilderAPI_MakeEdge(previousVertex, currentVertex)); + + previousVertex = currentVertex; + edge = edge->next; + } + catch (...) + { + } + } + mk_wire.Build(); + TopoDS_Wire wire = mk_wire.Wire(); + + TopoDS_Face faceOCC = BRepBuilderAPI_MakeFace(wire); + if (!faceOCC.IsNull()) + { + list_of_shapes.Append(faceOCC); + } + } + + if (closedShell) + { + //BRepBuilderAPI_MakeSolid makeSolid; + + TopoDS_Shape shape; + createSolid(list_of_shapes, shape, epsilon, nullptr); + + if (!shape.IsNull()) + { + result = shape; + return; + } + } + + try + { + TopoDS_Shell shell; + BRep_Builder builder; + builder.MakeShell(shell); + + TopTools_ListIteratorOfListOfShape face_iterator; + for (face_iterator.Initialize(list_of_shapes); face_iterator.More(); face_iterator.Next()) + { + builder.Add(shell, face_iterator.Value()); + } + if (!shell.IsNull()) + { + result = shell; + } + } + catch (Standard_Failure sf) + { + std::cout << sf.GetMessageString() << std::endl; + } + catch (...) + { + std::cout << __FUNC__ << " failed" << std::endl; + } + } + } + + static bool boolOpOCC(const TopoDS_Shape& operandA, const TopoDS_Shape& operandB, carve::csg::CSG::OP op, TopoDS_Shape& resultShape) + { + //TopoDS_Solid solidPos, solidNeg; + //TopoDS_Shape resultShape; + + //resultShapes.clear(); + //if (!Aux_MakeTwoHalfSpaceByFace(trimFaceTool, solidPos, solidNeg)) + // return false; + try + { + if (op == carve::csg::CSG::A_MINUS_B) + { + resultShape = BRepAlgoAPI_Cut(operandA, operandB); + //resultShapes.push_back(resultShape); + return true; + } + + if (op == carve::csg::CSG::UNION) + { + resultShape = BRepAlgoAPI_Fuse(operandA, operandB); + //resultShapes.push_back(resultShape); + return true; + } + + if (op == carve::csg::CSG::INTERSECTION) + { + resultShape = BRepAlgoAPI_Section(operandA, operandB); + //resultShapes.push_back(resultShape); + return true; + } + + if (op == carve::csg::CSG::B_MINUS_A) + { + resultShape = BRepAlgoAPI_Cut(operandB, operandA); + //resultShapes.push_back(resultShape); + return true; + } + + if (resultShape.ShapeType() == TopAbs_COMPOUND) + { + //for (TopoDS_Iterator iter(resultShape); iter.More(); iter.Next()) + // resultShapes.push_back(iter.Value()); + } + //else + // resultShapes.push_back(resultShape); + //resultShape = BRepAlgoAPI_Cut(shellSolid, solidNeg); + //if (resultShape.ShapeType() == TopAbs_COMPOUND) + //{ + // for (TopoDS_Iterator iter(resultShape); iter.More(); iter.Next()) + // resultShapes.push_back(iter.Value()); + //} + //else + // resultShapes.push_back(resultShape); + } + catch (...) + { + //resultShapes.clear(); + return false; + } + + return false; + } +}; +#endif \ No newline at end of file diff --git a/IfcPlusPlus/src/ifcpp/geometry/ConverterOSG.h b/IfcPlusPlus/src/ifcpp/geometry/ConverterOSG.h index 654398b13..3595ea171 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/ConverterOSG.h +++ b/IfcPlusPlus/src/ifcpp/geometry/ConverterOSG.h @@ -39,6 +39,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include #include +#include #include #include "GeometryInputData.h" #include "IncludeCarveHeaders.h" diff --git a/IfcPlusPlus/src/ifcpp/geometry/CurveConverter.h b/IfcPlusPlus/src/ifcpp/geometry/CurveConverter.h index d1549abfd..2d61cd1dd 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/CurveConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/CurveConverter.h @@ -460,32 +460,32 @@ class CurveConverter : public StatusCallback getTrimAngle(trim2_vec, circleCenter, maxRadius, endAngle, conicPositionMatrix->m_matrix, circlePositionInverse); computeOpeningAngle(startAngle, endAngle, epsilonMergePoints, senseAgreement, openingAngle); +#if defined( _DEBUG) || defined(_DEBUG_RELEASE) std::optional trimPoint1; std::optional trimPoint2; getTrimPoints(trim1_vec, trim2_vec, conicPositionMatrix->m_matrix, circleRadius, circleRadius2, senseAgreement, trimPoint1, trimPoint2); -#ifdef _DEBUG - if (m_debugDumpGeometry && trimPoint1.has_value()) - { - glm::vec4 color(0.4, 0.6, 0.6, 1.0); - vec3 circleCenter = conicPositionMatrix->m_matrix * carve::geom::VECTOR(0, 0, 0); - std::vector polyline = { trimPoint1.value() , circleCenter}; - GeomDebugDump::dumpPolyline(polyline, color, 3.0, false, false); - GeomDebugDump::dumpCoordinateSystem(conicPositionMatrix->m_matrix, circleRadius, false); - - vec3 circlePoint0 = conicPositionMatrix->m_matrix * carve::geom::VECTOR(circleRadius * cos(startAngle), circleRadius * sin(startAngle), 0); - vec3 circlePoint1 = conicPositionMatrix->m_matrix * carve::geom::VECTOR(circleRadius * cos(startAngle + openingAngle*0.1), circleRadius * sin(startAngle + openingAngle*0.1), 0); - glm::vec4 color3(0.3, 0.4, 0.94, 0.6); - polyline = { circlePoint0 , circlePoint1 }; - GeomDebugDump::dumpPolyline(polyline, color3, 2.0, false, false); - } - - + vec3 trimPoint1RelativeToCircle = circlePositionInverse * trimPoint1.value(); if (trimPoint1.has_value() ) { double distanceTrim1 = PointConverter::trimPointCircleDistance(startAngle, circleRadius, conicPositionMatrix->m_matrix, trimPoint1.value()); if (distanceTrim1 > epsilonMergePoints * 10000 ) { + + { + glm::vec4 color(0.4, 0.6, 0.6, 1.0); + vec3 circleCenter = conicPositionMatrix->m_matrix * carve::geom::VECTOR(0, 0, 0); + std::vector polyline = { trimPoint1.value() , circleCenter }; + GeomDebugDump::dumpPolyline(polyline, color, 3.0, false, false); + GeomDebugDump::dumpCoordinateSystem(conicPositionMatrix->m_matrix, circleRadius, false); + + vec3 circlePoint0 = conicPositionMatrix->m_matrix * carve::geom::VECTOR(circleRadius * cos(startAngle), circleRadius * sin(startAngle), 0); + vec3 circlePoint1 = conicPositionMatrix->m_matrix * carve::geom::VECTOR(circleRadius * cos(startAngle + openingAngle * 0.1), circleRadius * sin(startAngle + openingAngle * 0.1), 0); + glm::vec4 color3(0.3, 0.4, 0.94, 0.6); + polyline = { circlePoint0 , circlePoint1 }; + GeomDebugDump::dumpPolyline(polyline, color3, 2.0, false, false); + } + double startAngle = m_point_converter->getAngleOnCircle(circleCenter, circleRadius, trimPoint1.value(), conicPositionMatrix->m_matrix, circlePositionInverse, epsilonMergePoints); double distanceTrim12 = PointConverter::trimPointCircleDistance(startAngle, circleRadius, conicPositionMatrix->m_matrix, trimPoint2.value()); @@ -582,21 +582,24 @@ class CurveConverter : public StatusCallback return; } shared_ptr ifc_line_direction = line_vec->m_Orientation; - - std::vector >& direction_ratios = ifc_line_direction->m_DirectionRatios; - vec3 line_direction; - if (direction_ratios.size() > 1) + vec3 line_direction = carve::geom::VECTOR(1,0,0); + if (ifc_line_direction) { - if (direction_ratios.size() > 2) - { - line_direction = carve::geom::VECTOR(direction_ratios[0]->m_value, direction_ratios[1]->m_value, direction_ratios[2]->m_value); - } - else + std::vector >& direction_ratios = ifc_line_direction->m_DirectionRatios; + + if (direction_ratios.size() > 1) { - line_direction = carve::geom::VECTOR(direction_ratios[0]->m_value, direction_ratios[1]->m_value, 0); + if (direction_ratios.size() > 2) + { + line_direction = carve::geom::VECTOR(direction_ratios[0]->m_value, direction_ratios[1]->m_value, direction_ratios[2]->m_value); + } + else + { + line_direction = carve::geom::VECTOR(direction_ratios[0]->m_value, direction_ratios[1]->m_value, 0); + } } + line_direction.normalize(); } - line_direction.normalize(); line_end = line_origin + line_direction; // will be overwritten by trimming points in most cases if (line_vec->m_Magnitude) @@ -624,7 +627,7 @@ class CurveConverter : public StatusCallback vec3 closest_point_on_line; GeomUtils::closestPointOnLine(trimPoint, line_origin, line_direction, closest_point_on_line); - if ((closest_point_on_line - trimPoint).length() < epsilonMergePoints *10.0) + if ((closest_point_on_line - trimPoint).length() < epsilonMergePoints * 10.0) { // trimming point is on the line line_origin = trimPoint; @@ -924,20 +927,42 @@ class CurveConverter : public StatusCallback const shared_ptr basisCurve = trimmedCurve->m_BasisCurve; if (basisCurve) { + carve::math::Matrix matrix; + shared_ptr conic = dynamic_pointer_cast(basisCurve); + if (conic) + { + // ENTITY IfcConic ABSTRACT SUPERTYPE OF(ONEOF(IfcCircle, IfcEllipse)) + shared_ptr conicPositionMatrix(new TransformData()); + shared_ptr conic_placement = dynamic_pointer_cast(conic->m_Position); + if (conic_placement) + { + m_placement_converter->convertIfcPlacement(conic_placement, conicPositionMatrix, false); + + carve::math::Matrix circlePositionInverse; + GeomUtils::computeInverse(conicPositionMatrix->m_matrix, circlePositionInverse); + + matrix = circlePositionInverse; + } + } + vec3 p0inCirclePlacement = matrix * p0; + vec3 p1inCirclePlacement = matrix * p1; + // use IfcEdge.EdgeStart and IfcEdge.EdgeEnd as trimming points std::vector > curveTrim1vec; std::vector > curveTrim2vec; shared_ptr trim1(new IfcCartesianPoint()); - trim1->m_Coordinates[0] = p0.x / lengthFactor; // in convertIfcCurve, the trim point will be multiplied with lengthFactor - trim1->m_Coordinates[1] = p0.y / lengthFactor; - trim1->m_Coordinates[2] = p0.z / lengthFactor; + trim1->m_Coordinates[0] = p0inCirclePlacement.x; // in convertIfcCurve, the trim point will be multiplied with lengthFactor + trim1->m_Coordinates[1] = p0inCirclePlacement.y; + trim1->m_Coordinates[2] = p0inCirclePlacement.z; + trim1->m_size = 3; curveTrim1vec.push_back(trim1); shared_ptr trim2(new IfcCartesianPoint()); - trim2->m_Coordinates[0] = p1.x / lengthFactor; - trim2->m_Coordinates[1] = p1.y / lengthFactor; - trim2->m_Coordinates[2] = p1.z / lengthFactor; + trim2->m_Coordinates[0] = p1inCirclePlacement.x; + trim2->m_Coordinates[1] = p1inCirclePlacement.y; + trim2->m_Coordinates[2] = p1inCirclePlacement.z; + trim2->m_size = 3; curveTrim2vec.push_back(trim2); convertIfcCurve(basisCurve, curvePoints, segmentStartPoints, curveTrim1vec, curveTrim2vec, senseAgreement); } diff --git a/IfcPlusPlus/src/ifcpp/geometry/EdgeLoopFinder.h b/IfcPlusPlus/src/ifcpp/geometry/EdgeLoopFinder.h new file mode 100644 index 000000000..d757ce39f --- /dev/null +++ b/IfcPlusPlus/src/ifcpp/geometry/EdgeLoopFinder.h @@ -0,0 +1,967 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct EdgeLoopElement +{ + EdgeLoopElement(carve::mesh::Edge<3>* _edge, bool _sameSense) + { + edge = _edge; + sameSense = _sameSense; + } + carve::mesh::Edge<3>* edge = nullptr; + bool sameSense = true; +}; + +class EdgeLoopFinder +{ +public: + double m_epsMergePoints = 1e-8; + double m_epsMergeOpenEdgesToPoint = 50*1e-8; + bool m_openEdgesMergedToPoint = false; + std::set* > m_setOpenEdges; + std::unordered_map*, std::set* > > m_mapVertexOpenEdges; + std::vector*> > m_closedEdgeLoopsOfOpenEdges; + std::vector* > m_createdEdgesToDelete; + const carve::mesh::Mesh<3>* m_mesh = nullptr; + shared_ptr > m_meshset; + shared_ptr > m_meshsetCopyUnChanged; + //typedef carve::geom::RTreeNode<3, carve::mesh::Vertex<3>*> vertex_rtree_t; + typedef carve::geom::RTreeNode<3, carve::mesh::Face<3>*> face_rtree_t; + + EdgeLoopFinder(double epsMergePoints, double epsMergeOpenEdgesToPoint) + : m_epsMergePoints(epsMergePoints), m_epsMergeOpenEdgesToPoint(epsMergeOpenEdgesToPoint) + { + + } + + ~EdgeLoopFinder() + { + for (carve::mesh::Edge<3>*edge : m_createdEdgesToDelete) + { + delete edge; + } + } + + void createBackup() + { + if (!m_meshsetCopyUnChanged) + { + m_meshsetCopyUnChanged = shared_ptr >(m_meshset->clone()); + } + } + + void initFromMesh(const shared_ptr >& meshset, const carve::mesh::Mesh<3>* mesh, + const GeomProcessingParams& params, bool tryMergeShortOpenEdges, bool& meshsetChanged) + { + if (!mesh) + { + return; + } + meshsetChanged = false; + m_meshset = meshset; + m_mesh = mesh; + + if (mesh->open_edges.size() == 0) + { + return; + } + + std::set* > openEdgeVertices; + std::set openEdgeVertexPoints; + for (auto openEdge : mesh->open_edges) + { + if (openEdge) + { + m_setOpenEdges.insert(openEdge); + if (openEdge->vert) + { + openEdgeVertices.insert(openEdge->v1()); + openEdgeVertices.insert(openEdge->v2()); + openEdgeVertexPoints.insert(openEdge->v1()->v); + openEdgeVertexPoints.insert(openEdge->v2()->v); + + auto itFindVertexMapV1 = m_mapVertexOpenEdges.find(openEdge->v1()); + if (itFindVertexMapV1 == m_mapVertexOpenEdges.end()) + { + m_mapVertexOpenEdges.insert({ openEdge->v1(), {openEdge} }); + } + else + { + itFindVertexMapV1->second.insert(openEdge); + } + + auto itFindVertexMapV2 = m_mapVertexOpenEdges.find(openEdge->v2()); + if (itFindVertexMapV2 == m_mapVertexOpenEdges.end()) + { + m_mapVertexOpenEdges.insert({ openEdge->v2(), {openEdge} }); + } + else + { + itFindVertexMapV2->second.insert(openEdge); + } + } + } + } + + if (tryMergeShortOpenEdges) + { + carve::geom::aabb<3> bbox; + bbox.fit(openEdgeVertexPoints.begin(), openEdgeVertexPoints.end()); + + if (bbox.extent.x < m_epsMergeOpenEdgesToPoint) + { + if (bbox.extent.y < m_epsMergeOpenEdgesToPoint) + { + if (bbox.extent.z < m_epsMergeOpenEdgesToPoint) + { + createBackup(); + for (auto v : openEdgeVertices) + { + v->v = bbox.pos; + } + m_openEdgesMergedToPoint = true; + meshsetChanged = true; + } + } + } + } + + size_t numPointsMoved = 0; + mergeCloseVertices(numPointsMoved); + + if (numPointsMoved > 0 || m_openEdgesMergedToPoint) + { + meshsetChanged = true; + } + } + + bool findNextEdge(const carve::mesh::Vertex<3>* startVertex, carve::mesh::Vertex<3>* currentVertex, carve::mesh::Edge<3>* currentEdge, std::set*>& setCurrentPath, std::vector >& vecLoopEdges) + { + auto itFindVertexMapV1 = m_mapVertexOpenEdges.find(currentVertex); + + std::set* >& setEdgesFromStartVertex = itFindVertexMapV1->second; + + for (carve::mesh::Edge<3>*checkEdge : setEdgesFromStartVertex) + { + if (!checkEdge) + { + continue; + } + + if (!checkEdge->vert) + { + continue; + } + + if (currentEdge == checkEdge) + { + // we don't want to go back + continue; + } + + if (m_setOpenEdges.find(checkEdge) == m_setOpenEdges.end()) + { + continue; + } + if (setCurrentPath.find(checkEdge) != setCurrentPath.end()) + { + continue; + } + + carve::mesh::Vertex<3>* oppositeVertex = nullptr; + bool sameSenseCheckEdge = true; + if (checkEdge->v1() == currentVertex) + { + oppositeVertex = checkEdge->v2(); + } + else if (checkEdge->v2() == currentVertex) + { + oppositeVertex = checkEdge->v2(); + sameSenseCheckEdge = false; + } + else + { + // should not happen + std::cout << "checkEdge->v1() != currentVertex && checkEdge->v2() == currentVertex" << std::endl; + continue; + } + + if (oppositeVertex == startVertex) + { + if (currentEdge == nullptr) + { + continue; + } + else + { + // found closed loop + vecLoopEdges.push_back(make_shared(checkEdge, sameSenseCheckEdge)); + return true; + } + } + + // check if v2 is also in mapVertexOpenEdges + auto itFindV2 = m_mapVertexOpenEdges.find(oppositeVertex); + if (itFindV2 == m_mapVertexOpenEdges.end()) + { + // checkEdge leads somewhere else. It is not part of the set of open edges + continue; + } + + setCurrentPath.insert(checkEdge); + bool found = findNextEdge(startVertex, oppositeVertex, checkEdge, setCurrentPath, vecLoopEdges); + if (found) + { + vecLoopEdges.push_back(make_shared(checkEdge, sameSenseCheckEdge)); + return true; + } + + continue; + + std::set* >& setEdgesFromCurrentVertex = itFindVertexMapV1->second; + for (auto it : setEdgesFromStartVertex) + { + carve::mesh::Edge<3>* edgeFromOppositeVertex = it; + carve::mesh::Vertex<3>* vnext = nullptr; + bool sameSense = true; + if (edgeFromOppositeVertex->v1() == oppositeVertex) + { + carve::mesh::Vertex<3>* vnext = edgeFromOppositeVertex->v2(); + } + else if (edgeFromOppositeVertex->v2() == oppositeVertex) + { + carve::mesh::Vertex<3>* vnext = edgeFromOppositeVertex->v1(); + sameSense = false; + } + + if (vnext == startVertex) + { + if (currentEdge != nullptr) + { + vecLoopEdges.push_back(make_shared(edgeFromOppositeVertex, sameSense)); + return true; + } + } + + if (vnext != nullptr) + { + bool found = findNextEdge(startVertex, oppositeVertex, edgeFromOppositeVertex, setCurrentPath, vecLoopEdges); + if (found) + { + vecLoopEdges.push_back(make_shared(edgeFromOppositeVertex, sameSense)); + //vecLoopEdges.push_back(edgeFromOppositeVertex); + } + } + } + } + + if (currentEdge == nullptr) + { + // start + + } + return false; + } + + bool findAndEliminateShortOpenEdges( double epsMinDistanceInEachAxisDirection) + { + bool changesDone = false; + + for (auto openEdge : m_mesh->open_edges) + { + vec3 deltaEdge1 = openEdge->v1()->v - openEdge->v2()->v; + if (std::abs(deltaEdge1.x) < epsMinDistanceInEachAxisDirection) + { + if (std::abs(deltaEdge1.y) < epsMinDistanceInEachAxisDirection) + { + if (std::abs(deltaEdge1.z) < epsMinDistanceInEachAxisDirection) + { + // TODO: try to preserve the plane of faces with openings. + // If there are many faces in one plane, and current v1() or v2() is in that plane, move the other one + + // for now, simple solution: + openEdge->v1()->v = openEdge->v2()->v; + changesDone = true; + } + } + } + } + return changesDone; + } + + void traverseNode(face_rtree_t* node) + { + if (node) + { + for (face_rtree_t* childNode = node->child; node; node = node->sibling) + { + //childNode->findPartition() + traverseNode(childNode); + } + } + } + + bool findAndMoveOffsetVertices() + { + +#ifdef _DEBUG + //typedef std::unordered_map*, std::vector*> > face_pairs_t; + + auto itFaceBegin = m_meshset->faceBegin(); + auto itFaceEnd = m_meshset->faceEnd(); + std::unique_ptr vert_rtree(face_rtree_t::construct_STR(itFaceBegin, itFaceEnd, 4, 4)); + + if (vert_rtree->child) + { + traverseNode(vert_rtree.get()); + + //for (vertex_rtree_t* node = vert_rtree->child; node; node = node->sibling) + { + + //generateIntersectionCandidates(a, node, b, b_node, face_pairs, false); + } + } +#endif + + + + bool changesDone = false; + //typedef carve::geom::RTreeNode<3, carve::mesh::Face<3>*> face_rtree_t; + //typedef std::unordered_map*, std::vector*> > face_pairs_t; + + //auto itFaceBegin = m_meshset->faceBegin(); + //auto itFaceEnd = m_meshset->faceEnd(); + //std::unique_ptr a_rtree(face_rtree_t::construct_STR(itFaceBegin, itFaceEnd, 4, 4)); + + //if (!a_rtree->bbox.intersects(b_node->bbox, m_epsilon)) + //{ + // return; + //} + + //if (a_rtree->child )// && (descend_a || !b_node->child)) + //{ + // for (face_rtree_t* node = a_rtree->child; node; node = node->sibling) + // { + // generateIntersectionCandidates(a, node, b, b_node, face_pairs, false); + // } + //} + + return changesDone; + } + + bool findAndEliminateSimpleLoops1(const GeomProcessingParams& params) + { + bool changesDone = false; + + // case 1: + // openEdge1 + // startVertex o --------------------> simpleLoopEndVertex + // <-------------------- + // openEdge2 + +#ifdef _DEBUG + GeomDebugDump::clearMeshsetDump(); + carve::geom::aabb<3> bbox; + std::vector points; + glm::vec4 color(1, 0.5, 1, 1); + GeomDebugDump::dumpMeshset(m_meshset, color, true, true); + for (auto openEdge : m_mesh->open_edges) + { + vec3 deltaEdge1 = openEdge->v1()->v - openEdge->v2()->v; + points.push_back(openEdge->v1()->v); + points.push_back(openEdge->v2()->v); + + GeomDebugDump::dumpPolyline({ openEdge->v1()->v, openEdge->v2()->v }, color, 1, true, false); + GeomDebugDump::moveOffset(0.01); + } + bbox.fit(points.begin(), points.end()); + GeomDebugDump::moveOffset(bbox.extent.y*2.2 + 0.1); +#endif + + for (auto itFindVertexMapV1 = m_mapVertexOpenEdges.begin(); itFindVertexMapV1 != m_mapVertexOpenEdges.end(); ++itFindVertexMapV1) + { + // try to find 2 matching edges + carve::mesh::Vertex<3>* startVertex = itFindVertexMapV1->first; + carve::mesh::Edge<3>* openEdge1 = nullptr; + carve::mesh::Edge<3>* openEdge2 = nullptr; + + std::set* >& openEdgesFromAndToStartVertex = itFindVertexMapV1->second; + + for (carve::mesh::Edge<3>* openEdge1 : openEdgesFromAndToStartVertex) + { + vec3 deltaEdge1 = openEdge1->v1()->v - openEdge1->v2()->v; + if (deltaEdge1.length2() < m_epsMergePoints * m_epsMergePoints * 10) + { + continue; + } + carve::mesh::Vertex<3>* simpleLoopEndVertex = nullptr; + if (openEdge1->v1() == startVertex) + { + simpleLoopEndVertex = openEdge1->v2(); + } + else if (openEdge1->v2() == startVertex) + { + simpleLoopEndVertex = openEdge1->v1(); + } + else + { +#ifdef _DEBUG + std::cout << "edge in m_mapVertexOpenEdges not pointing to vertex" << std::endl; +#endif + } + +#ifdef _DEBUG + glm::vec4 color(1, 0.5, 1, 1); + + GeomDebugDump::dumpPolyline({ openEdge1->v1()->v, openEdge1->v2()->v }, color, 1, true, false); + GeomDebugDump::moveOffset(0.01); +#endif + + bool edgeIsReversed = false; + std::set*, carve::mesh::Vertex<3>*>> closeButNotIdinticalVertices; + openEdge2 = findEdgeByStartAndEndPoint(startVertex, simpleLoopEndVertex, openEdge1, edgeIsReversed, m_epsMergePoints, closeButNotIdinticalVertices); + if (!openEdge2) + { + // try with bigger epsilon + openEdge2 = findEdgeByStartAndEndPoint(startVertex, simpleLoopEndVertex, openEdge1, edgeIsReversed, m_epsMergePoints * 10, closeButNotIdinticalVertices); + } + + if (openEdge2) + { + if (closeButNotIdinticalVertices.size() > 0) + { + createBackup(); + auto pair = closeButNotIdinticalVertices.begin(); + carve::mesh::Vertex<3>* v1 = pair->first; + carve::mesh::Vertex<3>* v2 = pair->second; + v1->v = v2->v; + changesDone = true; + } + + + // v1 o --------------------> o v2 + // v2 <-------------------- v1 + // + // v1 o --------------------> o v2 + // v1 ---------------------> v2 + // openEdge2 + + + + + //bool connected1 = openEdge1->v1() == openEdge2->v2() && openEdge1->v2() == openEdge2->v1(); + //bool connected2 = openEdge1->v1() == openEdge2->v1() && openEdge1->v2() == openEdge2->v2(); // not a correct half-edge + + vec3 delta1 = openEdge1->v1()->v - openEdge2->v2()->v; + bool connected1 = false; + if (delta1.length2() < m_epsMergePoints * m_epsMergeOpenEdgesToPoint * 10) + { + vec3 delta2 = openEdge1->v2()->v - openEdge2->v1()->v; + if (delta2.length2() < m_epsMergePoints * m_epsMergeOpenEdgesToPoint * 10) + { + if (delta2.length2() > EPS_M16) + { + openEdge1->v2()->v = openEdge2->v1()->v; + } + connected1 = true; + } + } + + if(connected1) + { + createBackup(); + bool check = openEdge2->vert == openEdge1->v2(); + openEdge2->rev = openEdge1; + openEdge1->rev = openEdge2; + changesDone = true; + } + else + { + // check if connected reversed + delta1 = openEdge1->v1()->v - openEdge2->v1()->v; + bool connected2 = false; + if (delta1.length2() < m_epsMergePoints * m_epsMergeOpenEdgesToPoint * 10) + { + vec3 delta2 = openEdge1->v2()->v - openEdge2->v2()->v; + if (delta2.length2() < m_epsMergePoints * m_epsMergeOpenEdgesToPoint * 10) + { + connected2 = true; + } + } + + if (connected2) + { + createBackup(); + + // v1 o --------------------> o v2 + // ---> v1 ---------------------> v2 ------> + // prev next + + if (openEdge2->next) + { + openEdge2->next->vert = openEdge1->v2(); + } + openEdge2->vert = openEdge1->v1(); + openEdge2->rev = openEdge1; + openEdge1->rev = openEdge2; + changesDone = true; + } + } + + //if (openEdge1->vert == openEdge2->v2()) + //{ + // createBackup(); + // openEdge2->vert = openEdge1->v2(); + // openEdge2->rev = openEdge1; + // openEdge1->rev = openEdge2; + // changesDone = true; + //} + + //if (!changesDone) + //{ + // + // + // if (edgeIsReversed) + // { + // createBackup(); + // //openEdge1->vert = openEdge1->v2(); + // //openEdge2->vert = openEdge1->v1(); + // openEdge2->rev = openEdge1; + // openEdge1->rev = openEdge2; + // changesDone = true; + // } + // else + // { + // createBackup(); + // // TODO: check which one of the vertices is abandoned + // //openEdge1->vert = openEdge1->v1(); + // //openEdge2->vert = openEdge1->v2(); + // openEdge2->rev = openEdge1; + // openEdge1->rev = openEdge2; + // changesDone = true; + // } + //} + } + } + } + + if (m_meshsetCopyUnChanged) + { + MeshSetInfo infoChangedMesh; + MeshOps::checkMeshSetValidAndClosed(m_meshset, infoChangedMesh, params); + + MeshSetInfo infoBackup; + MeshOps::checkMeshSetValidAndClosed(m_meshsetCopyUnChanged, infoBackup, params); + + if (MeshOps::isBetterForBoolOp(infoBackup, infoChangedMesh, false)) + { + m_meshset = m_meshsetCopyUnChanged; + } + } + + return changesDone; + } + + + bool findAndEliminateSimpleLoops2() + { + bool changesDone = false; + + // case 2: + // openEdge1 + // startVertex o ---------------------------------------> o v2 + // <-------------------o<------------------ + // openEdge3 v3 openEdge2 + return changesDone; + } + + void mergeCloseVertices( size_t& numPointsMoved) + { + numPointsMoved = 0; + if (m_mapVertexOpenEdges.size() > 10000) + { + return; + } + + // find vertices that are close enough to merge + double eps2 = m_epsMergePoints * m_epsMergePoints * 10.0; + size_t numMapEntries = m_mapVertexOpenEdges.size(); + for (auto it1 = m_mapVertexOpenEdges.begin(); it1 != m_mapVertexOpenEdges.end(); ++it1) + { + carve::mesh::Vertex<3>* vertex1 = it1->first; + + for (auto it2 = m_mapVertexOpenEdges.begin(); it2 != m_mapVertexOpenEdges.end(); ++it2) + { + carve::mesh::Vertex<3>* vertex2 = it2->first; + + if (vertex1 == vertex2) + { + continue; + } + vec3 delt = vertex1->v - vertex2->v.v; + double length2 = delt.length2(); + if (length2 > 0 && length2 < eps2) + { + vec3 middlePoint = (vertex1->v + vertex2->v.v)*0.5; + createBackup(); + vertex1->v = middlePoint; + vertex2->v = middlePoint; + ++numPointsMoved; + } + } + } + } + + void findLoops() + { + if (m_mapVertexOpenEdges.size() > 100) + { + return; + } + + // find connected sets of vertices + //std::unordered_map*, std::set* > > mapVertexOpenEdges; + std::vector*> > connectedEdges; + std::set*> setVerticesDone; + std::set*> setEdgesDone; + for (auto it = m_mapVertexOpenEdges.begin(); it != m_mapVertexOpenEdges.end(); ++it) + { + // get all connected vertices + carve::mesh::Vertex<3>* startVertex = it->first; + //if (setVerticesDone.find(startVertex) != setVerticesDone.end()) + //{ + // continue; + //} + //setVerticesDone.insert(startVertex); + + std::set* > currentSetOfConnectedEdges; + + std::set* >& edgesConnectedToStartVertex = it->second; + for (auto it2 = edgesConnectedToStartVertex.begin(); it2 != edgesConnectedToStartVertex.end(); ++it2) + { + carve::mesh::Edge<3>* edgeConnected = *it2; + if (setEdgesDone.find(edgeConnected) != setEdgesDone.end()) + { + continue; + } + setEdgesDone.insert(edgeConnected); + + if (m_setOpenEdges.find(edgeConnected) == m_setOpenEdges.end()) + { + // it's not an open edge + continue; + } + + if (edgeConnected->v1() == startVertex) + { + currentSetOfConnectedEdges.insert(edgeConnected); + } + carve::mesh::Edge<3>* edgeConnectedNext = edgeConnected->next; + if (edgeConnectedNext->face) + { + size_t numEdges = edgeConnectedNext->face->n_edges; + for (size_t jj = 0; jj < numEdges; ++jj) + { + if (m_setOpenEdges.find(edgeConnected) != m_setOpenEdges.end()) + { + // it's an open edge + if (setEdgesDone.find(edgeConnectedNext) != setEdgesDone.end()) + { + continue; + } + setEdgesDone.insert(edgeConnectedNext); + currentSetOfConnectedEdges.insert(edgeConnected); + } + + edgeConnectedNext = edgeConnectedNext->next; + } + } + } + + } + + + + size_t numMapEntries = m_mapVertexOpenEdges.size(); + for (size_t ii = 0; ii < numMapEntries; ++ii) + { + // try to find closed loop of open edges + std::vector > vecLoopEdges; + auto itFindVertexMapV1 = m_mapVertexOpenEdges.begin(); + carve::mesh::Vertex<3>* startVertex = itFindVertexMapV1->first; + carve::mesh::Edge<3>* currentEdge = nullptr; + + std::set*> setCurrentPath; + bool foundLoop = findNextEdge(startVertex, startVertex, currentEdge, setCurrentPath, vecLoopEdges); + + itFindVertexMapV1 = m_mapVertexOpenEdges.erase(itFindVertexMapV1); + + if (foundLoop) + { + m_closedEdgeLoopsOfOpenEdges.push_back(std::vector*>()); + std::vector* >& edgeLoop = *m_closedEdgeLoopsOfOpenEdges.rbegin(); + for (auto it : vecLoopEdges) + { + shared_ptr& loopElement = it; + carve::mesh::Edge<3>* currentEdge = nullptr; + if (loopElement->sameSense) + { + currentEdge = loopElement->edge; + } + else + { + if (loopElement->edge->rev != nullptr) + { + currentEdge = loopElement->edge->rev; + } + else + { + carve::mesh::Vertex<3>* v1 = loopElement->edge->v1(); + carve::mesh::Vertex<3>* v2 = loopElement->edge->v2(); + currentEdge = new carve::mesh::Edge<3>(v1, nullptr); + m_createdEdgesToDelete.push_back(currentEdge); + } + } + + if (currentEdge != nullptr) + { + auto itFindErase = m_mapVertexOpenEdges.find(currentEdge->vert); + if (itFindErase != m_mapVertexOpenEdges.end()) + { + m_mapVertexOpenEdges.erase(itFindErase); + } + edgeLoop.push_back(currentEdge); + } + } + +#ifdef _DEBUG + GeomDebugDump::clearMeshsetDump(); + + glm::vec4 color(1, 0.5, 1, 1); + std::vector openEdgePoints; + std::vector lengthOfEdges; + int edgeCount = 0; + for (auto e : m_mesh->open_edges) + { + vec3 p1Scaled = e->vert->v * 500; + vec3 p2Scaled = e->v2()->v * 500; + openEdgePoints.push_back(p1Scaled); + openEdgePoints.push_back(p2Scaled); + vec3 delta = p1Scaled - p2Scaled; + double lengthDelta = delta.length(); + lengthOfEdges.push_back(lengthDelta); + glm::vec4 color(1, 0.5 + 0.1*edgeCount, 1, 1); + GeomDebugDump::dumpPolyline({ p1Scaled, p2Scaled }, color, 1, false, false); + ++edgeCount; + GeomDebugDump::moveOffset(0.001); + } + GeomDebugDump::moveOffset(0.2); + GeomDebugDump::dumpPolyline(openEdgePoints, color, 1, true, false); + GeomDebugDump::moveOffset(0.08); + + + std::vector vecPointLoop; + for (auto e : edgeLoop) + { + vec3 p1Scaled = e->vert->v*1000; + vec3 p2Scaled = e->v2()->v * 1000; + vecPointLoop.push_back(p1Scaled); + vec3 delta = p1Scaled - p2Scaled; + double lengthDelta = delta.length(); + GeomDebugDump::dumpPolyline({ p1Scaled, p2Scaled }, color, 1, false, false); + GeomDebugDump::moveOffset(0.01); + } + + GeomDebugDump::dumpPolyline(vecPointLoop, color, 1, true, false); +#endif + } + + if (m_mapVertexOpenEdges.size() < 2) + { + break; + } + } + } + + void findEdgesByStartPoint(carve::mesh::Vertex<3>* vertex, std::set* >& result, + carve::mesh::Edge<3>* excludeEdge, double epsMergePoints) + { + for (auto it : m_mapVertexOpenEdges) + { + std::set* >& openEdgesFromAndVertex = it.second; + + for (carve::mesh::Edge<3>*edge : openEdgesFromAndVertex) + { + if (edge == excludeEdge) + { + continue; + } + + carve::mesh::Vertex<3>* vert = edge->v1(); + vec3 delt = vert->v - vertex->v.v; + if (std::abs(delt.x) < epsMergePoints) + { + if (std::abs(delt.y) < epsMergePoints) + { + if (std::abs(delt.z) < epsMergePoints) + { + result.insert(edge); + } + } + } + } + } + } + + //void findEdgesByEndPoint(carve::mesh::Vertex<3>* vertex, std::vector* >& result, + // carve::mesh::Edge<3>* excludeEdge, double epsMergePoints) + //{ + // for (auto it : m_mapVertexOpenEdges) + // { + // carve::mesh::Vertex<3>* vert = it.first; + // vec3 delt = vert->v2() - vertex->v.v; + // if (std::abs(delt.x) < epsMergePoints) + // { + // if (std::abs(delt.y) < epsMergePoints) + // { + // if (std::abs(delt.z) < epsMergePoints) + // { + // std::set* >& openEdgesFromAndVertex = it.second; + + // for (carve::mesh::Edge<3>*edge : openEdgesFromAndVertex) + // { + // if (edge == excludeEdge) + // { + // continue; + // } + + // result.push_back(edge); + // } + // } + // } + // } + // } + //} + + carve::mesh::Edge<3>* findEdgeByStartAndEndPoint(carve::mesh::Vertex<3>* startVertex, carve::mesh::Vertex<3>* endVertex, + carve::mesh::Edge<3>* excludeEdge, bool& edgeIsReversed, double epsMergePoints, + std::set*, carve::mesh::Vertex<3>*>>& closeButNotIdinticalVertices) + { + std::set* > edgesFromStartVertex; + findEdgesByStartPoint(startVertex, edgesFromStartVertex, excludeEdge, epsMergePoints); + + for (carve::mesh::Edge<3>*edge : edgesFromStartVertex) + { + // v1() of edge matches startVertex already + vec3 deltEndPoint = edge->v2()->v - endVertex->v.v; + if (std::abs(deltEndPoint.x) < epsMergePoints) + { + if (std::abs(deltEndPoint.y) < epsMergePoints) + { + if (std::abs(deltEndPoint.z) < epsMergePoints) + { + edgeIsReversed = false; + + if (edge->v2() != endVertex) + { + closeButNotIdinticalVertices.insert({ edge->v2() , endVertex }); + } + return edge; + } + } + } + } + + std::set* > edgesFromEndVertex; + findEdgesByStartPoint(endVertex, edgesFromEndVertex, excludeEdge, epsMergePoints); + + for (carve::mesh::Edge<3>*edge : edgesFromEndVertex) + { + // v1() of edge matches endVertex already +#ifdef _DEBUG + vec3 deltV1_to_EndVertex = edge->v1()->v - endVertex->v.v; + + glm::vec4 color(1, 0.5, 1, 1); + + GeomDebugDump::dumpPolyline({ edge->v1()->v, edge->v2()->v }, color, 1, true, false); + GeomDebugDump::moveOffset(0.01); + +#endif + // now check if edge->v2() matches startVertex + vec3 deltEndPoint = edge->v2()->v - startVertex->v.v; + if (std::abs(deltEndPoint.x) < epsMergePoints) + { + if (std::abs(deltEndPoint.y) < epsMergePoints) + { + if (std::abs(deltEndPoint.z) < epsMergePoints) + { + edgeIsReversed = true; + if (edge->v2() != endVertex) + { + closeButNotIdinticalVertices.insert({ edge->v2() , startVertex }); + } + return edge; + } + } + } + } + + // for (auto it : m_mapVertexOpenEdges) + // { + // carve::mesh::Vertex<3>* vert = it.first; + // vec3 delt = vert->v - startVertex->v.v; + // if (std::abs(delt.x) < epsMergePoints) + // { + // if (std::abs(delt.y) < epsMergePoints) + // { + // if (std::abs(delt.z) < epsMergePoints) + // { + // std::set* >& openEdgesFromAndVertex = it.second; + // + // for (carve::mesh::Edge<3>*edge : openEdgesFromAndVertex) + // { + // if (edge == excludeEdge) + // { + // continue; + // } + // carve::mesh::Vertex<3>* vertOpposite = nullptr; + // if (edge->v1() == vert) // v1() is the start vertex of the edge + // { + // vertOpposite = edge->v2(); + // } + // else if (edge->v2() == vert) + // { + // vertOpposite = edge->v1(); + // } + // else + // { + //#ifdef _DEBUG + // std::cout << "incorrect edge in m_mapVertexOpenEdges" << std::endl; + //#endif + // continue; + // } + // + // vec3 deltEndPoint = vertOpposite->v - endVertex->v.v; + // if (std::abs(deltEndPoint.x) < epsMergePoints) + // { + // if (std::abs(deltEndPoint.y) < epsMergePoints) + // { + // if (std::abs(deltEndPoint.z) < epsMergePoints) + // { + // return edge; + // } + // } + // } + // } + // } + // } + // } + // } + return nullptr; + } +}; diff --git a/IfcPlusPlus/src/ifcpp/geometry/FaceConverter.h b/IfcPlusPlus/src/ifcpp/geometry/FaceConverter.h index 98eb2be0e..1d6ecaae4 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/FaceConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/FaceConverter.h @@ -331,6 +331,24 @@ class FaceConverter : public StatusCallback double eps = m_geom_settings->getEpsilonMergePoints(); PolyInputCache3D poly_cache(eps); GeomProcessingParams params( m_geom_settings, nullptr, this ); + + if (item_data) + { + // some linux debugging + std::string tagString = ""; + if(!item_data->m_ifc_representation.expired() ) + { + shared_ptr representation( item_data->m_ifc_representation ); + tagString = ", representation tag: " + std::to_string(representation->m_tag); + } + if (!item_data->m_product.expired()) + { + shared_ptr shapeData(item_data->m_product); + tagString = ", product guid: " + shapeData->m_entity_guid; + } + + printToDebugLog(__FUNC__, "num faces: " + std::to_string(vec_faces.size()) + tagString); + } for( size_t ii = 0; ii < vec_faces.size(); ++ii ) { @@ -369,6 +387,14 @@ class FaceConverter : public StatusCallback face_loops.push_back( std::vector() ); std::vector& loop_points = face_loops.back(); + + if (ii > 23) + { + if (IsPrintToDebugLogOn()) + { + printToDebugLog(__FUNC__, "convertIfcLoop for face " + std::to_string(ii) + " of " + std::to_string(vec_faces.size())); + } + } m_curve_converter->convertIfcLoop( loop, loop_points ); if( loop_points.size() < 3 ) @@ -403,20 +429,20 @@ class FaceConverter : public StatusCallback createTriangulated3DFace( face_loops, poly_cache, params ); #ifdef _DEBUG - if( ifc_face->m_tag == 1067431) + if( ifc_face->m_tag == 279929) { params.debugDump = true; } shared_ptr advancedFace = dynamic_pointer_cast( ifc_face ); - if( params.debugDump || advancedFace ) + if( params.debugDump )//|| advancedFace ) { glm::vec4 color(0.5, 0.6, 0.7, 1.0); for( size_t iiLoop = 0; iiLoop < face_loops.size(); ++iiLoop ) { std::vector& loop = face_loops[iiLoop]; - //GeomDebugDump::dumpPolyline(loop, color, 0, false); + GeomDebugDump::dumpPolyline(loop, color, 0, false, false); } //if( ii == 34 ) @@ -530,7 +556,8 @@ class FaceConverter : public StatusCallback addTriangleCheckDegenerate(idxA, idxC, idxD, meshOut, eps); } - static void triangulateCurvedPolygon(std::vector& loopPoints3D, PolyInputCache3D& meshOut, const GeomProcessingParams& params, double maxAllowedDistanceFromPlane) + static void triangulateCurvedPolygon(std::vector& loopPoints3D, PolyInputCache3D& meshOut, GeomProcessingParams& params, + double maxAllowedDistanceFromPlane) { // find corner with smallest angle if (loopPoints3D.size() > params.generalSettings->m_maxNumFaceEdges) @@ -551,8 +578,15 @@ class FaceConverter : public StatusCallback const vec3 BA = (pointA - pointB).normalized(); const vec3 BC = (pointC - pointB).normalized(); double dotProduct = dot(BA, BC); + if (isnan(dotProduct)) + { +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + std::cout << __FUNC__ << ": dotProduct is nan" << std::endl; +#endif + continue; + } double openingAngle = std::acos(dotProduct); - + if (openingAngle < smallestAngle) { smallestAngle = openingAngle; @@ -595,7 +629,7 @@ class FaceConverter : public StatusCallback vec3 normal = GeomUtils::computeNormal(pointA, pointB, pointC, eps); vec3 centroid = GeomUtils::computePolygonCentroid({ pointA, pointB, pointC }); - if (normal.length() > 0.5) + if (normal.length2() > 0.5*0.5) { // go forward in loop until the point is not in the plane any more for (size_t i = 0; i < numPoints; ++i) @@ -650,7 +684,10 @@ class FaceConverter : public StatusCallback else { // degenerated triangle - int wait = 0; + if (IsPrintToDebugLogOn()) + { + printToDebugLog(__FUNC__, "degenerated triangle"); + } } std::vector currentFlatPolygonPoints; @@ -761,7 +798,7 @@ class FaceConverter : public StatusCallback #ifdef _DEBUG PolyInputCache3D polyDebug(eps); #endif - + std::vector > > polygons2d; std::vector > polygons3d; std::vector polygon3DArea; @@ -825,7 +862,11 @@ class FaceConverter : public StatusCallback err << "unable to project to plane: nx" << nx << " ny " << ny << " nz " << nz << std::endl; if (params.callbackFunc) { - params.callbackFunc->messageCallback(err.str().c_str(), StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, params.ifc_entity); + if (IsPrintToDebugLogOn()) + { + printToDebugLog(__FUNC__, err.str()); + } + //params.callbackFunc->messageCallback(err.str().c_str(), StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, params.ifc_entity); } continue; } @@ -856,7 +897,10 @@ class FaceConverter : public StatusCallback } double distance = GeomUtils::distancePointPlane(point, normal, centroid); - curvature += std::abs(distance) / maxExtent*0.001; + if (std::abs(maxExtent) > eps) + { + curvature += std::abs(distance) / maxExtent * 0.001; + } } if( curvature > eps*1000 ) @@ -880,6 +924,13 @@ class FaceConverter : public StatusCallback size_t numPoints = currentPointLoop.size(); for (size_t jj = 0; jj < numPoints; ++jj) { + if (jj == 9) + { + if (IsPrintToDebugLogOn()) + { + printToDebugLog(__FUNC__, "triangulateCurvedPolygon: " + std::to_string(jj)); + } + } triangulateCurvedPolygon(loopPoints3DinputCopy, meshOut, paramsDebug, maxAllowedDistanceFromPlane); if (loopPoints3DinputCopy.size() < 3) { diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeomDebugDump.h b/IfcPlusPlus/src/ifcpp/geometry/GeomDebugDump.h index a46799d80..bb8eddaf7 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeomDebugDump.h +++ b/IfcPlusPlus/src/ifcpp/geometry/GeomDebugDump.h @@ -80,7 +80,7 @@ namespace GeomDebugDump return INSTANCE; } - double eps_debug_dump = 1.5 * EPS_M8; + double eps_debug_dump = EPS_RANDOM_FACTOR * EPS_M8; double dump_y_pos_geom; int dumpCount = 0; int maxDumpCount = 1000; @@ -842,6 +842,37 @@ namespace GeomDebugDump appendToOutput(strs_out); } + static void dumpBBox(carve::geom::aabb<3>& bbox, glm::vec4& color1, bool moveOffset) + { + std::vector > vertice_points; + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z + bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z - bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x - bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z + bbox.extent.z)); + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y - bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + vertice_points.push_back(carve::geom::VECTOR(bbox.pos.x + bbox.extent.x, bbox.pos.y + bbox.extent.y, bbox.pos.z - bbox.extent.z)); + + dumpPolyline(vertice_points, color1, 0, moveOffset, false); + } + static void moveOffset(double deltaY) { if (disableAllDebugDump) @@ -1327,7 +1358,7 @@ namespace GeomDebugDump moveOffsetNow = true; } - if (infoMeshsetInput.degenerateEdges.size() > 0) + if (infoMeshsetInput.degenerateEdges.size() > 0 && false ) { double lineThickness = 4.0; bool depthTestOff = true; diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeomUtils.h b/IfcPlusPlus/src/ifcpp/geometry/GeomUtils.h index 7943bcd00..91d82e452 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeomUtils.h +++ b/IfcPlusPlus/src/ifcpp/geometry/GeomUtils.h @@ -828,7 +828,7 @@ namespace GeomUtils if (len_square > 1.e-150) { double area = sqrt(len_square) * 0.5; - std::cout << "computePolygonArea: check division by " << len_square << std::endl; + std::cout << "computePolygonArea: len_square < 1.e-50, area = " << area << std::endl; } } #endif @@ -1201,9 +1201,12 @@ namespace GeomUtils { const double denom = point.x * line_direction.x + point.y * line_direction.y + point.z * line_direction.z - line_direction.x * line_origin.x - line_direction.y * line_origin.y - line_direction.z * line_origin.z; const double numer = line_direction.x * line_direction.x + line_direction.y * line_direction.y + line_direction.z * line_direction.z; - if (numer == 0.0) + if (std::abs(numer) < EPS_M16) { - throw BuildingException("Line is degenerated: the line's direction vector is a null vector!", __FUNC__); +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + std::cout << "Line is degenerated : the line's direction vector is a null vector!" << std::endl; +#endif + return; } const double lambda = denom / numer; closest = carve::geom::VECTOR(line_origin.x + lambda * line_direction.x, line_origin.y + lambda * line_direction.y, line_origin.z + lambda * line_direction.z); @@ -1213,9 +1216,12 @@ namespace GeomUtils { const double denom = point.x * line_direction.x + point.y * line_direction.y + -line_direction.x * line_origin.x - line_direction.y * line_origin.y; const double numer = line_direction.x * line_direction.x + line_direction.y * line_direction.y; - if (numer == 0.0) + if (std::abs(numer) < EPS_M16) { - throw BuildingException("Line is degenerated: the line's direction vector is a null vector!", __FUNC__); +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + std::cout << "Line is degenerated : the line's direction vector is a null vector!" << std::endl; +#endif + return; } const double lambda = denom / numer; closest = carve::geom::VECTOR(line_origin.x + lambda * line_direction.x, line_origin.y + lambda * line_direction.y); @@ -1486,8 +1492,88 @@ namespace GeomUtils extent = matrix * extent; } + inline void getBBoxCornerPoints(const carve::geom::aabb<3>& bbox, std::vector& points) + { + if (bbox.isEmpty()) + { + return; + } + + points.push_back(bbox.pos + carve::geom::VECTOR(bbox.extent.x, bbox.extent.y, bbox.extent.z)); + points.push_back(bbox.pos + carve::geom::VECTOR(bbox.extent.x, -bbox.extent.y, bbox.extent.z)); + points.push_back(bbox.pos + carve::geom::VECTOR(-bbox.extent.x, -bbox.extent.y, bbox.extent.z)); + points.push_back(bbox.pos + carve::geom::VECTOR(-bbox.extent.x, bbox.extent.y, bbox.extent.z)); + + points.push_back(points[0]); + points.push_back(points[1]); + points.push_back(points[2]); + points.push_back(points[3]); + points[4].z = bbox.pos.z - bbox.extent.z; + points[5].z = bbox.pos.z - bbox.extent.z; + points[6].z = bbox.pos.z - bbox.extent.z; + points[7].z = bbox.pos.z - bbox.extent.z; + } + + /** + * \brief unify bboxB with bboxA, but first apply a transformation to bboxB + */ + inline void unionBBox(carve::geom::aabb<3>& bboxA, const carve::geom::aabb<3>& bboxB)//, const carve::math::Matrix& transformApplyToB) + { + if (bboxB.isEmpty()) + { + return; + } + bool simpleCase = true; + if (simpleCase) + { + if (bboxA.isEmpty()) + { + bboxA = bboxB; + return; + } + bboxA.unionAABB(bboxB); + return; + } + + std::vector points; + getBBoxCornerPoints(bboxB, points); + + for (vec3& point : points) + { + //point = transformApplyToB * point; + } + + carve::geom::aabb<3> bboxB_transformed; + bboxB_transformed.fit(points.begin(), points.end()); + + bboxA.unionAABB(bboxB_transformed); + } + + /** + * \brief unify bboxB with bboxA, but first apply a transformation to bboxB + */ + inline void applyTransformToBBox(carve::geom::aabb<3>& bboxA, const carve::math::Matrix& transform) + { + if (bboxA.isEmpty()) + { + return; + } + std::vector points; + getBBoxCornerPoints(bboxA, points); + + for (vec3& point : points) + { + point = transform * point; + } + + carve::geom::aabb<3> bboxB_transformed; + bboxB_transformed.fit(points.begin(), points.end()); + + bboxA.unionAABB(bboxB_transformed); + } + /** matrix operations */ - inline bool computeInverse(const carve::math::Matrix& matrix_a, carve::math::Matrix& matrix_inv, const double eps = 0.01) + inline bool computeInverse(const carve::math::Matrix& matrix_a, carve::math::Matrix& matrix_inv, const double eps = EPS_M16) { double inv[16], det; double m[16] = { @@ -2156,144 +2242,6 @@ namespace GeomUtils return true; } - //\brief: collect connected edges and create face - static carve::mesh::Face<3>* createFaceFromEdgeLoop(carve::mesh::Edge<3>* start, double eps) - { - carve::mesh::Edge<3>* e = start; - std::vector*> loop_edges; - do { - if (e->rev != nullptr) - { - return nullptr; - } - loop_edges.push_back(e); - e = e->perimNext(); - } while (e != start); - - const size_t N = loop_edges.size(); - for (size_t i = 0; i < N; ++i) - { - loop_edges[i]->rev = new carve::mesh::Edge<3>(loop_edges[i]->v2(), nullptr); - } - - for (size_t i = 0; i < N; ++i) - { - carve::mesh::Edge<3>* openEdge = loop_edges[i]; - carve::mesh::Edge<3>* openEdgeNext = loop_edges[(i + 1) % N]; - carve::mesh::Edge<3>* e1 = openEdge->rev; - carve::mesh::Edge<3>* e2 = openEdgeNext->rev; - e1->prev = e2; - e2->next = e1; - - e1->rev = openEdge; - e2->rev = openEdgeNext; - } - - carve::mesh::Face<3>* f = new carve::mesh::Face<3>(start->rev, eps); - - if (f->n_edges != N) - { - delete f; - return nullptr; - } - - return f; - } - - //\brief: - static void closeMeshSet(carve::mesh::MeshSet<3>* meshset, double eps) - { - // try to fix open mesh - for (size_t i = 0; i < meshset->meshes.size(); ++i) - { - carve::mesh::MeshSet<3>::mesh_t* mesh = meshset->meshes[i]; - const size_t numOpenEdgesInitial = mesh->open_edges.size(); - if (numOpenEdgesInitial == 0) - { - continue; - } - for (size_t kk = 0; kk < numOpenEdgesInitial; ++kk) - { - const size_t numOpenEdges = mesh->open_edges.size(); - if (numOpenEdges == 0) - { - break; - } - - mesh->faces.reserve(numOpenEdges + 1); - - carve::mesh::Edge<3>* start = mesh->open_edges[0]; - - carve::mesh::Edge<3>* openEdge1 = nullptr; - carve::mesh::Edge<3>* openEdge2 = nullptr; - std::vector*> edges_to_close; - edges_to_close.resize(numOpenEdges); - carve::mesh::Edge<3>* edge = start; - size_t j = 0; - size_t numOpenEdgesCurrentLoop = 0; - do { - edges_to_close[j++] = edge; - - carve::mesh::Edge<3>* currentEdge = edge; - carve::mesh::Edge<3>* nextEdge = currentEdge->perimNext(); - ++numOpenEdgesCurrentLoop; - - if (openEdge1 == nullptr) - { - // check if nextEdge is also an open edge - for (size_t mm = 0; mm < mesh->open_edges.size(); ++mm) - { - carve::mesh::Edge<3>* e = mesh->open_edges[mm]; - if (e == nextEdge) - { - openEdge1 = currentEdge; - openEdge2 = nextEdge; - break; - } - } - } - edge = nextEdge; - } while (edge != start); - - if (numOpenEdgesCurrentLoop == 3) - { - if (openEdge1 != nullptr) - { - // close with triangle - carve::mesh::Face<3>* closingTriangle = createFaceFromEdgeLoop(openEdge1, eps); - if (closingTriangle != nullptr) - { - closingTriangle->mesh = mesh; - mesh->faces.push_back(closingTriangle); - } - } - } - else if (numOpenEdgesCurrentLoop > 3) - { - if (openEdge1 != nullptr && openEdge2 != nullptr) - { - // add triangle with 2 open edges and a new edge - carve::mesh::Face<3>* triangle = new carve::mesh::Face<3>(openEdge1->v2(), openEdge1->v1(), openEdge2->v2(), eps); - triangle->mesh = mesh; - openEdge1->rev = triangle->edge; - triangle->edge->rev = openEdge1; - mesh->faces.push_back(triangle); - - carve::mesh::Edge<3>* e1 = openEdge1->rev; - carve::mesh::Edge<3>* e2 = e1->prev; - openEdge2->rev = e2; - e2->rev = openEdge2; - //e1->validateLoop(); - } - } - - meshset->collectVertices(); - mesh->cacheEdges(); - mesh->calcOrientation(); - } - } - } - static double triangleArea(const glm::dvec3& A, const glm::dvec3& B, const glm::dvec3& C) { glm::dvec3 AB(B - A); diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeometryConverter.h b/IfcPlusPlus/src/ifcpp/geometry/GeometryConverter.h index 05aecc11d..64653ca9d 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeometryConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/GeometryConverter.h @@ -26,6 +26,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include #include #include +#include #include #include #include @@ -38,9 +39,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include #include #include +#include +#include #include #include #include +#include #include #include @@ -129,10 +133,10 @@ class GeometryConverter : public StatusCallback elementConvertedCallbackHandler = cb; } - GeometryConverter(shared_ptr& ifc_model) + GeometryConverter(shared_ptr& ifc_model, shared_ptr& geom_settings) { m_ifc_model = ifc_model; - m_geom_settings = shared_ptr(new GeometrySettings()); + m_geom_settings = geom_settings; resetNumVerticesPerCircle(); shared_ptr& unit_converter = m_ifc_model->getUnitConverter(); m_representation_converter = shared_ptr(new RepresentationConverter(m_geom_settings, unit_converter)); @@ -168,6 +172,160 @@ class GeometryConverter : public StatusCallback m_messages.clear(); } + void clearIfcRepresentationsInModel(bool resetRepresentationInProducts, bool clearStyles, bool clearIfcElements ) + { + std::map >& map_entities = m_ifc_model->getMapIfcEntities(); + for (auto it = map_entities.begin(); it != map_entities.end(); ) + { + shared_ptr& entity = it->second; + if(entity) + { + shared_ptr ele = dynamic_pointer_cast(entity); + if (ele) + { + if (clearIfcElements) + { + it = map_entities.erase(it); + continue; + } + else + { + ++it; + continue; + } + } + + if (resetRepresentationInProducts) + { + shared_ptr product = dynamic_pointer_cast(entity); + if (product) + { + product->m_Representation.reset(); + product->m_ObjectPlacement.reset(); + ++it; + continue; + } + } + + shared_ptr plc = dynamic_pointer_cast(entity); + if (plc) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr rep = dynamic_pointer_cast(entity); + if (rep) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr representationItem = dynamic_pointer_cast(entity); + if (representationItem) + { + shared_ptr style = dynamic_pointer_cast(entity); + if (style) + { + if (!clearStyles) + { + ++it; + continue; + } + } + + +#if defined _DEBUG || defined _DEBUG_RELEASE + shared_ptr entityKeepAlive = entity; + size_t useCount = entityKeepAlive.use_count(); +#endif + // this includes IfcCartesianPoint + it = map_entities.erase(it); + +#if defined _DEBUG || defined _DEBUG_RELEASE + size_t useCount2 = entityKeepAlive.use_count(); + entityKeepAlive.reset(); + if (useCount2 > 1) + { + int notDeletedYet = 1; + } + + if (useCount2 < 2) + { + int deletedHere = 1; + } + +#endif + continue; + } + + shared_ptr prof = dynamic_pointer_cast(entity); + if (prof) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr style = dynamic_pointer_cast(entity); + if (style) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr colour = dynamic_pointer_cast(entity); + if (colour) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr materialUsage = dynamic_pointer_cast(entity); + if (materialUsage) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr shapeRep = dynamic_pointer_cast(entity); + if (shapeRep) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr spaceBoundary = dynamic_pointer_cast(entity); + if (spaceBoundary) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr map = dynamic_pointer_cast(entity); + if (map) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr conn = dynamic_pointer_cast(entity); + if (conn) + { + it = map_entities.erase(it); + continue; + } + + shared_ptr context = dynamic_pointer_cast(entity); + if (context) + { + it = map_entities.erase(it); + continue; + } + } + ++it; + } + } + void resetNumVerticesPerCircle() { m_geom_settings->resetNumVerticesPerCircle(); @@ -683,6 +841,7 @@ class GeometryConverter : public StatusCallback { progressTextCallback("Creating geometry..."); progressValueCallback(0, "geometry"); + printToDebugLog(__FUNC__, "start converting"); m_product_shape_data.clear(); m_map_outside_spatial_structure.clear(); m_setResolvedProjectStructure.clear(); @@ -697,15 +856,15 @@ class GeometryConverter : public StatusCallback shared_ptr ifc_project_data; std::vector > vec_object_definitions; - const std::map >& map_entities = m_ifc_model->getMapIfcEntities(); + std::map >& map_entities = m_ifc_model->getMapIfcEntities(); if (map_entities.size() > 0) { - for (auto it = map_entities.begin(); it != map_entities.end(); ++it) + for (auto it = map_entities.begin(); it != map_entities.end(); ) { - shared_ptr obj = it->second; - if (obj) + shared_ptr entity = it->second; + if (entity) { - shared_ptr object_def = dynamic_pointer_cast(obj); + shared_ptr object_def = dynamic_pointer_cast(entity); if (object_def) { vec_object_definitions.push_back(object_def); @@ -722,21 +881,38 @@ class GeometryConverter : public StatusCallback } } } + + if (entity->classID() == IFC4X3::IFCCARTESIANPOINT) + { + // IfcCartesianPoint are referenced by IfcFace etc, so we don't need to keep them in the model + it = map_entities.erase(it); + continue; + } + + if (entity->classID() == IFC4X3::IFCPRODUCTREPRESENTATION) + { + // IfcCartesianPoint are referenced by IfcFace etc, so we don't need to keep them in the model + it = map_entities.erase(it); + continue; + } + + shared_ptr geomItem = dynamic_pointer_cast(entity); + if (geomItem) + { + // enable early free up of memory during geometry processing + //it = map_entities.erase(it); + } + ++it; } } } - if (m_clear_memory_immedeately) - { - //m_ifc_model->getMapIfcEntities().clear(); - } - // create geometry for for each IfcProduct independently, spatial structure will be resolved later const int num_object_definitions = (int)vec_object_definitions.size(); std::mutex writelock_map, writelock_ifc_project, writelock_progress; int i = 0; -#if defined(_DEBUG) || defined(_DEBUG_RELEASE) +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) //|| defined(__GNUC__) // disable threading for Linux due to unknown issues with std::thread std::for_each(std::execution::seq, #else std::for_each(std::execution::par, @@ -835,7 +1011,7 @@ class GeometryConverter : public StatusCallback { std::lock_guard lock(writelock_map); - m_product_shape_data.insert(std::make_pair(guid, product_geom_input_data)); + m_product_shape_data[guid] = product_geom_input_data; } if (thread_err.tellp() > 0) @@ -859,10 +1035,16 @@ class GeometryConverter : public StatusCallback m_recent_progress = progress; } ++i; - }); + }); // subtract openings in assemblies etc, in case the opening is attached at the top level - std::for_each(std::execution::par, vec_object_definitions.begin(), vec_object_definitions.end(), [&](shared_ptr& object_def) { +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) //|| defined(__GNUC__) // disable threading for Linux due to unknown issues with std::thread + std::for_each(std::execution::seq, +#else + std::for_each(std::execution::par, +#endif + + vec_object_definitions.begin(), vec_object_definitions.end(), [&](shared_ptr& object_def) { std::string guid; if (object_def->m_GlobalId) { @@ -874,7 +1056,7 @@ class GeometryConverter : public StatusCallback shared_ptr product_geom_input_data = it_find->second; subtractOpeningsInRelatedObjects(product_geom_input_data); } - }); + }); if (m_ifc_model->isLoadingCancelled()) { @@ -945,6 +1127,12 @@ class GeometryConverter : public StatusCallback guid = ifc_object_def->m_GlobalId->m_value; } + shared_ptr typeObject = dynamic_pointer_cast(ifc_object_def); + if (typeObject) + { + continue; + } + if (guid.size() > 18) { shared_ptr ifc_object_def_as_root = ifc_object_def; @@ -971,7 +1159,7 @@ class GeometryConverter : public StatusCallback messageCallback("undefined error", StatusCallback::MESSAGE_TYPE_ERROR, __FUNC__); } - m_representation_converter->getProfileCache()->clearProfileCache(); + m_representation_converter->clearCache(); progressTextCallback("Loading file done"); progressValueCallback(1.0, "geometry"); } @@ -1005,6 +1193,17 @@ class GeometryConverter : public StatusCallback return; } + // some linux debugging: + int productTag = ifc_product->m_tag; + printToDebugLog(__FUNC__, "converting element " + std::to_string(productTag)); + + std::vector > vec_rel_voids; + shared_ptr ifc_element = dynamic_pointer_cast(ifc_product); + if (ifc_element) + { + vec_rel_voids = ifc_element->m_HasOpenings_inverse; + } + // convert IFC geometry std::vector >& vec_representations = product_representation->m_Representations; for (size_t i_representations = 0; i_representations < vec_representations.size(); ++i_representations) @@ -1018,7 +1217,8 @@ class GeometryConverter : public StatusCallback try { shared_ptr representation_data(new ItemShapeData()); - m_representation_converter->convertIfcRepresentation(representation, representation_data); + representation_data->m_product = product_shape; + m_representation_converter->convertIfcRepresentation(representation, representation_data, vec_rel_voids); product_shape->addGeometricItem(representation_data, product_shape); } catch (BuildingException& e) @@ -1040,8 +1240,6 @@ class GeometryConverter : public StatusCallback m_representation_converter->getPlacementConverter()->convertIfcObjectPlacement(ifc_product->m_ObjectPlacement, product_shape, placement_already_applied, false); } - std::vector > vec_opening_data; - const shared_ptr ifc_element = dynamic_pointer_cast(ifc_product); if (ifc_element) { // handle openings @@ -1138,7 +1336,11 @@ class GeometryConverter : public StatusCallback if (m_clear_memory_immedeately) { - ifc_product->m_Representation.reset(); + int productReferenceCount = ifc_product.use_count(); + if (productReferenceCount < 2) + { + ifc_product->m_Representation.reset(); + } } } diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeometryException.h b/IfcPlusPlus/src/ifcpp/geometry/GeometryException.h index 4ed0c6ae9..678e7918d 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeometryException.h +++ b/IfcPlusPlus/src/ifcpp/geometry/GeometryException.h @@ -1,46 +1,46 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include - -class UnhandledRepresentationException : public std::exception -{ -public: - UnhandledRepresentationException() - { - } - UnhandledRepresentationException( shared_ptr item ) - { - m_item = item; - } - - ~UnhandledRepresentationException() throw() - { - } - - const char* what() const throw() - { - return "Unhandled IFC Representation"; - } - - shared_ptr m_item; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include + +class UnhandledRepresentationException : public std::exception +{ +public: + UnhandledRepresentationException() + { + } + UnhandledRepresentationException( shared_ptr item ) + { + m_item = item; + } + + ~UnhandledRepresentationException() throw() + { + } + + const char* what() const throw() + { + return "Unhandled IFC Representation"; + } + + shared_ptr m_item; +}; diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeometryInputData.cpp b/IfcPlusPlus/src/ifcpp/geometry/GeometryInputData.cpp index cdd2633c3..3c082cc3b 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeometryInputData.cpp +++ b/IfcPlusPlus/src/ifcpp/geometry/GeometryInputData.cpp @@ -26,11 +26,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include #include "IncludeCarveHeaders.h" #include "MeshOps.h" +#include "MeshFlattener.h" #include "GeometryInputData.h" using namespace IFC4X3; -bool ItemShapeData::addClosedPolyhedron(const shared_ptr& poly_data, const GeomProcessingParams& params, shared_ptr& geom_settings) +bool ItemShapeData::addClosedPolyhedron(const shared_ptr& poly_data, GeomProcessingParams& params, shared_ptr& geom_settings) { if (poly_data->getVertexCount() < 3) { @@ -78,6 +79,35 @@ bool ItemShapeData::addClosedPolyhedron(const shared_ptr 0) + { + size_t minNumFacesPerMesh = 4; + shared_ptr > meshsetChanged3; + MeshOps::MeshSet2Polyhedron2MeshSet(meshsetUnchanged, meshsetChanged3, params, minNumFacesPerMesh); + + MeshSetInfo info3; + MeshOps::checkMeshSetValidAndClosed(meshsetChanged3, info3, params); + + if (MeshOps::isBetterForBoolOp(info3, info, false)) + { + meshsetUnchanged = meshsetChanged3; + info.copyFromOther(info3); + } + + if (info3.meshSetValid) + { + m_meshsets.push_back(meshsetUnchanged); + return true; + } + } + } + + MeshOps::checkAndFixMeshsetInverted(meshsetUnchanged, info, params); if (poly_data->faceCount > 10000) @@ -119,17 +149,26 @@ bool ItemShapeData::addClosedPolyhedron(const shared_ptrisClosed()) - { - m_meshsets.push_back(meshsetChanged); - return true; - } + //if (meshsetChanged->isClosed()) + //{ + // m_meshsets.push_back(meshsetChanged); + // return true; + //} MeshSetInfo infoChanged; bool validMeshChanged = MeshOps::checkMeshSetValidAndClosed(meshsetChanged, infoChanged, params); if (MeshOps::isBetterForBoolOp(infoChanged, info, false)) { - m_meshsets_open.push_back(meshsetChanged); + if (meshsetChanged->isClosed()) + { + m_meshsets.push_back(meshsetChanged); + return true; + } + else + { + m_meshsets_open.push_back(meshsetChanged); + return false; + } #ifdef _DEBUG if (params.debugDump) @@ -245,4 +284,4 @@ void ItemShapeData::addOpenOrClosedPolyhedron(const shared_ptr* m_callStack = nullptr; + ScopedCallStackEntry(const std::string& funcName, std::vector& callStack) + { + callStack.push_back(funcName); + m_callStack = &callStack; + } + ~ScopedCallStackEntry() + { + m_callStack->pop_back(); + } +}; + + + +struct ScopedTimeMeasure +{ + int timeStart = 0; + int tag = 0; + int numElements = 10; + std::map, std::greater > * mapTimeTag = nullptr; + ScopedTimeMeasure(std::map, std::greater >* _mapTimeTag, int _tag, int _numElements) + { + mapTimeTag = _mapTimeTag; + tag = _tag; + numElements = _numElements; + timeStart = clock(); + } + ~ScopedTimeMeasure() + { + int timeEnd = clock(); + int duration = timeEnd - timeStart; + if (mapTimeTag->size() < numElements) + { + mapTimeTag->insert({ duration, { tag} }); + } + else + { + auto it10 = mapTimeTag->rbegin(); + if (it10 == mapTimeTag->rend()) + { + return; // what? + } + int duration10 = it10->first; + if (duration < duration10) + { + return; + } + + auto itFindExisting = mapTimeTag->find(duration); + if (itFindExisting != mapTimeTag->end()) + { + std::vector& vecTags = itFindExisting->second; + vecTags.push_back(tag); + return; + } + + mapTimeTag->erase(std::prev(mapTimeTag->end())); + mapTimeTag->insert({ duration, {tag} }); + } + } +}; class PolyInputCache3D; /** @@ -503,8 +567,8 @@ class ProductShapeData; class ItemShapeData { public: - weak_ptr m_parentProduct; - weak_ptr m_parentItem; + weak_ptr m_product; // IFC product to which the item belongs + weak_ptr m_parentItem; // in cas the current item is a child item of another item std::vector > m_child_items; weak_ptr m_ifc_representation; @@ -527,6 +591,29 @@ class ItemShapeData return true; } + void copyFrom(shared_ptr& other) + { + m_product = other->m_product; + m_parentItem = other->m_parentItem; + m_child_items = other->m_child_items; + m_ifc_representation = other->m_ifc_representation; + m_polylines = other->m_polylines; + for (shared_ptr >&meshset : other->m_meshsets) + { + shared_ptr > meshsetCopy(meshset->clone()); + m_meshsets.push_back(meshsetCopy); + } + for (shared_ptr >&meshset : other->m_meshsets_open) + { + shared_ptr > meshsetCopy(meshset->clone()); + m_meshsets_open.push_back(meshsetCopy); + } + + m_vec_text_literals = other->m_vec_text_literals; + m_vertex_points = other->m_vertex_points; + m_vec_styles = other->m_vec_styles; + } + void addOpenOrClosedPolyhedron(const shared_ptr& poly_data, const GeomProcessingParams& params); void addOpenPolyhedron(const shared_ptr& poly_data, const GeomProcessingParams& params) @@ -555,7 +642,7 @@ class ItemShapeData m_meshsets_open.push_back(meshset); } - bool addClosedPolyhedron(const shared_ptr& poly_data, const GeomProcessingParams& params, shared_ptr& geom_settings); + bool addClosedPolyhedron(const shared_ptr& poly_data, GeomProcessingParams& params, shared_ptr& geom_settings); void addPoint(const vec3& point) { @@ -621,6 +708,12 @@ class ItemShapeData } } + void releaseIfcObjects() + { + // nothing to do... + m_ifc_representation.reset(); + } + void applyTransformToItem(const carve::math::Matrix& mat, double eps, bool matrix_identity_checked) { if (!matrix_identity_checked) @@ -718,7 +811,66 @@ class ItemShapeData } } - void computeItemBoundingBox(carve::geom::aabb<3>& bbox, std::set& setVisited) const + void getAllMeshPoints(std::vector& points) const + { + for (size_t ii = 0; ii < m_vertex_points.size(); ++ii) + { + const shared_ptr& vertex_data = m_vertex_points[ii]; + for (size_t j = 0; j < vertex_data->points.size(); ++j) + { + points.push_back(vertex_data->points[j]); + } + } + + for (size_t polyline_i = 0; polyline_i < m_polylines.size(); ++polyline_i) + { + const shared_ptr& polyline_data = m_polylines[polyline_i]; + for (size_t j = 0; j < polyline_data->points.size(); ++j) + { + points.push_back(polyline_data->points[j]); + } + } + + for (size_t i_meshsets = 0; i_meshsets < m_meshsets_open.size(); ++i_meshsets) + { + const shared_ptr >& item_meshset = m_meshsets_open[i_meshsets]; + if (!item_meshset) + { + continue; + } + for (size_t i = 0; i < item_meshset->vertex_storage.size(); ++i) + { + points.push_back(item_meshset->vertex_storage[i].v); + } + } + + for (size_t i_meshsets = 0; i_meshsets < m_meshsets.size(); ++i_meshsets) + { + const shared_ptr >& item_meshset = m_meshsets[i_meshsets]; + if (!item_meshset) + { + continue; + } + for (size_t i = 0; i < item_meshset->vertex_storage.size(); ++i) + { + points.push_back(item_meshset->vertex_storage[i].v); + } + } + + for (size_t text_i = 0; text_i < m_vec_text_literals.size(); ++text_i) + { + const shared_ptr& text_literals = m_vec_text_literals[text_i]; + const carve::math::Matrix& mat = text_literals->m_text_position; + //vec3 text_pos = carve::geom::VECTOR(mat._41, mat._42,) + } + + for (auto child : m_child_items) + { + child->getAllMeshPoints(points); + } + } + + void computeItemBoundingBox(carve::geom::aabb<3>& bbox, /*carve::math::Matrix& parentTransform,*/ std::set& setVisited) const { for (size_t ii = 0; ii < m_vertex_points.size(); ++ii) { @@ -728,11 +880,11 @@ class ItemShapeData const vec3& point = vertex_data->points[j]; if (bbox.isEmpty()) { - bbox = carve::geom::aabb<3>(point, carve::geom::VECTOR(0, 0, 0)); + bbox = carve::geom::aabb<3>(point, /*parentTransform**/carve::geom::VECTOR(0, 0, 0)); } else { - bbox.unionAABB(carve::geom::aabb<3>(point, carve::geom::VECTOR(0, 0, 0))); + bbox.unionAABB(carve::geom::aabb<3>(point, /*parentTransform**/carve::geom::VECTOR(0, 0, 0))); } } } @@ -745,11 +897,11 @@ class ItemShapeData const vec3& point = polyline_data->points[j]; if (bbox.isEmpty()) { - bbox = carve::geom::aabb<3>(point, carve::geom::VECTOR(0, 0, 0)); + bbox = carve::geom::aabb<3>(point, /*parentTransform**/carve::geom::VECTOR(0, 0, 0)); } else { - bbox.unionAABB(carve::geom::aabb<3>(point, carve::geom::VECTOR(0, 0, 0))); + bbox.unionAABB(carve::geom::aabb<3>(point, /*parentTransform**/carve::geom::VECTOR(0, 0, 0))); } } } @@ -762,17 +914,7 @@ class ItemShapeData continue; } carve::geom::aabb<3> meshBBox = item_meshset->getAABB(); - if (bbox.isEmpty()) - { - bbox = meshBBox; - } - else - { - if (!meshBBox.isEmpty()) - { - bbox.unionAABB(meshBBox); - } - } + GeomUtils::unionBBox(bbox, meshBBox);// , parentTransform); } for (size_t i_meshsets = 0; i_meshsets < m_meshsets.size(); ++i_meshsets) @@ -783,17 +925,7 @@ class ItemShapeData continue; } carve::geom::aabb<3> meshBBox = item_meshset->getAABB(); - if (bbox.isEmpty()) - { - bbox = meshBBox; - } - else - { - if (!meshBBox.isEmpty()) - { - bbox.unionAABB(meshBBox); - } - } + GeomUtils::unionBBox(bbox, meshBBox);// , parentTransform); } for (size_t text_i = 0; text_i < m_vec_text_literals.size(); ++text_i) @@ -803,11 +935,11 @@ class ItemShapeData vec3 text_pos = carve::geom::VECTOR(mat._41, mat._42, mat._43); if (bbox.isEmpty()) { - bbox = carve::geom::aabb<3>(text_pos, carve::geom::VECTOR(0, 0, 0)); + bbox = carve::geom::aabb<3>(text_pos, /*parentTransform**/carve::geom::VECTOR(0, 0, 0)); } else { - bbox.unionAABB(carve::geom::aabb<3>(text_pos, carve::geom::VECTOR(0, 0, 0))); + bbox.unionAABB(carve::geom::aabb<3>(text_pos, /*parentTransform**/carve::geom::VECTOR(0, 0, 0))); } } @@ -820,18 +952,9 @@ class ItemShapeData setVisited.insert(child.get()); carve::geom::aabb<3> meshBBox; - child->computeItemBoundingBox(meshBBox, setVisited); - if (bbox.isEmpty()) - { - bbox = meshBBox; - } - else - { - if (!meshBBox.isEmpty()) - { - bbox.unionAABB(meshBBox); - } - } + carve::math::Matrix ident; + child->computeItemBoundingBox(meshBBox, /*ident,*/ setVisited); + GeomUtils::unionBBox(bbox, meshBBox/*, parentTransform*/); } } @@ -910,10 +1033,9 @@ class ProductShapeData std::cout << __FUNCTION__ << ": ptr_self.get() != this" << std::endl; } m_geometric_items.push_back(item); - item->m_parentProduct = ptr_self; + item->m_product = ptr_self; } - void addStyle(shared_ptr& newStyle) { if (!newStyle) @@ -937,7 +1059,7 @@ class ProductShapeData m_vec_styles.clear(); } - void clearMeshGeometry() + void clearMeshGeometry(bool clearChildElements) { m_vec_styles.clear(); m_object_placement.reset(); @@ -947,20 +1069,40 @@ class ProductShapeData shared_ptr& item_data = m_geometric_items[item_i]; item_data->clearItemMeshGeometry(); } - - m_added_to_spatial_structure = false; + + if (clearChildElements) + { + for (size_t item_i = 0; item_i < m_vec_child_products.size(); ++item_i) + { + shared_ptr& product_data = m_vec_child_products[item_i]; + product_data->clearMeshGeometry(clearChildElements); + } + } } void clearAll() { + clearMeshGeometry(true); m_vec_styles.clear(); m_ifc_object_definition.reset(); m_object_placement.reset(); m_vec_child_products.clear(); m_geometric_items.clear(); - m_added_to_spatial_structure = false; + } - clearMeshGeometry(); + void releaseIfcObjects() + { + for (size_t ii = 0; ii < m_vec_child_products.size(); ++ii) + { + const shared_ptr& existing_child = m_vec_child_products[ii]; + existing_child->releaseIfcObjects(); + } + + for (size_t ii = 0; ii < m_geometric_items.size(); ++ii) + { + const shared_ptr& geomItem = m_geometric_items[ii]; + geomItem->releaseIfcObjects(); + } } bool isContainedInParentsList( shared_ptr& product_data_check ) @@ -1016,12 +1158,12 @@ class ProductShapeData * \brief method getTransform: Computes the transformation matrix, that puts the geometry of this product into global coordinates * All transformation matrices of all parent coordinate systems are multiplied. */ - carve::math::Matrix getTransform() + carve::math::Matrix getTransform() const { carve::math::Matrix transform_matrix; if( m_vec_transforms.size() > 0 ) { - for( shared_ptr& transform : m_vec_transforms ) + for( const shared_ptr& transform : m_vec_transforms ) { if( transform ) { @@ -1125,6 +1267,39 @@ class ProductShapeData } } + void getAllMeshPoints(std::vector& points, bool includeChildren, bool globalCoords) const + { + std::vector itemPoints; + for (size_t i_item = 0; i_item < m_geometric_items.size(); ++i_item) + { + const shared_ptr& item_data = m_geometric_items[i_item]; + item_data->getAllMeshPoints(itemPoints); + } + + if (itemPoints.size() > 0) + { + if (globalCoords) + { + carve::math::Matrix transform = getTransform(); + for( vec3& point : itemPoints) + { + vec3 pointGlobal = transform * point; + point = pointGlobal; + } + } + std::copy(itemPoints.begin(), itemPoints.end(), std::back_inserter(points)); + } + + if (includeChildren) + { + for (size_t i_child = 0; i_child < m_vec_child_products.size(); ++i_child) + { + const shared_ptr& child_product_data = m_vec_child_products[i_child]; + child_product_data->getAllMeshPoints(points, includeChildren, globalCoords); + } + } + } + bool isShapeDataEmpty( bool check_also_children ) const { if(m_geometric_items.size() > 0 ) @@ -1182,8 +1357,6 @@ class ProductShapeData return false; } - - void addGeometricChildItem(shared_ptr& item_data, const shared_ptr& ptr_self) { if (ptr_self.get() != this) @@ -1191,11 +1364,9 @@ class ProductShapeData std::cout << __FUNCTION__ << "ptr_self != this" << std::endl; } m_geometric_items.push_back(item_data); - item_data->m_parentProduct = ptr_self; + item_data->m_product = ptr_self; } - - void applyTransformToItem(const shared_ptr& transform, double eps, bool matrix_identity_checked ) { if (!transform) @@ -1285,48 +1456,6 @@ inline bool isEqual(const shared_ptr& existingItem, const shared_ return true; } -static carve::geom::aabb<3> computeBoundingBoxLocalCoords( const shared_ptr& productData, bool includeChildren ) -{ - carve::geom::aabb<3> bbox; - std::set setVisited; - for( auto item : productData->getGeometricItems() ) - { - carve::geom::aabb<3> repBBox; - item->computeItemBoundingBox(repBBox, setVisited); - if( bbox.isEmpty() ) - { - bbox = repBBox; - } - else - { - if (!repBBox.isEmpty()) - { - bbox.unionAABB(repBBox); - } - } - } - - if( includeChildren ) - { - for( auto child : productData->getChildElements()) - { - carve::geom::aabb<3> childBBox = computeBoundingBoxLocalCoords(child, true); - if( bbox.isEmpty() ) - { - bbox = childBBox; - } - else - { - if (!childBBox.isEmpty()) - { - bbox.unionAABB(childBBox); - } - } - } - } - return bbox; -} - static carve::geom::aabb<3> computeBoundingBox(const shared_ptr& productData, bool includeChildren, bool applyTransformToGlobalCoordinates) { carve::geom::aabb<3> bbox; @@ -1335,58 +1464,9 @@ static carve::geom::aabb<3> computeBoundingBox(const shared_ptrgetTransform(); - } - - std::set setVisited; - for( auto item : productData->getGeometricItems() ) - { - carve::geom::aabb<3> repBBox; - item->computeItemBoundingBox(repBBox, setVisited); - - if( repBBox.isEmpty() ) - { - continue; - } - - if( bbox.isEmpty() ) - { - if( applyTransformToGlobalCoordinates ) - { - repBBox.pos = transform * repBBox.pos; - } - bbox = repBBox; - } - else - { - bbox.unionAABB(repBBox); - } - } - - if( includeChildren ) - { - for( const shared_ptr& child : productData->getChildElements()) - { - carve::geom::aabb<3> childBBox = computeBoundingBox(child, true, applyTransformToGlobalCoordinates); - - if( childBBox.isEmpty() ) - { - continue; - } - - if( bbox.isEmpty() ) - { - bbox = childBBox; - } - else - { - bbox.unionAABB(childBBox); - } - } - } + std::vector points; + productData->getAllMeshPoints(points, includeChildren, applyTransformToGlobalCoordinates); + bbox = carve::geom::aabb<3>(points.begin(), points.end()); return bbox; } diff --git a/IfcPlusPlus/src/ifcpp/geometry/GeometrySettings.h b/IfcPlusPlus/src/ifcpp/geometry/GeometrySettings.h index 66996c101..0cb512950 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/GeometrySettings.h +++ b/IfcPlusPlus/src/ifcpp/geometry/GeometrySettings.h @@ -41,7 +41,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #define EPS_M6 1e-6 #define EPS_M5 1e-5 #define EPS_M4 1e-4 -#define EPS_DEFAULT 1.5*EPS_M8 +#define EPS_RANDOM_FACTOR 1.51634527 +#define EPS_DEFAULT EPS_RANDOM_FACTOR*EPS_M8 #define EPS_ALIGNED_EDGES 1e-8 #define EPS_ANGLE_COPLANAR_FACES 1e-9 #define EPS_MIN_FACE_AREA 1e-10 @@ -153,6 +154,7 @@ class GeometrySettings size_t m_maxNumFaceEdges = MAX_NUM_EDGES; bool m_mergeAlignedEdges = true; MeshSimplifyCallbackType m_callback_simplify_mesh; + std::map, std::greater > m_mapCsgTimeTag; protected: int m_num_vertices_per_circle = 14; diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshFlattener.h b/IfcPlusPlus/src/ifcpp/geometry/MeshFlattener.h new file mode 100644 index 000000000..1878ae03a --- /dev/null +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshFlattener.h @@ -0,0 +1,947 @@ +/* -*-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. +*/ + +#pragma once +#pragma warning( disable: 4251 4702 ) + +#include +#include "GeomUtils.h" +#include +#include "GeometryInputData.h" +#include "MeshOps.h" + +class MeshFlattener +{ +public: + size_t m_numCorrectedVertices = 0; + struct SetOfFacesInPlane { + carve::geom::plane<3> plane; + std::set* > setFaces; + }; + std::multimap > > m_mapInPlaneFaces; + + shared_ptr findPlaneForDistance(const std::vector >& vecOfPlanes, const vec3& normal, double epsAngle) + { + for (const shared_ptr& setOfFacesInPlane : vecOfPlanes) + { + vec3& setOfFacesNormal = setOfFacesInPlane->plane.N; + double dotProduct = dot(setOfFacesNormal, normal); + if (std::abs(dotProduct + 1.0) > epsAngle && std::abs(dotProduct - 1.0) > epsAngle) + { + continue; + } + return setOfFacesInPlane; + } + return shared_ptr(); + } + + shared_ptr findPlaneForDistance(double d, const vec3& normal, double epsMergePoints, double epsPlaneAngle) + { + auto it = m_mapInPlaneFaces.lower_bound(d); + + auto low = m_mapInPlaneFaces.lower_bound(d); + if (low == m_mapInPlaneFaces.end()) + { + if (m_mapInPlaneFaces.size() > 0) + { + double lastElement = m_mapInPlaneFaces.rbegin()->first; + double delta = lastElement - d; + if (std::abs(delta) <= epsMergePoints) + { + std::vector >& existingPlanes = m_mapInPlaneFaces.rbegin()->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + } + + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + else if (low == m_mapInPlaneFaces.begin()) + { + double existingD = low->first; + double delta = existingD - d; + if (std::abs(delta) <= epsMergePoints) + { + if (low != m_mapInPlaneFaces.end()) + { + // check if next element is closer + auto itNext = low; + ++itNext; + if (itNext != m_mapInPlaneFaces.end()) + { + double nextD = itNext->first; + double deltaNext = nextD - d; + if (std::abs(deltaNext) <= std::abs(delta)) + { + std::vector >& existingPlanes = itNext->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + } + } + std::vector >& existingPlanes = low->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + else + { + auto prev = std::prev(low); + double dzPrev = d - prev->first; + double dLow = low->first - d; + if (std::abs(dzPrev) < std::abs(dLow)) + { + if (std::abs(dzPrev) <= epsMergePoints) + { + std::vector >& existingPlanes = prev->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + else + { + if (std::abs(dLow) <= epsMergePoints) + { + std::vector >& existingPlanes = low->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + } + + return shared_ptr(); + } + + void addMeshToPlaneCache(shared_ptr >& meshset, const GeomProcessingParams& params) + { + if (!meshset) + { + return; + } + + double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; + double epsDistanceSinglePoints = params.epsMergePoints * 10.0; + double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; + + for (auto mesh1 : meshset->meshes) + { + if (mesh1->faces.size() > 1000) + { + continue; + } + + for (auto face1 : mesh1->faces) + { + carve::geom::plane<3>& facePlane = face1->plane; + const vec3 face1Normal = facePlane.N; + + shared_ptr existingPlane = findPlaneForDistance(facePlane.d, face1Normal, epsDistanceSinglePoints, epsAngle); + if (!existingPlane) + { + existingPlane = make_shared(); + existingPlane->setFaces.insert(face1); + existingPlane->plane = facePlane; + m_mapInPlaneFaces.insert({ facePlane.d, {existingPlane} }); + continue; + } + +#ifdef _DEBUG + const vec3 setOfFacesPlaneNormal = existingPlane->plane.N; + double dotProduct = dot(face1Normal, setOfFacesPlaneNormal); + if (std::abs(dotProduct + 1.0) > epsAngle * 1.1 && std::abs(dotProduct - 1.0) > epsAngle * 1.1) + { + std::cerr << "check angle in plane failed" << std::endl; + } + + double deltaD = facePlane.d - existingPlane->plane.d; + if (std::abs(deltaD) > epsDistanceFaceCentroids * 1.1) + { + std::cerr << "check distance in plane failed" << std::endl; + } +#endif + + existingPlane->plane.d = (existingPlane->plane.d + facePlane.d) * 0.5; + existingPlane->plane.N = (existingPlane->plane.N + facePlane.N) * 0.5; // could be also weighted by face area + existingPlane->setFaces.insert(face1); + + } + } + } + + void flattenFacePlanes(shared_ptr >& meshset1, shared_ptr >& meshset2, const GeomProcessingParams& params) + { + if (!meshset1) + { + return; + } + + + // collect all faces in one plane with high epsilon + + addMeshToPlaneCache(meshset1, params); + addMeshToPlaneCache(meshset2, params); + + // then re-compute the set of faces plane normals. Then check again which faces are in the plane + compute(params); + + } + + void compute( const GeomProcessingParams& params) + { + // project face points into coplanar face + double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; + double epsDistanceSinglePoints = params.epsMergePoints * 10.0; + double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; + double epsMinDistanceMovePoints2 = params.epsMergePoints * 0.01 * params.epsMergePoints * 0.01; + + for (auto it : m_mapInPlaneFaces) + { + std::vector >& vecOfSetOfFaces = it.second; + + for (shared_ptr& setOfFaces : vecOfSetOfFaces) + { + std::vector*> verticesAllFacesInPlane; + for (carve::mesh::Face<3>*face : setOfFaces->setFaces) + { + const carve::mesh::Edge<3>* e = face->edge; + for (size_t ii = 0; ii < face->n_edges; ++ii) + { + verticesAllFacesInPlane.push_back(e->vert); + e = e->next; + } + face->getVertices(verticesAllFacesInPlane); + } + + vec3 normalAllFaces = GeomUtils::computePolygonNormal(verticesAllFacesInPlane); + + double dotProduct = dot(setOfFaces->plane.N, normalAllFaces); + if (std::abs(dotProduct + 1.0) > epsAngle * 100) + { + // opposite direction + normalAllFaces = -normalAllFaces; + } + + dotProduct = dot(setOfFaces->plane.N, normalAllFaces); + + if (std::abs(dotProduct - 1.0) > epsAngle) + { + // should not be so much off, but can happen + } + //setOfFaces->plane.N = normalAllFaces; + + for (carve::mesh::Face<3>*face : setOfFaces->setFaces) + { + face->plane.d = setOfFaces->plane.d; + face->plane.N = setOfFaces->plane.N; + std::vector*> vertices; + face->getVertices(vertices); + for (auto vert : vertices) + { + vec3 v; + double t; + + const vec3& rayPoint1 = vert->v; + vec3 rayPoint2 = rayPoint1 + face->plane.N; + // const Plane& p, const Vector& v1, const Vector& v2, Vector& v, double& t, double eps) + carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(face->plane, rayPoint1, rayPoint2, v, t, params.epsMergePoints); + if (intersect > 0) + { + double dx = vert->v.x - v.x; + double dy = vert->v.y - v.y; + double dz = vert->v.z - v.z; + + double distance2 = dx * dx + dy * dy + dz * dz; + + if (distance2 > 1e-20 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + { + vert->v = v; + ++m_numCorrectedVertices; + if (face->mesh) + { + face->mesh->m_volume = std::numeric_limits::quiet_NaN(); + } +#ifdef _DEBUG + if (distance2 > EPS_M9) + { + int wait = 0; + } +#endif + } + } + } + } + } + } + } + + + + static void flattenFacePlanes_old(shared_ptr >& op1, shared_ptr >& op2, const GeomProcessingParams& params) + { + // project face points into coplanar face + double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; + double epsDistanceSinglePoints = params.epsMergePoints * 10.0; + double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; + double epsMinDistanceMovePoints2 = params.epsMergePoints * 0.01 * params.epsMergePoints * 0.01; + + // TODO: integrate this into other function. First merge all coplanar faces, then do this: + + // TODO: use caching from single mesh flattening, first for op1, then op1 + + for (auto mesh1 : op1->meshes) + { + if (mesh1->faces.size() > 500) + { + continue; + } + + for (auto face1 : mesh1->faces) + { + vec3 face1Centroid = face1->centroid(); + const vec3 face1Normal = face1->plane.N; + bool face1dumped = false; + + for (auto mesh2 : op2->meshes) + { + if (mesh2->faces.size() > 1000) + { + continue; + } + for (auto face2 : mesh2->faces) + { + const vec3 face2Normal = face2->plane.N; + + double dotProduct = dot(face1Normal, face2Normal); + if (std::abs(dotProduct + 1.0) > epsAngle && std::abs(dotProduct - 1.0) > epsAngle) + { + continue; + } + + // check if faces overlap + carve::geom::aabb<3> bbox1 = face1->getAABB(); + carve::geom::aabb<3> bbox2 = face2->getAABB(); + if (bbox1.extent.x < epsDistanceSinglePoints) { bbox1.extent.x = epsDistanceSinglePoints * 2; bbox1.pos.x -= epsDistanceSinglePoints; } + if (bbox1.extent.y < epsDistanceSinglePoints) { bbox1.extent.y = epsDistanceSinglePoints * 2; bbox1.pos.y -= epsDistanceSinglePoints; } + if (bbox1.extent.z < epsDistanceSinglePoints) { bbox1.extent.z = epsDistanceSinglePoints * 2; bbox1.pos.z -= epsDistanceSinglePoints; } + + if (bbox2.extent.x < epsDistanceSinglePoints) { bbox2.extent.x = epsDistanceSinglePoints * 2; bbox2.pos.x -= epsDistanceSinglePoints; } + if (bbox2.extent.y < epsDistanceSinglePoints) { bbox2.extent.y = epsDistanceSinglePoints * 2; bbox2.pos.y -= epsDistanceSinglePoints; } + if (bbox2.extent.z < epsDistanceSinglePoints) { bbox2.extent.z = epsDistanceSinglePoints * 2; bbox2.pos.z -= epsDistanceSinglePoints; } + + bbox1.extent.x *= 2; + bbox1.extent.y *= 2; + bbox1.extent.z *= 2; + + bbox2.extent.x *= 2; + bbox2.extent.y *= 2; + bbox2.extent.z *= 2; + + if (!bbox1.intersects(bbox2, params.epsMergePoints)) + { + continue; + } + + // faces are parallel + vec3 face2Centroid = face2->centroid(); + double distance = GeomUtils::distancePointPlane(face2Centroid, face1Normal, face1Centroid); + if (std::abs(distance) < epsDistanceFaceCentroids) + { +#ifdef _DEBUG + if (params.debugDump) + { + if (!face1dumped) + { + glm::vec4 color(0, 0.4, 0.3, 1); + GeomDebugDump::dumpFacePolygon(face1, color, false); + face1dumped = true; + } + + glm::vec4 color(0, 1, 1, 1); + GeomDebugDump::dumpFacePolygon(face2, color, false); + } + + carve::geom::aabb<3> bbox1test; + carve::geom::aabb<3> bbox2test; + bbox1test.pos = carve::geom::VECTOR(0, 0, 0); + bbox1test.extent = carve::geom::VECTOR(1, 1, 1); + + bbox2test.pos = carve::geom::VECTOR(2 + params.epsMergePoints, 2 + params.epsMergePoints, 2 + params.epsMergePoints); + bbox2test.extent = carve::geom::VECTOR(1, 1, 1); + bool intersects = bbox1test.intersects(bbox2test, params.epsMergePoints); +#endif + + // faces are in plane. Now check if all vertices have a similar distance + bool allPointsInPlane = true; + auto edge2 = face2->edge; + for (size_t jjEdge = 0; jjEdge < face2->n_edges; ++jjEdge) + { + carve::mesh::Vertex<3>* vert = edge2->vert; + + double distance = GeomUtils::distancePointPlane(vert->v, face1Normal, face1Centroid); + + edge2 = edge2->next; + + if (std::abs(distance) > epsDistanceSinglePoints) + { + + allPointsInPlane = false; + break; + } + //mapDistances.insert({ distance, { face1, vert } }); + } + + + if (allPointsInPlane) + { + //intersectRayTriangle() + double area1 = MeshOps::computeFaceArea(face1); + double area2 = MeshOps::computeFaceArea(face2); + + carve::mesh::Face<3>* smallerFace = face1; + carve::mesh::Face<3>* biggerFace = face2; + if (area1 > area2) + { + smallerFace = face2; + biggerFace = face1; + } + + std::vector*> vertices; + smallerFace->getVertices(vertices); + for (auto vert : vertices) + { + vec3 v; + double t; + const carve::geom3d::Plane& plane = biggerFace->plane; + const vec3& rayPoint1 = vert->v; + vec3 rayPoint2 = rayPoint1 + plane.N; + // const Plane& p, const Vector& v1, const Vector& v2, Vector& v, double& t, double eps) + carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(plane, rayPoint1, rayPoint2, v, t, params.epsMergePoints); + if (intersect > 0) + { + double dx = vert->v.x - v.x; + double dy = vert->v.y - v.y; + double dz = vert->v.z - v.z; + + double distance2 = dx * dx + dy * dy + dz * dz; + //if (distance2 > epsMinDistanceMovePoints2 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + //{ + // vert->v = v; + //} + + if (distance2 > 1e-18 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + { + vert->v = v; + } + } + } + } + } + } + } + + //#ifdef _DEBUG + // carve::geom::aabb<3> bbox1 = op1->getAABB(); + // carve::geom::aabb<3> bbox2 = op2->getAABB(); + // bbox1.unionAABB(bbox2); + // //GeomDebugDump::moveOffset(bbox1.extent.y + 0.2); + //#endif + } + } + } +}; + + + +/* -*-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. + + +#pragma once + +#include +#include "GeomUtils.h" +#include +#include "GeometryInputData.h" +#include "MeshOps.h" + +class MeshFlattener +{ +public: + size_t m_numCorrectedVertices = 0; + struct SetOfFacesInPlane { + carve::geom::plane<3> plane; + std::set* > setFaces; + }; + std::multimap > > m_mapInPlaneFaces; + + shared_ptr findPlaneForDistance( const std::vector >& vecOfPlanes, const vec3& normal, double epsAngle) + { + for (const shared_ptr& setOfFacesInPlane : vecOfPlanes) + { + vec3& setOfFacesNormal = setOfFacesInPlane->plane.N; + double dotProduct = dot(setOfFacesNormal, normal); + if (std::abs(dotProduct + 1.0) > epsAngle && std::abs(dotProduct - 1.0) > epsAngle) + { + continue; + } + return setOfFacesInPlane; + } + return shared_ptr(); + } + + shared_ptr findPlaneForDistance(double d, const vec3& normal, double epsMergePoints, double epsPlaneAngle) + { + auto it = m_mapInPlaneFaces.lower_bound(d); + + auto low = m_mapInPlaneFaces.lower_bound(d); + if (low == m_mapInPlaneFaces.end()) + { + if (m_mapInPlaneFaces.size() > 0) + { + double lastElement = m_mapInPlaneFaces.rbegin()->first; + double delta = lastElement - d; + if (std::abs(delta) <= epsMergePoints) + { + std::vector >& existingPlanes = m_mapInPlaneFaces.rbegin()->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + } + + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + else if (low == m_mapInPlaneFaces.begin()) + { + double existingD = low->first; + double delta = existingD - d; + if (std::abs(delta) <= epsMergePoints) + { + if (low != m_mapInPlaneFaces.end()) + { + // check if next element is closer + auto itNext = low; + ++itNext; + if (itNext != m_mapInPlaneFaces.end()) + { + double nextD = itNext->first; + double deltaNext = nextD - d; + if (std::abs(deltaNext) <= std::abs(delta)) + { + std::vector >& existingPlanes = itNext->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + } + } + std::vector >& existingPlanes = low->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + else + { + auto prev = std::prev(low); + double dzPrev = d - prev->first; + double dLow = low->first - d; + if (std::abs(dzPrev) < std::abs(dLow)) + { + if (std::abs(dzPrev) <= epsMergePoints) + { + std::vector >& existingPlanes = prev->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + else + { + if (std::abs(dLow) <= epsMergePoints) + { + std::vector >& existingPlanes = low->second; + return findPlaneForDistance(existingPlanes, normal, epsPlaneAngle); + } + else + { + return shared_ptr(); + //size_t vertex_index = m_poly_data->addVertex(pt); + //m_mapInPlaneFaces.insert({ { vertex_z, vertex_index } }); + //return vertex_index; + } + } + } + + return shared_ptr(); + } + + void flattenFacePlanes(shared_ptr >& meshset, const GeomProcessingParams& params) + { + if (!meshset) + { + return; + } + // project face points into coplanar face + double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; + double epsDistanceSinglePoints = params.epsMergePoints * 10.0; + double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; + double epsMinDistanceMovePoints2 = params.epsMergePoints * 0.01 * params.epsMergePoints * 0.01; + + // collect all faces in one plane with high epsilon + + + for (auto mesh1 : meshset->meshes) + { + if (mesh1->faces.size() > 1000) + { + continue; + } + + for (auto face1 : mesh1->faces) + { + carve::geom::plane<3>& facePlane = face1->plane; + const vec3 face1Normal = facePlane.N; + + shared_ptr existingPlane = findPlaneForDistance(facePlane.d, face1Normal, epsDistanceSinglePoints, epsAngle); + if (!existingPlane) + { + existingPlane = make_shared(); + existingPlane->setFaces.insert(face1); + existingPlane->plane = facePlane; + m_mapInPlaneFaces.insert({ facePlane.d, {existingPlane} }); + continue; + } + +#ifdef _DEBUG + const vec3 setOfFacesPlaneNormal = existingPlane->plane.N; + double dotProduct = dot(face1Normal, setOfFacesPlaneNormal); + if (std::abs(dotProduct + 1.0) > epsAngle * 1.1 && std::abs(dotProduct - 1.0) > epsAngle*1.1) + { + std::cerr << "check angle in plane failed" << std::endl; + } + + double deltaD = facePlane.d - existingPlane->plane.d; + if (std::abs(deltaD) > epsDistanceFaceCentroids*1.1) + { + std::cerr << "check distance in plane failed" << std::endl; + } +#endif + + existingPlane->plane.d = (existingPlane->plane.d + facePlane.d) * 0.5; + existingPlane->plane.N = (existingPlane->plane.N + facePlane.N) * 0.5; // could be also weighted by face area + existingPlane->setFaces.insert(face1); + + } + + } + + // then re-compute the set of faces plane normals. Then check again which faces are in the plane + + for (auto it : m_mapInPlaneFaces) + { + std::vector >& vecOfSetOfFaces = it.second; + + for (shared_ptr& setOfFaces : vecOfSetOfFaces) + { + std::vector*> verticesAllFacesInPlane; + for (carve::mesh::Face<3>*face : setOfFaces->setFaces) + { + const carve::mesh::Edge<3>* e = face->edge; + for (size_t ii = 0; ii < face->n_edges; ++ii) + { + verticesAllFacesInPlane.push_back(e->vert); + e = e->next; + } + face->getVertices(verticesAllFacesInPlane); + } + + vec3 normalAllFaces = GeomUtils::computePolygonNormal(verticesAllFacesInPlane); + + double dotProduct = dot(setOfFaces->plane.N, normalAllFaces); + if (std::abs(dotProduct + 1.0) > epsAngle*100) + { + // opposite direction + normalAllFaces = -normalAllFaces; + } + + dotProduct = dot(setOfFaces->plane.N, normalAllFaces); + + if (std::abs(dotProduct - 1.0) > epsAngle) + { + // should not be so much off, but can happen + } + //setOfFaces->plane.N = normalAllFaces; + + for (carve::mesh::Face<3>*face : setOfFaces->setFaces) + { + face->plane.d = setOfFaces->plane.d; + face->plane.N = setOfFaces->plane.N; + std::vector*> vertices; + face->getVertices(vertices); + for (auto vert : vertices) + { + vec3 v; + double t; + + const vec3& rayPoint1 = vert->v; + vec3 rayPoint2 = rayPoint1 + face->plane.N; + // const Plane& p, const Vector& v1, const Vector& v2, Vector& v, double& t, double eps) + carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(face->plane, rayPoint1, rayPoint2, v, t, params.epsMergePoints); + if (intersect > 0) + { + double dx = vert->v.x - v.x; + double dy = vert->v.y - v.y; + double dz = vert->v.z - v.z; + + double distance2 = dx * dx + dy * dy + dz * dz; + + if (distance2 > 0.0 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + { + vert->v = v; + ++m_numCorrectedVertices; +#ifdef _DEBUG + if (distance2 > EPS_M9) + { + int wait = 0; + } +#endif + } + } + } + } + } + } + } + + + + static void flattenFacePlanes(shared_ptr >& op1, shared_ptr >& op2, const GeomProcessingParams& params) + { + // project face points into coplanar face + double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; + double epsDistanceSinglePoints = params.epsMergePoints * 10.0; + double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; + double epsMinDistanceMovePoints2 = params.epsMergePoints * 0.01 * params.epsMergePoints * 0.01; + + // TODO: integrate this into other function. First merge all coplanar faces, then do this: + + // TODO: use caching from single mesh flattening, first for op1, then op1 + + for (auto mesh1 : op1->meshes) + { + if (mesh1->faces.size() > 500) + { + continue; + } + + for (auto face1 : mesh1->faces) + { + vec3 face1Centroid = face1->centroid(); + const vec3 face1Normal = face1->plane.N; + bool face1dumped = false; + + for (auto mesh2 : op2->meshes) + { + if (mesh2->faces.size() > 1000) + { + continue; + } + for (auto face2 : mesh2->faces) + { + const vec3 face2Normal = face2->plane.N; + + double dotProduct = dot(face1Normal, face2Normal); + if (std::abs(dotProduct + 1.0) > epsAngle && std::abs(dotProduct - 1.0) > epsAngle) + { + continue; + } + + // check if faces overlap + carve::geom::aabb<3> bbox1 = face1->getAABB(); + carve::geom::aabb<3> bbox2 = face2->getAABB(); + if (bbox1.extent.x < epsDistanceSinglePoints) { bbox1.extent.x = epsDistanceSinglePoints * 2; bbox1.pos.x -= epsDistanceSinglePoints; } + if (bbox1.extent.y < epsDistanceSinglePoints) { bbox1.extent.y = epsDistanceSinglePoints * 2; bbox1.pos.y -= epsDistanceSinglePoints; } + if (bbox1.extent.z < epsDistanceSinglePoints) { bbox1.extent.z = epsDistanceSinglePoints * 2; bbox1.pos.z -= epsDistanceSinglePoints; } + + if (bbox2.extent.x < epsDistanceSinglePoints) { bbox2.extent.x = epsDistanceSinglePoints * 2; bbox2.pos.x -= epsDistanceSinglePoints; } + if (bbox2.extent.y < epsDistanceSinglePoints) { bbox2.extent.y = epsDistanceSinglePoints * 2; bbox2.pos.y -= epsDistanceSinglePoints; } + if (bbox2.extent.z < epsDistanceSinglePoints) { bbox2.extent.z = epsDistanceSinglePoints * 2; bbox2.pos.z -= epsDistanceSinglePoints; } + + bbox1.extent.x *= 2; + bbox1.extent.y *= 2; + bbox1.extent.z *= 2; + + bbox2.extent.x *= 2; + bbox2.extent.y *= 2; + bbox2.extent.z *= 2; + + if (!bbox1.intersects(bbox2, params.epsMergePoints)) + { + continue; + } + + // faces are parallel + vec3 face2Centroid = face2->centroid(); + double distance = GeomUtils::distancePointPlane(face2Centroid, face1Normal, face1Centroid); + if (std::abs(distance) < epsDistanceFaceCentroids) + { +#ifdef _DEBUG + if (params.debugDump) + { + if (!face1dumped) + { + glm::vec4 color(0, 0.4, 0.3, 1); + GeomDebugDump::dumpFacePolygon(face1, color, false); + face1dumped = true; + } + + glm::vec4 color(0, 1, 1, 1); + GeomDebugDump::dumpFacePolygon(face2, color, false); + } + + carve::geom::aabb<3> bbox1test; + carve::geom::aabb<3> bbox2test; + bbox1test.pos = carve::geom::VECTOR(0, 0, 0); + bbox1test.extent = carve::geom::VECTOR(1, 1, 1); + + bbox2test.pos = carve::geom::VECTOR(2 + params.epsMergePoints, 2 + params.epsMergePoints, 2 + params.epsMergePoints); + bbox2test.extent = carve::geom::VECTOR(1, 1, 1); + bool intersects = bbox1test.intersects(bbox2test, params.epsMergePoints); +#endif + + // faces are in plane. Now check if all vertices have a similar distance + bool allPointsInPlane = true; + auto edge2 = face2->edge; + for (size_t jjEdge = 0; jjEdge < face2->n_edges; ++jjEdge) + { + carve::mesh::Vertex<3>* vert = edge2->vert; + + double distance = GeomUtils::distancePointPlane(vert->v, face1Normal, face1Centroid); + + edge2 = edge2->next; + + if (std::abs(distance) > epsDistanceSinglePoints) + { + + allPointsInPlane = false; + break; + } + //mapDistances.insert({ distance, { face1, vert } }); + } + + + if (allPointsInPlane) + { + //intersectRayTriangle() + double area1 = MeshOps::computeFaceArea(face1); + double area2 = MeshOps::computeFaceArea(face2); + + carve::mesh::Face<3>* smallerFace = face1; + carve::mesh::Face<3>* biggerFace = face2; + if (area1 > area2) + { + smallerFace = face2; + biggerFace = face1; + } + + std::vector*> vertices; + smallerFace->getVertices(vertices); + for (auto vert : vertices) + { + vec3 v; + double t; + const carve::geom3d::Plane& plane = biggerFace->plane; + const vec3& rayPoint1 = vert->v; + vec3 rayPoint2 = rayPoint1 + plane.N; + // const Plane& p, const Vector& v1, const Vector& v2, Vector& v, double& t, double eps) + carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(plane, rayPoint1, rayPoint2, v, t, params.epsMergePoints); + if (intersect > 0) + { + double dx = vert->v.x - v.x; + double dy = vert->v.y - v.y; + double dz = vert->v.z - v.z; + + double distance2 = dx * dx + dy * dy + dz * dz; + //if (distance2 > epsMinDistanceMovePoints2 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + //{ + // vert->v = v; + //} + + if (distance2 > 0.0 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) + { + vert->v = v; + } + } + } + } + } + } + } + + //#ifdef _DEBUG + // carve::geom::aabb<3> bbox1 = op1->getAABB(); + // carve::geom::aabb<3> bbox2 = op2->getAABB(); + // bbox1.unionAABB(bbox2); + // //GeomDebugDump::moveOffset(bbox1.extent.y + 0.2); + //#endif + } + } + } +}; +*/ \ No newline at end of file diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshOps - Copy.cpp b/IfcPlusPlus/src/ifcpp/geometry/MeshOps - Copy.cpp new file mode 100644 index 000000000..7710659cf --- /dev/null +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshOps - Copy.cpp @@ -0,0 +1,3734 @@ +/* -*-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 "IncludeCarveHeaders.h" +#include +#include +#include "EdgeLoopFinder.h" +#include "MeshSimplifier.h" +#include "MeshOps.h" +using namespace IFC4X3; + +size_t MeshOps::getNumFaces(const carve::mesh::MeshSet<3>* meshset) +{ + size_t num_faces = 0; + for (size_t i = 0; i < meshset->meshes.size(); ++i) + { + num_faces += meshset->meshes[i]->faces.size(); + } + return num_faces; +} + +void MeshOps::recalcMeshSet(shared_ptr >& meshset, double eps) +{ + for (auto mesh : meshset->meshes) + { + mesh->cacheEdges(); + mesh->recalc(eps); + } +} + +double MeshOps::computeFaceArea(const carve::mesh::Face<3>* face) +{ + if (face->edge == nullptr) + { + return 0; + } + + double face_area = 0; + if (face->nVertices() == 3) + { + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + carve::geom::vector<3>& v1 = edge->v1()->v; + carve::geom::vector<3>& v2 = edge->v2()->v; + if (edge->next) + { + carve::geom::vector<3>& v3 = edge->next->v2()->v; + carve::geom::vector<3> side1 = v2 - v1; + carve::geom::vector<3> side2 = v3 - v2; + carve::geom::vector<3> c = cross(side1, side2); + double len_square = c.length2(); + if (len_square > EPS_M14 * 0.001) + { + double area = sqrt(len_square) * 0.5; + face_area += std::abs(area); + } + } + } + } + else + { + std::vector::vertex_t* > faceVertices; + face->getVertices(faceVertices); + if (faceVertices.size() > 2) + { + size_t n = faceVertices.size(); + + carve::geom::vector<3> normal; + carve::geom::vector<3> a; + carve::geom::vector<3> b = faceVertices[n - 2]->v; + carve::geom::vector<3> c = faceVertices[n - 1]->v; + carve::geom::vector<3> s; + + for (int i = 0; i < n; ++i) + { + a = b; + b = c; + c = faceVertices[i]->v; + + normal.x += b.y * (c.z - a.z); + normal.y += b.z * (c.x - a.x); + normal.z += b.x * (c.y - a.y); + + s += c; + } + + double length = normal.length();// glm::length(normal); + if (std::abs(length) < EPS_M8) + { + return false; + } + + normal /= length; + double area = 0.5 * length; + face_area += area; + } + } + return face_area; +} + +double MeshOps::computeFaceArea(const carve::mesh::Face<3>* face, double& longestEdge) +{ + if (face->edge == nullptr) + { + return 0; + } + + longestEdge = 0; + double face_area = 0; + if (face->nVertices() == 3) + { + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + carve::geom::vector<3>& v1 = edge->v1()->v; + carve::geom::vector<3>& v2 = edge->v2()->v; + if (edge->next) + { + carve::geom::vector<3>& v3 = edge->next->v2()->v; + carve::geom::vector<3> side1 = v2 - v1; + carve::geom::vector<3> side2 = v3 - v2; + carve::geom::vector<3> c = cross(side1, side2); + double len_square = c.length2(); + //if (len_square > EPS_M14 * 0.001) + { + double length1 = side1.length(); + double length2 = side2.length(); + double length3 = (v3 - v1).length(); + double area = sqrt(len_square) * 0.5; + if (length1 > longestEdge) + { + longestEdge = length1; + } + if (length2 > longestEdge) + { + longestEdge = length2; + } + if (length3 > longestEdge) + { + longestEdge = length3; + } + face_area += std::abs(area); + } + } + } + } + else + { + std::vector::vertex_t* > faceVertices; + const carve::mesh::Edge<3>* e = face->edge; + for (size_t ii = 0; ii < face->n_edges; ++ii) + { + faceVertices.push_back(e->vert); + + double length = e->length(); + if (length > longestEdge) + { + longestEdge = length; + } + + e = e->next; + } + + if (faceVertices.size() > 2) + { + size_t n = faceVertices.size(); + + carve::geom::vector<3> normal; + carve::geom::vector<3> a; + carve::geom::vector<3> b = faceVertices[n - 2]->v; + carve::geom::vector<3> c = faceVertices[n - 1]->v; + carve::geom::vector<3> s; + + for (int i = 0; i < n; ++i) + { + a = b; + b = c; + c = faceVertices[i]->v; + + normal.x += b.y * (c.z - a.z); + normal.y += b.z * (c.x - a.x); + normal.z += b.x * (c.y - a.y); + + s += c; + } + + double length = normal.length(); + if (std::abs(length) < EPS_M8) + { + return 0; + } + + normal /= length; + double area = 0.5 * length; + face_area += area; + } + } + return face_area; +} + + +void splitIntoSubLoops(const std::vector >& polygonMerged, std::vector > >& polygonLoops, std::map >& mapInOut, double eps, bool dumpPolygon) +{ + // find inner loops and shift them such that there is no self-intersection + std::vector> previousLoop; + + for (size_t ii = 0; ii < polygonMerged.size(); ++ii) + { + const carve::geom::vector<2>& point = polygonMerged[ii]; + + + for (size_t jj = 0; jj < previousLoop.size(); ++jj) + { + const carve::geom::vector<2>& previousPoint = previousLoop[jj]; + double dx = previousPoint[0] - point.x; + if (std::abs(dx) < eps) + { + double dy = previousPoint[1] - point.y; + if (std::abs(dy) < eps) + { + // a loop should have at least 3 points + if (previousLoop.size() < 3) + { + previousLoop.clear(); + break; + } + else + { + if (jj == 1) + { + // connecting edge between outer and inner loop, erase + if (previousLoop.size() > 1) + { + previousLoop.erase(previousLoop.begin()); + } + polygonLoops.push_back(previousLoop); + previousLoop.clear(); + break; + } + // back on point in previous loop. Split here and begin new loop + polygonLoops.push_back(previousLoop); + previousLoop.clear(); + } + break; + } + } + } + + size_t loopIndex = polygonLoops.size(); + size_t pointIndex = previousLoop.size(); + mapInOut[ii] = { loopIndex, pointIndex }; + previousLoop.push_back(point); + } + + if (previousLoop.size() > 2) + { + polygonLoops.push_back(previousLoop); + } + + std::map > > > mapAreaLoops; + for (size_t ii = 0; ii < polygonLoops.size(); ++ii) + { + std::vector >& loop = polygonLoops[ii]; + double area = std::abs(GeomUtils::signedArea(loop)); + auto itFind = mapAreaLoops.find(area); + if (itFind == mapAreaLoops.end()) + { + std::vector > >& vecOfLoops = itFind->second; + vecOfLoops.push_back({ loop }); + } + else + { + mapAreaLoops.insert({ area, { loop } }); + } + } + polygonLoops.clear(); + for (auto it = mapAreaLoops.rbegin(); it != mapAreaLoops.rend(); ++it) + { + std::vector > >& vecOfLoops = it->second; + for (std::vector >& loop : vecOfLoops) + { + polygonLoops.push_back(loop); + } + } +} + +void shiftSubLoops(std::vector >& polygonMerged, std::map& mapInOut, double eps, bool dumpPolygon) +{ + // find inner loops and shift them such that there is no self-intersection + std::vector> previousLoop; + std::vector > > polygonLoops; + for (size_t ii = 0; ii < polygonMerged.size(); ++ii) + { + const carve::geom::vector<2>& point = polygonMerged[ii]; + + for (size_t jj = 0; jj < previousLoop.size(); ++jj) + { + const carve::geom::vector<2>& previousPoint = previousLoop[jj]; + double dx = previousPoint[0] - point.x; + if (std::abs(dx) < eps) + { + double dy = previousPoint[1] - point.y; + if (std::abs(dy) < eps) + { + // a loop should have at least 3 points + if (previousLoop.size() < 3) + { + previousLoop.clear(); + break; + } + else + { + if (jj == 1) + { + // connecting edge between outer and inner loop, erase + if (previousLoop.size() > 1) + { + previousLoop.erase(previousLoop.begin()); + } + polygonLoops.push_back(previousLoop); + previousLoop.clear(); + break; + } + // back on point in previous loop. Split here and begin new loop + polygonLoops.push_back(previousLoop); + previousLoop.clear(); + } + break; + } + } + } + + size_t loopIndex = polygonLoops.size(); + size_t pointIndex = previousLoop.size(); + //mapInOut[ii] = { loopIndex, pointIndex }; + previousLoop.push_back(point); + } + + if (previousLoop.size() > 2) + { + //polygon_earcut_out.push_back(previousLoop); + } + +#ifdef _DEBUG + if (polygonMerged.size() > 5 && dumpPolygon) + { + glm::vec4 color(0.3, 0.33, 0.33, 1.); + GeomDebugDump::dumpLocalCoordinateSystem(); + GeomDebugDump::moveOffset(0.1); + GeomDebugDump::dumpPolyline(polygonMerged, color, 0, true, false); + GeomDebugDump::moveOffset(0.1); + GeomDebugDump::dumpPolyline(polygonLoops, color, true, false); + } +#endif +} + +inline double getFaceArea2D(const carve::mesh::Face<3>* face) +{ + std::vector > facePoints; + std::vector > facePointsArray2D; + face->getProjectedVertices(facePoints); + GeomUtils::poly2VecToArray2(facePoints, facePointsArray2D); + double area = GeomUtils::signedArea(facePointsArray2D); + return area; +} + +void getFacePoints(const carve::mesh::Face<3>* face, std::vector& facePoints) +{ + facePoints.clear(); + std::vector::vertex_t* > verts3d; + face->getVertices(verts3d); + + for (size_t i3 = 0; i3 < verts3d.size(); ++i3) + { + const carve::geom::vector<3>& vertex_point = verts3d[i3]->v; + facePoints.push_back(vertex_point); + } +} + +bool isBetterForExport(MeshSetInfo infoTriangulated, MeshSetInfo infoBefore) +{ + if (infoTriangulated.meshSetValid && infoTriangulated.maxNumberOfEdgesPerFace == 3) + { + return true; + } + + if (infoTriangulated.maxNumberOfEdgesPerFace == 3) + { + size_t numEdgesBefore = infoBefore.numClosedEdges + infoBefore.numOpenEdges(); + size_t numEdgesTriangulated = infoTriangulated.numClosedEdges + infoTriangulated.numOpenEdges(); + bool condition2 = infoTriangulated.numClosedEdges > numEdgesBefore * 0.7; + bool condition3 = infoTriangulated.numFaces > infoBefore.numFaces * 0.7; + if (infoTriangulated.meshSetValid || condition2 || condition3) + { + return true; + } + } + + if (infoTriangulated.maxNumberOfEdgesPerFace == 3 && infoBefore.maxNumberOfEdgesPerFace == 3 ) + { + // was already triangulated + return false; + } + + // futher tests + + return false; +} + +void MeshOps::checkAndFixMeshsetInverted( shared_ptr>& meshset, MeshSetInfo& info, const GeomProcessingParams& params) +{ + // this approach is maybe too simple: get highest and lowest face in x/y/z, then check if normal vector points in the same direction + // we can definitely not rely on meshset->is_negative + carve::mesh::Face<3>* minXFace = nullptr; + carve::mesh::Face<3>* maxXFace = nullptr; + carve::mesh::Face<3>* minYFace = nullptr; + carve::mesh::Face<3>* maxYFace = nullptr; + carve::mesh::Face<3>* minZFace = nullptr; + carve::mesh::Face<3>* maxZFace = nullptr; + double minX = DBL_MAX; + double maxX = -DBL_MAX; + double minY = DBL_MAX; + double maxY = -DBL_MAX; + double minZ = DBL_MAX; + double maxZ = -DBL_MAX; + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + size_t numOutwardNormals = 0; + size_t numInwardNormals = 0; + size_t numFaces = mesh->faces.size(); + + for (size_t jj = 0; jj < numFaces; ++jj) + { + carve::mesh::Face<3>* face = mesh->faces[jj]; + if (face == nullptr) + { + continue; + } + vec3 faceCentroid = face->centroid(); + if (faceCentroid.x < minX) + { + minX = faceCentroid.x; + minXFace = face; + } + + if (faceCentroid.z > maxX) + { + maxX = faceCentroid.x; + maxXFace = face; + } + + if (faceCentroid.y < minY) + { + minY = faceCentroid.y; + minYFace = face; + } + + if (faceCentroid.y > maxY) + { + maxY = faceCentroid.y; + maxYFace = face; + } + + if (faceCentroid.z < minZ) + { + minZ = faceCentroid.z; + minZFace = face; + } + + if (faceCentroid.z > maxZ) + { + maxZ = faceCentroid.z; + maxZFace = face; + } + } + } + + size_t countWindingProbablyCorrect = 0; + size_t countWindingProbablyInCorrect = 0; + if (minXFace) + { + vec3 faceNormal = minXFace->computeNormal(params.epsMergePoints); + if (faceNormal.x < 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (maxXFace) + { + vec3 faceNormal = maxXFace->computeNormal(params.epsMergePoints); + if (faceNormal.x > 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (minYFace) + { + vec3 faceNormal = minYFace->computeNormal(params.epsMergePoints); + if (faceNormal.y < 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (maxYFace) + { + vec3 faceNormal = maxYFace->computeNormal(params.epsMergePoints); + if (faceNormal.y > 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (minZFace) + { + vec3 faceNormal = minZFace->computeNormal(params.epsMergePoints); + if (faceNormal.z < 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (maxZFace) + { + vec3 faceNormal = maxZFace->computeNormal(params.epsMergePoints); + if (faceNormal.z > 0) ++countWindingProbablyCorrect; + else ++countWindingProbablyInCorrect; + } + + if (countWindingProbablyCorrect > countWindingProbablyInCorrect) + { + return; + } + + + { + std::set* > setSkipFaces; + std::set* > setFlipFaces; + PolyInputCache3D polyInput(params.epsMergePoints); + polyhedronFromMeshSet(meshset, setSkipFaces, setFlipFaces, polyInput); + + std::map mesh_input_options; + std::string details; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(polyInput.m_poly_data, params); + std::string details2; + correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + } + + bool invertDone = false; + if (correct) + { + reverseFacesInPolyhedronData(polyInput.m_poly_data); + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(polyInput.m_poly_data, params); + std::string details2; + correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + } + + MeshSetInfo infoInput; + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + + shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo infoFlippedFaces; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoFlippedFaces, params); + + if (MeshOps::isBetterForBoolOp(infoFlippedFaces, infoInput, true)) + { + meshset = meshsetFromPolyhedron; + invertDone = true; + } + else + { + // could be same and valid, so ok + if (infoFlippedFaces.meshSetValid) + { + meshset = meshsetFromPolyhedron; + invertDone = true; + } + } + } + + if (!invertDone) + { + meshset->invert(); + } + } +} + +void MeshOps::classifyMeshesInside(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params) +{ + // TODO: if this fails, sort meshes by volume. Biggest volume first -> probably outer mesh + + shared_ptr > outerMeshAsMeshset; + 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); + 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 (!outerMeshAsMeshset) + { + outerMeshAsMeshset = currentMeshAsMeshset; + continue; + } + + for (auto resultMesh : outerMeshAsMeshset->meshes) + { + if (resultMesh->is_negative) + { + resultMesh->invert(); + } + } + +#ifdef _DEBUG + glm::vec4 color(0.2, 0.2, 0.2, 1.); + if (params.debugDump) + { + GeomDebugDump::dumpMeshset(outerMeshAsMeshset.get(), color, true, false); + GeomDebugDump::dumpMeshset(currentMeshAsMeshset.get(), color, true, false); + } +#endif + + carve::geom::aabb<3> bbox1 = outerMeshAsMeshset->getAABB(); + carve::geom::aabb<3> bbox2 = currentMeshAsMeshset->getAABB(); + + bool intersectsWithEpsilon = bbox1.intersects(bbox2, params.epsMergePoints); + if (!intersectsWithEpsilon) + { + // currentMeshAsMeshset is not an inner mesh, so merge it with outerMeshAsMeshset + carve::csg::CSG csg(params.epsMergePoints); + shared_ptr > resultMerge(csg.compute(outerMeshAsMeshset.get(), currentMeshAsMeshset.get(), carve::csg::CSG::UNION, nullptr, carve::csg::CSG::CLASSIFY_EDGE)); + if (resultMerge) + { + outerMeshAsMeshset = resultMerge; + result = resultMerge; + } + continue; + } + + + size_t numOuterMeshes = 0; + for (auto mesh : outerMeshAsMeshset->meshes) + { + if (!mesh->is_inner_mesh) + { + ++numOuterMeshes; + } + } + + size_t resultNumOuterMeshes = 0; + size_t resultNumInnerMeshes = 0; + carve::csg::CSG csg(params.epsMergePoints); + shared_ptr > resultSubtract(csg.compute(outerMeshAsMeshset.get(), currentMeshAsMeshset.get(), carve::csg::CSG::A_MINUS_B, nullptr, carve::csg::CSG::CLASSIFY_EDGE)); + if (resultSubtract) + { + //if (resultSubtract->meshes.size() > 1) + { + for (auto mesh : resultSubtract->meshes) + { + if (mesh->is_inner_mesh) + { + ++resultNumInnerMeshes; + } + else + { + ++resultNumOuterMeshes; + + if (resultNumOuterMeshes > 1) + { +#ifdef _DEBUG + GeomDebugDump::moveOffset(1); + GeomDebugDump::dumpMesh(mesh, color, true); +#endif + } + } + } + } +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(1); + GeomDebugDump::dumpMeshset(resultSubtract.get(), color, true, false); + } +#endif + if (resultNumOuterMeshes == numOuterMeshes) + { + outerMeshAsMeshset = resultSubtract; + result = resultSubtract; + continue; + } + } + + carve::csg::CSG csg2(params.epsMergePoints); + shared_ptr > resultMerge(csg2.compute(outerMeshAsMeshset.get(), currentMeshAsMeshset.get(), carve::csg::CSG::UNION, nullptr, carve::csg::CSG::CLASSIFY_EDGE)); + if (resultMerge) + { + outerMeshAsMeshset = resultMerge; + result = resultMerge; + } + } +} + + +void MeshOps::retriangulateMeshSetForExport( shared_ptr >& meshset, const GeomProcessingParams& paramsInput) +{ + if (!meshset) + { + return; + } + + MeshSetInfo infoInput; + bool validInput = MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, paramsInput); + MeshOps::checkMeshSetNonNegativeAndClosed(meshset, paramsInput); + + if( infoInput.meshSetValid && infoInput.maxNumberOfEdgesPerFace == 3 ) + { + return; + } + + if ( infoInput.maxNumberOfEdgesPerFace == 3 ) + { + // TODO: check if this is sufficient + return; + } + + GeomProcessingParams params(paramsInput); + params.checkZeroAreaFaces = false; + params.allowDegenerateEdges = true; + params.allowFinEdges = true; + params.mergeAlignedEdges = true; + PolyInputCache3D poly_cache(params.epsMergePoints); + + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + const carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + const std::vector* >& vec_faces = mesh->faces; + + for (size_t i2 = 0; i2 < vec_faces.size(); ++i2) + { + const carve::mesh::Face<3>* face = vec_faces[i2]; + + std::vector faceBound; + getFacePoints(face, faceBound); + + if (faceBound.size() < 3) + { + // ignore face + continue; + } + + if (faceBound.size() == 3 && false) + { + int vertex_index_a = poly_cache.addPoint(faceBound[0]); + int vertex_index_b = poly_cache.addPoint(faceBound[1]); + int vertex_index_c = poly_cache.addPoint(faceBound[2]); + + if (vertex_index_a == vertex_index_b || vertex_index_a == vertex_index_c || vertex_index_b == vertex_index_c) + { + continue; + } + + poly_cache.m_poly_data->addFace(vertex_index_a, vertex_index_b, vertex_index_c); + continue; + } + + std::vector > inputBounds3D = { faceBound }; + FaceConverter::createTriangulated3DFace(inputBounds3D, poly_cache, params); + } + } + + std::string details = ""; + bool correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(poly_cache.m_poly_data, params); + correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); + +#ifdef _DEBUG + if (!correct) + { + std::cout << "fixPolyhedronData failed" << std::endl; + } +#endif + } + + shared_ptr> meshsetTriangulated = shared_ptr >(poly_cache.m_poly_data->createMesh(carve::input::opts(), params.epsMergePoints)); + MeshSetInfo infoTriangulated; + MeshOps::checkMeshSetValidAndClosed(meshsetTriangulated, infoTriangulated, params); + + if( isBetterForExport( infoTriangulated, infoInput )) + { + meshset.reset(); + meshset = meshsetTriangulated; + } + + //if (!validTriangulatedMesh) + { +#ifdef _DEBUG + bool dumpMesh = true; + if (validInput && dumpMesh && meshset->vertex_storage.size() > 60) + { + GeomDebugDump::DumpSettingsStruct dumpSet; + dumpSet.triangulateBeforeDump = false; + GeomProcessingParams paramCopy(params); + paramCopy.checkZeroAreaFaces = false; + + GeomDebugDump::dumpLocalCoordinateSystem(); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpWithLabel("triangulate:input ", meshset, dumpSet, paramCopy, true, true); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpWithLabel("triangulate:result", meshsetTriangulated, dumpSet, paramCopy, true, true); + } +#endif + MeshOps::checkMeshSetValidAndClosed(meshsetTriangulated, infoTriangulated, params); + + if (isBetterForExport(infoTriangulated, infoInput)) + { + meshset.reset(); + meshset = meshsetTriangulated; + } + } + + checkAndFixMeshsetInverted(meshset, infoTriangulated, params); +} + +void MeshOps::simplifyMeshSet(shared_ptr >& meshset, MeshSetInfo& infoMeshOut, const GeomProcessingParams& params) +{ + if (!meshset) + { + infoMeshOut.meshSetValid = false; + return; + } + + bool useDefaultSimplifier = true; + if (useDefaultSimplifier) + { + MeshSimplifier::simplifyMeshSet(meshset, params); + } + else + { + if (params.generalSettings) + { + if (params.generalSettings->m_callback_simplify_mesh) + { + params.generalSettings->m_callback_simplify_mesh(meshset, params); + } + } + } + + MeshOps::checkMeshSetValidAndClosed(meshset, infoMeshOut, params); +} + +void MeshOps::retriangulateMeshSetForBoolOp(shared_ptr >& meshset, bool ignoreResultOpenEdges, const GeomProcessingParams& paramsInput, size_t retryCount) +{ + if (!meshset) + { + return; + } + + MeshSetInfo info; + bool validInput = MeshOps::checkMeshSetValidAndClosed(meshset, info, paramsInput); + MeshOps::checkMeshSetNonNegativeAndClosed(meshset, paramsInput); + + if (info.maxNumberOfEdgesPerFace == 3 && validInput) + { + return; + } + + PolyInputCache3D poly_cache(paramsInput.epsMergePoints); + GeomProcessingParams params(paramsInput); + params.mergeAlignedEdges = true; + + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + const carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + const std::vector* >& vec_faces = mesh->faces; + + for (size_t i2 = 0; i2 < vec_faces.size(); ++i2) + { + const carve::mesh::Face<3>* face = vec_faces[i2]; + + std::vector faceBound; + getFacePoints(face, faceBound); + + if (faceBound.size() < 3) + { + // ignore face + continue; + } + + if (faceBound.size() == 3 ) + { + int vertex_index_a = poly_cache.addPoint(faceBound[0]); + int vertex_index_b = poly_cache.addPoint(faceBound[1]); + int vertex_index_c = poly_cache.addPoint(faceBound[2]); + + if (vertex_index_a == vertex_index_b || vertex_index_a == vertex_index_c || vertex_index_b == vertex_index_c) + { + continue; + } + + poly_cache.m_poly_data->addFace(vertex_index_a, vertex_index_b, vertex_index_c); + continue; + } + + std::vector > inputBounds3D = { faceBound }; + FaceConverter::createTriangulated3DFace(inputBounds3D, poly_cache, params); + } + } + + shared_ptr> meshsetTrinangulated1 = shared_ptr >(poly_cache.m_poly_data->createMesh(carve::input::opts(), params.epsMergePoints)); + + std::string details = ""; + bool correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(poly_cache.m_poly_data, params); +#ifdef _DEBUG + bool correct2 = checkPolyhedronData(poly_cache.m_poly_data, params, details); + if (!correct2) + { + std::cout << "fixPolyhedronData failed" << std::endl; + } +#endif + } + + shared_ptr> meshsetTrinangulated = shared_ptr >(poly_cache.m_poly_data->createMesh(carve::input::opts(), params.epsMergePoints)); + MeshSetInfo infoTriangulated; + bool validTriangulatedMesh = MeshOps::checkMeshSetValidAndClosed(meshsetTrinangulated, infoTriangulated, params); + if (!validTriangulatedMesh) + { +#ifdef _DEBUG + bool dumpMesh = false; + if (validInput && dumpMesh) + { + GeomDebugDump::DumpSettingsStruct dumpSet; + dumpSet.triangulateBeforeDump = false; + + GeomProcessingParams paramCopy(params); + paramCopy.checkZeroAreaFaces = false; + + GeomDebugDump::dumpLocalCoordinateSystem(); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpWithLabel("triangulate:input ", meshset, dumpSet, paramCopy, true, true); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpWithLabel("triangulate:result", meshsetTrinangulated, dumpSet, paramCopy, true, true); + } +#endif + + if (isBetterForBoolOp(infoTriangulated, info, true)) + { + meshset.reset(); + meshset = meshsetTrinangulated1; + return; + } + + bool validTriangulatedMesh1 = MeshOps::checkMeshSetValidAndClosed(meshsetTrinangulated1, infoTriangulated, params); + if (validTriangulatedMesh1) + { + meshset.reset(); + meshset = meshsetTrinangulated1; + return; + } + + if (!ignoreResultOpenEdges) + { + return; + } + } + meshset.reset(); + meshset = meshsetTrinangulated; +} + +bool MeshOps::isBetterForBoolOp(const MeshSetInfo& infoNew, const MeshSetInfo& infoBefore, bool considerTriangulation) +{ + if (infoNew.numOpenEdges() > infoBefore.numOpenEdges()) + { + return false; + } + + if (!infoNew.meshSetValid && infoBefore.meshSetValid) + { + return false; + } + + // fewer open edges is better + int numOpenEdgesRemoved = infoBefore.numOpenEdges() - infoNew.numOpenEdges(); + int numClosedEdgesRemoved = infoBefore.numClosedEdges - infoNew.numClosedEdges; + + int numDegenerateEdgesRemoved = infoBefore.degenerateEdges.size() - infoNew.degenerateEdges.size(); + int numFinEdgesRemoved = infoBefore.finEdges.size() - infoNew.finEdges.size(); + int numFinFacesRemoved = infoBefore.finFaces.size() - infoNew.finFaces.size(); + + size_t numAllEdgesNew = infoNew.numClosedEdges + infoNew.numOpenEdges(); + bool fewerOpenEdges = numOpenEdgesRemoved > 0 && numAllEdgesNew > 0; + + if ( infoNew.meshSetValid ) + { + if (fewerOpenEdges) + { + return true; + } + + // fewer faces is better. But only when it is > 0 + bool fewerFaces = infoNew.numFaces < infoBefore.numFaces && infoNew.numFaces > 0; + if (fewerFaces) + { + return true; + } + + if (considerTriangulation) + { + if (infoNew.maxNumberOfEdgesPerFace == 3 && infoBefore.maxNumberOfEdgesPerFace > 3) + { + if (infoNew.degenerateEdges.size() <= infoBefore.degenerateEdges.size()) + { + // mesh before was not triangulated, new is triangulated + return true; + } + } + } + + if (numDegenerateEdgesRemoved > 0 && numFinEdgesRemoved >= 0 && numFinFacesRemoved >= 0) + { + // fewer degenerate edges + return true; + } + + if (numDegenerateEdgesRemoved >= 0 && numFinEdgesRemoved > 0 && numFinFacesRemoved >= 0) + { + // fewer fin edges + return true; + } + + if (numDegenerateEdgesRemoved >= 0 && numFinEdgesRemoved >= 0 && numFinFacesRemoved > 0) + { + // fewer fin faces + return true; + } + } + + if (!infoNew.meshSetValid && !infoBefore.meshSetValid) + { + if (fewerOpenEdges) + { + if ( infoNew.allPointersValid || !infoBefore.allPointersValid) + { + return true; + } + } + } + +#ifdef _DEBUG + bool isEqual = infoNew.isEqual(infoBefore); + if (!isEqual) + { + bool OpenEdgesRemove = numOpenEdgesRemoved > 0; + bool ClosedEdgesRemove = numClosedEdgesRemoved > 0; + int numFacesRemoved = infoBefore.numFaces - infoNew.numFaces; + bool meshBeforeValid = infoBefore.meshSetValid; + bool meshNewValid = infoNew.meshSetValid; + int check = 0; + } +#endif + + return false; +} + +void findFinEdges(const shared_ptr >& meshset, std::set* >& setFinEdges, const GeomProcessingParams& params) +{ + for (const carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + if (mesh->closed_edges.size() > 1000) + { + continue; + } + for (carve::mesh::Edge<3>*edge : mesh->closed_edges) + { + if (!edge) + { + continue; + } + + carve::mesh::Edge<3>* reverseEdge = edge->rev; + if (!reverseEdge) + { + continue; + } + + carve::mesh::Face<3>* face = edge->face; + if (!face) + { + continue; + } + + carve::mesh::Face<3>* adjacentFace = reverseEdge->face; + if (!adjacentFace) + { + continue; + } + + // re-compute face normal here + face->computeNormal(params.epsMergePoints); + adjacentFace->computeNormal(params.epsMergePoints); + const vec3 faceNormal = face->plane.N; + const vec3 face2Normal = adjacentFace->plane.N; + + // adjacent faces back-to-back have -1 as normal vector dot product + double dotProduct = dot(faceNormal, face2Normal); + if (std::abs(dotProduct + 1.0) < params.epsMergeAlignedEdgesAngle*10) + { + setFinEdges.insert(edge); + } + } + } +} + +void MeshOps::removeDegenerateMeshes(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput, bool ensureValidMesh) +{ + if (!meshsetInput) + { + return; + } + + GeomProcessingParams params(paramsInput); + params.allowZeroAreaFaces = true; + MeshSetInfo infoInput; + bool meshInputOk = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoInput, params); + size_t numRemovedMeshes = 0; + + for (auto it = meshsetInput->meshes.begin(); it != meshsetInput->meshes.end(); ++it ) + { + carve::mesh::Mesh<3>* mesh = *it; + double meshVolume = mesh->volume(); + if (meshVolume < params.epsMergePoints) + { + it = meshsetInput->meshes.erase(it); + delete mesh; + ++numRemovedMeshes; + it = meshsetInput->meshes.begin(); + if( it == meshsetInput->meshes.end() ) + { + break; + } + } + } + + if( numRemovedMeshes > 0 ) + { + for( auto it = meshsetInput->meshes.begin(); it != meshsetInput->meshes.end(); ++it ) + { + carve::mesh::Mesh<3>* mesh = *it; + mesh->recalc(paramsInput.epsMergePoints); + } + + + MeshSetInfo info; + bool meshOk = MeshOps::checkMeshSetValidAndClosed(meshsetInput, info, params); +#ifdef _DEBUG + if (meshInputOk && !meshOk) + { + GeomDebugDump::DumpSettingsStruct dumpColorSettings; + GeomDebugDump::dumpWithLabel("removeDegenerateMeshes--input", meshsetInput, dumpColorSettings, params, true, true); + + } +#endif + if (ensureValidMesh) + { + if (isBetterForBoolOp(info, infoInput, true)) + { + // TODO check if backup is necessary + } + } + } +} + +static void checkEdgeIntegrity(carve::mesh::Edge<3>* e, const carve::mesh::Face<3>* face, bool checkForDegenerateEdges, MeshSetInfo& info, double eps) +{ + if (!e) + { + info.allPointersValid = false; + info.details = "edge is nullptr"; + info.degenerateFaces.insert(face); + return; + } + + if (!e->rev) + { + info.allPointersValid = false; + info.details = "edge->rev is nullptr"; + info.openEdges.insert(e); + info.degenerateFaces.insert(face); + return; + } + + if (!e->prev) + { + info.allPointersValid = false; + info.details = "edge->prev is nullptr"; + info.degenerateFaces.insert(face); + return; + } + + if (!e->next) + { + info.allPointersValid = false; + info.details = "edge->next is nullptr"; + info.degenerateFaces.insert(face); + return; + } + + if (!e->vert) + { + info.allPointersValid = false; + info.details = "edge->vert is nullptr"; + info.degenerateFaces.insert(face); + return; + } + + if (!e->face) + { + info.allPointersValid = false; + info.details = "edge->face is nullptr"; + info.degenerateFaces.insert(face); + return; + } + + if (checkForDegenerateEdges) + { + if (e->rev == nullptr || e->next == nullptr) + { + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->rev->rev != e) + { + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->next == e ) + { +#ifdef _DEBUG + double length = e->length(); +#endif + info.details = "e->next == e"; + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->next->vert == e->vert) + { + // this can actually happen, when an inner mesh touches the outer mesh with just one edge + double length2 = e->length2(); + if (length2 < eps * eps) + { + info.details = "e->next->vert == e->vert"; + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + } + + if (e->prev == e ) + { + info.details = "e->prev == e"; + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if ( e->prev->vert == e->vert) + { +#ifdef _DEBUG + double lengthPreviousEdge = e->prev->length(); +#endif + info.details = "e->prev->vert == e->vert"; + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->next->prev != e) + { + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->prev->next != e) + { + info.degenerateFaces.insert(e->face); + info.degenerateEdges.insert(e); + return; + } + + if (e->face != face ) + { + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + return; + } + + if (e->rev->rev != e) + { + info.degenerateFaces.insert(face); + info.degenerateEdges.insert(e); + info.details = "edge->rev->rev != edge"; + } + + if (e->prev == e->next) + { + info.degenerateEdges.insert(e); + info.details = "e->prev == e->next"; + } + + double length2 = (e->v1()->v - e->v2()->v).length2(); + if (length2 < eps * eps) + { + info.degenerateEdges.insert(e); + info.details = "e->length < eps"; + } + + if (e->prev->vert == e->next->vert) + { + double length2 = e->length2(); +#ifdef _DEBUG + double length2prev = e->prev->length2(); + double length2next = e->next->length2(); + + carve::mesh::Face<3>* f1 = e->face; + carve::mesh::Face<3>* f2 = e->prev->face; + carve::mesh::Face<3>* f3 = e->next->face; + double area1 = MeshOps::computeFaceArea(f1); + double area2 = MeshOps::computeFaceArea(f2); + double area3 = MeshOps::computeFaceArea(f3); + + //glm::vec4 color(0.6, 0.6, 0.6, 1.); + //GeomDebugDump::clearMeshsetDump(); + //GeomDebugDump::dumpFacePolygon(e->face, color, true); +#endif + + if (length2 < eps * eps) + { + info.degenerateEdges.insert(e); + info.details = "e->prev->vert == e->next->vert"; + } + } + + if (e->next == e->rev) + { + if (e->next->next == e) + { + // 2 edges referencing itself + info.details = "e->next == e->rev"; + info.degenerateEdges.insert(e); + } + } + } +} + +void MeshOps::checkFaceIntegrity(const carve::mesh::Face<3>* face, bool checkForDegenerateEdges, MeshSetInfo& info, double eps) +{ + if (!face) + { + info.allPointersValid = false; + return; + } + + carve::mesh::Edge<3>* e = face->edge; + if (!e) + { + info.allPointersValid = false; + info.details = "face->edge is nullptr"; + return; + } + + try + { + const size_t n_edges = face->n_edges; + if (n_edges > 10000) + { + info.maxNumEdgesExceeded = true; +#ifdef _DEBUG + std::cout << "n_edges > 10000" << std::endl; + glm::vec4 color(0.3, 0.3, 0.3, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); +#endif + return; + } + for (size_t i_edge = 0; i_edge < n_edges; ++i_edge) + { + checkEdgeIntegrity(e, face, checkForDegenerateEdges, info, eps); + + if (!info.allPointersValid) + { + return; + } + + if (e->rev) + { + carve::mesh::Face<3>* reverseFace = e->rev->face; + checkEdgeIntegrity(e->rev, reverseFace, checkForDegenerateEdges, info, eps); + } + + if (!info.allPointersValid) + { + return; + } + + // continue + e = e->next; + } + + if (e != face->edge) + { + info.details = "e != face->edge"; + info.degenerateFaces.insert(face); + return; + } + } + catch (std::exception& ex) + { +#ifdef _DEBUG + std::cout << ex.what() << std::endl; +#endif + info.allPointersValid = false; + return; + } + catch (std::exception* ex) + { +#ifdef _DEBUG + std::cout << ex->what() << std::endl; +#endif + info.allPointersValid = false; + return; + } + catch (carve::exception& ex) + { +#ifdef _DEBUG + std::cout << ex.str() << std::endl; +#endif + info.allPointersValid = false; + return; + } + catch (...) + { + info.allPointersValid = false; + return; + } +} + +void MeshOps::checkMeshIntegrity(const carve::mesh::Mesh<3>* mesh, bool checkForDegenerateEdges, const GeomProcessingParams& params, MeshSetInfo& info) +{ + if (!mesh) + { + info.allPointersValid = false; + return; + } + const std::vector* >& vec_faces = mesh->faces; + for (size_t j = 0; j < vec_faces.size(); ++j) + { + carve::mesh::Face<3>* face = vec_faces[j]; + checkFaceIntegrity(face, checkForDegenerateEdges, info, params.epsMergePoints); + + if (!info.allPointersValid) + { + return; + } + } +} + +inline bool edgeToEdgeIntersect(const carve::mesh::Edge<3>* edge1, const carve::mesh::Edge<3>* edge2, double eps, vec3& intersectionPoint) +{ + const vec3 pointA = edge1->v1()->v; + const vec3 pointB = edge1->v2()->v; + const vec3 pointC = edge2->v1()->v; + const vec3 pointD = edge2->v2()->v; + + vec3 AB = pointB - pointA; + vec3 CD = pointD - pointC; + vec3 CA = pointA - pointC; + + double a = dot(AB, AB); // always >= 0 + double b = dot(AB, CD); + double c = dot(CD, CD); // always >= 0 + double d = dot(AB, CA); + double e = dot(CD, CA); + double sc, sN, sD = a * c - b * b; // sc = sN / sD, sD >= 0 + double tc, tN, tD = a * c - b * b; // tc = tN / tD, tD >= 0 + + // compute the line parameters of the two closest points + if (sD < eps) + { + // the lines are almost parallel + sN = 0.0; // force using point A on segment AB to prevent possible division by 0.0 later + sD = 1.0; + tN = e; + tD = c; + } + else + { + // get the closest points on the infinite lines + sN = (b * e - c * d); + tN = (a * e - b * d); + if (sN < 0.0) + { + // sc < 0 => the s=0 edge is visible + sN = 0.0; // compute shortest connection of A to segment CD + tN = e; + tD = c; + } + else if (sN > sD) // sc > 1 => the s=1 edge is visible + { + sN = sD; // compute shortest connection of B to segment CD + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) + { + // tc < 0 => the t=0 edge is visible + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) // compute shortest connection of C to segment AB + { + sN = 0.0; + } + else if (-d > a) + { + sN = sD; + } + else + { + sN = -d; + sD = a; + } + } + else if (tN > tD) + { + // tc > 1 => the t=1 edge is visible + tN = tD; + // recompute sc for this edge + if ((-d + b) < 0.0) // compute shortest connection of D to segment AB + { + sN = 0; + } + else if ((-d + b) > a) + { + sN = sD; + } + else + { + sN = (-d + b); + sD = a; + } + } + + sc = fabs(sN) < eps ? 0.0 : sN / sD; + tc = fabs(tN) < eps ? 0.0 : tN / tD; + + vec3 P1 = pointA + (sc * AB); + vec3 P2 = pointC + (tc * CD); + vec3 delt = P2 - P1; + if (delt.length2() < eps * eps) + { + // intersecting + if (sc < eps) + { + // intersection is at edge1->v1() -> split edge2 + + if (tc < eps || fabs(tc - 1.0) < eps) + { + // intersecting at point, not edge + return false; + } + } + else if (fabs(sc - 1.0) < eps) + { + if (tc < eps || fabs(tc - 1.0) < eps) + { + // intersecting at point, not edge + return false; + } + + // intersection is at edge1->v2() -> split edge2 + } + + // split edge1 + carve::mesh::Face<3>* face = edge1->face; + + if (edge1->rev) + { + carve::mesh::Face<3>* faceRev = edge1->rev->face; + } + + intersectionPoint = P1; + return true; + } + return false; +} + +size_t flipFacesOnOpenEdges(shared_ptr>& meshset, MeshSetInfo& infoInput, const GeomProcessingParams& params) +{ + size_t numEdgesAll = infoInput.numClosedEdges + infoInput.numOpenEdges(); + if (numEdgesAll > 5000) + { + return 0; + } + + if (infoInput.numFaces > 5000) + { + return 0; + } + + size_t numChanges = 0; + std::set* > setFacesDone; + std::set* > setFlipFaces; + + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + for (size_t jj = 0; jj < mesh->open_edges.size(); ++jj) + { + if (jj >= mesh->open_edges.size()) + { + continue; + } + carve::mesh::Edge<3>* edge = mesh->open_edges[jj]; + if (edge) + { + if (edge->face) + { + if (setFacesDone.find(edge->face) == setFacesDone.end()) + { + setFacesDone.insert(edge->face); + setFlipFaces.insert(edge->face); + ++numChanges; + } + } + } + } + } + + std::set* > setSkipFaces; + PolyInputCache3D polyInput(params.epsMergePoints); + MeshOps::polyhedronFromMeshSet(meshset, setSkipFaces, setFlipFaces, polyInput); + + std::map mesh_input_options; + std::string details; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(polyInput.m_poly_data, params); + std::string details2; + correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + } + + if (correct) + { + shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo infoFlippedFaces; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoFlippedFaces, params); + + if (MeshOps::isBetterForBoolOp(infoFlippedFaces, infoInput, true)) + { + meshset = meshsetFromPolyhedron; + } + return numChanges; + } + return 0; +} + +void MeshOps::resolveOpenEdges(shared_ptr>& meshset, const GeomProcessingParams& params) +{ + MeshSetInfo infoInput; + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + resolveOpenEdges(meshset, infoInput, params); +} + +void MeshOps::resolveOpenEdges(shared_ptr>& meshset, MeshSetInfo& infoInput, const GeomProcessingParams& params) +{ + if (!meshset) + { + return; + } + + if (meshset->vertex_storage.size() > 5000) + { + return; + } + + bool tryFlipFaces = true; + if (tryFlipFaces) + { + size_t numChanges = flipFacesOnOpenEdges(meshset, infoInput, params); + if (numChanges > 0) + { + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + } + } + + if (infoInput.meshSetValid) + { + return; + } + + bool tryMergeShortOpenEdges = true; + size_t numMeshesChanged = 0; + shared_ptr > meshsetCopyUnChanged; + + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + // check if all open edges are close together -> merge points to center + EdgeLoopFinder loopFinder(params.epsMergePoints, params.epsMergePoints * 50); + bool meshsetChanged = false; + loopFinder.initFromMesh(meshset, mesh, params, tryMergeShortOpenEdges, meshsetChanged); + if (meshsetChanged) + { + meshsetCopyUnChanged = loopFinder.m_meshsetCopyUnChanged; + ++numMeshesChanged; + } + } + + if (numMeshesChanged > 0) + { + shared_ptr > meshsetFromPolyhedron; + int minNumFacesPerMesh = 4; + MeshSet2Polyhedron2MeshSet(meshset, meshsetFromPolyhedron, params, minNumFacesPerMesh); + + MeshSetInfo infoChanged; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoChanged, params); + + if (MeshOps::isBetterForBoolOp(infoChanged, infoInput, true)) + { + meshset = meshsetFromPolyhedron; + infoInput.copyFromOther(infoChanged); + } + else + { + meshset = meshsetCopyUnChanged; + } + + if (infoInput.meshSetValid) + { + return; + } + } + + size_t maxNumRepeats = meshset->meshes.size(); + if( maxNumRepeats > 1 ) maxNumRepeats = 1; + for (size_t repeat = 0; repeat < maxNumRepeats; ++repeat) + { + size_t numChanges = 0; + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + // check if all open edges are close together -> merge points to center + EdgeLoopFinder loopFinder(params.epsMergePoints, params.epsMergePoints * 50); + bool meshsetChanged = false; + loopFinder.initFromMesh(meshset, mesh, params, tryMergeShortOpenEdges, meshsetChanged); + + float ratioOpenEdges = 0; + if (infoInput.numClosedEdges > 0) + { + ratioOpenEdges = float(infoInput.numOpenEdges()) / float(infoInput.numClosedEdges); + } + + if (loopFinder.m_mapVertexOpenEdges.size() > 0) + { + if (ratioOpenEdges < 0.2) + { +#ifdef _DEBUG + GeomDebugDump::clearMeshsetDump(); +#endif + loopFinder.findLoops(); + } + + if (loopFinder.m_closedEdgeLoopsOfOpenEdges.size() > 0) + { + PolyInputCache3D polyInput(params.epsMergePoints); + + polyhedronFromMeshSet(meshset, polyInput); + + for (std::vector* >&loop : loopFinder.m_closedEdgeLoopsOfOpenEdges) + { + std::vector loopPointIndexes; + for (carve::mesh::Edge<3>*edge : loop) + { + vec3& point = edge->vert->v; + int idx = polyInput.addPoint(point); + loopPointIndexes.push_back(idx); + } + polyInput.m_poly_data->addFace(loopPointIndexes.begin(), loopPointIndexes.end()); + } + + + std::map mesh_input_options; + std::string details; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(polyInput.m_poly_data, params); + std::string details2; + correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + } + + if (correct) + { + shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo infoAddedPatches; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoAddedPatches, params); + + if (MeshOps::isBetterForBoolOp(infoAddedPatches, infoInput, true)) + { + infoInput.copyFromOther(infoAddedPatches); + meshset = meshsetFromPolyhedron; + ++numChanges; + break; + } + } + } + } + } + + if(numChanges == 0 ) + { + break; + } + } + + // check if all open edges are close together -> merge points to center + bool openEdgesMergedToPoint = false; + + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + if (infoInput.meshSetValid) + { + return; + } + + // try to find loop of open edges -> close with a new face + PolyInputCache3D polyInput(params.epsMergePoints); + size_t numMeshesInput = meshset->meshes.size(); + size_t numFacesInput = 0; + size_t numOpenEdgesInput = 0; + size_t numClosedEdgesInput = 0; + std::set* > allOpenEdges; + std::set* > allEdges; + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + numOpenEdgesInput += mesh->open_edges.size(); + numClosedEdgesInput += mesh->closed_edges.size(); + numFacesInput += mesh->faces.size(); + + for (auto edge : mesh->closed_edges) + { + if (edge) + { + allEdges.insert(edge); + } + } + + for (auto edge : mesh->open_edges) + { + if (edge) + { + allEdges.insert(edge); + } + } + + if (mesh->faces.size() >= 6) + { + for (auto edge : mesh->open_edges) + { + if (edge) + { + allOpenEdges.insert(edge); + } + } + } + } + + if (numOpenEdgesInput + numClosedEdgesInput > params.generalSettings->m_maxNumFaceEdges ) + { + return; + } + + if (numOpenEdgesInput == 0) + { + return; + } + + if (numOpenEdgesInput > 100) + { + return; + } + + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + + for (auto f : mesh->faces) + { + std::vector vecPointIndexes; + std::vector vecFaceLoop; + carve::mesh::Edge<3>* edge = f->edge; + + for (size_t jj = 0; jj < f->n_edges; ++jj) + { + vec3& edgePoint2 = edge->v2()->v; + + auto itFind = allOpenEdges.find(edge); + if (itFind != allOpenEdges.end()) + { + // intersect with all points and edges of MeshSet + vec3& edgePoint1 = edge->v1()->v; + + const vec3 edgeDelta = edgePoint2 - edgePoint1; + double dotLineSegDelta = dot(edgeDelta, edgeDelta); + + std::map mapIntersectionsOnEdge; + + // check if current edge needs to be split + for (size_t iiVertex = 0; iiVertex < meshset->vertex_storage.size(); ++iiVertex) + { + const carve::mesh::Vertex<3>& vert = meshset->vertex_storage[iiVertex]; + vec3 vertexPoint = vert.v; + + + double t = -1; + bool onSegment = GeomUtils::isPointOnLineSegment(edgePoint1, edgeDelta, dotLineSegDelta, vertexPoint, t, params.epsMergePoints); + + if (onSegment) + { + mapIntersectionsOnEdge.insert({ t, vertexPoint }); + } + } + + // intersect edge-edge + for (carve::mesh::Edge<3>*edgeToIntersect : allEdges) + { + if (edgeToIntersect == edge) + { + continue; + } + + vec3 intersectionPoint; + bool intersect = edgeToEdgeIntersect(edgeToIntersect, edge, params.epsMergePoints, intersectionPoint); + if (intersect) + { + double t = -1; + bool onSegment = GeomUtils::isPointOnLineSegment(edgePoint1, edgeDelta, dotLineSegDelta, intersectionPoint, t, params.epsMergePoints); + + if (onSegment) + { + mapIntersectionsOnEdge.insert({ t, intersectionPoint }); + } + } + } + + + for (auto it : mapIntersectionsOnEdge) + { + vec3& vertexPoint = it.second; + int idx = polyInput.addPoint(vertexPoint); + if (vecPointIndexes.size() > 0) + { + size_t lastIndex = vecPointIndexes.back(); + if (lastIndex == idx) + { + continue; + } + } + + vecPointIndexes.push_back(idx); + vecFaceLoop.push_back(vertexPoint); + } + } + + int idx = polyInput.addPoint(edgePoint2); + vecPointIndexes.push_back(idx); + vecFaceLoop.push_back(edgePoint2); + + edge = edge->next; + if (edge == f->edge) + { + break; + } + } + + + if (vecPointIndexes.size() < 3) + { +#ifdef _DEBUG + std::cout << "face with < 3 edges" << std::endl; +#endif + continue; + } + + double area = GeomUtils::computePolygonArea(vecFaceLoop, params.epsMergePoints); + if (area < params.minFaceArea) + { + continue; + } + polyInput.m_poly_data->addFace(vecPointIndexes.begin(), vecPointIndexes.end()); + } + } + + std::string details = ""; + bool polyInputCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!polyInputCorrect) + { + fixPolyhedronData(polyInput.m_poly_data, params); + polyInputCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + } + + if (polyInputCorrect) + { + shared_ptr > meshsetNew(polyInput.m_poly_data->createMesh(carve::input::opts(), params.epsMergePoints)); + + size_t numOpenEdges = 0; + size_t numClosedEdges = 0; + for (size_t ii = 0; ii < meshsetNew->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshsetNew->meshes[ii]; + mesh->recalc(params.epsMergePoints); + numOpenEdges += mesh->open_edges.size(); + numClosedEdges += mesh->closed_edges.size(); + } + +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.4); + GeomDebugDump::moveOffset(meshset->getAABB().extent.y * 1.1); + glm::vec4 color(0, 1, 1, 1); + if (numOpenEdges > 0) + { + glm::vec4 color(1, 0.5, 1, 1); + //GeomDebugDump::dumpMeshsetOpenEdges(meshsetNew, color, false, false); + } + bool drawNormals = true; + GeomDebugDump::dumpMeshset(meshsetNew, color, drawNormals, true); + } +#endif + + MeshSetInfo infoNewMesh; + MeshOps::checkMeshSetValidAndClosed(meshsetNew, infoNewMesh, params); + + if (isBetterForBoolOp(infoNewMesh, infoInput, false)) + { + meshset = meshsetNew; + } + } +} + +double MeshOps::computeMeshSetSurface(const shared_ptr >& meshset) +{ + double surface_area = 0; + const std::vector* >& vec_meshes = meshset->meshes; + for (size_t kk = 0; kk < vec_meshes.size(); ++kk) + { + const carve::mesh::Mesh<3>* mesh = vec_meshes[kk]; + const std::vector* >& vec_faces = mesh->faces; + for (size_t mm = 0; mm < vec_faces.size(); ++mm) + { + const carve::mesh::Face<3>* face = vec_faces[mm]; + surface_area += computeFaceArea(face); + } + } + return surface_area; +} + +double MeshOps::computeShapeSurfaceArea(const shared_ptr& geomItem) +{ + double surface_area = 0; + double volume = 0; + if (geomItem) + { + { + for (size_t jj = 0; jj < geomItem->m_meshsets.size(); ++jj) + { + surface_area += computeMeshSetSurface(geomItem->m_meshsets[jj]); + } + + for (size_t jj = 0; jj < geomItem->m_meshsets_open.size(); ++jj) + { + surface_area += computeMeshSetSurface(geomItem->m_meshsets_open[jj]); + } + + for (const shared_ptr& item_data : geomItem->m_child_items ) + { + double childArea = computeShapeSurfaceArea(geomItem); + surface_area += childArea; + } + } + } + return surface_area; +} + +double MeshOps::computeShapeSurfaceArea(const shared_ptr& shape_input_data) +{ + double surface_area = 0; + double volume = 0; + if (shape_input_data) + { + for (auto geomItem : shape_input_data->getGeometricItems()) + { + for (size_t jj = 0; jj < geomItem->m_meshsets.size(); ++jj) + { + surface_area += computeMeshSetSurface(geomItem->m_meshsets[jj]); + } + + for (size_t jj = 0; jj < geomItem->m_meshsets_open.size(); ++jj) + { + surface_area += computeMeshSetSurface(geomItem->m_meshsets_open[jj]); + } + } + + for (const shared_ptr& item_data : shape_input_data->getChildElements() ) + { + double childArea = computeShapeSurfaceArea(item_data); + surface_area += childArea; + } + } + return surface_area; +} + +double MeshOps::computeMeshsetVolume(const carve::mesh::MeshSet<3>* meshset) +{ + if (!meshset) + { + return 0; + } + double volume = 0; + for (size_t kk = 0; kk < meshset->meshes.size(); ++kk) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[kk]; + double meshVolume = mesh->volume(); + + if (meshVolume < 0) + { + mesh->invert(); + meshVolume = -meshVolume; + } + volume += meshVolume; + } + return volume; +} + +size_t MeshOps::countFaces(carve::mesh::MeshSet<3>* mesh) +{ + if (!mesh) + { + return 0; + } + + size_t numFaces = 0; + for (auto& mesh : mesh->meshes) + { + numFaces += mesh->faces.size(); + } + return numFaces; +} + +size_t MeshOps::countDegeneratedFaces(carve::mesh::MeshSet<3>* mesh) +{ + if (!mesh) + { + return 0; + } + + size_t numDegenerateFaces = 0; + for (auto& mesh : mesh->meshes) + { + for (auto& f : mesh->faces) + { + if (f->n_edges == 0) + { + ++numDegenerateFaces; + } + } + } + return numDegenerateFaces; +} + +bool MeshOps::checkMeshSetNonNegativeAndClosed(const shared_ptr > mesh_set, const GeomProcessingParams& params) +{ + bool meshes_closed = true; + if (mesh_set) + { + for (size_t i = 0; i < mesh_set->meshes.size(); ++i) + { + carve::mesh::Mesh<3>* mesh_i = mesh_set->meshes[i]; + if (mesh_i) + { + if (mesh_i->isNegative()) + { + mesh_i->invert(); + if (mesh_i->isNegative()) + { + mesh_i->recalc(params.epsMergePoints); + mesh_i->calcOrientation(); + if (mesh_i->isNegative()) + { + std::cout << "could not invert mesh_set->meshes[" << i << "] " << std::endl; + } + } + } + + if (!mesh_i->isClosed()) + { + meshes_closed = false; + } + + if (mesh_i->open_edges.size() > 0) + { + meshes_closed = false; + } + } + } + } + return meshes_closed; +} + +void MeshOps::checkMeshSetIntegrity(const shared_ptr >& meshset, bool checkForDegenerateEdges, const GeomProcessingParams& params, MeshSetInfo& info) +{ + if (meshset) + { + for (size_t i = 0; i < meshset->meshes.size(); ++i) + { + carve::mesh::Mesh<3>* mesh_i = meshset->meshes[i]; + MeshOps::checkMeshIntegrity(mesh_i, checkForDegenerateEdges, params, info); + if (!info.allPointersValid) + { + return; + } + } + } +} + +bool MeshOps::checkMeshSetValidAndClosed(const shared_ptr>& meshset, MeshSetInfo& info, const GeomProcessingParams& params) +{ + info.meshSetValid = false; + if (!meshset) + { + return false; + } + + info.meshset = meshset; + bool allowFinEdges = params.allowFinEdges; + info.resetInfoResult(); + if (!meshset) + { +#ifdef _DEBUG + if (info.entity) + { + std::cout << "MeshSet of entity #" << info.entity->m_tag << " not valid" << std::endl; + } +#endif + info.allPointersValid = false; + info.meshSetValid = false; + return false; + } + if (meshset->meshes.size() == 0) + { +#ifdef _DEBUG + if (info.entity) + { + //std::cout << "MeshSet of entity #" << info.entity->m_tag << " has no meshes" << std::endl; + } +#endif + info.meshSetValid = false; + return info.meshSetValid; + } + + // check open edges first, since it is the easiest and cheapest + info.meshSetValid = true; + info.maxNumberOfEdgesPerFace = 0; + + for (size_t kk = 0; kk < meshset->meshes.size(); ++kk) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[kk]; + + if (!mesh) + { + info.allPointersValid = false; + continue; + } + for (auto e : mesh->open_edges) + { + info.openEdges.insert(e); + } + info.numClosedEdges += mesh->closed_edges.size(); + info.numFaces += mesh->faces.size(); + + for (carve::mesh::Face<3>* inputFace : mesh->faces) + { + if (inputFace == nullptr) + { + info.allPointersValid = false; + continue; + } + + if (inputFace->n_edges > info.maxNumberOfEdgesPerFace) + { + info.maxNumberOfEdgesPerFace = inputFace->n_edges; + } + + if (inputFace->n_edges < 3) + { + info.degenerateFaces.insert(inputFace); + } + + double longestEdge = 0; + double face_area = MeshOps::computeFaceArea(inputFace, longestEdge); + if (std::abs(face_area) < params.minFaceArea ) + { + info.degenerateFaces.insert(inputFace); + if (params.treatLongThinFaceAsDegenerate) + { + info.zeroAreaFaces.insert(inputFace); + } + else if (longestEdge < params.epsMergePoints ) + { + info.zeroAreaFaces.insert(inputFace); + } + } + info.surfaceArea += face_area; + } + } + + // check for valid pointers first + bool checkForDegenerateEdges = true; + checkMeshSetIntegrity(meshset, checkForDegenerateEdges, params, info); + if (!info.allPointersValid) + { + info.meshSetValid = false; + return false; + } + + if (!params.allowDegenerateEdges) + { + if (info.degenerateEdges.size() > 0) + { + info.meshSetValid = false; + return info.meshSetValid; + } + } + + // check for fin edges, where a face is back-to-back to face of reverse edge + + findFinEdges(meshset, info.finEdges, params); + + if (info.finEdges.size() > 0) + { + if (!allowFinEdges) + { + info.meshSetValid = false; + } + } + + if (!allowFinEdges) + { + for (size_t i = 0; i < meshset->meshes.size(); ++i) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[i]; + + if (!mesh) + { + continue; + } + const std::vector* >& vec_faces = mesh->faces; + for (size_t j = 0; j < vec_faces.size(); ++j) + { + carve::mesh::Face<3>* face = vec_faces[j]; + + const size_t n_edges = face->n_edges; + if (n_edges > 10000) + { +#ifdef _DEBUG + std::cout << "n_edges > 10000" << std::endl; + glm::vec4 color(0.3, 0.3, 0.3, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); +#endif + continue; + } + + carve::mesh::Edge<3>* e = face->edge; + for (size_t i_edge = 0; i_edge < n_edges; ++i_edge) + { + // check for adjacent fin faces + if (e->face) + { + const carve::geom::vector<3>& normal = e->face->plane.N; + if (e->face->edge) + { + if (e->face->edge->rev) + { + if (e->face->edge->rev->face) + { + const carve::geom::vector<3>& reverserFaceNormal = e->face->edge->rev->face->plane.N; + + double dotProduct = dot(reverserFaceNormal, normal); + if (std::abs(dotProduct + 1.0) < params.epsMergePoints) + { + double face_area = MeshOps::computeFaceArea(face); + if (std::abs(face_area) > params.minFaceArea * 10) + { +#ifdef _DEBUG + //std::cout << "opposite face is coplanar" << std::endl; +#endif + info.finFaces.insert( face ); + } + } + } + } + } + } + + // continue + e = e->next; + } + } + } + + //if (!checkMeshFins(meshset, params.epsMergePoints)) + if( info.finEdges.size() > 0) + { + info.meshSetValid = false; + return info.meshSetValid; + } + } + + if (info.numOpenEdges() > 0) + { + info.meshSetValid = false; + return info.meshSetValid; + } + + if (info.zeroAreaFaces.size() > 0) + { + if (!params.allowZeroAreaFaces) + { + info.meshSetValid = false; + return info.meshSetValid; + } + } + + std::stringstream err; + bool meshes_closed = checkMeshSetNonNegativeAndClosed(meshset, params); + if (meshes_closed) + { + // check volume + double meshset_volume = 0; + for (size_t kk = 0; kk < meshset->meshes.size(); ++kk) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[kk]; + double mesh_volume = mesh->volume(); + + if (mesh_volume < 0) + { + mesh->invert(); + if (mesh->isNegative()) + { + mesh->recalc(params.epsMergePoints); + mesh->calcOrientation(); + if (mesh->isNegative()) + { + std::cout << "could not invert negative mesh[" << kk << "] " << std::endl; + } + } + mesh_volume = mesh->volume(); + } + + if (mesh_volume < 0) + { + err << "mesh_volume < 0" << std::endl; + } + + meshset_volume += mesh_volume; + } + } + else + { + err << "mesh_set not closed" << std::endl; + } + + if (err.tellp() > 0) + { +#ifdef _DEBUG + if (info.report_callback) + { + info.report_callback->messageCallback(err.str().c_str(), StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, info.entity); + } +#endif + info.meshSetValid = false; + return false; + } + + return info.meshSetValid; +} + +bool intersectRayTriangle(const glm::dvec3& rayOrigin, const glm::dvec3& rayDirection, const glm::dvec3& p0, const glm::dvec3& p1, const glm::dvec3& p2) +{ + double t = 0; + glm::dvec2 baryPosition(1, 1); + bool intersects = glm::intersectRayTriangle(rayOrigin, rayDirection, p0, p1, p2, baryPosition, t); + bool edgeIntersected = false; + if (intersects && t >= 0) + { + return true; + } + return false; +} + +///\brief method intersectOpenEdges: Intersect open edges of MeshSet with closed edges, and split the open edges in case of intersection +///\param[in/out] meshset: MeshSet with open edges. If fix is found, a new MeshSet is assigned to the smart pointer +///\param[in] eps: tolerance to find edge-edge intersections +///\param[in] dumpMeshes: write meshes to dump file for debugging +void MeshOps::intersectOpenEdgesWithPoints(shared_ptr >& meshsetInput, const GeomProcessingParams& params) +{ + if (!meshsetInput) + { + return; + } + + size_t maxNumFaces = 2000; + size_t maxNumEdges = 2000; + size_t maxNumOpenEdges = 100; + double eps = params.epsMergePoints * 1.2; + +#ifdef _DEBUG + glm::vec4 color(0.5, 0.6, 0.7, 1.0); + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.8); + //GeomDebugDump::dumpMeshset(meshset, color, true); + double dy = meshsetInput->getAABB().extent.y; + GeomDebugDump::moveOffset(dy * 2.2); + GeomDebugDump::dumpMeshsetOpenEdges(meshsetInput, color, false, false); + GeomDebugDump::moveOffset(dy * 2.2); + } +#endif + + for (size_t round = 0; round < 5; ++round) + { + size_t numClosedEdgesBefore = 0; + std::vector* > allOpenEdges; + std::vector* > allFaces; + + for (size_t ii = 0; ii < meshsetInput->meshes.size(); ++ii) + { + const carve::mesh::Mesh<3>* mesh = meshsetInput->meshes[ii]; + numClosedEdgesBefore += mesh->closed_edges.size(); + std::copy(mesh->open_edges.begin(), mesh->open_edges.end(), std::back_inserter(allOpenEdges)); + std::copy(mesh->faces.begin(), mesh->faces.end(), std::back_inserter(allFaces)); + } + + std::set* > setOpenEdgesAdjacentFaces; + for (size_t ii = 0; ii < allOpenEdges.size(); ++ii) + { + carve::mesh::Edge<3>* openEdge = allOpenEdges[ii]; + carve::mesh::Face<3>* adjacentFace = openEdge->face; + setOpenEdgesAdjacentFaces.insert(adjacentFace); + } + + PolyInputCache3D polyInput(params.epsMergePoints); + + // intersect with closed edges + size_t numSplitEdges = 0; + for (size_t iiFace = 0; iiFace < allFaces.size(); ++iiFace) + { + if (iiFace > maxNumFaces) + { + return; + } + carve::mesh::Face<3>* face = allFaces[iiFace]; + + const size_t n_edges = face->n_edges; + if (n_edges > maxNumEdges) + { +#ifdef _DEBUG + if (params.debugDump) + { + std::cout << "n_edges=" << n_edges << " > max (" << maxNumEdges << ")" << std::endl; + glm::vec4 color(0.3, 0.3, 0.3, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); + } +#endif + return; + } + + std::vector faceLoop; + carve::mesh::Edge<3>* edge = face->edge; + faceLoop.push_back(edge->v1()->v); + + bool addFaceDirectly = true; + bool tryIntersect = false; + auto itFind = setOpenEdgesAdjacentFaces.find(face); + if (itFind != setOpenEdgesAdjacentFaces.end()) + { + tryIntersect = true; + } + + // face is adjacent to an open edge, so try to intersect with points + for (size_t i_edge = 0; i_edge < n_edges; ++i_edge) + { + carve::mesh::Vertex<3>* vertex1 = edge->v1(); + carve::mesh::Vertex<3>* vertex2 = edge->v2(); + const vec3& edgePoint1 = vertex1->v; + const vec3& edgePoint2 = vertex2->v; + + if (tryIntersect) + { + const vec3 edgeDelta = edgePoint2 - edgePoint1; + double dotLineSegDelta = dot(edgeDelta, edgeDelta); + + std::map mapIntersections; + + // check if current edge needs to be split + for (size_t iiVertex = 0; iiVertex < meshsetInput->vertex_storage.size(); ++iiVertex) + { + const carve::mesh::Vertex<3>& vert = meshsetInput->vertex_storage[iiVertex]; + vec3 vertexPoint = vert.v; + + double t = -1; + bool onSegment = GeomUtils::isPointOnLineSegment(edgePoint1, edgeDelta, dotLineSegDelta, vertexPoint, t, eps); + + if (onSegment) + { + mapIntersections.insert({ t, vertexPoint }); + ++numSplitEdges; + } + } + + for (auto itIntersections = mapIntersections.begin(); itIntersections != mapIntersections.end(); ++itIntersections) + { + const vec3& vertexPoint = itIntersections->second; + +#ifdef _DEBUG + if (params.debugDump && false) + { + glm::vec4 color(0.3, 0.3, 0.3, 1.); + if (itIntersections == mapIntersections.begin()) + { + GeomDebugDump::dumpFacePolygon(face, color, false); + } + GeomDebugDump::dumpPolyline(faceLoop, color, 0, false, false); + } +#endif + + faceLoop.push_back(vertexPoint); + } + } + faceLoop.push_back(edgePoint2); + + edge = edge->next; + + if (edge == face->edge) + { + break; + } + } + + if (addFaceDirectly) + { + std::vector faceIndexes; + for (size_t iiPoint = 0; iiPoint < faceLoop.size(); ++iiPoint) + { + const vec3& v0 = faceLoop[iiPoint]; + int idxA = polyInput.addPoint(v0); + if (faceIndexes.size() > 0) + { + int previousIndex = faceIndexes.back(); + if (idxA == previousIndex) + { + continue; + } + } + faceIndexes.push_back(idxA); + } + + GeomUtils::removeLastIfEqualToFirst(faceIndexes); + + if (faceIndexes.size() > 2 && faceIndexes.size() < 500) + { + size_t n = std::distance(faceIndexes.begin(), faceIndexes.end()); + + size_t numFaceIndices = polyInput.m_poly_data->faceIndices.size(); + if (numFaceIndices > 10000) + { +#ifdef _DEBUG + std::cout << "numFaceIndices > 10000" << std::endl; +#endif + } + polyInput.m_poly_data->addFace(faceIndexes.begin(), faceIndexes.end()); + } + } + else + { + std::vector > faceLoops = { faceLoop }; + bool mergeAlignedEdges = false; + bool dumpPolygon = true; + + GeomProcessingParams paramsTriangulate(params); + paramsTriangulate.epsMergeAlignedEdgesAngle = 0.0; + FaceConverter::createTriangulated3DFace(faceLoops, polyInput, paramsTriangulate); + } + } + + if (numSplitEdges > 0) + { + std::string details = ""; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + bool correct2 = fixPolyhedronData(polyInput.m_poly_data, params); +#ifdef _DEBUG + if (!correct2) + { + std::cout << "fixPolyhedronData failed" << std::endl; + } +#endif + return; + } + + shared_ptr > meshsetNew(polyInput.m_poly_data->createMesh(carve::input::opts(), eps)); + MeshSetInfo infoNew; + //checkMeshSetValidAndClosed(meshsetNew, infoNew, params); + + MeshSetInfo infoInput; + checkMeshSetValidAndClosed(meshsetInput, infoInput, params); + + assignIfBetterForBoolOp(meshsetNew, meshsetInput, infoNew, infoInput, false, params, false); + +#ifdef _DEBUG + if (params.debugDump) + { + glm::vec4 color(0.3, 0.3, 0.3, 1.); + bool drawNormals = true; + GeomDebugDump::dumpMeshset(meshsetNew, color, drawNormals, false); + + double dy = meshsetNew->getAABB().extent.y; + GeomDebugDump::moveOffset(dy * 2.2); + GeomDebugDump::dumpMeshsetOpenEdges(meshsetNew, color, false, false); + GeomDebugDump::moveOffset(dy * 3.2); + } +#endif + } + } +} + +bool MeshOps::assignIfBetterForBoolOp(shared_ptr >& meshsetNew, shared_ptr >& meshsetBefore, MeshSetInfo& infoNew, MeshSetInfo& infoBefore, bool considerTriangulation, const GeomProcessingParams& params, bool deepCopyMesh) +{ + MeshOps::checkMeshSetValidAndClosed(meshsetNew, infoNew, params); + if (MeshOps::isBetterForBoolOp(infoNew, infoBefore, considerTriangulation)) + { +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + std::unordered_map changes; + changes["numClosedEdgesRemoved"] = infoBefore.numClosedEdges - infoNew.numClosedEdges; + changes["numOpenEdgesRemoved"] = infoBefore.numOpenEdges() - infoNew.numOpenEdges(); + changes["numFacesRemoved"] = infoBefore.numFaces - infoNew.numFaces; + changes["numZeroAreaFacesRemoved"] = infoBefore.zeroAreaFaces.size() - infoNew.zeroAreaFaces.size(); + changes["numFinEdgesRemoved"] = infoBefore.finEdges.size() - infoNew.finEdges.size(); + changes["numFinFacesRemoved"] = infoBefore.finFaces.size() - infoNew.finFaces.size(); + changes["numDegeneratedEdgesRemoved"] = infoBefore.degenerateEdges.size() - infoNew.degenerateEdges.size(); + + int numChanges = 0; + for (auto it : changes) + { + numChanges += it.second; + } + if (numChanges > 0) + { + //std::cout << "assignIfBetterForBoolOp: changes: " << numChanges << std::endl; + } +#endif + if (deepCopyMesh) + { + meshsetBefore = shared_ptr>(meshsetNew->clone()); + } + else + { + meshsetBefore = meshsetNew; + } + infoBefore.copyFromOther(infoNew); + return true; + } + + return false; +} + +///\brief method intersectOpenEdges: Intersect open edges of MeshSet with closed edges, and split the open edges in case of intersection +///\param[in/out] meshset: MeshSet with open edges. If fix is found, a new MeshSet is assigned to the smart pointer +///\param[in] eps: tolerance to find edge-edge intersections +///\param[in] dumpMeshes: write meshes to dump file for debugging +void MeshOps::intersectOpenEdgesWithEdges(shared_ptr >& meshset, GeomProcessingParams& params) +{ + if (!meshset) + { + return; + } + + size_t maxNumFaces = 600; + size_t maxNumEdges = 600; + size_t maxNumOpenEdges = 100; + double eps = params.epsMergePoints; + +#ifdef _DEBUG + glm::vec4 color(0.5, 0.6, 0.7, 1.0); + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.8); + //GeomDebugDump::dumpMeshset(meshset, color, true); + double dy = meshset->getAABB().extent.y; + GeomDebugDump::moveOffset(dy * 2.2); + GeomDebugDump::dumpMeshsetOpenEdges(meshset, color, false, false); + GeomDebugDump::moveOffset(1.2); + } +#endif + + for (size_t round = 0; round < 5; ++round) + { + size_t numClosedEdgesBefore = 0; + std::vector* > allOpenEdges; + std::vector* > allFaces; + + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + numClosedEdgesBefore += mesh->closed_edges.size(); + std::copy(mesh->open_edges.begin(), mesh->open_edges.end(), std::back_inserter(allOpenEdges)); + std::copy(mesh->faces.begin(), mesh->faces.end(), std::back_inserter(allFaces)); + } + + PolyInputCache3D polyInput(params.epsMergePoints); + + // intersect with closed edges + size_t numSplitEdges = 0; + for (size_t iiFace = 0; iiFace < allFaces.size(); ++iiFace) + { + if (iiFace > maxNumFaces) + { + return; + } + carve::mesh::Face<3>* face = allFaces[iiFace]; + + const size_t n_edges = face->n_edges; + if (n_edges > maxNumEdges) + { +#ifdef _DEBUG + if (params.debugDump) + { + std::cout << "n_edges=" << n_edges << " > max (" << maxNumEdges << ")" << std::endl; + glm::vec4 color(0.3, 0.3, 0.3, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); + } +#endif + return; + } + + std::vector faceLoop; + carve::mesh::Edge<3>* edge = face->edge; + faceLoop.push_back(edge->v1()->v); + + for (size_t i_edge = 0; i_edge < n_edges; ++i_edge) + { + // check if current edge needs to be split + for (size_t iiOpenEdge = 0; iiOpenEdge < allOpenEdges.size(); ++iiOpenEdge) + { + if (iiOpenEdge > maxNumOpenEdges) + { + return; + } + const carve::mesh::Edge<3>* openEdge = allOpenEdges[iiOpenEdge]; + vec3 openEdgeStart = openEdge->v1()->v; + vec3 openEdgeEnd = openEdge->v2()->v; + + vec3 intersectionPoint; + bool intersect = edgeToEdgeIntersect(openEdge, edge, params.epsMergePoints, intersectionPoint); + + if (intersect) + { + faceLoop.push_back(intersectionPoint); + ++numSplitEdges; + + // handle only first split. TODO: sort split points and add to faceLoop, sorted + break; + } + } + faceLoop.push_back(edge->v2()->v); + + edge = edge->next; + + if (edge == face->edge) + { + break; + } + } + + bool addFaceDirectly = true; + if (addFaceDirectly) + { + std::vector faceIndexes; + for (size_t iiPoint = 0; iiPoint < faceLoop.size(); ++iiPoint) + { + const vec3& v0 = faceLoop[iiPoint]; + int idxA = polyInput.addPoint(v0); + if (faceIndexes.size() > 0) + { + int previousIndex = faceIndexes.back(); + if (idxA == previousIndex) + { + continue; + } + } + faceIndexes.push_back(idxA); + } + + if (faceIndexes.size() > 1) + { + for (size_t iiPoint = 0; iiPoint < faceIndexes.size(); ++iiPoint) + { + size_t idxFirst = faceIndexes.front(); + size_t idxLast = faceIndexes.back(); + if (idxFirst == idxLast) + { + faceIndexes.pop_back(); + if (faceIndexes.size() < 2) + { + break; + } + } + else + { + break; + } + } + } + + if (faceIndexes.size() > 2 && faceIndexes.size() < 500) + { + size_t n = std::distance(faceIndexes.begin(), faceIndexes.end()); + + size_t numFaceIndices = polyInput.m_poly_data->faceIndices.size(); + if (numFaceIndices > 1000) + { + //std::cout << numFaceIndices << " "; + } + polyInput.m_poly_data->addFace(faceIndexes.begin(), faceIndexes.end()); + } + } + else + { + std::vector > faceLoops = { faceLoop }; + bool mergeAlignedEdges = false; + bool dumpPolygon = true; + FaceConverter::createTriangulated3DFace(faceLoops, polyInput, params); + } + } + + if (numSplitEdges > 0) + { + std::string details = ""; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + bool correct2 = fixPolyhedronData(polyInput.m_poly_data, params); +#ifdef _DEBUG + if (!correct2) + { + std::cout << "fixPolyhedronData failed" << std::endl; + } +#endif + return; + } + + shared_ptr > meshsetNew(polyInput.m_poly_data->createMesh(carve::input::opts(), eps)); + if (meshsetNew->isClosed()) + { + meshset = meshsetNew; + return; + } + else + { + size_t numOpenEdgesNew = 0; + size_t numClosedEdgesNew = 0; + for (size_t ii = 0; ii < meshsetNew->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshsetNew->meshes[ii]; + numOpenEdgesNew += mesh->open_edges.size(); + numClosedEdgesNew += mesh->closed_edges.size(); + } + + if (numOpenEdgesNew < allOpenEdges.size()) + { + size_t numAllEdgesNew = numOpenEdgesNew + numClosedEdgesNew; + size_t numAllEdgesBefore = numClosedEdgesBefore + allOpenEdges.size(); + if (numClosedEdgesNew >= numClosedEdgesBefore) + { + meshset = meshsetNew; + } + } + +#ifdef _DEBUG + if (params.debugDump) + { + glm::vec4 color(0.3, 0.3, 0.3, 1.); + bool drawNormals = true; + GeomDebugDump::dumpMeshset(meshsetNew, color, drawNormals, false); + + double dy = meshsetNew->getAABB().extent.y; + GeomDebugDump::moveOffset(dy * 2.2); + GeomDebugDump::dumpMeshsetOpenEdges(meshsetNew, color, false, false); + GeomDebugDump::moveOffset(dy * 3.2); + } +#endif + } + } + } +} + +void MeshOps::boundingBox2Mesh(const carve::geom::aabb<3>& bbox, shared_ptr >& meshset, double eps) +{ + double minX = bbox.pos.x - bbox.extent.x; + double maxX = bbox.pos.x + bbox.extent.x; + double minY = bbox.pos.y - bbox.extent.y; + double maxY = bbox.pos.y + bbox.extent.y; + double minZ = bbox.pos.z - bbox.extent.z; + double maxZ = bbox.pos.z + bbox.extent.z; + + shared_ptr polyhedron_data(new carve::input::PolyhedronData()); + polyhedron_data->addVertex(carve::geom::VECTOR(maxX, maxY, maxZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(minX, maxY, maxZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(minX, minY, maxZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(maxX, minY, maxZ)); + + polyhedron_data->addVertex(carve::geom::VECTOR(maxX, maxY, minZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(minX, maxY, minZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(minX, minY, minZ)); + polyhedron_data->addVertex(carve::geom::VECTOR(maxX, minY, minZ)); + + polyhedron_data->addFace(0, 1, 2); + polyhedron_data->addFace(2, 3, 0); + + polyhedron_data->addFace(7, 6, 5); + polyhedron_data->addFace(5, 4, 7); + + polyhedron_data->addFace(0, 4, 5); + polyhedron_data->addFace(5, 1, 0); + polyhedron_data->addFace(1, 5, 6); + polyhedron_data->addFace(6, 2, 1); + polyhedron_data->addFace(2, 6, 7); + polyhedron_data->addFace(7, 3, 2); + polyhedron_data->addFace(3, 7, 4); + polyhedron_data->addFace(4, 0, 3); + + meshset = shared_ptr >(polyhedron_data->createMesh(carve::input::opts(), eps)); +} + +std::shared_ptr > MeshOps::createPlaneMesh(vec3& p0, vec3& p1, vec3& p2, double eps) +{ + carve::input::PolyhedronData polyhedron_data; + polyhedron_data.addVertex(p0); + polyhedron_data.addVertex(p1); + polyhedron_data.addVertex(p2); + polyhedron_data.addFace(0, 1, 2); + std::shared_ptr > mesh(polyhedron_data.createMesh(carve::input::opts(), eps)); + return mesh; +} + +std::shared_ptr > MeshOps::createPlaneMesh(vec3& p0, vec3& p1, vec3& p2, vec3& p3, double eps) +{ + carve::input::PolyhedronData polyhedron_data; + polyhedron_data.addVertex(p0); + polyhedron_data.addVertex(p1); + polyhedron_data.addVertex(p2); + polyhedron_data.addVertex(p3); + + polyhedron_data.addFace(0, 1, 2, 3); + //polyhedron_data.addFace(2, 3, 0); + std::shared_ptr > mesh(polyhedron_data.createMesh(carve::input::opts(), eps)); + return mesh; +} + +std::shared_ptr > MeshOps::createBoxMesh(vec3& pos, vec3& extent, carve::math::Matrix& transform, double eps) +{ + carve::input::PolyhedronData polyhedron_data; + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(extent.x, extent.y, -extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(extent.x, -extent.y, -extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(-extent.x, -extent.y, -extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(-extent.x, extent.y, -extent.z))); + + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(extent.x, extent.y, extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(extent.x, -extent.y, extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(-extent.x, -extent.y, extent.z))); + polyhedron_data.addVertex(transform * (pos + carve::geom::VECTOR(-extent.x, extent.y, extent.z))); + + polyhedron_data.addFace(0, 1, 2); + polyhedron_data.addFace(2, 3, 0); + polyhedron_data.addFace(7, 6, 5); + polyhedron_data.addFace(5, 4, 7); + polyhedron_data.addFace(0, 4, 5); + polyhedron_data.addFace(5, 1, 0); + polyhedron_data.addFace(1, 5, 6); + polyhedron_data.addFace(6, 2, 1); + polyhedron_data.addFace(2, 6, 7); + polyhedron_data.addFace(7, 3, 2); + polyhedron_data.addFace(3, 7, 4); + polyhedron_data.addFace(4, 0, 3); + + std::shared_ptr > mesh(polyhedron_data.createMesh(carve::input::opts(), eps)); + return mesh; +} + +bool checkPolyhedronData(const shared_ptr& poly_data, const GeomProcessingParams& params, std::string& details) +{ + bool allowZeroAreaFaces = false; + + if (poly_data) + { + const std::vector& faceIndices = poly_data->faceIndices; + if (faceIndices.size() > 0) + { + size_t iiFace = 0; + for (; iiFace < faceIndices.size(); ) + { + int numPoints = faceIndices[iiFace]; + int numPointsIdx = iiFace; + +#ifdef _DEBUG + std::vector checkIndexes1; + if (faceIndices.size() < 500) + { + auto it = faceIndices.begin() + iiFace; + std::copy(it, faceIndices.end(), std::back_inserter(checkIndexes1)); + } +#endif + + if (iiFace + numPoints >= faceIndices.size()) + { + return false; + } + + if (numPoints < 3) + { +#ifdef _DEBUG + //std::cout << "checkPolyhedronData: face with < 3 points" << std::endl; +#endif + return false; + } + + ++iiFace; + + int idxFirst = faceIndices[iiFace]; + int idxLast = faceIndices[iiFace + numPoints - 1]; + if (idxFirst == idxLast) + { +#ifdef _DEBUG + //std::cout << "checkPolyhedronData: closed polygon of " << numPoints << " points" << std::endl; +#endif + return false; + } + + std::vector facePoints; + for (size_t iiPoint = 0; iiPoint < numPoints; ++iiPoint) + { + int idx = faceIndices[iiFace + iiPoint]; + if (idx >= poly_data->points.size()) + { +#ifdef _DEBUG + //std::cout << "checkPolyhedronData: incorrect idx" << std::endl; +#endif + return false; + } + + if (iiPoint < numPoints - 1) + { + int idxNext = faceIndices[iiFace + iiPoint + 1]; + if (idx == idxNext) + { +#ifdef _DEBUG + //std::cout << "checkPolyhedronData: duplicate point" << std::endl; +#endif + details += "duplicate point"; + return false; + } + } + + vec3& point = poly_data->points[idx]; + facePoints.push_back(point); + } + + if (!allowZeroAreaFaces) + { + double area = GeomUtils::computePolygonArea(facePoints, params.epsMergePoints); + if (std::abs(area) < params.minFaceArea) + { + details += "face area < eps"; + //TODO: if face is triangle + // if all points dx/dy/dz < eps, remove false + // else if 2 points are close, collapse 2 long edges + // else if there are 3 long edges and 1 point is on longest edge, split longest edge by point, and collapse all 4 edges + + return false; + } + } + + iiFace = iiFace + numPoints; + } + + if (iiFace != faceIndices.size()) + { + details = "iiFace != faceIndices.size()"; + return false; + } + } + } + return true; +} + +bool allPointsWithinEpsilon(const std::vector& points, double eps) +{ + if (points.size() < 1) + { + return true; + } + + double minX = points[0].x; + double minY = points[0].y; + double minZ = points[0].z; + + double maxX = points[0].x; + double maxY = points[0].y; + double maxZ = points[0].z; + + //if (values.size() < 100000) + { + for (size_t ii = 1; ii < points.size(); ii += 3) + { + const vec3& pt = points[ii]; + const double& x = pt.x; + const double& y = pt.y; + const double& z = pt.z; + + if (x > maxX) maxX = x; + else if (x < minX) minX = x; + + if (y > maxY) maxY = y; + else if (y < minY) minY = y; + + if (z > maxZ) maxZ = z; + else if (z < minZ) minZ = z; + + if (maxX - minX > eps) + { + return false; + } + if (maxY - minY > eps) + { + return false; + } + if (maxZ - minZ > eps) + { + return false; + } + } + } + return true; +} + +bool fixPolyhedronData(const shared_ptr& poly_data, const GeomProcessingParams& params) +{ + if (!poly_data) + { + return false; + } + + std::vector& faceIndices = poly_data->faceIndices; + if (faceIndices.size() == 0) + { + return true; + } + + size_t numPointsAll = poly_data->points.size(); + if (numPointsAll < 2) + { + return true; + } + bool inputCorrect = true; + size_t maxPointIndex = numPointsAll - 1; + double epsMergePoints = params.epsMergePoints; + double epsMinFaceArea = params.minFaceArea; + std::vector polyDataCorrected; + int numFacesCorrected = 0; + int numSkippedPoints = 0; + + for (size_t iiFace = 0; iiFace < faceIndices.size(); ) + { + int numPoints = faceIndices[iiFace]; + int numPointsIdx = iiFace; + + if (iiFace + numPoints >= faceIndices.size()) + { + // skip face + break; + } + + std::vector pointIdxCurrentFace; + for (size_t iiPoint = 1; iiPoint <= numPoints; ++iiPoint) + { + int idx = faceIndices[iiFace + iiPoint]; + if (idx > maxPointIndex) + { + // incorrect point index, skip current point + ++numSkippedPoints; + continue; + } + + if (pointIdxCurrentFace.size() > 0) + { + if (idx == pointIdxCurrentFace.back()) + { + // duplicate index, skip + ++numSkippedPoints; + continue; + } + } + pointIdxCurrentFace.push_back(idx); + } + + if (pointIdxCurrentFace.size() > 2) + { + int firstPointIndex = pointIdxCurrentFace.front(); + int lastPointIndex = pointIdxCurrentFace.back(); + if (firstPointIndex == lastPointIndex) + { + // duplicate index, remove last point + pointIdxCurrentFace.pop_back(); + ++numSkippedPoints; + } + + if (pointIdxCurrentFace.size() > 2) + { + int idx = pointIdxCurrentFace[0]; + + vec3 pointPrevious = poly_data->points[idx]; + std::vector polygonPoints; + bool duplicatePointFound = false; + + for (size_t iiPoint = 0; iiPoint < pointIdxCurrentFace.size(); ++iiPoint) + { + int idx = pointIdxCurrentFace[iiPoint]; + const vec3& point = poly_data->points[idx]; +#ifdef _DEBUG + int idxNext = pointIdxCurrentFace[(iiPoint + 1) % pointIdxCurrentFace.size()]; + const vec3& pointNext = poly_data->points[idxNext]; + + if (std::abs(pointNext.x - point.x) < epsMergePoints) + { + if (std::abs(pointNext.y - point.y) < epsMergePoints) + { + if (std::abs(pointNext.z - point.z) < epsMergePoints) + { + // delete current point and start over + //auto itErase = pointIdxCurrentFace.begin(); + //std::advance(itErase, iiPoint); + //iiPoint = 0; + //polygonPoints.clear(); + //continue; + duplicatePointFound = true; + } + } + } +#endif + polygonPoints.push_back(point); + } + + GeomUtils::removeDuplicates(polygonPoints, epsMergePoints); + + bool allowLongZeroAreaFaces = true; + if (allowLongZeroAreaFaces) + { + // remove face only when all points are within epsMergePoints + + bool faceIsPoint = allPointsWithinEpsilon(polygonPoints, epsMergePoints); + if (!faceIsPoint) + { + // found correct face + ++numFacesCorrected; + int numPointsInFace = pointIdxCurrentFace.size(); + polyDataCorrected.push_back(numPointsInFace); + std::copy(pointIdxCurrentFace.begin(), pointIdxCurrentFace.end(), std::back_inserter(polyDataCorrected)); + } + } + else + { + + // checking just the face area does not work, since it could be a long face with 0 width. Removing it, would result in open edges + double area = GeomUtils::computePolygonArea(polygonPoints, epsMergePoints); + if (area > epsMinFaceArea) + { + // found correct face + ++numFacesCorrected; + int numPointsInFace = pointIdxCurrentFace.size(); + polyDataCorrected.push_back(numPointsInFace); + std::copy(pointIdxCurrentFace.begin(), pointIdxCurrentFace.end(), std::back_inserter(polyDataCorrected)); + } + else + { +#ifdef _DEBUG + //std::cout << "fixPolyhedronData: removing face with area " << area << std::endl; +#endif + } + } + } + } + + iiFace += numPoints + 1; + + if (iiFace > faceIndices.size()) + { + inputCorrect = false; + break; + } + if (iiFace == faceIndices.size()) + { + break; + } + } + + poly_data->faceCount = numFacesCorrected; + faceIndices = polyDataCorrected; + + return inputCorrect; +} + +bool reverseFacesInPolyhedronData(const shared_ptr& poly_data) +{ + if (!poly_data) + { + return false; + } + + std::vector& faceIndices = poly_data->faceIndices; + if (faceIndices.size() == 0) + { + return true; + } + + size_t numPointsAll = poly_data->points.size(); + if (numPointsAll < 2) + { + return true; + } + bool inputCorrect = true; + size_t maxPointIndex = numPointsAll - 1; + + std::vector polyDataReversed; + int numFacesCorrected = 0; + + for (size_t iiFace = 0; iiFace < faceIndices.size(); ) + { + int numPoints = faceIndices[iiFace]; + int numPointsIdx = iiFace; + + if (iiFace + numPoints >= faceIndices.size()) + { + // skip face + break; + } + + std::vector pointIdxCurrentFace; + for (size_t iiPoint = 1; iiPoint <= numPoints; ++iiPoint) + { + int idx = faceIndices[iiFace + iiPoint]; + pointIdxCurrentFace.push_back(idx); + } + + polyDataReversed.push_back(numPoints); + std::copy(pointIdxCurrentFace.rbegin(), pointIdxCurrentFace.rend(), std::back_inserter(polyDataReversed)); + + iiFace += numPoints + 1; + + if (iiFace > faceIndices.size()) + { + inputCorrect = false; + break; + } + if (iiFace == faceIndices.size()) + { + break; + } + } + + faceIndices = polyDataReversed; + + return inputCorrect; +} + +void MeshOps::polyhedronFromMeshSet(const shared_ptr>& meshset, PolyInputCache3D& polyInput, int minNumFacesPerMesh) +{ + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + + if (minNumFacesPerMesh > 0) + { + if (mesh->faces.size() < minNumFacesPerMesh) + { + continue; + } + } + + for (size_t jj = 0; jj < mesh->faces.size(); ++jj) + { + carve::mesh::Face<3>* face = mesh->faces[jj]; + + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + std::vector vecPointIndexes; + size_t maxNumEdges = 1000; + for (size_t kk = 0; kk < face->n_edges; ++kk) + { + vec3& edgeEndPoint = edge->v2()->v; + int idx = polyInput.addPoint(edgeEndPoint); + vecPointIndexes.push_back(idx); + + edge = edge->next; + if (edge == face->edge) + { + break; + } + } + if (vecPointIndexes.size() < 3) + { +#ifdef _DEBUG + std::cout << "face with < 3 edges" << std::endl; +#endif + continue; + } + polyInput.m_poly_data->addFace(vecPointIndexes.begin(), vecPointIndexes.end()); + } + } + } +} + +void MeshOps::polyhedronFromMeshSet(const shared_ptr>& meshset, const std::set* >& setSkipFaces, const std::set* >& setFlipFaces, PolyInputCache3D& polyInput) +{ + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + + for (size_t jj = 0; jj < mesh->faces.size(); ++jj) + { + carve::mesh::Face<3>* face = mesh->faces[jj]; + + if (setSkipFaces.find(face) != setSkipFaces.end()) + { + continue; + } + + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + std::vector vecPointIndexes; + size_t maxNumEdges = 1000; + for (size_t kk = 0; kk < face->n_edges; ++kk) + { + vec3& edgeEndPoint = edge->v2()->v; + int idx = polyInput.addPoint(edgeEndPoint); + vecPointIndexes.push_back(idx); + + edge = edge->next; + if (edge == face->edge) + { + break; + } + } + if (vecPointIndexes.size() < 3) + { +#ifdef _DEBUG + std::cout << "face with < 3 edges" << std::endl; +#endif + continue; + } + + // TODO: fix polyline, detect overlapping edges + if (setFlipFaces.find(face) != setFlipFaces.end()) + { + polyInput.m_poly_data->addFace(vecPointIndexes.rbegin(), vecPointIndexes.rend()); + } + else + { + polyInput.m_poly_data->addFace(vecPointIndexes.begin(), vecPointIndexes.end()); + } + } + } + } +} + +void MeshOps::polyhedronFromMeshSet(const shared_ptr>& meshset, const std::set* >& setSkipFaces, PolyInputCache3D& polyInput) +{ + for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + { + carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + + for (size_t jj = 0; jj < mesh->faces.size(); ++jj) + { + carve::mesh::Face<3>* face = mesh->faces[jj]; + + if (setSkipFaces.find(face) != setSkipFaces.end()) + { + continue; + } + + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + std::vector vecPointIndexes; + size_t maxNumEdges = 1000; + for (size_t kk = 0; kk < face->n_edges; ++kk) + { + vec3& edgeEndPoint = edge->v2()->v; + int idx = polyInput.addPoint(edgeEndPoint); + vecPointIndexes.push_back(idx); + + edge = edge->next; + if (edge == face->edge) + { + if (kk != face->n_edges - 1) + { + std::cout << "kk != face->n_edges - 1" << std::endl; + } + break; + } + } + if (vecPointIndexes.size() < 3) + { +#ifdef _DEBUG + std::cout << "face with < 3 edges" << std::endl; +#endif + continue; + } + + // TODO: fix polyline, detect overlapping edges + + polyInput.m_poly_data->addFace(vecPointIndexes.begin(), vecPointIndexes.end()); + } + } + } +} + +void MeshOps::polyhedronFromMesh(const carve::mesh::Mesh<3>* mesh, PolyInputCache3D& polyInput) +{ + for (size_t jj = 0; jj < mesh->faces.size(); ++jj) + { + carve::mesh::Face<3>* face = mesh->faces[jj]; + carve::mesh::Edge<3>* edge = face->edge; + if (edge) + { + std::vector vecPointIndexes; + size_t maxNumEdges = 1000; + for (size_t kk = 0; kk < face->n_edges; ++kk) + { + vec3& edgeEndPoint = edge->v2()->v; + int idx = polyInput.addPoint(edgeEndPoint); + vecPointIndexes.push_back(idx); + + edge = edge->next; + if (edge == face->edge) + { + break; + } + } + if (vecPointIndexes.size() < 3) + { +#ifdef _DEBUG + std::cout << "face with < 3 edges" << std::endl; +#endif + continue; + } + polyInput.m_poly_data->addFace(vecPointIndexes.begin(), vecPointIndexes.end()); + } + } +} + +void MeshOps::MeshSet2Polyhedron2MeshSet(const shared_ptr >& meshsetIn, + shared_ptr >& meshsetOut, const GeomProcessingParams& params, int minNumFacesPerMesh) +{ + PolyInputCache3D polyInput(params.epsMergePoints); + MeshOps::polyhedronFromMeshSet(meshsetIn, polyInput, minNumFacesPerMesh); + + std::string details; + bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!polyCorrect) + { + fixPolyhedronData(polyInput.m_poly_data, params); + polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + } + + std::map mesh_input_options; + meshsetOut = shared_ptr >(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); +} diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshOps.cpp b/IfcPlusPlus/src/ifcpp/geometry/MeshOps.cpp index d91053a09..b14e474b7 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/MeshOps.cpp +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshOps.cpp @@ -18,6 +18,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include "IncludeCarveHeaders.h" #include #include +#include "EdgeLoopFinder.h" +#include "MeshSimplifier.h" #include "MeshOps.h" using namespace IFC4X3; @@ -270,17 +272,30 @@ void splitIntoSubLoops(const std::vector >& polygonMerged polygonLoops.push_back(previousLoop); } - std::multimap > > mapAreaLoops; + std::map > > > mapAreaLoops; for (size_t ii = 0; ii < polygonLoops.size(); ++ii) { std::vector >& loop = polygonLoops[ii]; double area = std::abs(GeomUtils::signedArea(loop)); - mapAreaLoops.insert({ area, loop }); + auto itFind = mapAreaLoops.find(area); + if (itFind == mapAreaLoops.end()) + { + std::vector > >& vecOfLoops = itFind->second; + vecOfLoops.push_back({ loop }); + } + else + { + mapAreaLoops.insert({ area, { loop } }); + } } polygonLoops.clear(); for (auto it = mapAreaLoops.rbegin(); it != mapAreaLoops.rend(); ++it) { - polygonLoops.push_back(it->second); + std::vector > >& vecOfLoops = it->second; + for (std::vector >& loop : vecOfLoops) + { + polygonLoops.push_back(loop); + } } } @@ -409,6 +424,20 @@ bool isBetterForExport(MeshSetInfo infoTriangulated, MeshSetInfo infoBefore) void MeshOps::checkAndFixMeshsetInverted( shared_ptr>& meshset, MeshSetInfo& info, const GeomProcessingParams& params) { + if( !meshset ) + { + return; + } + + if (meshset->meshes.size() > 100) + { + return; + } + if (meshset->vertex_storage.size() > 5000) + { + return; + } + // this approach is maybe too simple: get highest and lowest face in x/y/z, then check if normal vector points in the same direction // we can definitely not rely on meshset->is_negative carve::mesh::Face<3>* minXFace = nullptr; @@ -525,8 +554,17 @@ void MeshOps::checkAndFixMeshsetInverted( shared_ptr>& m return; } - +#ifdef _DEBUG + glm::vec4 color(0.2, 0.2, 0.2, 1.); + if (params.debugDump) + { + GeomDebugDump::dumpMeshset(meshset.get(), color, false, true); + } +#endif + + if (countWindingProbablyCorrect*3 < countWindingProbablyInCorrect*2) { + // majority of faces have inward normals, so invert mesh std::set* > setSkipFaces; std::set* > setFlipFaces; PolyInputCache3D polyInput(params.epsMergePoints); @@ -756,7 +794,7 @@ void MeshOps::retriangulateMeshSetForExport( shared_ptr continue; } - if (faceBound.size() == 3 && false) + if (faceBound.size() == 3 ) { int vertex_index_a = poly_cache.addPoint(faceBound[0]); int vertex_index_b = poly_cache.addPoint(faceBound[1]); @@ -776,19 +814,23 @@ void MeshOps::retriangulateMeshSetForExport( shared_ptr } } - std::string details = ""; - bool correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); - if (!correct) + bool checkPolyData = false; // probably not necessary for openGL or other renderers + if (checkPolyData) { - fixPolyhedronData(poly_cache.m_poly_data, params); - correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); - -#ifdef _DEBUG + std::string details = ""; + bool correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); if (!correct) { - std::cout << "fixPolyhedronData failed" << std::endl; - } + fixPolyhedronData(poly_cache.m_poly_data, params); + correct = checkPolyhedronData(poly_cache.m_poly_data, params, details); + +#ifdef _DEBUG + if (!correct) + { + std::cout << "fixPolyhedronData failed" << std::endl; + } #endif + } } shared_ptr> meshsetTriangulated = shared_ptr >(poly_cache.m_poly_data->createMesh(carve::input::opts(), params.epsMergePoints)); @@ -801,8 +843,6 @@ void MeshOps::retriangulateMeshSetForExport( shared_ptr meshset = meshsetTriangulated; } - //if (!validTriangulatedMesh) - { #ifdef _DEBUG bool dumpMesh = true; if (validInput && dumpMesh && meshset->vertex_storage.size() > 60) @@ -819,14 +859,6 @@ void MeshOps::retriangulateMeshSetForExport( shared_ptr GeomDebugDump::dumpWithLabel("triangulate:result", meshsetTriangulated, dumpSet, paramCopy, true, true); } #endif - MeshOps::checkMeshSetValidAndClosed(meshsetTriangulated, infoTriangulated, params); - - if (isBetterForExport(infoTriangulated, infoInput)) - { - meshset.reset(); - meshset = meshsetTriangulated; - } - } checkAndFixMeshsetInverted(meshset, infoTriangulated, params); } @@ -839,11 +871,19 @@ void MeshOps::simplifyMeshSet(shared_ptr >& meshset, Mes return; } - if (params.generalSettings) + bool useDefaultSimplifier = true; + if (useDefaultSimplifier) + { + MeshSimplifier::simplifyMeshSet(meshset, params); + } + else { - if (params.generalSettings->m_callback_simplify_mesh) + if (params.generalSettings) { - params.generalSettings->m_callback_simplify_mesh(meshset, params); + if (params.generalSettings->m_callback_simplify_mesh) + { + params.generalSettings->m_callback_simplify_mesh(meshset, params); + } } } @@ -1070,6 +1110,10 @@ void findFinEdges(const shared_ptr >& meshset, std::set< { for (const carve::mesh::Mesh<3>*mesh : meshset->meshes) { + if (mesh->closed_edges.size() > 1000) + { + continue; + } for (carve::mesh::Edge<3>*edge : mesh->closed_edges) { if (!edge) @@ -1156,7 +1200,7 @@ void MeshOps::removeDegenerateMeshes(shared_ptr >& meshs if (meshInputOk && !meshOk) { GeomDebugDump::DumpSettingsStruct dumpColorSettings; - GeomDebugDump::dumpWithLabel("removeDegenerateFacesInMeshSet--input", meshsetInput, dumpColorSettings, params, true, true); + GeomDebugDump::dumpWithLabel("removeDegenerateMeshes--input", meshsetInput, dumpColorSettings, params, true, true); } #endif @@ -1601,17 +1645,20 @@ inline bool edgeToEdgeIntersect(const carve::mesh::Edge<3>* edge1, const carve:: return false; } -void flipFacesOnOpenEdges(shared_ptr>& meshset, const GeomProcessingParams& params) +size_t flipFacesOnOpenEdges(shared_ptr>& meshset, MeshSetInfo& infoInput, const GeomProcessingParams& params) { - MeshSetInfo infoInput; - MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); - size_t numEdgesAll = infoInput.numClosedEdges + infoInput.numOpenEdges(); - if (numEdgesAll > 10000) + if (numEdgesAll > 5000) { - return; + return 0; + } + + if (infoInput.numFaces > 5000) + { + return 0; } + size_t numChanges = 0; std::set* > setFacesDone; std::set* > setFlipFaces; @@ -1633,6 +1680,7 @@ void flipFacesOnOpenEdges(shared_ptr>& meshset, const Ge { setFacesDone.insert(edge->face); setFlipFaces.insert(edge->face); + ++numChanges; } } } @@ -1663,381 +1711,169 @@ void flipFacesOnOpenEdges(shared_ptr>& meshset, const Ge { meshset = meshsetFromPolyhedron; } + return numChanges; } + return 0; } -struct EdgeLoopElement +void MeshOps::resolveOpenEdges(shared_ptr>& meshset, const GeomProcessingParams& params) { - EdgeLoopElement(carve::mesh::Edge<3>* _edge, bool _sameSense) - { - edge = _edge; - sameSense = _sameSense; - } - carve::mesh::Edge<3>* edge = nullptr; - bool sameSense = true; -}; + MeshSetInfo infoInput; + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + resolveOpenEdges(meshset, infoInput, params); +} -class EdgeLoopFinder +void MeshOps::resolveOpenEdges(shared_ptr>& meshset, MeshSetInfo& infoInput, const GeomProcessingParams& params) { -public: - std::set* > m_setOpenEdges; - std::unordered_map*, std::set* > > m_mapVertexOpenEdges; - std::vector > m_closedLoopsToCoverOpenEdges; - - bool findNextEdge(const carve::mesh::Vertex<3>* startVertex, carve::mesh::Vertex<3>* currentVertex, carve::mesh::Edge<3>* currentEdge, std::set*>& setCurrentPath, std::vector >& vecLoopEdges) + if (!meshset) { - auto itFindVertexMapV1 = m_mapVertexOpenEdges.find(currentVertex); - - std::set* >& setEdgesFromStartVertex = itFindVertexMapV1->second; - - for (carve::mesh::Edge<3>* checkEdge : setEdgesFromStartVertex) - { - if (!checkEdge) - { - continue; - } - - if (!checkEdge->vert) - { - continue; - } - - if (currentEdge == checkEdge) - { - // we don't want to go back - continue; - } - - if (m_setOpenEdges.find(checkEdge) == m_setOpenEdges.end()) - { - continue; - } - if (setCurrentPath.find(checkEdge) != setCurrentPath.end()) - { - continue; - } - - carve::mesh::Vertex<3>* oppositeVertex = nullptr; - bool sameSenseCheckEdge = true; - if (checkEdge->v1() == currentVertex) - { - oppositeVertex = checkEdge->v2(); - } - else if (checkEdge->v2() == currentVertex) - { - oppositeVertex = checkEdge->v2(); - sameSenseCheckEdge = false; - } - else - { - // should not happen - std::cout << "checkEdge->v1() != currentVertex && checkEdge->v2() == currentVertex" << std::endl; - continue; - } - - if (oppositeVertex == startVertex) - { - if (currentEdge == nullptr) - { - continue; - } - else - { - // found closed loop - vecLoopEdges.push_back(make_shared(checkEdge, sameSenseCheckEdge)); - return true; - } - } - - // check if v2 is also in mapVertexOpenEdges - auto itFindV2 = m_mapVertexOpenEdges.find(oppositeVertex); - if (itFindV2 == m_mapVertexOpenEdges.end()) - { - // checkEdge leads somewhere else. It is not part of the set of open edges - continue; - } - - setCurrentPath.insert(checkEdge); - bool found = findNextEdge(startVertex, oppositeVertex, checkEdge, setCurrentPath, vecLoopEdges); - if (found) - { - vecLoopEdges.push_back(make_shared(checkEdge, sameSenseCheckEdge)); - return true; - } - - continue; - - std::set* >& setEdgesFromCurrentVertex = itFindVertexMapV1->second; - for (auto it : setEdgesFromStartVertex) - { - carve::mesh::Edge<3>* edgeFromOppositeVertex = it; - carve::mesh::Vertex<3>* vnext = nullptr; - bool sameSense = true; - if (edgeFromOppositeVertex->v1() == oppositeVertex) - { - carve::mesh::Vertex<3>* vnext = edgeFromOppositeVertex->v2(); - } - else if (edgeFromOppositeVertex->v2() == oppositeVertex) - { - carve::mesh::Vertex<3>* vnext = edgeFromOppositeVertex->v1(); - sameSense = false; - } - - if (vnext == startVertex) - { - if (currentEdge != nullptr) - { - vecLoopEdges.push_back(make_shared(edgeFromOppositeVertex, sameSense)); - return true; - } - } - - if (vnext != nullptr) - { - bool found = findNextEdge(startVertex, oppositeVertex, edgeFromOppositeVertex, setCurrentPath, vecLoopEdges); - if (found) - { - vecLoopEdges.push_back(make_shared(edgeFromOppositeVertex, sameSense)); - //vecLoopEdges.push_back(edgeFromOppositeVertex); - } - } - } - } - - if (currentEdge == nullptr) - { - // start - - } - return false; + return; } - void findLoops() + if (meshset->vertex_storage.size() > 5000) { return; - - for (size_t ii = 0; ii < m_mapVertexOpenEdges.size(); ++ii) - { - // try to find closed loop of open edges - std::vector > vecLoopEdges; - auto itFindVertexMapV1 = m_mapVertexOpenEdges.begin(); - carve::mesh::Vertex<3>* startVertex = itFindVertexMapV1->first; - carve::mesh::Edge<3>* currentEdge = nullptr; - - std::set*> setCurrentPath; - bool foundLoop = findNextEdge(startVertex, startVertex, currentEdge, setCurrentPath, vecLoopEdges); - - itFindVertexMapV1 = m_mapVertexOpenEdges.erase(itFindVertexMapV1); - - if (foundLoop) - { - m_closedLoopsToCoverOpenEdges.push_back(std::vector()); - std::vector& pointLoop = *m_closedLoopsToCoverOpenEdges.rbegin(); - for (auto it : vecLoopEdges) - { - shared_ptr& loopElement = it; - carve::mesh::Vertex<3>* currentVertex = nullptr; - if (loopElement->sameSense) - { - currentVertex = loopElement->edge->v1(); - } - else - { - currentVertex = loopElement->edge->v2(); - } - - if (currentVertex != nullptr) - { - auto itFindErase = m_mapVertexOpenEdges.find(currentVertex); - if (itFindErase != m_mapVertexOpenEdges.end()) - { - m_mapVertexOpenEdges.erase(itFindErase); - } - pointLoop.push_back(currentVertex->v); - } - } - -#ifdef _DEBUG - glm::vec4 color(1, 0.5, 1, 1); - GeomDebugDump::dumpPolyline(pointLoop, color, 3, true, false); -#endif - } - - if (m_mapVertexOpenEdges.size() < 2) - { - break; - } - } } -}; -void MeshOps::resolveOpenEdges(shared_ptr>& meshset, const GeomProcessingParams& params) -{ - if (!meshset) + bool tryFlipFaces = true; + if (tryFlipFaces) { - return; + size_t numChanges = flipFacesOnOpenEdges(meshset, infoInput, params); + if (numChanges > 0) + { + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + } } - flipFacesOnOpenEdges(meshset, params); - - MeshSetInfo infoInput; - MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); - if (infoInput.meshSetValid) { return; } - // simple approach: collect vertices of open edges, store as 3D point loop. Generate PolyInputCache3D, then add face from open edges point loop + bool tryMergeShortOpenEdges = true; + size_t numMeshesChanged = 0; + shared_ptr > meshsetCopyUnChanged; - // check if all open edges are close together -> merge points to center - bool openEdgesMergedToPoint = false; - EdgeLoopFinder loopFinder; - - for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) { - carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; - - if (mesh->open_edges.size() > 0) + // check if all open edges are close together -> merge points to center + EdgeLoopFinder loopFinder(params.epsMergePoints, params.epsMergePoints * 50); + bool meshsetChanged = false; + loopFinder.initFromMesh(meshset, mesh, params, tryMergeShortOpenEdges, meshsetChanged); + if (meshsetChanged) { - std::set* > openEdgeVertices; - std::set openEdgeVertexPoints; - for (auto openEdge : mesh->open_edges) - { - auto e = openEdge; - if (e) - { - loopFinder.m_setOpenEdges.insert(e); - if (e->vert) - { - openEdgeVertices.insert(e->v1()); - openEdgeVertices.insert(e->v2()); - openEdgeVertexPoints.insert(e->v1()->v); - openEdgeVertexPoints.insert(e->v2()->v); - - auto itFindVertexMapV1 = loopFinder.m_mapVertexOpenEdges.find(e->v1()); - if (itFindVertexMapV1 == loopFinder.m_mapVertexOpenEdges.end()) - { - loopFinder.m_mapVertexOpenEdges.insert({ e->v1(), {e} }); - } - else - { - itFindVertexMapV1->second.insert(e); - } - - auto itFindVertexMapV2 = loopFinder.m_mapVertexOpenEdges.find(e->v2()); - if (itFindVertexMapV2 == loopFinder.m_mapVertexOpenEdges.end()) - { - loopFinder.m_mapVertexOpenEdges.insert({ e->v2(), {e} }); - } - else - { - itFindVertexMapV2->second.insert(e); - } - } - } - } - - carve::geom::aabb<3> bbox; - bbox.fit(openEdgeVertexPoints.begin(), openEdgeVertexPoints.end() ); - - if (bbox.extent.x < params.epsMergePoints * 500) - { - if (bbox.extent.y < params.epsMergePoints * 500) - { - if (bbox.extent.z < params.epsMergePoints * 500) - { - for (auto v : openEdgeVertices) - { - v->v = bbox.pos; - } - openEdgesMergedToPoint = true; - } - } - } + meshsetCopyUnChanged = loopFinder.m_meshsetCopyUnChanged; + ++numMeshesChanged; } } - if (openEdgesMergedToPoint) + if (numMeshesChanged > 0) { - PolyInputCache3D polyInput(params.epsMergePoints); + shared_ptr > meshsetFromPolyhedron; + int minNumFacesPerMesh = 4; + MeshSet2Polyhedron2MeshSet(meshset, meshsetFromPolyhedron, params, minNumFacesPerMesh); - polyhedronFromMeshSet(meshset, polyInput); + MeshSetInfo infoChanged; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoChanged, params); - std::map mesh_input_options; - std::string details; - bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); - if (!correct) + if (MeshOps::isBetterForBoolOp(infoChanged, infoInput, true)) { - fixPolyhedronData(polyInput.m_poly_data, params); - std::string details2; - correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + meshset = meshsetFromPolyhedron; + infoInput.copyFromOther(infoChanged); } - - if (correct) + else { - shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); - MeshSetInfo infoFlippedFaces; - MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoFlippedFaces, params); - - if (MeshOps::isBetterForBoolOp(infoFlippedFaces, infoInput, true)) - { - meshset = meshsetFromPolyhedron; - } + meshset = meshsetCopyUnChanged; } - } - MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); - if (infoInput.meshSetValid) - { - return; + if (infoInput.meshSetValid) + { + return; + } } - if (loopFinder.m_mapVertexOpenEdges.size() > 0) + size_t maxNumRepeats = meshset->meshes.size(); + if (maxNumRepeats > 1) maxNumRepeats = 1; + for (size_t repeat = 0; repeat < maxNumRepeats; ++repeat) { - loopFinder.findLoops(); - - if (loopFinder.m_closedLoopsToCoverOpenEdges.size() > 0) + size_t numChanges = 0; + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) { - PolyInputCache3D polyInput(params.epsMergePoints); + // check if all open edges are close together -> merge points to center + EdgeLoopFinder loopFinder(params.epsMergePoints, params.epsMergePoints * 50); + bool meshsetChanged = false; + loopFinder.initFromMesh(meshset, mesh, params, tryMergeShortOpenEdges, meshsetChanged); - polyhedronFromMeshSet(meshset, polyInput); + float ratioOpenEdges = 0; + if (infoInput.numClosedEdges > 0) + { + ratioOpenEdges = float(infoInput.numOpenEdges()) / float(infoInput.numClosedEdges); + } - for (std::vector& loop : loopFinder.m_closedLoopsToCoverOpenEdges) + if (loopFinder.m_mapVertexOpenEdges.size() > 0) { - std::vector loopPointIndexes; - for (vec3& point : loop) + if (ratioOpenEdges < 0.2) { - int idx = polyInput.addPoint(point); - loopPointIndexes.push_back(idx); +#ifdef _DEBUG + GeomDebugDump::clearMeshsetDump(); +#endif + loopFinder.findLoops(); } - polyInput.m_poly_data->addFace(loopPointIndexes.begin(), loopPointIndexes.end()); - } - - std::map mesh_input_options; - std::string details; - bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); - if (!correct) - { - fixPolyhedronData(polyInput.m_poly_data, params); - std::string details2; - correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); - } + if (loopFinder.m_closedEdgeLoopsOfOpenEdges.size() > 0) + { + PolyInputCache3D polyInput(params.epsMergePoints); - if (correct) - { - shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); - MeshSetInfo infoAddedPatches; - MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoAddedPatches, params); + polyhedronFromMeshSet(meshset, polyInput); - if (MeshOps::isBetterForBoolOp(infoAddedPatches, infoInput, true)) - { - meshset = meshsetFromPolyhedron; + for (std::vector* >&loop : loopFinder.m_closedEdgeLoopsOfOpenEdges) + { + std::vector loopPointIndexes; + for (carve::mesh::Edge<3>*edge : loop) + { + vec3& point = edge->vert->v; + int idx = polyInput.addPoint(point); + loopPointIndexes.push_back(idx); + } + polyInput.m_poly_data->addFace(loopPointIndexes.begin(), loopPointIndexes.end()); + } + + + std::map mesh_input_options; + std::string details; + bool correct = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!correct) + { + fixPolyhedronData(polyInput.m_poly_data, params); + std::string details2; + correct = checkPolyhedronData(polyInput.m_poly_data, params, details2); + } + + if (correct) + { + shared_ptr > meshsetFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo infoAddedPatches; + MeshOps::checkMeshSetValidAndClosed(meshsetFromPolyhedron, infoAddedPatches, params); + + if (MeshOps::isBetterForBoolOp(infoAddedPatches, infoInput, true)) + { + infoInput.copyFromOther(infoAddedPatches); + meshset = meshsetFromPolyhedron; + ++numChanges; + break; + } + } } } } + if (numChanges == 0) + { + break; + } } + + // check if all open edges are close together -> merge points to center + bool openEdgesMergedToPoint = false; MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); if (infoInput.meshSetValid) @@ -2722,178 +2558,6 @@ bool intersectRayTriangle(const glm::dvec3& rayOrigin, const glm::dvec3& rayDire return false; } -void MeshOps::flattenFacePlanes(shared_ptr >& op1, shared_ptr >& op2, const GeomProcessingParams& params) -{ - // project face points into coplanar face - double epsAngle = params.epsMergeAlignedEdgesAngle * 100.0; - double epsDistanceSinglePoints = params.epsMergePoints * 10.0; - double epsDistanceFaceCentroids = params.epsMergePoints * 10.0; - double epsMinDistanceMovePoints2 = params.epsMergePoints * 0.01 * params.epsMergePoints * 0.01; - - // TODO: integrate this into other function. First merge all coplanar faces, then do this: - - for (auto mesh1 : op1->meshes) - { - if (mesh1->faces.size() > 1000) - { - continue; - } - - for (auto face1 : mesh1->faces) - { - vec3 face1Centroid = face1->centroid(); - const vec3 face1Normal = face1->plane.N; - bool face1dumped = false; - - for (auto mesh2 : op2->meshes) - { - if (mesh2->faces.size() > 1000) - { - continue; - } - for (auto face2 : mesh2->faces) - { - const vec3 face2Normal = face2->plane.N; - - double dotProduct = dot(face1Normal, face2Normal); - if (std::abs(dotProduct + 1.0) > epsAngle && std::abs(dotProduct - 1.0) > epsAngle) - { - continue; - } - - // check if faces overlap - carve::geom::aabb<3> bbox1 = face1->getAABB(); - carve::geom::aabb<3> bbox2 = face2->getAABB(); - if (bbox1.extent.x < epsDistanceSinglePoints) { bbox1.extent.x = epsDistanceSinglePoints * 2; bbox1.pos.x -= epsDistanceSinglePoints; } - if (bbox1.extent.y < epsDistanceSinglePoints) { bbox1.extent.y = epsDistanceSinglePoints * 2; bbox1.pos.y -= epsDistanceSinglePoints; } - if (bbox1.extent.z < epsDistanceSinglePoints) { bbox1.extent.z = epsDistanceSinglePoints * 2; bbox1.pos.z -= epsDistanceSinglePoints; } - - if (bbox2.extent.x < epsDistanceSinglePoints) { bbox2.extent.x = epsDistanceSinglePoints * 2; bbox2.pos.x -= epsDistanceSinglePoints; } - if (bbox2.extent.y < epsDistanceSinglePoints) { bbox2.extent.y = epsDistanceSinglePoints * 2; bbox2.pos.y -= epsDistanceSinglePoints; } - if (bbox2.extent.z < epsDistanceSinglePoints) { bbox2.extent.z = epsDistanceSinglePoints * 2; bbox2.pos.z -= epsDistanceSinglePoints; } - - bbox1.extent.x *= 2; - bbox1.extent.y *= 2; - bbox1.extent.z *= 2; - - bbox2.extent.x *= 2; - bbox2.extent.y *= 2; - bbox2.extent.z *= 2; - - if (!bbox1.intersects(bbox2, params.epsMergePoints)) - { - continue; - } - - // faces are parallel - vec3 face2Centroid = face2->centroid(); - double distance = GeomUtils::distancePointPlane(face2Centroid, face1Normal, face1Centroid); - if (std::abs(distance) < epsDistanceFaceCentroids) - { -#ifdef _DEBUG - if (params.debugDump) - { - if (!face1dumped) - { - glm::vec4 color(0, 0.4, 0.3, 1); - GeomDebugDump::dumpFacePolygon(face1, color, false); - face1dumped = true; - } - - glm::vec4 color(0, 1, 1, 1); - GeomDebugDump::dumpFacePolygon(face2, color, false); - } - - carve::geom::aabb<3> bbox1test; - carve::geom::aabb<3> bbox2test; - bbox1test.pos = carve::geom::VECTOR(0, 0, 0); - bbox1test.extent = carve::geom::VECTOR(1, 1, 1); - - bbox2test.pos = carve::geom::VECTOR(2 + params.epsMergePoints, 2 + params.epsMergePoints, 2 + params.epsMergePoints); - bbox2test.extent = carve::geom::VECTOR(1, 1, 1); - bool intersects = bbox1test.intersects(bbox2test, params.epsMergePoints); -#endif - - // faces are in plane. Now check if all vertices have a similar distance - bool allPointsInPlane = true; - auto edge2 = face2->edge; - for (size_t jjEdge = 0; jjEdge < face2->n_edges; ++jjEdge) - { - carve::mesh::Vertex<3>* vert = edge2->vert; - - double distance = GeomUtils::distancePointPlane(vert->v, face1Normal, face1Centroid); - - edge2 = edge2->next; - - if (std::abs(distance) > epsDistanceSinglePoints) - { - - allPointsInPlane = false; - break; - } - //mapDistances.insert({ distance, { face1, vert } }); - } - - - if (allPointsInPlane) - { - //intersectRayTriangle() - double area1 = MeshOps::computeFaceArea(face1); - double area2 = MeshOps::computeFaceArea(face2); - - carve::mesh::Face<3>* smallerFace = face1; - carve::mesh::Face<3>* biggerFace = face2; - if (area1 > area2) - { - smallerFace = face2; - biggerFace = face1; - } - - std::vector*> vertices; - smallerFace->getVertices(vertices); - for (auto vert : vertices) - { - vec3 v; - double t; - const carve::geom3d::Plane& plane = biggerFace->plane; - const vec3& rayPoint1 = vert->v; - vec3 rayPoint2 = rayPoint1 + plane.N; - // const Plane& p, const Vector& v1, const Vector& v2, Vector& v, double& t, double eps) - carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(plane, rayPoint1, rayPoint2, v, t, params.epsMergePoints); - if (intersect > 0) - { - double dx = vert->v.x - v.x; - double dy = vert->v.y - v.y; - double dz = vert->v.z - v.z; - - double distance2 = dx * dx + dy * dy + dz * dz; - //if (distance2 > epsMinDistanceMovePoints2 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) - //{ - // vert->v = v; - //} - - if (distance2 > 0.0 && distance2 < epsDistanceSinglePoints * epsDistanceSinglePoints) - { - vert->v = v; - } - } - } - } - } - } - } - -//#ifdef _DEBUG -// carve::geom::aabb<3> bbox1 = op1->getAABB(); -// carve::geom::aabb<3> bbox2 = op2->getAABB(); -// bbox1.unionAABB(bbox2); -// //GeomDebugDump::moveOffset(bbox1.extent.y + 0.2); -//#endif - } - } -} - - ///\brief method intersectOpenEdges: Intersect open edges of MeshSet with closed edges, and split the open edges in case of intersection ///\param[in/out] meshset: MeshSet with open edges. If fix is found, a new MeshSet is assigned to the smart pointer ///\param[in] eps: tolerance to find edge-edge intersections @@ -3600,6 +3264,56 @@ bool checkPolyhedronData(const shared_ptr& poly_da return true; } +bool allPointsWithinEpsilon(const std::vector& points, double eps) +{ + if (points.size() < 1) + { + return true; + } + + double minX = points[0].x; + double minY = points[0].y; + double minZ = points[0].z; + + double maxX = points[0].x; + double maxY = points[0].y; + double maxZ = points[0].z; + + //if (values.size() < 100000) + { + for (size_t ii = 1; ii < points.size(); ii += 3) + { + const vec3& pt = points[ii]; + const double& x = pt.x; + const double& y = pt.y; + const double& z = pt.z; + + if (x > maxX) maxX = x; + else if (x < minX) minX = x; + + if (y > maxY) maxY = y; + else if (y < minY) minY = y; + + if (z > maxZ) maxZ = z; + else if (z < minZ) minZ = z; + + if (maxX - minX > eps) + { + return false; + } + if (maxY - minY > eps) + { + return false; + } + if (maxZ - minZ > eps) + { + return false; + } + } + } + return true; +} + bool fixPolyhedronData(const shared_ptr& poly_data, const GeomProcessingParams& params) { if (!poly_data) @@ -3709,21 +3423,40 @@ bool fixPolyhedronData(const shared_ptr& poly_data GeomUtils::removeDuplicates(polygonPoints, epsMergePoints); - // checking just the face area does not work, since it could be a long face with 0 width. Removing it, would result in open edges - double area = GeomUtils::computePolygonArea(polygonPoints, epsMergePoints); - if (area > epsMinFaceArea) + bool allowLongZeroAreaFaces = true; + if (allowLongZeroAreaFaces) { - // found correct face - ++numFacesCorrected; - int numPointsInFace = pointIdxCurrentFace.size(); - polyDataCorrected.push_back(numPointsInFace); - std::copy(pointIdxCurrentFace.begin(), pointIdxCurrentFace.end(), std::back_inserter(polyDataCorrected)); + // remove face only when all points are within epsMergePoints + + bool faceIsPoint = allPointsWithinEpsilon(polygonPoints, epsMergePoints); + if (!faceIsPoint) + { + // found correct face + ++numFacesCorrected; + int numPointsInFace = pointIdxCurrentFace.size(); + polyDataCorrected.push_back(numPointsInFace); + std::copy(pointIdxCurrentFace.begin(), pointIdxCurrentFace.end(), std::back_inserter(polyDataCorrected)); + } } else { + + // checking just the face area does not work, since it could be a long face with 0 width. Removing it, would result in open edges + double area = GeomUtils::computePolygonArea(polygonPoints, epsMergePoints); + if (area > epsMinFaceArea) + { + // found correct face + ++numFacesCorrected; + int numPointsInFace = pointIdxCurrentFace.size(); + polyDataCorrected.push_back(numPointsInFace); + std::copy(pointIdxCurrentFace.begin(), pointIdxCurrentFace.end(), std::back_inserter(polyDataCorrected)); + } + else + { #ifdef _DEBUG - std::cout << "fixPolyhedronData: removing face with area " << area << std::endl; + //std::cout << "fixPolyhedronData: removing face with area " << area << std::endl; #endif + } } } } @@ -3810,12 +3543,20 @@ bool reverseFacesInPolyhedronData(const shared_ptr return inputCorrect; } -void MeshOps::polyhedronFromMeshSet(const shared_ptr>& meshset, PolyInputCache3D& polyInput) +void MeshOps::polyhedronFromMeshSet(const shared_ptr>& meshset, PolyInputCache3D& polyInput, int minNumFacesPerMesh) { for (size_t ii = 0; ii < meshset->meshes.size(); ++ii) { carve::mesh::Mesh<3>* mesh = meshset->meshes[ii]; + if (minNumFacesPerMesh > 0) + { + if (mesh->faces.size() < minNumFacesPerMesh) + { + continue; + } + } + for (size_t jj = 0; jj < mesh->faces.size(); ++jj) { carve::mesh::Face<3>* face = mesh->faces[jj]; @@ -3989,3 +3730,21 @@ void MeshOps::polyhedronFromMesh(const carve::mesh::Mesh<3>* mesh, PolyInputCach } } } + +void MeshOps::MeshSet2Polyhedron2MeshSet(const shared_ptr >& meshsetIn, + shared_ptr >& meshsetOut, const GeomProcessingParams& params, int minNumFacesPerMesh) +{ + PolyInputCache3D polyInput(params.epsMergePoints); + MeshOps::polyhedronFromMeshSet(meshsetIn, polyInput, minNumFacesPerMesh); + + std::string details; + bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!polyCorrect) + { + fixPolyhedronData(polyInput.m_poly_data, params); + polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + } + + std::map mesh_input_options; + meshsetOut = shared_ptr >(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); +} diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshOps.h b/IfcPlusPlus/src/ifcpp/geometry/MeshOps.h index cca1560fb..525e74aaf 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/MeshOps.h +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshOps.h @@ -59,8 +59,7 @@ class IFCQUERY_EXPORT MeshOps static void checkMeshSetIntegrity(const shared_ptr >& meshset, bool checkForDegenerateEdges, const GeomProcessingParams& params, MeshSetInfo& info); static void resolveOpenEdges(shared_ptr>& meshset, const GeomProcessingParams& params); - - static void flattenFacePlanes(shared_ptr >& op1, shared_ptr >& op2, const GeomProcessingParams& params); + static void resolveOpenEdges(shared_ptr>& meshset, MeshSetInfo& info, const GeomProcessingParams& params); static void removeDegenerateMeshes(shared_ptr >& meshsetInput, const GeomProcessingParams& params, bool ensureValidMesh); @@ -68,7 +67,6 @@ class IFCQUERY_EXPORT MeshOps static void checkAndFixMeshsetInverted( shared_ptr>& meshset, MeshSetInfo& info, const GeomProcessingParams& params); - static void mergeMeshesToMeshset(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params); static void classifyMeshesInside(std::vector*>& meshes, shared_ptr >& result, GeomProcessingParams& params); static std::shared_ptr > createPlaneMesh(vec3& p0, vec3& p1, vec3& p2, double eps); @@ -78,7 +76,8 @@ class IFCQUERY_EXPORT MeshOps static void polyhedronFromMesh(const carve::mesh::Mesh<3>* mesh, PolyInputCache3D& polyInput); - static void polyhedronFromMeshSet(const shared_ptr>& meshset, PolyInputCache3D& polyInput); + static void polyhedronFromMeshSet(const shared_ptr>& meshset, PolyInputCache3D& polyInput, int minNumFacesPerMesh = -1); static void polyhedronFromMeshSet(const shared_ptr>& meshset, const std::set* >& setSkipFaces, PolyInputCache3D& polyInput); static void polyhedronFromMeshSet(const shared_ptr>& meshset, const std::set* >& setSkipFaces, const std::set* >& setFlipFaces, PolyInputCache3D& polyInput); + static void MeshSet2Polyhedron2MeshSet(const shared_ptr >& meshsetIn, shared_ptr >& meshsetOut, const GeomProcessingParams& params, int minNumFacesPerMesh); }; diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.cpp b/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.cpp new file mode 100644 index 000000000..253879a8c --- /dev/null +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.cpp @@ -0,0 +1,1268 @@ +/* -*-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 "MeshSimplifier.h" + +void MeshSimplifier::simplifyMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput) +{ + if (!meshsetInput) + { + return; + } + + printToDebugLog(__FUNC__, ""); + GeomProcessingParams params(paramsInput); + StatusCallback* report_callback = params.callbackFunc; + BuildingEntity* entity = params.ifc_entity; + bool dumpPolygon = params.debugDump; + double eps = params.epsMergePoints; + MeshSetInfo infoInput(report_callback, entity); + bool validMeshsetInput = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoInput, params); + bool deepCopyMesh = true; + + if (meshsetInput->vertex_storage.size() < 9 + && infoInput.openEdges.size() == 0 + && infoInput.degenerateEdges.size() == 0 + && infoInput.zeroAreaFaces.size() == 0 + && infoInput.meshSetValid) + { + return; + } + + if (infoInput.finEdges.size() > 0) + { + // if input mesh has fin edges, allow that also for the result + params.allowFinEdges = true; + } + + MeshSetInfo info(report_callback, entity); + shared_ptr > meshset(meshsetInput->clone()); + MeshOps::checkMeshSetValidAndClosed(meshset, info, params); + + bool useCarveMeshSimplifier = false; + if (useCarveMeshSimplifier) + { + try + { + size_t numChanges = 0; + carve::mesh::MeshSimplifier carveSimplifier(params.epsMergePoints); + //numChanges += carveSimplifier.removeFins(meshset.get()); + //numChanges += carveSimplifier.mergeCoplanarFaces(meshset.get(), params.epsMergeAlignedEdgesAngle); + numChanges += carveSimplifier.improveMesh_conservative(meshset.get(), params.epsMergePoints); + + //MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params); + + double min_deltaV = params.epsMergeAlignedEdgesAngle; + //numChanges += carveSimplifier.improveMesh(meshset.get(), params.epsMergeAlignedEdgesAngle, min_deltaV, params.epsMergeAlignedEdgesAngle, params.epsMergePoints ); + if (numChanges > 0) + { +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + MeshSetInfo infoChanges(report_callback, entity); + MeshOps::checkMeshSetValidAndClosed(meshset, infoChanges, params); + if (MeshOps::isBetterForBoolOp(infoChanges, info, false)) + { + std::cout << "carve::mesh::MeshSimplifier: numChanges: " << numChanges << std::endl; + } +#endif + } + } + catch (carve::exception& e) + { +#ifdef _DEBUG + std::cout << e.str() << std::endl; +#endif + } + + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); + } + +#ifdef _DEBUG + GeomDebugDump::DumpSettingsStruct dumpColorSettings; + + if (dumpPolygon) + { + GeomProcessingParams par(params); + par.checkZeroAreaFaces = false; + GeomDebugDump::dumpWithLabel("simplify--input", meshset, dumpColorSettings, par, true, true); + } + + if (dumpPolygon) + { + GeomDebugDump::moveOffset(0.2); + + glm::vec4 color1(0.7, 0.7, 0.7, 0.88); + std::string labelStr = "simplify--merged-faces"; + GeomDebugDump::dumpVertex(GeomDebugDump::DumpData::instance().labelPos, color1, labelStr); + GeomDebugDump::dumpCountLabel(GeomDebugDump::DumpData::instance().countLabelPos); + } + + if (GeomDebugDump::getDumpCount() >= 16) + { + int wait = 0; + } + shared_ptr > meshset_copy_input(meshset->clone()); +#endif + + if (infoInput.zeroAreaFaces.size() > 0) + { + if (infoInput.openEdges.size() == 0) + { + removeDegenerateFacesInMeshSet(meshset, params); + MeshOps::checkMeshSetValidAndClosed(meshset, info, params); + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, true, params, deepCopyMesh); + } + } + + if (!info.meshSetValid) + { + MeshOps::resolveOpenEdges(meshset, params); + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); + + if (!info.meshSetValid) + { + MeshOps::resolveOpenEdges(meshset, params); + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); + } + } + + dumpPolygon = false; + + try + { + removeDegenerateFacesInMeshSet(meshset, params); + + const clock_t begin_time = clock(); + bool mergedFacesMeshShouldBeClosed = false; // allow open edges, will try to sew them together later + size_t numChanges = mergeCoplanarFacesInMeshSet(meshset, params, mergedFacesMeshShouldBeClosed); + int millisecs = (clock() - begin_time); + if (millisecs > 5 && numChanges == 0) + { +#if defined _DEBUG || defined _DEBUG_RELEASE + std::cout << "no success in mergeCoplanarFacesInMeshSet, vertex count: " << meshset->vertex_storage.size() << std::endl; +#endif + } + + size_t numEdgesRemoved = mergeAlignedEdges(meshset, params); + + MeshOps::recalcMeshSet(meshset, eps); + + // TODO: find faces with biggest area, and trim all points to plane + + MeshSetInfo infoMergedFaces(report_callback, entity); + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, false, params, deepCopyMesh); + +#ifdef _DEBUG + shared_ptr > meshset_merged_faces(meshset->clone()); + GeomProcessingParams paramCopy(params); + paramCopy.checkZeroAreaFaces = false; + + if (dumpPolygon)//|| numChanges > 0 ) + { + GeomDebugDump::moveOffset(0.3); + shared_ptr > meshset_dump = shared_ptr >(meshset->clone()); + dumpColorSettings.triangulateBeforeDump = false; + + dumpWithLabel("mesh-merged-faces", meshset_dump, dumpColorSettings, paramCopy, true, true); + dumpColorSettings.triangulateBeforeDump = true; + + GeomDebugDump::moveOffset(0.3); + glm::vec4 color1(0.7, 0.7, 0.7, 0.88); + for (carve::mesh::Mesh<3>*mesh : meshset_dump->meshes) + { + GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); + } + } + +#endif + MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); + if (!infoMergedFaces.meshSetValid) + { + size_t retry_count = 0; + MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, retry_count); + + MeshSetInfo infoRetriangulated(report_callback, entity); + MeshOps::checkMeshSetValidAndClosed(meshset, infoRetriangulated, params); + } + + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, false, params, deepCopyMesh); + + MeshOps::recalcMeshSet(meshset, eps); + MeshOps::checkMeshSetValidAndClosed(meshset, info, params); + + size_t numEdgesRemoved2 = mergeAlignedEdges(meshset, params); + if (numEdgesRemoved2 > 0) + { + MeshSetInfo infoMergedAlignedEdges(report_callback, entity); + bool validMergedAlignedEdges = MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedAlignedEdges, params); + +#ifdef _DEBUG + if (dumpPolygon) + { + GeomDebugDump::moveOffset(0.4); + dumpWithLabel("mesh-simplify-input", meshset_copy_input, dumpColorSettings, paramCopy, true, true); + + GeomDebugDump::moveOffset(0.2); + dumpWithLabel("mesh-merged-faces", meshset_merged_faces, dumpColorSettings, paramCopy, true, true); + + GeomDebugDump::moveOffset(0.2); + dumpWithLabel("mesh-merged-aligned-edges", meshset, dumpColorSettings, paramCopy, true, true); + + GeomDebugDump::moveOffset(0.1); + glm::vec4 color1(0.7, 0.7, 0.7, 0.88); + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); + } + + GeomDebugDump::moveOffset(0.3); + } +#endif + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedAlignedEdges, infoInput, false, params, deepCopyMesh); + + if (validMergedAlignedEdges) + { + if (paramsInput.triangulateResult) + { + MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, 0); + MeshSetInfo infoTriangulated(report_callback, entity); + bool validTriangulated = MeshOps::checkMeshSetValidAndClosed(meshset, infoTriangulated, params); + + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoTriangulated, infoInput, true, params, deepCopyMesh); + } +#ifdef _DEBUG + GeomDebugDump::clearBuffer(); +#endif + return; + } + } + +#ifdef _DEBUG + GeomDebugDump::clearBuffer(); +#endif + + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, true, params, deepCopyMesh); + + MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); + MeshOps::checkAndFixMeshsetInverted(meshset, infoMergedFaces, params); + + MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, true, params, deepCopyMesh); + +#ifdef _DEBUG + MeshSetInfo infoResult(report_callback, entity); + bool validResult = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoResult, params); + if (!validResult) + { + if (validMeshsetInput) + { + std::cout << "validMeshsetInput && !validResult: should not happen" << std::endl; + } + } +#endif + + return; + } + catch (std::exception& ex) + { +#ifdef _DEBUG + std::cout << ex.what() << std::endl; +#endif + } + catch (carve::exception& ex) + { + std::cout << ex.str() << std::endl; + } + catch (...) + { + + } +} + +size_t MeshSimplifier::mergeCoplanarFacesInMeshSet(shared_ptr >& meshset, const GeomProcessingParams& paramsInput, bool shouldBeClosedManifold) +{ + if (!meshset) + { + return 0; + } + + if (meshset->vertex_storage.size() > 5000) + { + return 0; + } + + shared_ptr > meshset_copy(meshset->clone()); + GeomProcessingParams params(paramsInput); + params.allowFinEdges = false; + double epsAngleMergeEdges = params.epsMergeAlignedEdgesAngle * 10; + double eps = params.epsMergePoints; + + MeshSetInfo infoInput; + bool validMeshsetInput = MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + if (infoInput.finEdges.size() > 0) + { + // if input already has fin edges, allow it also for the result mesh + params.allowFinEdges = true; + } + + size_t numChanges = 0; + double volume = MeshOps::computeMeshsetVolume(meshset.get()); + + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + for (carve::mesh::Edge<3>*edge : mesh->closed_edges) + { + if (!edge) + { + continue; + } + + carve::mesh::Edge<3>* reverseEdge = edge->rev; + if (!reverseEdge) + { + continue; + } + + carve::mesh::Face<3>* face = edge->face; + if (!face) + { + continue; + } + + carve::mesh::Face<3>* adjacentFace = reverseEdge->face; + if (!adjacentFace) + { + continue; + } + + if (adjacentFace == face) + { + // can happen with opening + continue; + } + + // re-compute face normal here + face->computeNormal(eps); + adjacentFace->computeNormal(eps); + const vec3 faceNormal = face->plane.N; + const vec3 face2Normal = adjacentFace->plane.N; + + // adjacent faces have 1 as normal vector dot product + + double dotProduct = dot(faceNormal, face2Normal); + if (std::abs(dotProduct - 1.0) < epsAngleMergeEdges) + { + numChanges += removeEdgeAndMergeFaces(edge, params); + } + } + } + + if (numChanges > 0) + { + if (!shouldBeClosedManifold) + { + meshset = meshset_copy; + return numChanges; + } + MeshSetInfo infoResult; + bool validMeshsetResult = MeshOps::checkMeshSetValidAndClosed(meshset, infoResult, params); + + if (!validMeshsetResult) + { + MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, 0); + validMeshsetResult = MeshOps::checkMeshSetValidAndClosed(meshset, infoResult, params); + } + + double volumeResult = MeshOps::computeMeshsetVolume(meshset.get()); + if (volumeResult < 0.9 * volume) + { + validMeshsetResult = false; + } + + if (!validMeshsetResult) + { + meshset = meshset_copy; + numChanges = 0; + return 0; + } + + int numFacesRemoved = infoInput.numFaces - infoResult.numFaces; + int numClosedEdgesRemoved = infoInput.numClosedEdges - infoResult.numClosedEdges; + } + return numChanges; +} + + +size_t MeshSimplifier::mergeAlignedEdges(shared_ptr >& meshset, GeomProcessingParams& params) +{ +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.3); + } +#endif + + size_t numEdgesRemoved = 0; + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + for (carve::mesh::Face<3>*face : mesh->faces) + { + if (!face) + { + continue; + } + + if (!face->edge) + { + continue; + } + + bool faceDumped = false; + carve::mesh::Edge<3>* edge = face->edge; + + // check how many edges are connected to end of edge + size_t numEdges = face->n_edges; + for (size_t ii = 0; ii < numEdges; ++ii) + { + if (!edge) + { + continue; + } + if (edge->next) + { + if (edge->next->rev) + { + if (edge->next->rev->next) + { + if (edge->next->rev->next->rev == edge) + { + // only one edge is connected, now check angle + + // edge->rev->next edge->rev edge->next->rev edge->next->next->rev + // <--------------------p1<------------------p2<-------------------------p3<----------------------- + // -------------------> ------------------> -----------------------> ----------------------> + // edge->prev edge edge->next edge->next->next + + carve::mesh::Vertex<3>* vertex1 = edge->v1(); + carve::mesh::Vertex<3>* vertex2 = edge->v2(); + carve::mesh::Vertex<3>* vertex3 = edge->next->v2(); + + std::set* > setEdges; + getEdgesOnVertex(mesh, vertex2, setEdges); + size_t numEdgesOnVertex = setEdges.size(); + + const carve::geom::vector<3>& p1 = vertex1->v; + const carve::geom::vector<3>& p2 = vertex2->v; + const carve::geom::vector<3>& p3 = vertex3->v; + + carve::geom::vector<3> edgeVector = p2 - p1; + carve::geom::vector<3> edgeNextVector = p3 - p2; + edgeVector.normalize(); + edgeNextVector.normalize(); + +#ifdef _DEBUG + glm::vec4 color1(0.4, 0.45, 0.45, 1.); + if (params.debugDump) + { + for (auto edgeOnVertex : setEdges) + { + const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; + const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; + std::vector > vecLine = { p1, p2 }; + GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); + } + } + + if (face->plane.N.x > 0.9) + { + int wait = 0; + } +#endif + + double dotProduct = dot(edgeVector, edgeNextVector); + if (std::abs(dotProduct - 1.0) < params.epsMergeAlignedEdgesAngle * 1000) + { + carve::mesh::Edge<3>* edgeRemove = edge;// ->next; + +#ifdef _DEBUG + if (params.debugDump) + { + if (edge->rev && edge->prev->rev) + { + std::vector* > vecAdjacentFaces = { edge->face, edge->rev->face, edgeRemove->face, edgeRemove->rev->face, edge->prev->face, edge->prev->rev->face }; + GeomDebugDump::moveOffset(0.05); + GeomDebugDump::dumpFaces(vecAdjacentFaces, color1, false); + GeomDebugDump::dumpFacePolygons(vecAdjacentFaces, color1, false); + } + } +#endif + + std::set* > setEdgePointersToRemovedEdge; + std::set* > setFacePointersToRemovedEdge; + getPointerToEdge(mesh, edgeRemove, setEdgePointersToRemovedEdge, setFacePointersToRemovedEdge); + size_t numVertexChanges = removePointerToVertex(mesh, vertex2, vertex1); + edge = edgeRemove->removeEdge(); // returns ->next + carve::geom::vector<3> distanceV1 = edge->v1()->v - p1; + carve::geom::vector<3> distanceV3 = edge->v2()->v - p3; + + //double epsMinFaceArea = params.minFaceArea;// *0.001; + //MeshOps::removeZeroAreaFacesInMesh(mesh, epsMinFaceArea, eps, dumpFaces); + + ++numEdgesRemoved; + mesh->cacheEdges(); + mesh->recalc(params.epsMergePoints); + + // edge->rev->next edge->rev edge->next->rev + // <--------------------v1<---------------------------------------------v2<------------------------ + // -------------------> --------------------------------------------> ----------------------> + // edge->prev edge edge->next + +#ifdef _DEBUG + if (params.debugDump) + { + glm::vec4 color(0.4, 0.45, 0.45, 1.); + + std::set* > setEdges1; + getEdgesOnVertex(mesh, vertex1, setEdges1); + + std::set* > setEdges2; + getEdgesOnVertex(mesh, vertex2, setEdges2); + + std::set* > setEdges3; + getEdgesOnVertex(mesh, vertex3, setEdges3); + + + for (auto edgeOnVertex : setEdges1) + { + const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; + const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; + std::vector > vecLine = { p1, p2 }; + GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); + } + + + for (auto edgeOnVertex : setEdges2) + { + const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; + const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; + std::vector > vecLine = { p1, p2 }; + GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); + } + + for (auto edgeOnVertex : setEdges3) + { + const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; + const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; + std::vector > vecLine = { p1, p2 }; + GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); + } + + GeomDebugDump::moveOffset(0.4); + GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); + + GeomDebugDump::moveOffset(0.05); + std::vector > edgePolygon = { p1,p2,p3 }; + GeomDebugDump::dumpPolyline(edgePolygon, color, 0, false, false); + + std::vector > edgePolygon2 = { edge->v1()->v, edge->v2()->v }; + GeomDebugDump::moveOffset(0.001); + GeomDebugDump::dumpPolyline(edgePolygon2, color, 0, false, false); + } + + MeshSetInfo infoMergedFaces; + bool validMeshsetMergedFaces = MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); + + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::DumpSettingsStruct dumpColorSettings; + params.checkZeroAreaFaces = true; + GeomDebugDump::dumpWithLabel("mesh-merged-faces", meshset, dumpColorSettings, params, true, true); + } + + double dx = edgeVector.x - edgeNextVector.x; + double dy = edgeVector.y - edgeNextVector.y; + double dz = edgeVector.z - edgeNextVector.z; + if (std::abs(dx) > EPS_M8) + { + std::cout << "align check" << std::endl; + } + if (std::abs(dy) > EPS_M8) + { + std::cout << "align check" << std::endl; + } + if (std::abs(dz) > EPS_M8) + { + std::cout << "align check" << std::endl; + } +#endif + + continue; + } + } + } + } + edge = edge->next; + } + } +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.002); + glm::vec4 color(0.4, 0.45, 0.45, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); + } +#endif + + } + } + + if (numEdgesRemoved > 0) + { +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.2); + } +#endif + + for (auto mesh : meshset->meshes) + { + mesh->cacheEdges(); + mesh->recalc(params.epsMergePoints); + +#ifdef _DEBUG + if (params.debugDump) + { + for (carve::mesh::Face<3>*face : mesh->faces) + { + glm::vec4 color(0.4, 0.45, 0.45, 1.); + GeomDebugDump::dumpFacePolygon(face, color, false); + } + } +#endif + } + } + + return numEdgesRemoved; +} + +size_t MeshSimplifier::removeEdgeAndMergeFaces(carve::mesh::Edge<3>* edgeIn, const GeomProcessingParams& params) +{ + double eps = params.epsMergePoints; + carve::mesh::Face<3>* face = edgeIn->face; + double faceArea = MeshOps::computeFaceArea(face); + if (std::abs(faceArea) < eps * 10) + { + return 0; + } + + vec3& facePosition_carve = edgeIn->v2()->v; + size_t numChanges = 0; + size_t numFacesDeleted = 0; + +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.4); + glm::vec4 color2(0.3, 0.2, 0.2, 0.8); + //dumpAdjacentFaces(par.setAdjacentCoplanarFaces, color2); + } +#endif + + carve::mesh::Face<3>* faceOnRverseEdge = edgeIn->rev->face; + carve::mesh::Face<3>* faceOnEdge = edgeIn->face; + if (faceOnEdge == nullptr) + { + return 0; + } + + if (faceOnRverseEdge == nullptr) + { + return 0; + } + + if (faceOnEdge == faceOnRverseEdge) + { + // remaining edge between inner opening of face and outer boundary. Leave it for triangulation + return 0; + } + size_t numFacesBeforeMerge = faceOnRverseEdge->mesh->faces.size(); + + carve::mesh::Edge<3>* edgeErase = edgeIn; + size_t numEdgesFace = countEdges(faceOnEdge); + size_t numEdgesFaceReverse = countEdges(faceOnRverseEdge); + + if (numEdgesFace >= params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("edgeCount > m_maxNumFaceEdges"); + throw std::exception(ex); + } + if (numEdgesFaceReverse >= params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("edgeCount > m_maxNumFaceEdges"); + throw std::exception(ex); + } + +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.05); + glm::vec4 color2(0.3, 0.2, 0.2, 0.8); + std::vector > vecEdgePoints = { edgeErase->v1()->v, edgeErase->v2()->v }; + GeomDebugDump::dumpPolyline(vecEdgePoints, color2, 0, false, false); + } +#endif + + MeshSetInfo info2; + bool checkForDegenerateEdges = false; + MeshOps::checkFaceIntegrity(edgeErase->face, checkForDegenerateEdges, info2, params.epsMergePoints); + + MeshSetInfo info3; + MeshOps::checkFaceIntegrity(edgeErase->rev->face, checkForDegenerateEdges, info3, params.epsMergePoints); + + if (!info2.allPointersValid) + { + return 0; + } + if (!info3.allPointersValid) + { + return 0; + } + + carve::mesh::Edge<3>* edgeMergeNext = checkMergeFaces(edgeErase, params); + if (!edgeMergeNext) + { + return 0; + } + + carve::mesh::Face<3>* faceRemain = nullptr; + carve::mesh::Face<3>* faceRemove = nullptr; + + if (faceOnRverseEdge->edge) + { + faceRemain = faceOnRverseEdge; + size_t numEdgesFaceRemove_afterMerge = countEdges(faceOnRverseEdge); + if (numEdgesFaceRemove_afterMerge > params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("faceOnRverseEdge->edge count > maxNumFaceEdges"); + throw std::exception(ex); + } + if (numEdgesFaceReverse != numEdgesFaceRemove_afterMerge) + { + ++numChanges; + } + } + else + { + faceRemove = faceOnRverseEdge; + } + + if (faceOnEdge->edge) + { + faceRemain = faceOnEdge; + size_t numEdgesFaceRemain_afterMerge = countEdges(faceOnEdge); + if (numEdgesFaceRemain_afterMerge > params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("faceOnEdge->edge count > maxNumFaceEdges"); + throw std::exception(ex); + } + if (numEdgesFace != numEdgesFaceRemain_afterMerge) + { + ++numChanges; + } + } + else + { + faceRemove = faceOnEdge; + } + +#ifdef _DEBUG + if (params.debugDump) + { + GeomDebugDump::moveOffset(0.05); + glm::vec4 color2(0.3, 0.2, 0.2, 0.8); + GeomDebugDump::dumpFacePolygon({ faceRemain }, color2, false); + } +#endif + + // replace pointers to faceRemove + carve::mesh::Edge<3>* e = faceRemove->edge; + for (size_t ii = 0; ii < faceRemove->n_edges; ++ii) + { + if (e == nullptr) + { + continue; + } + + if (e->face == faceRemove) + { + e->face = faceRemain; + } + e = e->next; + } + + numChanges += removeFaceFromMesh(faceRemove); + delete faceRemove; + ++numFacesDeleted; + + if (!faceRemain) + { + return numChanges; + } + + auto mesh = faceRemain->mesh; + try + { + faceRemain->edge->validateLoop(); + } + catch (carve::exception& e) + { +#ifdef _DEBUG + std::cout << "validateLoop failed: " << e.str(); +#endif + } + + mesh->cacheEdges(); + //mesh->recalc(eps); + ++numChanges; + + // TODO: enforceMergedFacesToCommonPlane() : compute normal vector and centroid of merged face, then move all vertices precisely into that plane + + bool allVerticesInPlane = true; + std::vector* > faceVertices; + faceRemain->getVertices(faceVertices); + if (faceVertices.size() > 3) + { + carve::geom::vector<3> normalVector = GeomUtils::computePolygonNormal(faceVertices); + carve::geom::vector<3> centroid = faceRemain->centroid(); + GeomUtils::Plane plane(glm::dvec3(centroid.x, centroid.y, centroid.z), glm::dvec3(normalVector.x, normalVector.y, normalVector.z)); + + for (carve::mesh::Face<3>::vertex_t * vertex : faceVertices) + { + const carve::geom::vector<3>& facePoint_carve = vertex->v; + glm::dvec3 facePoint(facePoint_carve.x, facePoint_carve.y, facePoint_carve.z); + + double distanceToPlane = plane.distancePointPlane(facePoint); + + if (std::abs(distanceToPlane) > params.epsMergePoints) + { + vec3 pointOnPlane = facePoint_carve + normalVector * distanceToPlane; + +#ifdef _DEBUG + glm::dvec3 pointOnPlane_glm(pointOnPlane.x, pointOnPlane.y, pointOnPlane.z); + double distanceToPlaneCheck = plane.distancePointPlane(pointOnPlane_glm); + + double maxAllowedDistance = params.epsMergePoints * 5.0; + if (std::abs(distanceToPlaneCheck) > maxAllowedDistance) + { + //std::cout << "distanceToPlaneCheck too big: " << distanceToPlaneCheck << std::endl; + } +#endif + vertex->v = pointOnPlane; + + // TODO: average out current vertices between all faces that are connected + // map> + } + } + + if (allVerticesInPlane) + { + return numChanges; + } + } + +#ifdef _DEBUG + size_t numFacesAfterMerge = mesh->faces.size(); + int numFacesMerged = numFacesBeforeMerge - numFacesAfterMerge; + + MeshSetInfo info6; + MeshOps::checkMeshIntegrity(mesh, checkForDegenerateEdges, params, info6); + if (!info6.allPointersValid || !allVerticesInPlane) + { + glm::vec4 color(0.2, 0.2, 0.2, 1.); + GeomDebugDump::stopBuffering(); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpMesh(mesh, color, true); + } +#endif + + return numChanges; +} + +void MeshSimplifier::removeDegenerateFacesInMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput) +{ + return; + + + if (!meshsetInput) + { + return; + } + + GeomProcessingParams params(paramsInput); + params.allowZeroAreaFaces = true; + MeshSetInfo infoInput; + bool meshInputOk = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoInput, params); + + std::set* > setFacesRemove = infoInput.degenerateFaces; + std::set* > setEdgesRemove = infoInput.degenerateEdges; + + for (carve::mesh::Mesh<3>*mesh : meshsetInput->meshes) + { + double meshVolume = mesh->volume(); + if (meshVolume < params.epsMergePoints) + { + // remove complete mesh + for (carve::mesh::Face<3>*face : mesh->faces) + { + if (!face) + { + continue; + } + + setFacesRemove.insert(face); + } + continue; + } + } + + if (setFacesRemove.size() > 0 || setEdgesRemove.size() > 0) + { + PolyInputCache3D polyInput(params.epsMergePoints); + MeshOps::polyhedronFromMeshSet(meshsetInput, setFacesRemove, polyInput); + + int numFacesInput = infoInput.numFaces; + std::string details; + bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!polyCorrect) + { + fixPolyhedronData(polyInput.m_poly_data, params); + polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + } + + std::map mesh_input_options; + shared_ptr > resultFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo info; + MeshOps::checkMeshSetValidAndClosed(resultFromPolyhedron, info, params); + int numFacesPoly = info.numFaces; +#ifdef _DEBUG + if (meshInputOk && !info.meshSetValid && false) + { + GeomDebugDump::DumpSettingsStruct dumpColorSettings; + GeomDebugDump::dumpWithLabel("removeDegenerateFacesInMeshSet--input", meshsetInput, dumpColorSettings, params, true, true); + GeomDebugDump::dumpMeshsetOpenEdges(resultFromPolyhedron, dumpColorSettings.colorMesh, false, true); + GeomDebugDump::dumpWithLabel("removeDegenerateFacesInMeshSet--result", resultFromPolyhedron, dumpColorSettings, params, true, true); + } +#endif + + if (info.numOpenEdges() > 0) + { + MeshOps::resolveOpenEdges(resultFromPolyhedron, params); + MeshOps::checkMeshSetValidAndClosed(resultFromPolyhedron, info, params); + } + + if (MeshOps::isBetterForBoolOp(info, infoInput, true)) + { + meshsetInput = resultFromPolyhedron; + } + } +} + +void MeshSimplifier::removeFinFaces(shared_ptr >& meshset, const GeomProcessingParams& params) +{ + return; + + + + MeshSetInfo infoInput; + MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); + + std::set* > setFacesRemove; + for (carve::mesh::Mesh<3>*mesh : meshset->meshes) + { + for (carve::mesh::Edge<3>*edge : mesh->closed_edges) + { + if (!edge) + { + continue; + } + + carve::mesh::Edge<3>* reverseEdge = edge->rev; + if (!reverseEdge) + { + continue; + } + + carve::mesh::Face<3>* face = edge->face; + if (!face) + { + continue; + } + + carve::mesh::Face<3>* adjacentFace = reverseEdge->face; + if (!adjacentFace) + { + continue; + } + + // re-compute face normal here + face->computeNormal(params.epsMergePoints); + adjacentFace->computeNormal(params.epsMergePoints); + const vec3 faceNormal = face->plane.N; + const vec3 face2Normal = adjacentFace->plane.N; + + // adjacent faces back-to-back have -1 as normal vector dot product + double dotProduct = dot(faceNormal, face2Normal); + if (std::abs(dotProduct + 1.0) < params.epsMergeAlignedEdgesAngle) + { + setFacesRemove.insert(face); + setFacesRemove.insert(adjacentFace); + } + } + } + + if (setFacesRemove.size() > 0) + { + PolyInputCache3D polyInput(params.epsMergePoints); + MeshOps::polyhedronFromMeshSet(meshset, setFacesRemove, polyInput); + + std::string details; + bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); + if (!polyCorrect) + { + fixPolyhedronData(polyInput.m_poly_data, params); + } + + std::map mesh_input_options; + shared_ptr > resultFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); + MeshSetInfo info; + MeshOps::assignIfBetterForBoolOp(resultFromPolyhedron, meshset, info, infoInput, false, params, false); + + if (info.meshSetValid) + { +#ifdef _DEBUG + if (params.debugDump) + { + glm::vec4 color(0.5, 0.5, 0.5, 1); + GeomDebugDump::stopBuffering(); + GeomDebugDump::moveOffset(0.15); + bool drawNormals = true; + GeomDebugDump::dumpMeshset(meshset, color, drawNormals, true); + GeomDebugDump::moveOffset(0.1); + GeomDebugDump::dumpMeshset(resultFromPolyhedron, color, drawNormals, true); + } +#endif + } + } +} + +void MeshSimplifier::removeFinEdges(carve::mesh::Mesh<3>* mesh, const GeomProcessingParams& params) +{ + return; // degenerate edge = degenerate face, so remove degenerate faces instead + + if (!mesh) + { + return; + } + + const std::vector* >& vec_faces = mesh->faces; + size_t numFaces = vec_faces.size(); + +#ifdef _DEBUG + if (numFaces == 1 && false) + { + glm::vec4 color(0.4, 0.2, 0.2, 1.); + std::vector* > vecFaces; + vecFaces.push_back(vec_faces[0]); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::stopBuffering(); + GeomDebugDump::dumpFaces(vecFaces, color); + GeomDebugDump::moveOffset(0.3); + GeomDebugDump::dumpFacePolygon(vec_faces[0], color, true); + } +#endif + + for (size_t ii = 0; ii < numFaces; ++ii) + { + size_t numChangesAll = 0; + for (size_t jj = 0; jj < vec_faces.size(); ++jj) + { + size_t numChangesCurrentFace = 0; + carve::mesh::Face<3>* face = vec_faces[jj]; + removeFinEdgesFromFace(face, numChangesCurrentFace, params.epsMergePoints); + numChangesAll += numChangesCurrentFace; + } + + // several fin-edges (where edge->next == edge->reverse) can be concatenated. Repeat until there are no changes + if (numChangesAll > 0) + { + if (mesh->faces.size() < 2) + { + continue; + } + + bool checkForDegenerateEdges = false; + MeshSetInfo minf; + MeshOps::checkMeshIntegrity(mesh, checkForDegenerateEdges, params, minf); + + if (!minf.allPointersValid) + { + continue; + } + + mesh->cacheEdges(); + mesh->recalc(params.epsMergePoints); + } + + if (numChangesAll == 0) + { + break; + } + } +} + +carve::mesh::Edge<3>* MeshSimplifier::checkMergeFaces(carve::mesh::Edge<3>* e, const GeomProcessingParams& params) +{ + if (e->rev == nullptr) + { + return nullptr; + } + + + carve::mesh::Face<3>* fwdface = e->face; + carve::mesh::Face<3>* revface = e->rev->face; + + if (fwdface == revface) + { + return nullptr; + } + + if (fwdface->n_edges > params.generalSettings->m_maxNumFaceEdges) + { + return nullptr; + } + if (revface->n_edges > params.generalSettings->m_maxNumFaceEdges) + { + return nullptr; + } + + +#ifdef _DEBUG + + GeomDebugDump::ScopedDumpBuffering scoped_buffer; + if (params.debugDump) + { + glm::vec4 color(0.3, 0.3, 0.3, 1.); + std::vector* > vecFaces = { fwdface }; + GeomDebugDump::dumpFaces(vecFaces, color, false); + GeomDebugDump::dumpFacePolygon(revface, color, false); + } +#endif + + size_t n_removed = 0; + + carve::mesh::Edge<3>* splice_beg = e; + for (size_t ii = 0; ii < fwdface->n_edges; ++ii) + { + splice_beg = splice_beg->prev; + ++n_removed; + + if (splice_beg == e) { break; } + if (!splice_beg->rev) { break; } + if (splice_beg->next->rev->prev != splice_beg->rev) { break; } + } + + if (splice_beg == e) + { + // edge loops are completely matched. + return nullptr; + } + + carve::mesh::Edge<3>* splice_end = e; + do { + splice_end = splice_end->next; + ++n_removed; + } while (splice_end->rev && splice_end->prev->rev->next == splice_end->rev); + + --n_removed; + + carve::mesh::Edge<3>* link1_p = splice_beg; + carve::mesh::Edge<3>* link1_n = splice_beg->next->rev->next; + + carve::mesh::Edge<3>* link2_p = splice_end->prev->rev->prev; + carve::mesh::Edge<3>* link2_n = splice_end; + + CARVE_ASSERT(link1_p->face == fwdface); + CARVE_ASSERT(link1_n->face == revface); + + CARVE_ASSERT(link2_p->face == revface); + CARVE_ASSERT(link2_n->face == fwdface); + + carve::mesh::Edge<3>* left_loop = link1_p->next; + + CARVE_ASSERT(left_loop->rev == link1_n->prev); + + linkEdges(link2_n->prev, link1_p->next); + linkEdges(link1_n->prev, link2_p->next); + + linkEdges(link1_p, link1_n); + linkEdges(link2_p, link2_n); + + fwdface->edge = link1_p; + + size_t edgeCount = 0; + for (carve::mesh::Edge<3>*e = link1_n; e != link2_n; e = e->next) + { + CARVE_ASSERT(e->face == revface); + e->face = fwdface; + fwdface->n_edges++; + ++edgeCount; + if (edgeCount > params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("edgeCount > m_maxNumFaceEdges"); + throw std::exception(ex); + return nullptr; + } + } + edgeCount = 0; + for (carve::mesh::Edge<3>*e = link2_n; e != link1_n; e = e->next) + { + CARVE_ASSERT(e->face == fwdface); + ++edgeCount; + if (edgeCount > params.generalSettings->m_maxNumFaceEdges) + { + std::logic_error ex("edgeCount > m_maxNumFaceEdges"); + throw std::exception(ex); + return nullptr; + } + } + + fwdface->n_edges -= n_removed; + + revface->n_edges = 0; + revface->edge = nullptr; + + setFacePointerToEdgeLoop(left_loop, nullptr); + setFacePointerToEdgeLoop(left_loop->rev, nullptr); + +#ifdef _DEBUG + GeomDebugDump::clearBuffer(); +#endif + + return left_loop; +} diff --git a/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.h b/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.h index 1cd11f717..ce9bb47c5 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.h +++ b/IfcPlusPlus/src/ifcpp/geometry/MeshSimplifier.h @@ -1,10 +1,27 @@ +/* -*-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. +*/ + #pragma once #include #include #include #include -#include + class MeshSimplifier { @@ -16,521 +33,10 @@ class MeshSimplifier * @param entity IFC entity that is currently being processed * @param ignoreOpenEdgesInResult If true, the result is kept even with open edges (good for visualization). If false, the result will be the input mesh in case open edges occur after triangulation (good for further boolean operations) */ - static void simplifyMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput) - { - if (!meshsetInput) - { - return; - } - - GeomProcessingParams params(paramsInput); - StatusCallback* report_callback = params.callbackFunc; - BuildingEntity* entity = params.ifc_entity; - bool dumpPolygon = params.debugDump; - double eps = params.epsMergePoints; - MeshSetInfo infoInput(report_callback, entity); - bool validMeshsetInput = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoInput, params); - bool deepCopyMesh = true; - - if (meshsetInput->vertex_storage.size() < 9 - && infoInput.openEdges.size() == 0 - && infoInput.degenerateEdges.size() == 0 - && infoInput.zeroAreaFaces.size() == 0 - && infoInput.meshSetValid) - { - return; - } - - if (infoInput.finEdges.size() > 0) - { - // if input mesh has fin edges, allow that also for the result - params.allowFinEdges = true; - } - - MeshSetInfo info(report_callback, entity); - shared_ptr > meshset(meshsetInput->clone()); - MeshOps::checkMeshSetValidAndClosed(meshset, info, params); - - bool useCarveMeshSimplifier = false; - if (useCarveMeshSimplifier) - { - try - { - size_t numChanges = 0; - carve::mesh::MeshSimplifier carveSimplifier(params.epsMergePoints); - //numChanges += carveSimplifier.removeFins(meshset.get()); - //numChanges += carveSimplifier.mergeCoplanarFaces(meshset.get(), params.epsMergeAlignedEdgesAngle); - numChanges += carveSimplifier.improveMesh_conservative(meshset.get(), params.epsMergePoints); - - //MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params); - - double min_deltaV = params.epsMergeAlignedEdgesAngle; - //numChanges += carveSimplifier.improveMesh(meshset.get(), params.epsMergeAlignedEdgesAngle, min_deltaV, params.epsMergeAlignedEdgesAngle, params.epsMergePoints ); - if (numChanges > 0) - { -#if defined(_DEBUG) || defined(_DEBUG_RELEASE) - MeshSetInfo infoChanges(report_callback, entity); - MeshOps::checkMeshSetValidAndClosed(meshset, infoChanges, params); - if (MeshOps::isBetterForBoolOp(infoChanges, info, false)) - { - std::cout << "carve::mesh::MeshSimplifier: numChanges: " << numChanges << std::endl; - } -#endif - } - } - catch (carve::exception& e) - { -#ifdef _DEBUG - std::cout << e.str() << std::endl; -#endif - } - - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); - } - -#ifdef _DEBUG - GeomDebugDump::DumpSettingsStruct dumpColorSettings; - - if (dumpPolygon) - { - GeomProcessingParams par(params); - par.checkZeroAreaFaces = false; - GeomDebugDump::dumpWithLabel("simplify--input", meshset, dumpColorSettings, par, true, true); - } - - if (dumpPolygon) - { - GeomDebugDump::moveOffset(0.2); - - glm::vec4 color1(0.7, 0.7, 0.7, 0.88); - std::string labelStr = "simplify--merged-faces"; - GeomDebugDump::dumpVertex(GeomDebugDump::DumpData::instance().labelPos, color1, labelStr); - GeomDebugDump::dumpCountLabel(GeomDebugDump::DumpData::instance().countLabelPos); - } - - if (GeomDebugDump::getDumpCount() >= 16) - { - int wait = 0; - } - shared_ptr > meshset_copy_input(meshset->clone()); -#endif - - if (infoInput.zeroAreaFaces.size() > 0) - { - if (infoInput.openEdges.size() == 0) - { - removeDegenerateFacesInMeshSet(meshset, params); - MeshOps::checkMeshSetValidAndClosed(meshset, info, params); - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, true, params, deepCopyMesh); - } - } - - if (!info.meshSetValid) - { - MeshOps::resolveOpenEdges(meshset, params); - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); - - if (!info.meshSetValid) - { - MeshOps::resolveOpenEdges(meshset, params); - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, info, infoInput, false, params, deepCopyMesh); - } - } - - dumpPolygon = false; - - try - { - removeDegenerateFacesInMeshSet(meshset, params); - bool mergedFacesMeshShouldBeClosed = false; // allow open edges, will try to sew them together later - size_t numChanges = mergeCoplanarFacesInMeshSet(meshset, params, mergedFacesMeshShouldBeClosed); - - size_t numEdgesRemoved = mergeAlignedEdges(meshset, params); - - MeshOps::recalcMeshSet(meshset, eps); - - // TODO: find faces with biggest area, and trim all points to plane - - MeshSetInfo infoMergedFaces(report_callback, entity); - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, false, params, deepCopyMesh); - -#ifdef _DEBUG - shared_ptr > meshset_merged_faces(meshset->clone()); - GeomProcessingParams paramCopy(params); - paramCopy.checkZeroAreaFaces = false; - - if (dumpPolygon)//|| numChanges > 0 ) - { - GeomDebugDump::moveOffset(0.3); - shared_ptr > meshset_dump = shared_ptr >(meshset->clone()); - dumpColorSettings.triangulateBeforeDump = false; - - dumpWithLabel("mesh-merged-faces", meshset_dump, dumpColorSettings, paramCopy, true, true); - dumpColorSettings.triangulateBeforeDump = true; - - GeomDebugDump::moveOffset(0.3); - glm::vec4 color1(0.7, 0.7, 0.7, 0.88); - for (carve::mesh::Mesh<3>*mesh : meshset_dump->meshes) - { - GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); - } - } - -#endif - MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); - if (!infoMergedFaces.meshSetValid) - { - size_t retry_count = 0; - MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, retry_count); - - MeshSetInfo infoRetriangulated(report_callback, entity); - MeshOps::checkMeshSetValidAndClosed(meshset, infoRetriangulated, params); - } - - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, false, params, deepCopyMesh); - - MeshOps::recalcMeshSet(meshset, eps); - MeshOps::checkMeshSetValidAndClosed(meshset, info, params); - - size_t numEdgesRemoved2 = mergeAlignedEdges(meshset, params); - if (numEdgesRemoved2 > 0) - { - MeshSetInfo infoMergedAlignedEdges(report_callback, entity); - bool validMergedAlignedEdges = MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedAlignedEdges, params); - -#ifdef _DEBUG - if (dumpPolygon) - { - GeomDebugDump::moveOffset(0.4); - dumpWithLabel("mesh-simplify-input", meshset_copy_input, dumpColorSettings, paramCopy, true, true); - - GeomDebugDump::moveOffset(0.2); - dumpWithLabel("mesh-merged-faces", meshset_merged_faces, dumpColorSettings, paramCopy, true, true); - - GeomDebugDump::moveOffset(0.2); - dumpWithLabel("mesh-merged-aligned-edges", meshset, dumpColorSettings, paramCopy, true, true); - - GeomDebugDump::moveOffset(0.1); - glm::vec4 color1(0.7, 0.7, 0.7, 0.88); - for (carve::mesh::Mesh<3>*mesh : meshset->meshes) - { - GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); - } - - GeomDebugDump::moveOffset(0.3); - } -#endif - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedAlignedEdges, infoInput, false, params, deepCopyMesh); - - if (validMergedAlignedEdges) - { - if (paramsInput.triangulateResult) - { - MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, 0); - MeshSetInfo infoTriangulated(report_callback, entity); - bool validTriangulated = MeshOps::checkMeshSetValidAndClosed(meshset, infoTriangulated, params); - - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoTriangulated, infoInput, true, params, deepCopyMesh); - } -#ifdef _DEBUG - GeomDebugDump::clearBuffer(); -#endif - return; - } - } - -#ifdef _DEBUG - GeomDebugDump::clearBuffer(); -#endif - - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, true, params, deepCopyMesh); - - MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); - MeshOps::checkAndFixMeshsetInverted(meshset, infoMergedFaces, params); - - MeshOps::assignIfBetterForBoolOp(meshset, meshsetInput, infoMergedFaces, infoInput, true, params, deepCopyMesh); - -#ifdef _DEBUG - MeshSetInfo infoResult(report_callback, entity); - bool validResult = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoResult, params); - if (!validResult) - { - if (validMeshsetInput) - { - std::cout << "validMeshsetInput && !validResult: should not happen" << std::endl; - } - } -#endif - - return; - } - catch (std::exception& ex) - { -#ifdef _DEBUG - std::cout << ex.what() << std::endl; -#endif - } - catch (carve::exception& ex) - { - std::cout << ex.str() << std::endl; - } - catch (...) - { - - } - } - - - static size_t mergeAlignedEdges(shared_ptr >& meshset, GeomProcessingParams& params) - { -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.3); - } -#endif - - size_t numEdgesRemoved = 0; - for (carve::mesh::Mesh<3>*mesh : meshset->meshes) - { - for (carve::mesh::Face<3>*face : mesh->faces) - { - if (!face) - { - continue; - } - - if (!face->edge) - { - continue; - } - - bool faceDumped = false; - carve::mesh::Edge<3>* edge = face->edge; - - // check how many edges are connected to end of edge - size_t numEdges = face->n_edges; - for (size_t ii = 0; ii < numEdges; ++ii) - { - if (!edge) - { - continue; - } - if (edge->next) - { - if (edge->next->rev) - { - if (edge->next->rev->next) - { - if (edge->next->rev->next->rev == edge) - { - // only one edge is connected, now check angle - - // edge->rev->next edge->rev edge->next->rev edge->next->next->rev - // <--------------------p1<------------------p2<-------------------------p3<----------------------- - // -------------------> ------------------> -----------------------> ----------------------> - // edge->prev edge edge->next edge->next->next - - carve::mesh::Vertex<3>* vertex1 = edge->v1(); - carve::mesh::Vertex<3>* vertex2 = edge->v2(); - carve::mesh::Vertex<3>* vertex3 = edge->next->v2(); - - std::set* > setEdges; - getEdgesOnVertex(mesh, vertex2, setEdges); - size_t numEdgesOnVertex = setEdges.size(); - - const carve::geom::vector<3>& p1 = vertex1->v; - const carve::geom::vector<3>& p2 = vertex2->v; - const carve::geom::vector<3>& p3 = vertex3->v; - - carve::geom::vector<3> edgeVector = p2 - p1; - carve::geom::vector<3> edgeNextVector = p3 - p2; - edgeVector.normalize(); - edgeNextVector.normalize(); - -#ifdef _DEBUG - glm::vec4 color1(0.4, 0.45, 0.45, 1.); - if (params.debugDump) - { - for (auto edgeOnVertex : setEdges) - { - const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; - const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; - std::vector > vecLine = { p1, p2 }; - GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); - } - } - - if (face->plane.N.x > 0.9) - { - int wait = 0; - } -#endif - - double dotProduct = dot(edgeVector, edgeNextVector); - if (std::abs(dotProduct - 1.0) < params.epsMergeAlignedEdgesAngle * 1000) - { - carve::mesh::Edge<3>* edgeRemove = edge;// ->next; - -#ifdef _DEBUG - if (params.debugDump) - { - std::vector* > vecAdjacentFaces = { edge->face, edge->rev->face, edgeRemove->face, edgeRemove->rev->face, edge->prev->face, edge->prev->rev->face }; - GeomDebugDump::moveOffset(0.05); - GeomDebugDump::dumpFaces(vecAdjacentFaces, color1, false); - GeomDebugDump::dumpFacePolygons(vecAdjacentFaces, color1, false); - } -#endif - - std::set* > setEdgePointersToRemovedEdge; - std::set* > setFacePointersToRemovedEdge; - getPointerToEdge(mesh, edgeRemove, setEdgePointersToRemovedEdge, setFacePointersToRemovedEdge); - size_t numVertexChanges = removePointerToVertex(mesh, vertex2, vertex1); - edge = edgeRemove->removeEdge(); // returns ->next - carve::geom::vector<3> distanceV1 = edge->v1()->v - p1; - carve::geom::vector<3> distanceV3 = edge->v2()->v - p3; - - //double epsMinFaceArea = params.minFaceArea;// *0.001; - //MeshOps::removeZeroAreaFacesInMesh(mesh, epsMinFaceArea, eps, dumpFaces); - - ++numEdgesRemoved; - mesh->cacheEdges(); - mesh->recalc(params.epsMergePoints); - - // edge->rev->next edge->rev edge->next->rev - // <--------------------v1<---------------------------------------------v2<------------------------ - // -------------------> --------------------------------------------> ----------------------> - // edge->prev edge edge->next - -#ifdef _DEBUG - if (params.debugDump) - { - glm::vec4 color(0.4, 0.45, 0.45, 1.); - - std::set* > setEdges1; - getEdgesOnVertex(mesh, vertex1, setEdges1); - - std::set* > setEdges2; - getEdgesOnVertex(mesh, vertex2, setEdges2); - - std::set* > setEdges3; - getEdgesOnVertex(mesh, vertex3, setEdges3); - - - for (auto edgeOnVertex : setEdges1) - { - const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; - const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; - std::vector > vecLine = { p1, p2 }; - GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); - } - - - for (auto edgeOnVertex : setEdges2) - { - const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; - const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; - std::vector > vecLine = { p1, p2 }; - GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); - } - - for (auto edgeOnVertex : setEdges3) - { - const carve::geom::vector<3>& p1 = edgeOnVertex->v1()->v; - const carve::geom::vector<3>& p2 = edgeOnVertex->v2()->v; - std::vector > vecLine = { p1, p2 }; - GeomDebugDump::dumpPolyline(vecLine, color1, 0, false, false); - } - - GeomDebugDump::moveOffset(0.4); - GeomDebugDump::dumpFacePolygons(mesh->faces, color1, false); + static void simplifyMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput); - GeomDebugDump::moveOffset(0.05); - std::vector > edgePolygon = { p1,p2,p3 }; - GeomDebugDump::dumpPolyline(edgePolygon, color, 0, false, false); - std::vector > edgePolygon2 = { edge->v1()->v, edge->v2()->v }; - GeomDebugDump::moveOffset(0.001); - GeomDebugDump::dumpPolyline(edgePolygon2, color, 0, false, false); - } - - MeshSetInfo infoMergedFaces; - bool validMeshsetMergedFaces = MeshOps::checkMeshSetValidAndClosed(meshset, infoMergedFaces, params); - - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.3); - GeomDebugDump::DumpSettingsStruct dumpColorSettings; - params.checkZeroAreaFaces = true; - GeomDebugDump::dumpWithLabel("mesh-merged-faces", meshset, dumpColorSettings, params, true, true); - } - - double dx = edgeVector.x - edgeNextVector.x; - double dy = edgeVector.y - edgeNextVector.y; - double dz = edgeVector.z - edgeNextVector.z; - if (std::abs(dx) > EPS_M8) - { - std::cout << "align check" << std::endl; - } - if (std::abs(dy) > EPS_M8) - { - std::cout << "align check" << std::endl; - } - if (std::abs(dz) > EPS_M8) - { - std::cout << "align check" << std::endl; - } -#endif - - continue; - } - } - } - } - edge = edge->next; - } - } -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.002); - glm::vec4 color(0.4, 0.45, 0.45, 1.); - GeomDebugDump::dumpFacePolygon(face, color, false); - } -#endif - - } - } - - if (numEdgesRemoved > 0) - { -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.2); - } -#endif - - for (auto mesh : meshset->meshes) - { - mesh->cacheEdges(); - mesh->recalc(params.epsMergePoints); - -#ifdef _DEBUG - if (params.debugDump) - { - for (carve::mesh::Face<3>*face : mesh->faces) - { - glm::vec4 color(0.4, 0.45, 0.45, 1.); - GeomDebugDump::dumpFacePolygon(face, color, false); - } - } -#endif - } - } - - return numEdgesRemoved; - } + static size_t mergeAlignedEdges(shared_ptr >& meshset, GeomProcessingParams& params); static size_t removePointerToVertex(carve::mesh::Mesh<3>* mesh, carve::mesh::Vertex<3>* vertRemove, carve::mesh::Vertex<3>* vertReplace) { @@ -628,351 +134,9 @@ class MeshSimplifier } } - static size_t mergeCoplanarFacesInMeshSet(shared_ptr >& meshset, const GeomProcessingParams& paramsInput, bool shouldBeClosedManifold) - { - shared_ptr > meshset_copy(meshset->clone()); - GeomProcessingParams params(paramsInput); - params.allowFinEdges = false; - double epsAngleMergeEdges = params.epsMergeAlignedEdgesAngle * 10; - double eps = params.epsMergePoints; - - MeshSetInfo infoInput; - bool validMeshsetInput = MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); - if (infoInput.finEdges.size() > 0) - { - // if input already has fin edges, allow it also for the result mesh - params.allowFinEdges = true; - } - - size_t numChanges = 0; - double volume = MeshOps::computeMeshsetVolume(meshset.get()); - - for (carve::mesh::Mesh<3>*mesh : meshset->meshes) - { - for (carve::mesh::Edge<3>*edge : mesh->closed_edges) - { - if (!edge) - { - continue; - } - - carve::mesh::Edge<3>* reverseEdge = edge->rev; - if (!reverseEdge) - { - continue; - } - - carve::mesh::Face<3>* face = edge->face; - if (!face) - { - continue; - } - - carve::mesh::Face<3>* adjacentFace = reverseEdge->face; - if (!adjacentFace) - { - continue; - } - - if (adjacentFace == face) - { - // can happen with opening - continue; - } - - // re-compute face normal here - face->computeNormal(eps); - adjacentFace->computeNormal(eps); - const vec3 faceNormal = face->plane.N; - const vec3 face2Normal = adjacentFace->plane.N; - - // adjacent faces have 1 as normal vector dot product - - double dotProduct = dot(faceNormal, face2Normal); - if (std::abs(dotProduct - 1.0) < epsAngleMergeEdges) - { - numChanges += removeEdgeAndMergeFaces(edge, params); - } - } - } - - if (numChanges > 0) - { - if (!shouldBeClosedManifold) - { - meshset = meshset_copy; - return numChanges; - } - MeshSetInfo infoResult; - bool validMeshsetResult = MeshOps::checkMeshSetValidAndClosed(meshset, infoResult, params); - - if (!validMeshsetResult) - { - MeshOps::retriangulateMeshSetForBoolOp(meshset, false, params, 0); - validMeshsetResult = MeshOps::checkMeshSetValidAndClosed(meshset, infoResult, params); - } - - double volumeResult = MeshOps::computeMeshsetVolume(meshset.get()); - if (volumeResult < 0.9 * volume) - { - validMeshsetResult = false; - } - - if (!validMeshsetResult) - { - meshset = meshset_copy; - numChanges = 0; - return 0; - } - - int numFacesRemoved = infoInput.numFaces - infoResult.numFaces; - int numClosedEdgesRemoved = infoInput.numClosedEdges - infoResult.numClosedEdges; - } - return numChanges; - } - - static size_t removeEdgeAndMergeFaces(carve::mesh::Edge<3>* edgeIn, const GeomProcessingParams& params) - { - double eps = params.epsMergePoints; - carve::mesh::Face<3>* face = edgeIn->face; - double faceArea = MeshOps::computeFaceArea(face); - if (std::abs(faceArea) < eps * 10) - { - return 0; - } - - vec3& facePosition_carve = edgeIn->v2()->v; - size_t numChanges = 0; - size_t numFacesDeleted = 0; + static size_t mergeCoplanarFacesInMeshSet(shared_ptr >& meshset, const GeomProcessingParams& paramsInput, bool shouldBeClosedManifold); -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.4); - glm::vec4 color2(0.3, 0.2, 0.2, 0.8); - //dumpAdjacentFaces(par.setAdjacentCoplanarFaces, color2); - } -#endif - - carve::mesh::Face<3>* faceOnRverseEdge = edgeIn->rev->face; - carve::mesh::Face<3>* faceOnEdge = edgeIn->face; - if (faceOnEdge == nullptr) - { - return 0; - } - - if (faceOnRverseEdge == nullptr) - { - return 0; - } - - if (faceOnEdge == faceOnRverseEdge) - { - // remaining edge between inner opening of face and outer boundary. Leave it for triangulation - return 0; - } - size_t numFacesBeforeMerge = faceOnRverseEdge->mesh->faces.size(); - - carve::mesh::Edge<3>* edgeErase = edgeIn; - size_t numEdgesFace = countEdges(faceOnEdge); - size_t numEdgesFaceReverse = countEdges(faceOnRverseEdge); - - if (numEdgesFace >= params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("edgeCount > m_maxNumFaceEdges"); - throw std::exception(ex); - } - if (numEdgesFaceReverse >= params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("edgeCount > m_maxNumFaceEdges"); - throw std::exception(ex); - } - -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.05); - glm::vec4 color2(0.3, 0.2, 0.2, 0.8); - std::vector > vecEdgePoints = { edgeErase->v1()->v, edgeErase->v2()->v }; - GeomDebugDump::dumpPolyline(vecEdgePoints, color2, 0, false, false); - } -#endif - - MeshSetInfo info2; - bool checkForDegenerateEdges = false; - MeshOps::checkFaceIntegrity(edgeErase->face, checkForDegenerateEdges, info2, params.epsMergePoints); - - MeshSetInfo info3; - MeshOps::checkFaceIntegrity(edgeErase->rev->face, checkForDegenerateEdges, info3, params.epsMergePoints); - - if (!info2.allPointersValid) - { - return 0; - } - if (!info3.allPointersValid) - { - return 0; - } - - carve::mesh::Edge<3>* edgeMergeNext = checkMergeFaces(edgeErase, params); - if (!edgeMergeNext) - { - return 0; - } - - carve::mesh::Face<3>* faceRemain = nullptr; - carve::mesh::Face<3>* faceRemove = nullptr; - - if (faceOnRverseEdge->edge) - { - faceRemain = faceOnRverseEdge; - size_t numEdgesFaceRemove_afterMerge = countEdges(faceOnRverseEdge); - if (numEdgesFaceRemove_afterMerge > params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("faceOnRverseEdge->edge count > maxNumFaceEdges"); - throw std::exception(ex); - } - if (numEdgesFaceReverse != numEdgesFaceRemove_afterMerge) - { - ++numChanges; - } - } - else - { - faceRemove = faceOnRverseEdge; - } - - if (faceOnEdge->edge) - { - faceRemain = faceOnEdge; - size_t numEdgesFaceRemain_afterMerge = countEdges(faceOnEdge); - if (numEdgesFaceRemain_afterMerge > params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("faceOnEdge->edge count > maxNumFaceEdges"); - throw std::exception(ex); - } - if (numEdgesFace != numEdgesFaceRemain_afterMerge) - { - ++numChanges; - } - } - else - { - faceRemove = faceOnEdge; - } - -#ifdef _DEBUG - if (params.debugDump) - { - GeomDebugDump::moveOffset(0.05); - glm::vec4 color2(0.3, 0.2, 0.2, 0.8); - GeomDebugDump::dumpFacePolygon({ faceRemain }, color2, false); - } -#endif - - // replace pointers to faceRemove - carve::mesh::Edge<3>* e = faceRemove->edge; - for (size_t ii = 0; ii < faceRemove->n_edges; ++ii) - { - if (e == nullptr) - { - continue; - } - - if (e->face == faceRemove) - { - e->face = faceRemain; - } - e = e->next; - } - - numChanges += removeFaceFromMesh(faceRemove); - delete faceRemove; - ++numFacesDeleted; - - if (!faceRemain) - { - return numChanges; - } - - auto mesh = faceRemain->mesh; - try - { - faceRemain->edge->validateLoop(); - } - catch (carve::exception& e) - { -#ifdef _DEBUG - std::cout << "validateLoop failed: " << e.str(); -#endif - } - - mesh->cacheEdges(); - //mesh->recalc(eps); - ++numChanges; - - // TODO: enforceMergedFacesToCommonPlane() : compute normal vector and centroid of merged face, then move all vertices precisely into that plane - - bool allVerticesInPlane = true; - std::vector* > faceVertices; - faceRemain->getVertices(faceVertices); - if (faceVertices.size() > 3) - { - carve::geom::vector<3> normalVector = GeomUtils::computePolygonNormal(faceVertices); - carve::geom::vector<3> centroid = faceRemain->centroid(); - GeomUtils::Plane plane(glm::dvec3(centroid.x, centroid.y, centroid.z), glm::dvec3(normalVector.x, normalVector.y, normalVector.z)); - - for (carve::mesh::Face<3>::vertex_t * vertex : faceVertices) - { - const carve::geom::vector<3>& facePoint_carve = vertex->v; - glm::dvec3 facePoint(facePoint_carve.x, facePoint_carve.y, facePoint_carve.z); - - double distanceToPlane = plane.distancePointPlane(facePoint); - - if (std::abs(distanceToPlane) > params.epsMergePoints) - { - vec3 pointOnPlane = facePoint_carve + normalVector * distanceToPlane; - -#ifdef _DEBUG - glm::dvec3 pointOnPlane_glm(pointOnPlane.x, pointOnPlane.y, pointOnPlane.z); - double distanceToPlaneCheck = plane.distancePointPlane(pointOnPlane_glm); - - double maxAllowedDistance = params.epsMergePoints * 5.0; - if (std::abs(distanceToPlaneCheck) > maxAllowedDistance) - { - //std::cout << "distanceToPlaneCheck too big: " << distanceToPlaneCheck << std::endl; - } -#endif - vertex->v = pointOnPlane; - - // TODO: average out current vertices between all faces that are connected - // map> - } - } - - if (allVerticesInPlane) - { - return numChanges; - } - } - -#ifdef _DEBUG - size_t numFacesAfterMerge = mesh->faces.size(); - int numFacesMerged = numFacesBeforeMerge - numFacesAfterMerge; - - MeshSetInfo info6; - MeshOps::checkMeshIntegrity(mesh, checkForDegenerateEdges, params, info6); - if (!info6.allPointersValid || !allVerticesInPlane) - { - glm::vec4 color(0.2, 0.2, 0.2, 1.); - GeomDebugDump::stopBuffering(); - GeomDebugDump::moveOffset(0.3); - GeomDebugDump::dumpMesh(mesh, color, true); - } -#endif - - return numChanges; - } + static size_t removeEdgeAndMergeFaces(carve::mesh::Edge<3>* edgeIn, const GeomProcessingParams& params); static size_t removeFaceFromMesh(carve::mesh::Face<3>* fx) @@ -1014,136 +178,7 @@ class MeshSimplifier } - static carve::mesh::Edge<3>* checkMergeFaces(carve::mesh::Edge<3>* e, const GeomProcessingParams& params) - { - if (e->rev == nullptr) - { - return nullptr; - } - - - carve::mesh::Face<3>* fwdface = e->face; - carve::mesh::Face<3>* revface = e->rev->face; - - if (fwdface == revface) - { - return nullptr; - } - - if (fwdface->n_edges > params.generalSettings->m_maxNumFaceEdges) - { - return nullptr; - } - if (revface->n_edges > params.generalSettings->m_maxNumFaceEdges) - { - return nullptr; - } - - -#ifdef _DEBUG - - GeomDebugDump::ScopedDumpBuffering scoped_buffer; - if (params.debugDump) - { - glm::vec4 color(0.3, 0.3, 0.3, 1.); - std::vector* > vecFaces = { fwdface }; - GeomDebugDump::dumpFaces(vecFaces, color, false); - GeomDebugDump::dumpFacePolygon(revface, color, false); - } -#endif - - size_t n_removed = 0; - - carve::mesh::Edge<3>* splice_beg = e; - for (size_t ii = 0; ii < fwdface->n_edges; ++ii) - { - splice_beg = splice_beg->prev; - ++n_removed; - - if (splice_beg == e) { break; } - if (!splice_beg->rev) { break; } - if (splice_beg->next->rev->prev != splice_beg->rev) { break; } - } - - if (splice_beg == e) - { - // edge loops are completely matched. - return nullptr; - } - - carve::mesh::Edge<3>* splice_end = e; - do { - splice_end = splice_end->next; - ++n_removed; - } while (splice_end->rev && splice_end->prev->rev->next == splice_end->rev); - - --n_removed; - - carve::mesh::Edge<3>* link1_p = splice_beg; - carve::mesh::Edge<3>* link1_n = splice_beg->next->rev->next; - - carve::mesh::Edge<3>* link2_p = splice_end->prev->rev->prev; - carve::mesh::Edge<3>* link2_n = splice_end; - - CARVE_ASSERT(link1_p->face == fwdface); - CARVE_ASSERT(link1_n->face == revface); - - CARVE_ASSERT(link2_p->face == revface); - CARVE_ASSERT(link2_n->face == fwdface); - - carve::mesh::Edge<3>* left_loop = link1_p->next; - - CARVE_ASSERT(left_loop->rev == link1_n->prev); - - linkEdges(link2_n->prev, link1_p->next); - linkEdges(link1_n->prev, link2_p->next); - - linkEdges(link1_p, link1_n); - linkEdges(link2_p, link2_n); - - fwdface->edge = link1_p; - - size_t edgeCount = 0; - for (carve::mesh::Edge<3>*e = link1_n; e != link2_n; e = e->next) - { - CARVE_ASSERT(e->face == revface); - e->face = fwdface; - fwdface->n_edges++; - ++edgeCount; - if (edgeCount > params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("edgeCount > m_maxNumFaceEdges"); - throw std::exception(ex); - return nullptr; - } - } - edgeCount = 0; - for (carve::mesh::Edge<3>*e = link2_n; e != link1_n; e = e->next) - { - CARVE_ASSERT(e->face == fwdface); - ++edgeCount; - if (edgeCount > params.generalSettings->m_maxNumFaceEdges) - { - std::logic_error ex("edgeCount > m_maxNumFaceEdges"); - throw std::exception(ex); - return nullptr; - } - } - - fwdface->n_edges -= n_removed; - - revface->n_edges = 0; - revface->edge = nullptr; - - setFacePointerToEdgeLoop(left_loop, nullptr); - setFacePointerToEdgeLoop(left_loop->rev, nullptr); - -#ifdef _DEBUG - GeomDebugDump::clearBuffer(); -#endif - - return left_loop; - } + static carve::mesh::Edge<3>* checkMergeFaces(carve::mesh::Edge<3>* e, const GeomProcessingParams& params); static void linkEdges(carve::mesh::Edge<3>* a, carve::mesh::Edge<3>* b) { @@ -1165,234 +200,11 @@ class MeshSimplifier } } - static void removeDegenerateFacesInMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput) - { - if (!meshsetInput) - { - return; - } - - GeomProcessingParams params(paramsInput); - params.allowZeroAreaFaces = true; - MeshSetInfo infoInput; - bool meshInputOk = MeshOps::checkMeshSetValidAndClosed(meshsetInput, infoInput, params); - - std::set* > setFacesRemove = infoInput.degenerateFaces; - std::set* > setEdgesRemove = infoInput.degenerateEdges; - - for (carve::mesh::Mesh<3>*mesh : meshsetInput->meshes) - { - double meshVolume = mesh->volume(); - if (meshVolume < params.epsMergePoints) - { - // remove complete mesh - for (carve::mesh::Face<3>*face : mesh->faces) - { - if (!face) - { - continue; - } - - setFacesRemove.insert(face); - } - continue; - } - } - - if (setFacesRemove.size() > 0 || setEdgesRemove.size() > 0) - { - PolyInputCache3D polyInput(params.epsMergePoints); - MeshOps::polyhedronFromMeshSet(meshsetInput, setFacesRemove, polyInput); - - int numFacesInput = infoInput.numFaces; - std::string details; - bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); - if (!polyCorrect) - { - fixPolyhedronData(polyInput.m_poly_data, params); - polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); - } - - std::map mesh_input_options; - shared_ptr > resultFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); - MeshSetInfo info; - MeshOps::checkMeshSetValidAndClosed(resultFromPolyhedron, info, params); - int numFacesPoly = info.numFaces; -#ifdef _DEBUG - if (meshInputOk && !info.meshSetValid && false) - { - GeomDebugDump::DumpSettingsStruct dumpColorSettings; - GeomDebugDump::dumpWithLabel("removeDegenerateFacesInMeshSet--input", meshsetInput, dumpColorSettings, params, true, true); - GeomDebugDump::dumpMeshsetOpenEdges(resultFromPolyhedron, dumpColorSettings.colorMesh, false, true ); - GeomDebugDump::dumpWithLabel("removeDegenerateFacesInMeshSet--result", resultFromPolyhedron, dumpColorSettings, params, true, true); - } -#endif - - if (info.numOpenEdges() > 0) - { - MeshOps::resolveOpenEdges(resultFromPolyhedron, params); - MeshOps::checkMeshSetValidAndClosed(resultFromPolyhedron, info, params); - } - - if (MeshOps::isBetterForBoolOp(info, infoInput, true)) - { - meshsetInput = resultFromPolyhedron; - } - } - } - - static void removeFinFaces(shared_ptr >& meshset, const GeomProcessingParams& params) - { - return; - + static void removeDegenerateFacesInMeshSet(shared_ptr >& meshsetInput, const GeomProcessingParams& paramsInput); - - MeshSetInfo infoInput; - MeshOps::checkMeshSetValidAndClosed(meshset, infoInput, params); - - std::set* > setFacesRemove; - for (carve::mesh::Mesh<3>*mesh : meshset->meshes) - { - for (carve::mesh::Edge<3>*edge : mesh->closed_edges) - { - if (!edge) - { - continue; - } + static void removeFinFaces(shared_ptr >& meshset, const GeomProcessingParams& params); - carve::mesh::Edge<3>* reverseEdge = edge->rev; - if (!reverseEdge) - { - continue; - } - - carve::mesh::Face<3>* face = edge->face; - if (!face) - { - continue; - } - - carve::mesh::Face<3>* adjacentFace = reverseEdge->face; - if (!adjacentFace) - { - continue; - } - - // re-compute face normal here - face->computeNormal(params.epsMergePoints); - adjacentFace->computeNormal(params.epsMergePoints); - const vec3 faceNormal = face->plane.N; - const vec3 face2Normal = adjacentFace->plane.N; - - // adjacent faces back-to-back have -1 as normal vector dot product - double dotProduct = dot(faceNormal, face2Normal); - if (std::abs(dotProduct + 1.0) < params.epsMergeAlignedEdgesAngle) - { - setFacesRemove.insert(face); - setFacesRemove.insert(adjacentFace); - } - } - } - - if (setFacesRemove.size() > 0) - { - PolyInputCache3D polyInput(params.epsMergePoints); - MeshOps::polyhedronFromMeshSet(meshset, setFacesRemove, polyInput); - - std::string details; - bool polyCorrect = checkPolyhedronData(polyInput.m_poly_data, params, details); - if (!polyCorrect) - { - fixPolyhedronData(polyInput.m_poly_data, params); - } - - std::map mesh_input_options; - shared_ptr > resultFromPolyhedron(polyInput.m_poly_data->createMesh(mesh_input_options, params.epsMergePoints)); - MeshSetInfo info; - MeshOps::assignIfBetterForBoolOp(resultFromPolyhedron, meshset, info, infoInput, false, params, false); - - if (info.meshSetValid) - { -#ifdef _DEBUG - if (params.debugDump) - { - glm::vec4 color(0.5, 0.5, 0.5, 1); - GeomDebugDump::stopBuffering(); - GeomDebugDump::moveOffset(0.15); - bool drawNormals = true; - GeomDebugDump::dumpMeshset(meshset, color, drawNormals, true); - GeomDebugDump::moveOffset(0.1); - GeomDebugDump::dumpMeshset(resultFromPolyhedron, color, drawNormals, true); - } -#endif - } - } - } - - static void removeFinEdges(carve::mesh::Mesh<3>* mesh, const GeomProcessingParams& params) - { - return; // degenerate edge = degenerate face, so remove degenerate faces instead - - if (!mesh) - { - return; - } - - const std::vector* >& vec_faces = mesh->faces; - size_t numFaces = vec_faces.size(); - -#ifdef _DEBUG - if (numFaces == 1 && false) - { - glm::vec4 color(0.4, 0.2, 0.2, 1.); - std::vector* > vecFaces; - vecFaces.push_back(vec_faces[0]); - GeomDebugDump::moveOffset(0.3); - GeomDebugDump::stopBuffering(); - GeomDebugDump::dumpFaces(vecFaces, color); - GeomDebugDump::moveOffset(0.3); - GeomDebugDump::dumpFacePolygon(vec_faces[0], color, true); - } -#endif - - for (size_t ii = 0; ii < numFaces; ++ii) - { - size_t numChangesAll = 0; - for (size_t jj = 0; jj < vec_faces.size(); ++jj) - { - size_t numChangesCurrentFace = 0; - carve::mesh::Face<3>* face = vec_faces[jj]; - removeFinEdgesFromFace(face, numChangesCurrentFace, params.epsMergePoints); - numChangesAll += numChangesCurrentFace; - } - - // several fin-edges (where edge->next == edge->reverse) can be concatenated. Repeat until there are no changes - if (numChangesAll > 0) - { - if (mesh->faces.size() < 2) - { - continue; - } - - bool checkForDegenerateEdges = false; - MeshSetInfo minf; - MeshOps::checkMeshIntegrity(mesh, checkForDegenerateEdges, params, minf); - - if (!minf.allPointersValid) - { - continue; - } - - mesh->cacheEdges(); - mesh->recalc(params.epsMergePoints); - } - - if (numChangesAll == 0) - { - break; - } - } - } + static void removeFinEdges(carve::mesh::Mesh<3>* mesh, const GeomProcessingParams& params); static void removeFinEdges(shared_ptr >& meshset, const GeomProcessingParams& params) { diff --git a/IfcPlusPlus/src/ifcpp/geometry/PlacementConverter.h b/IfcPlusPlus/src/ifcpp/geometry/PlacementConverter.h index 0e7c19bb2..ba3af396a 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/PlacementConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/PlacementConverter.h @@ -157,6 +157,13 @@ class PlacementConverter : public StatusCallback } } +#ifdef _DEBUG_RELEASE + if (translate.x > 500) + { + std::cout << "500" << std::endl; + } +#endif + if( axis2placement3d->m_Axis ) { // local z-axis @@ -190,6 +197,18 @@ class PlacementConverter : public StatusCallback { resulting_matrix = shared_ptr( new TransformData() ); } + +#ifdef _DEBUG_RELEASE + if( resulting_matrix->m_matrix._41 > 500 ) + { + std::cout << "500" << std::endl; + } + if (resulting_matrix->m_matrix.v[12] > 500) + { + std::cout << "500" << std::endl; + } +#endif + resulting_matrix->m_matrix = carve::math::Matrix( local_x.x, local_y.x, local_z.x, translate.x, local_x.y, local_y.y, local_z.y, translate.y, diff --git a/IfcPlusPlus/src/ifcpp/geometry/RepresentationConverter.h b/IfcPlusPlus/src/ifcpp/geometry/RepresentationConverter.h index 1db21d230..4f33399e2 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/RepresentationConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/RepresentationConverter.h @@ -68,6 +68,17 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #include "FaceConverter.h" #include "ProfileCache.h" +struct ItemCacheContainer +{ + ItemCacheContainer(shared_ptr& _geometricItem, std::vector >& _vec_rel_voids) + { + geometricItem = _geometricItem; + vec_rel_voids = _vec_rel_voids; + } + shared_ptr geometricItem; + std::vector > vec_rel_voids; +}; + class RepresentationConverter : public StatusCallback { shared_ptr m_geom_settings; @@ -81,6 +92,8 @@ class RepresentationConverter : public StatusCallback shared_ptr m_profile_cache; shared_ptr m_face_converter; shared_ptr m_solid_converter; + std::map > m_itemCache; + bool m_geometricItemCaching = false; public: RepresentationConverter( shared_ptr geom_settings, shared_ptr unit_converter ) @@ -150,9 +163,11 @@ class RepresentationConverter : public StatusCallback // } //} - void convertIfcRepresentation( const shared_ptr& ifcRepresentation, shared_ptr& representationData ) + void convertIfcRepresentation( const shared_ptr& ifcRepresentation, shared_ptr& representationData, + std::vector >& vec_rel_voids) { representationData->m_ifc_representation = ifcRepresentation; + printToDebugLog(__FUNC__, ""); for( const shared_ptr& representationItem : ifcRepresentation->m_Items ) { @@ -161,10 +176,11 @@ class RepresentationConverter : public StatusCallback if( geomItem ) { shared_ptr geomItemData( new ItemShapeData() ); + geomItemData->m_product = representationData->m_product; try { - convertIfcGeometricRepresentationItem( geomItem, geomItemData ); + convertIfcGeometricRepresentationItem( geomItem, geomItemData, vec_rel_voids); representationData->addGeometricChildItem(geomItemData, representationData); } catch( BuildingException& e ) @@ -223,7 +239,7 @@ class RepresentationConverter : public StatusCallback try { - convertIfcRepresentation( mapped_representation, mapped_input_data ); + convertIfcRepresentation( mapped_representation, mapped_input_data, vec_rel_voids ); } catch( BuildingException& e ) { @@ -332,7 +348,8 @@ class RepresentationConverter : public StatusCallback - void convertIfcGeometricRepresentationItem( const shared_ptr& geom_item, shared_ptr& item_data ) + void convertIfcGeometricRepresentationItem( const shared_ptr& geom_item, shared_ptr& item_data, + std::vector >& vec_rel_voids) { //ENTITY IfcGeometricRepresentationItem //ABSTRACT SUPERTYPE OF(ONEOF(IfcAnnotationFillArea, IfcBooleanResult, IfcBoundingBox, IfcCartesianPointList, IfcCartesianTransformationOperator, IfcCsgPrimitive3D, IfcCurve, @@ -354,6 +371,61 @@ class RepresentationConverter : public StatusCallback } } + + if (m_geometricItemCaching) + { + int itemTag = geom_item->m_tag; + auto findItemCacheIt = m_itemCache.find(itemTag); + if (findItemCacheIt != m_itemCache.end()) + { + const shared_ptr& currentItemCache = findItemCacheIt->second; + bool sameOpenings = true; + if (currentItemCache->vec_rel_voids.size() == vec_rel_voids.size()) + { + for (size_t ii = 0; ii < currentItemCache->vec_rel_voids.size(); ++ii) + { + shared_ptr relVoids(vec_rel_voids[ii]); + shared_ptr relVoidsCache(currentItemCache->vec_rel_voids[ii]); + if (!relVoids && !relVoidsCache) + { + continue; + } + if (!relVoids) + { + sameOpenings = false; + continue; + } + if (!relVoidsCache) + { + sameOpenings = false; + continue; + } + if (relVoids->m_tag != relVoidsCache->m_tag) + { + // TODO: more sophisticated comparison. This captures only same objects in same order. + sameOpenings = false; + } + } + } + else + { + sameOpenings = false; + } + + if (sameOpenings) + { + if (!item_data) + { + item_data = make_shared(); + } + item_data->copyFrom(currentItemCache->geometricItem); + //item_data = currentItemCache->geometricItem; + return; + } + } + m_itemCache.insert({ itemTag, make_shared(item_data, vec_rel_voids) }); + } + shared_ptr surface_model = dynamic_pointer_cast( geom_item ); if( surface_model ) { @@ -922,7 +994,7 @@ class RepresentationConverter : public StatusCallback try { - convertIfcRepresentation(ifc_opening_representation, opening_item); + convertIfcRepresentation(ifc_opening_representation, opening_item, vec_rel_voids); } catch (BuildingException& e) { diff --git a/IfcPlusPlus/src/ifcpp/geometry/SceneGraphUtils.h b/IfcPlusPlus/src/ifcpp/geometry/SceneGraphUtils.h index 23f2988ad..129f53471 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/SceneGraphUtils.h +++ b/IfcPlusPlus/src/ifcpp/geometry/SceneGraphUtils.h @@ -1,754 +1,754 @@ -/* -*-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. -*/ - -#pragma once - -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 -#endif - -namespace SceneGraphUtils -{ - inline bool inParentList(const std::string guid, const osg::Group* group) - { - if (!group) - { - return false; - } - - const osg::Group::ParentList& vec_parents = group->getParents(); - for (size_t ii = 0; ii < vec_parents.size(); ++ii) - { - const osg::Group* parent = vec_parents[ii]; - if (parent) - { - const std::string parent_name = parent->getName(); - if (parent_name.length() >= 22) - { - std::string parent_name_id = parent_name.substr(22); - if (parent_name_id == guid) - { - return true; - } - bool in_parent_list = inParentList(guid, parent); - if (in_parent_list) - { - return true; - } - } - } - } - return false; - } - inline bool inParentList( const int entity_id, const osg::Group* group ) - { - if( !group ) - { - return false; - } - - const osg::Group::ParentList& vec_parents = group->getParents(); - for( size_t ii = 0; ii < vec_parents.size(); ++ii ) - { - const osg::Group* parent = vec_parents[ii]; - if( parent ) - { - const std::string parent_name = parent->getName(); - if( parent_name.length() > 0 ) - { - if( parent_name.at( 0 ) == '#' ) - { - // extract entity id - std::string parent_name_id = parent_name.substr( 1 ); - size_t last_index = parent_name_id.find_first_not_of( "0123456789" ); - std::string id_str = parent_name_id.substr( 0, last_index ); - const int id = std::stoi( id_str.c_str() ); - if( id == entity_id ) - { - return true; - } - bool in_parent_list = inParentList( entity_id, parent ); - if( in_parent_list ) - { - return true; - } - } - } - } - } - return false; - } - inline osg::Vec3d computePolygonNormal( const osg::Vec3dArray* polygon ) - { - const int num_points = polygon->size(); - osg::Vec3d polygon_normal( 0, 0, 0 ); - for( int k = 0; k < num_points; ++k ) - { - const osg::Vec3d& vertex_current = polygon->at( k ); - const osg::Vec3d& vertex_next = polygon->at( (k + 1) % num_points ); - polygon_normal._v[0] += (vertex_current.y() - vertex_next.y())*(vertex_current.z() + vertex_next.z()); - polygon_normal._v[1] += (vertex_current.z() - vertex_next.z())*(vertex_current.x() + vertex_next.x()); - polygon_normal._v[2] += (vertex_current.x() - vertex_next.x())*(vertex_current.y() + vertex_next.y()); - } - polygon_normal.normalize(); - return polygon_normal; - } - inline osg::Vec3f computePolygonNormal( const osg::Vec3Array* polygon ) - { - const int num_points = polygon->size(); - osg::Vec3f polygon_normal( 0, 0, 0 ); - for( int k = 0; k < num_points; ++k ) - { - const osg::Vec3f& vertex_current = polygon->at( k ); - const osg::Vec3f& vertex_next = polygon->at( (k + 1) % num_points ); - polygon_normal._v[0] += (vertex_current.y() - vertex_next.y())*(vertex_current.z() + vertex_next.z()); - polygon_normal._v[1] += (vertex_current.z() - vertex_next.z())*(vertex_current.x() + vertex_next.x()); - polygon_normal._v[2] += (vertex_current.x() - vertex_next.x())*(vertex_current.y() + vertex_next.y()); - } - polygon_normal.normalize(); - return polygon_normal; - } - inline osg::ref_ptr createCoordinateAxes( double length ) - { - osg::ref_ptr geode = new osg::Geode(); - osg::ref_ptr stateset = geode->getOrCreateStateSet(); - stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); - float alpha = 0.5f; - - // positive axes - { - osg::ref_ptr geom = new osg::Geometry(); - geode->addDrawable( geom ); - - osg::ref_ptr vertices = new osg::Vec3Array(); - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( length, 0.0, 0.0 ) ); - - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( 0.0, length, 0.0 ) ); - - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( 0.0, 0.0, length ) ); - -#ifndef COORDINATE_AXES_NO_COLORS - osg::ref_ptr colors = new osg::Vec4Array(); - colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.8f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.8f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); - colors->setBinding( osg::Array::BIND_PER_VERTEX ); - - geom->setColorArray( colors ); - //geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); -#endif - geom->setVertexArray( vertices ); - geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, 6 ) ); - } - - // positive axes - { - osg::ref_ptr geom = new osg::Geometry(); - geode->addDrawable( geom ); - - osg::ref_ptr vertices = new osg::Vec3Array(); - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( -length, 0.0, 0.0 ) ); - - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( 0.0, -length, 0.0 ) ); - - vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); - vertices->push_back( osg::Vec3f( 0.0, 0.0, -length ) ); - -#ifndef COORDINATE_AXES_NO_COLORS - osg::ref_ptr colors = new osg::Vec4Array(); - colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 1.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 1.f, 0.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); - colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); - colors->setBinding( osg::Array::BIND_PER_VERTEX ); - - geom->setColorArray( colors ); - //geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); -#endif - geom->setVertexArray( vertices ); - geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, 6 ) ); - - // make negative axed dotted - osg::ref_ptr stateset_negative = geom->getOrCreateStateSet(); - osg::ref_ptr linestipple = new osg::LineStipple(); - linestipple->setFactor( 2 ); - linestipple->setPattern( 0xAAAA ); - stateset_negative->setAttributeAndModes( linestipple, osg::StateAttribute::ON ); - } - - // x axis label - bool add_x_label = false; - if( add_x_label ) - { - osg::ref_ptr label_x = new osgText::Text(); - label_x->setFont( "ARIAL.TTF" ); - label_x->setAlignment( osgText::Text::RIGHT_TOP ); - label_x->setAxisAlignment( osgText::Text::SCREEN ); - label_x->setColor( osg::Vec4( 0.8f, 0.0f, 0.0f, 1.0f ) ); - label_x->setCharacterSize( 0.5f ); - label_x->setText( "x" ); - label_x->setPosition( osg::Vec3( 1, 0, 0 ) ); - label_x->setEnableDepthWrites( false ); - geode->addDrawable( label_x ); - } - - return geode; - } - inline osg::ref_ptr createCoordinateAxesArrows() - { - float cone_tip = 1.2f; - float cone_base = 1.f; - float cone_radius = 0.06f; - float cone_height = cone_tip - cone_base; - osg::ref_ptr group = new osg::Group(); - - { - // x - osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); - osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, -cone_base*0.5 ), cone_radius*0.2, cone_base ) ); - cone_drawable->setColor( osg::Vec4f( 0.8f, 0.0f, 0.0f, 0.7f ) ); - cyl_drawable->setColor( osg::Vec4f( 0.8f, 0.0f, 0.0f, 0.7f ) ); - osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::rotate( M_PI_2, osg::Vec3d( 0, 1, 0 ) )*osg::Matrix::translate( 1, 0, 0 ) ); - osg::ref_ptr geode = new osg::Geode(); - geode->addDrawable( cone_drawable ); - geode->addDrawable( cyl_drawable ); - mt1->addChild( geode ); - group->addChild( mt1 ); - - osg::ref_ptr material = new osg::Material(); - material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.7f, 0.f, 0.f, 0.7f ) ); - material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.7f, 0.f, 0.f, 0.7f ) ); - material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 1.f, 0.4f, 0.4f, 0.7f ) ); - material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); - cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - - } - - { - // y - osg::ref_ptr cone = new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ); - - osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); - osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, cone_base*0.5 ), cone_radius*0.2, cone_base ) ); - cone_drawable->setColor( osg::Vec4f( 0.f, 0.7f, 0.f, 0.7f ) ); - cyl_drawable->setColor( osg::Vec4f( 0.f, 0.7f, 0.f, 0.7f ) ); - - - osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) )*osg::Matrix::translate( 0, 1, 0 ) ); - osg::ref_ptr mt2 = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) )*osg::Matrix::translate( 0, 1 + cone_height, 0 ) ); - osg::ref_ptr mt_cyl = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) ) ); - osg::ref_ptr geode = new osg::Geode(); - geode->addDrawable( cone_drawable ); - - osg::ref_ptr geode_cyl = new osg::Geode(); - geode_cyl->addDrawable( cyl_drawable ); - - mt1->addChild( geode ); - mt2->addChild( geode ); - mt_cyl->addChild( geode_cyl ); - group->addChild( mt1 ); - group->addChild( mt2 ); - group->addChild( mt_cyl ); - - osg::ref_ptr material = new osg::Material(); - material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.0f, 0.7f, 0.f, 0.7f ) ); - material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.0f, 0.7f, 0.f, 0.7f ) ); - material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.4f, 1.f, 0.4f, 0.7f ) ); - material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); - cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - } - - { - // z - osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); - osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, cone_base*0.5 ), cone_radius*0.2, cone_base ) ); - cone_drawable->setColor( osg::Vec4f( 0.0f, 0.0f, 0.8f, 0.7f ) ); - cyl_drawable->setColor( osg::Vec4f( 0.0f, 0.0f, 0.8f, 0.7f ) ); - - osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 ) ); - osg::ref_ptr mt2 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 + cone_height ) ); - osg::ref_ptr mt3 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 + cone_height * 2 ) ); - - osg::ref_ptr geode = new osg::Geode(); - geode->addDrawable( cone_drawable ); - osg::ref_ptr geode_cyl = new osg::Geode(); - geode_cyl->addDrawable( cyl_drawable ); - mt1->addChild( geode ); - mt2->addChild( geode ); - mt3->addChild( geode ); - group->addChild( mt1 ); - group->addChild( mt2 ); - group->addChild( mt3 ); - group->addChild( geode_cyl ); - - osg::Material* material = new osg::Material(); - material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.f, 0.f, 0.8f, 0.7f ) ); - material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.f, 0.f, 0.8f, 0.7f ) ); - material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.4f, 0.4f, 1.f, 0.7f ) ); - material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); - cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); - } - return group; - } - inline osg::ref_ptr createCoordinateGrid() - { - osg::ref_ptr geode = new osg::Geode(); - osg::ref_ptr stateset = geode->getOrCreateStateSet(); - stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); - - { - osg::ref_ptr geom = new osg::Geometry(); - geode->addDrawable( geom ); - - osg::ref_ptr vertices = new osg::Vec3Array(); - - for( int i = 0; i <= 20; ++i ) - { - vertices->push_back( osg::Vec3f( -10, -10 + i, 0.0 ) ); - vertices->push_back( osg::Vec3f( 10, -10 + i, 0.0 ) ); - - vertices->push_back( osg::Vec3f( -10 + i, -10, 0.0 ) ); - vertices->push_back( osg::Vec3f( -10 + i, 10, 0.0 ) ); - } - - osg::ref_ptr colors = new osg::Vec4Array(); - colors->push_back( osg::Vec4f( 0.7f, 0.7f, 0.7f, 0.5f ) ); - geom->setColorArray( colors ); - colors->setBinding( osg::Array::BIND_OVERALL ); - //geom->setColorBinding( osg::Geometry::BIND_OVERALL ); - - geom->setVertexArray( vertices ); - geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, vertices->size() ) ); - } - - return geode; - } - inline void WireFrameModeOn( osg::StateSet* state ) - { - osg::ref_ptr polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); - if( !polygon_mode ) - { - polygon_mode = new osg::PolygonMode(); - state->setAttribute( polygon_mode ); - } - polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE ); - } - inline void disableWireFrameOnText( osg::Node* node ) - { - if( node == nullptr ) - { - return; - } - - osg::Group* grp = dynamic_cast( node ); - if( grp ) - { - for( size_t i = 0; i < grp->getNumChildren(); ++i ) - { - osg::Node* child = grp->getChild( i ); - disableWireFrameOnText( child ); - } - } - else - { - osg::Geode* geode = dynamic_cast( node ); - if( geode ) - { - for( size_t i = 0; i < geode->getNumDrawables(); ++i ) - { - osg::Drawable* child = geode->getDrawable( i ); - - osgText::Text* txt = dynamic_cast( child ); - if( txt ) - { - osg::StateSet* state = txt->getOrCreateStateSet(); - - osg::PolygonMode* polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); - - if( !polygon_mode ) - { - polygon_mode = new osg::PolygonMode(); - state->setAttribute( polygon_mode ); - } - polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ); - } - } - } - } - } - inline void WireFrameModeOn( osg::Node* node ) - { - if( node == nullptr ) - { - return; - } - - osg::StateSet* state = node->getOrCreateStateSet(); - WireFrameModeOn( state ); - disableWireFrameOnText( node ); - } - inline void WireFrameModeOff( osg::StateSet* state ) - { - osg::ref_ptr polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); - - if( !polygon_mode ) - { - polygon_mode = new osg::PolygonMode(); - state->setAttribute( polygon_mode ); - } - polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ); - } - inline void WireFrameModeOff( osg::Node *node ) - { - if( node == nullptr ) - { - return; - } - - osg::StateSet *state = node->getOrCreateStateSet(); - WireFrameModeOff( state ); - } - inline void HiddenLineModeOn( osg::Group* node ) - { - return; - osg::ref_ptr ss = node->getOrCreateStateSet(); - ss->setAttributeAndModes( new osg::ColorMask( false, false, false, false ), osg::StateAttribute::ON ); - ss->setBinName( "RenderBin" ); - ss->setBinNumber( 1 ); - ss->setRenderBinDetails( 1, "RenderBin" ); - - osg::ref_ptr node_lines = new osg::Group(); - // TODO: create lines - - ss = node_lines->getOrCreateStateSet(); - ss->setBinName( "RenderBin" ); - ss->setBinNumber( 2 ); - ss->setRenderBinDetails( 2, "RenderBin" ); - ss->setMode( GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON ); - osg::ref_ptr po = new osg::PolygonOffset( 10.0f, 10.0f ); - ss->setAttributeAndModes( po, osg::StateAttribute::ON ); - } - inline void cullFrontBack( bool front, bool back, osg::StateSet* stateset ) - { - if( front ) - { - if( back ) - { - // cull back and front - osg::ref_ptr cull = new osg::CullFace( osg::CullFace::FRONT_AND_BACK ); - stateset->setAttributeAndModes( cull.get(), osg::StateAttribute::ON ); - } - else - { - // cull back face off - osg::ref_ptr cull_back_off = new osg::CullFace( osg::CullFace::BACK ); - stateset->setAttributeAndModes( cull_back_off.get(), osg::StateAttribute::OFF ); - - // cull front face on - osg::ref_ptr cull_front_on = new osg::CullFace( osg::CullFace::FRONT ); - stateset->setAttributeAndModes( cull_front_on.get(), osg::StateAttribute::ON ); - } - } - else - { - if( back ) - { - // cull front face off - osg::ref_ptr cull_front_off = new osg::CullFace( osg::CullFace::FRONT ); - stateset->setAttributeAndModes( cull_front_off.get(), osg::StateAttribute::OFF ); - - // cull back face on - osg::ref_ptr cull_back_on = new osg::CullFace( osg::CullFace::BACK ); - stateset->setAttributeAndModes( cull_back_on.get(), osg::StateAttribute::ON ); - - } - else - { - // cull back and front off - osg::ref_ptr cull = new osg::CullFace( osg::CullFace::FRONT_AND_BACK ); - stateset->setAttributeAndModes( cull.get(), osg::StateAttribute::OFF ); - } - } - } - - inline bool hasTrianglesWithMaterial(osg::Node* node) - { - osg::ref_ptr mat; - osg::StateSet* stateset = node->getStateSet(); - if (stateset) - { - mat = dynamic_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - } - - osg::Geometry* geom = dynamic_cast(node); - if (geom) - { - for (unsigned int ii = 0; ii < geom->getNumPrimitiveSets(); ++ii) - { - osg::PrimitiveSet* prim = geom->getPrimitiveSet(ii); - - if (prim->getMode() == osg::PrimitiveSet::TRIANGLES || prim->getMode() == osg::PrimitiveSet::QUADS) - { - if (mat) - { - return true; - } - } - } - } - - osg::Group* group = dynamic_cast(node); - if (group) - { - for (unsigned int ii = 0; ii < group->getNumChildren(); ++ii) - { - osg::Node* child_node = group->getChild(ii); - if (hasTrianglesWithMaterial(child_node)) - { - return true; - } - } - } - return false; - } - - inline void setMaterialAlpha( osg::Node* node, float alpha, bool create_material_if_not_existing ) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - if( stateset ) - { - osg::ref_ptr mat = dynamic_cast( stateset->getAttribute( osg::StateAttribute::MATERIAL ) ); - if( mat ) - { - osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK); - if (ambient.a() > alpha) - { - mat->setAlpha(osg::Material::FRONT_AND_BACK, alpha); - } - } - else if( create_material_if_not_existing ) - { - osg::ref_ptr mat = new osg::Material(); - mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.8f, 0.83f, 0.84f, alpha)); - mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.8f, 0.83f, 0.84f, alpha)); - stateset->setAttribute(mat, osg::StateAttribute::ON); - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - } - } - - osg::Group* group = dynamic_cast( node ); - if( group ) - { - for( unsigned int ii = 0; ii < group->getNumChildren(); ++ii ) - { - osg::Node* child_node = group->getChild( ii ); - setMaterialAlpha( child_node, alpha, false ); - } - } - } - - inline void removeChildren( osg::Group* group ) - { - if( group ) - { - group->removeChildren( 0, group->getNumChildren() ); - } - } - - inline void clearAllChildNodes( osg::Group* group ) - { - int num_children = group->getNumChildren(); - for( int i = 0; i < num_children; ++i ) - { - osg::Node* node = group->getChild( i ); - osg::Group* child_group = dynamic_cast( node ); - if( child_group ) - { - osg::Geode* geode = dynamic_cast( child_group ); - if( geode ) - { - geode->removeDrawables( 0, geode->getNumDrawables() ); - - } - clearAllChildNodes( child_group ); - } - node->releaseGLObjects(); - if( node->getStateSet() ) - { - node->setStateSet( nullptr ); - } - } - group->removeChildren( 0, group->getNumChildren() ); - } - inline void removeDrawables( osg::Geode* geode ) - { - if( geode ) - { - geode->removeDrawables( 0, geode->getNumDrawables() ); - } - } - - inline void translateGroup( osg::Group* grp, const osg::Vec3d& trans, std::unordered_set& set_applied, double minTranslateLength ) - { - if (trans.length2() < minTranslateLength*minTranslateLength ) - { - return; - } - - osg::Vec3d currentTranslate = trans; - int num_children = grp->getNumChildren(); - for (int i = 0; i < num_children; ++i) - { - osg::Node* node = grp->getChild(i); - if (!node) - { - continue; - } - - osg::MatrixTransform* child_transform = dynamic_cast(node); - if (child_transform) - { - osg::Matrix matrix = child_transform->getMatrix(); - osg::Vec3d delta = trans - matrix.getTrans(); - - matrix.preMult(osg::Matrix::translate(trans)); - child_transform->setMatrix(matrix); - - osg::Vec3d matrix_translate2 = child_transform->getMatrix().getTrans(); - currentTranslate = osg::Vec3d(); - } - - osg::Group* child_group = dynamic_cast(node); - if (child_group) - { - translateGroup(child_group, currentTranslate, set_applied, minTranslateLength); - continue; - } - - osg::Geode* child_geode = dynamic_cast(node); - if (child_geode) - { - if (set_applied.find(child_geode) != set_applied.end()) - { - continue; - } - set_applied.insert(child_geode); - for (size_t ii_drawables = 0; ii_drawables < child_geode->getNumDrawables(); ++ii_drawables) - { - osg::Drawable* drawable = child_geode->getDrawable(ii_drawables); - - osg::Geometry* child_geometry = dynamic_cast(drawable); - if (!child_geometry) - { -#ifdef _DEBUG - std::cout << "!child_geometry" << std::endl; -#endif - continue; - } - osg::Array* vertices_array = child_geometry->getVertexArray(); - osg::Vec3Array* vertices_float = dynamic_cast(vertices_array); - - if (!vertices_float) - { -#ifdef _DEBUG - std::cout << "!vertices_float" << std::endl; -#endif - continue; - } - - for (osg::Vec3Array::iterator it_array = vertices_float->begin(); it_array != vertices_float->end(); ++it_array) - { - osg::Vec3f& vertex = (*it_array); - vertex = vertex + trans; - } - - vertices_float->dirty(); - child_geometry->dirtyBound(); - child_geometry->dirtyDisplayList(); - child_geode->dirtyBound(); - grp->dirtyBound(); - } - - continue; - } - - osg::Geometry* child_geometry = dynamic_cast(node); - if (child_geometry) - { - osg::Array* vertices_array = child_geometry->getVertexArray(); - osg::Vec3Array* vertices_float = dynamic_cast(vertices_array); - - if (!vertices_float) - { -#ifdef _DEBUG - std::cout << "!vertices_float" << std::endl; -#endif - continue; - } - - for (osg::Vec3Array::iterator it_array = vertices_float->begin(); it_array != vertices_float->end(); ++it_array) - { - osg::Vec3f& vertex = (*it_array); - vertex = vertex + trans; - } - - vertices_float->dirty(); - child_geometry->dirtyBound(); - child_geometry->dirtyDisplayList(); - continue; - } - -#ifdef _DEBUG - std::cout << __FUNC__ << ": unhandled node: " << node->className() << std::endl; -#endif - } - } -} +/* -*-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. +*/ + +#pragma once + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif + +namespace SceneGraphUtils +{ + inline bool inParentList(const std::string guid, const osg::Group* group) + { + if (!group) + { + return false; + } + + const osg::Group::ParentList& vec_parents = group->getParents(); + for (size_t ii = 0; ii < vec_parents.size(); ++ii) + { + const osg::Group* parent = vec_parents[ii]; + if (parent) + { + const std::string parent_name = parent->getName(); + if (parent_name.length() >= 22) + { + std::string parent_name_id = parent_name.substr(22); + if (parent_name_id == guid) + { + return true; + } + bool in_parent_list = inParentList(guid, parent); + if (in_parent_list) + { + return true; + } + } + } + } + return false; + } + inline bool inParentList( const int entity_id, const osg::Group* group ) + { + if( !group ) + { + return false; + } + + const osg::Group::ParentList& vec_parents = group->getParents(); + for( size_t ii = 0; ii < vec_parents.size(); ++ii ) + { + const osg::Group* parent = vec_parents[ii]; + if( parent ) + { + const std::string parent_name = parent->getName(); + if( parent_name.length() > 0 ) + { + if( parent_name.at( 0 ) == '#' ) + { + // extract entity id + std::string parent_name_id = parent_name.substr( 1 ); + size_t last_index = parent_name_id.find_first_not_of( "0123456789" ); + std::string id_str = parent_name_id.substr( 0, last_index ); + const int id = std::stoi( id_str.c_str() ); + if( id == entity_id ) + { + return true; + } + bool in_parent_list = inParentList( entity_id, parent ); + if( in_parent_list ) + { + return true; + } + } + } + } + } + return false; + } + inline osg::Vec3d computePolygonNormal( const osg::Vec3dArray* polygon ) + { + const int num_points = polygon->size(); + osg::Vec3d polygon_normal( 0, 0, 0 ); + for( int k = 0; k < num_points; ++k ) + { + const osg::Vec3d& vertex_current = polygon->at( k ); + const osg::Vec3d& vertex_next = polygon->at( (k + 1) % num_points ); + polygon_normal._v[0] += (vertex_current.y() - vertex_next.y())*(vertex_current.z() + vertex_next.z()); + polygon_normal._v[1] += (vertex_current.z() - vertex_next.z())*(vertex_current.x() + vertex_next.x()); + polygon_normal._v[2] += (vertex_current.x() - vertex_next.x())*(vertex_current.y() + vertex_next.y()); + } + polygon_normal.normalize(); + return polygon_normal; + } + inline osg::Vec3f computePolygonNormal( const osg::Vec3Array* polygon ) + { + const int num_points = polygon->size(); + osg::Vec3f polygon_normal( 0, 0, 0 ); + for( int k = 0; k < num_points; ++k ) + { + const osg::Vec3f& vertex_current = polygon->at( k ); + const osg::Vec3f& vertex_next = polygon->at( (k + 1) % num_points ); + polygon_normal._v[0] += (vertex_current.y() - vertex_next.y())*(vertex_current.z() + vertex_next.z()); + polygon_normal._v[1] += (vertex_current.z() - vertex_next.z())*(vertex_current.x() + vertex_next.x()); + polygon_normal._v[2] += (vertex_current.x() - vertex_next.x())*(vertex_current.y() + vertex_next.y()); + } + polygon_normal.normalize(); + return polygon_normal; + } + inline osg::ref_ptr createCoordinateAxes( double length ) + { + osg::ref_ptr geode = new osg::Geode(); + osg::ref_ptr stateset = geode->getOrCreateStateSet(); + stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); + float alpha = 0.5f; + + // positive axes + { + osg::ref_ptr geom = new osg::Geometry(); + geode->addDrawable( geom ); + + osg::ref_ptr vertices = new osg::Vec3Array(); + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( length, 0.0, 0.0 ) ); + + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( 0.0, length, 0.0 ) ); + + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( 0.0, 0.0, length ) ); + +#ifndef COORDINATE_AXES_NO_COLORS + osg::ref_ptr colors = new osg::Vec4Array(); + colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.8f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.8f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); + colors->setBinding( osg::Array::BIND_PER_VERTEX ); + + geom->setColorArray( colors ); + //geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); +#endif + geom->setVertexArray( vertices ); + geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, 6 ) ); + } + + // positive axes + { + osg::ref_ptr geom = new osg::Geometry(); + geode->addDrawable( geom ); + + osg::ref_ptr vertices = new osg::Vec3Array(); + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( -length, 0.0, 0.0 ) ); + + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( 0.0, -length, 0.0 ) ); + + vertices->push_back( osg::Vec3f( 0.0, 0.0, 0.0 ) ); + vertices->push_back( osg::Vec3f( 0.0, 0.0, -length ) ); + +#ifndef COORDINATE_AXES_NO_COLORS + osg::ref_ptr colors = new osg::Vec4Array(); + colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 1.f, 0.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 1.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 1.f, 0.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); + colors->push_back( osg::Vec4f( 0.f, 0.f, 1.f, alpha ) ); + colors->setBinding( osg::Array::BIND_PER_VERTEX ); + + geom->setColorArray( colors ); + //geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); +#endif + geom->setVertexArray( vertices ); + geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, 6 ) ); + + // make negative axed dotted + osg::ref_ptr stateset_negative = geom->getOrCreateStateSet(); + osg::ref_ptr linestipple = new osg::LineStipple(); + linestipple->setFactor( 2 ); + linestipple->setPattern( 0xAAAA ); + stateset_negative->setAttributeAndModes( linestipple, osg::StateAttribute::ON ); + } + + // x axis label + bool add_x_label = false; + if( add_x_label ) + { + osg::ref_ptr label_x = new osgText::Text(); + label_x->setFont( "ARIAL.TTF" ); + label_x->setAlignment( osgText::Text::RIGHT_TOP ); + label_x->setAxisAlignment( osgText::Text::SCREEN ); + label_x->setColor( osg::Vec4( 0.8f, 0.0f, 0.0f, 1.0f ) ); + label_x->setCharacterSize( 0.5f ); + label_x->setText( "x" ); + label_x->setPosition( osg::Vec3( 1, 0, 0 ) ); + label_x->setEnableDepthWrites( false ); + geode->addDrawable( label_x ); + } + + return geode; + } + inline osg::ref_ptr createCoordinateAxesArrows() + { + float cone_tip = 1.2f; + float cone_base = 1.f; + float cone_radius = 0.06f; + float cone_height = cone_tip - cone_base; + osg::ref_ptr group = new osg::Group(); + + { + // x + osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); + osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, -cone_base*0.5 ), cone_radius*0.2, cone_base ) ); + cone_drawable->setColor( osg::Vec4f( 0.8f, 0.0f, 0.0f, 0.7f ) ); + cyl_drawable->setColor( osg::Vec4f( 0.8f, 0.0f, 0.0f, 0.7f ) ); + osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::rotate( M_PI_2, osg::Vec3d( 0, 1, 0 ) )*osg::Matrix::translate( 1, 0, 0 ) ); + osg::ref_ptr geode = new osg::Geode(); + geode->addDrawable( cone_drawable ); + geode->addDrawable( cyl_drawable ); + mt1->addChild( geode ); + group->addChild( mt1 ); + + osg::ref_ptr material = new osg::Material(); + material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.7f, 0.f, 0.f, 0.7f ) ); + material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.7f, 0.f, 0.f, 0.7f ) ); + material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 1.f, 0.4f, 0.4f, 0.7f ) ); + material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); + cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + + } + + { + // y + osg::ref_ptr cone = new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ); + + osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); + osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, cone_base*0.5 ), cone_radius*0.2, cone_base ) ); + cone_drawable->setColor( osg::Vec4f( 0.f, 0.7f, 0.f, 0.7f ) ); + cyl_drawable->setColor( osg::Vec4f( 0.f, 0.7f, 0.f, 0.7f ) ); + + + osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) )*osg::Matrix::translate( 0, 1, 0 ) ); + osg::ref_ptr mt2 = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) )*osg::Matrix::translate( 0, 1 + cone_height, 0 ) ); + osg::ref_ptr mt_cyl = new osg::MatrixTransform( osg::Matrix::rotate( -M_PI_2, osg::Vec3d( 1, 0, 0 ) ) ); + osg::ref_ptr geode = new osg::Geode(); + geode->addDrawable( cone_drawable ); + + osg::ref_ptr geode_cyl = new osg::Geode(); + geode_cyl->addDrawable( cyl_drawable ); + + mt1->addChild( geode ); + mt2->addChild( geode ); + mt_cyl->addChild( geode_cyl ); + group->addChild( mt1 ); + group->addChild( mt2 ); + group->addChild( mt_cyl ); + + osg::ref_ptr material = new osg::Material(); + material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.0f, 0.7f, 0.f, 0.7f ) ); + material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.0f, 0.7f, 0.f, 0.7f ) ); + material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.4f, 1.f, 0.4f, 0.7f ) ); + material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); + cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + } + + { + // z + osg::ref_ptr cone_drawable = new osg::ShapeDrawable( new osg::Cone( osg::Vec3f( 0.f, 0.f, 0.f ), cone_radius, cone_height ) ); + osg::ref_ptr cyl_drawable = new osg::ShapeDrawable( new osg::Cylinder( osg::Vec3f( 0.f, 0.f, cone_base*0.5 ), cone_radius*0.2, cone_base ) ); + cone_drawable->setColor( osg::Vec4f( 0.0f, 0.0f, 0.8f, 0.7f ) ); + cyl_drawable->setColor( osg::Vec4f( 0.0f, 0.0f, 0.8f, 0.7f ) ); + + osg::ref_ptr mt1 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 ) ); + osg::ref_ptr mt2 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 + cone_height ) ); + osg::ref_ptr mt3 = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, 1 + cone_height * 2 ) ); + + osg::ref_ptr geode = new osg::Geode(); + geode->addDrawable( cone_drawable ); + osg::ref_ptr geode_cyl = new osg::Geode(); + geode_cyl->addDrawable( cyl_drawable ); + mt1->addChild( geode ); + mt2->addChild( geode ); + mt3->addChild( geode ); + group->addChild( mt1 ); + group->addChild( mt2 ); + group->addChild( mt3 ); + group->addChild( geode_cyl ); + + osg::Material* material = new osg::Material(); + material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.f, 0.f, 0.8f, 0.7f ) ); + material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.f, 0.f, 0.8f, 0.7f ) ); + material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.4f, 0.4f, 1.f, 0.7f ) ); + material->setShininess( osg::Material::FRONT_AND_BACK, 30.0 ); + cone_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + cyl_drawable->getOrCreateStateSet()->setAttribute( material, osg::StateAttribute::ON ); + } + return group; + } + inline osg::ref_ptr createCoordinateGrid() + { + osg::ref_ptr geode = new osg::Geode(); + osg::ref_ptr stateset = geode->getOrCreateStateSet(); + stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); + + { + osg::ref_ptr geom = new osg::Geometry(); + geode->addDrawable( geom ); + + osg::ref_ptr vertices = new osg::Vec3Array(); + + for( int i = 0; i <= 20; ++i ) + { + vertices->push_back( osg::Vec3f( -10, -10 + i, 0.0 ) ); + vertices->push_back( osg::Vec3f( 10, -10 + i, 0.0 ) ); + + vertices->push_back( osg::Vec3f( -10 + i, -10, 0.0 ) ); + vertices->push_back( osg::Vec3f( -10 + i, 10, 0.0 ) ); + } + + osg::ref_ptr colors = new osg::Vec4Array(); + colors->push_back( osg::Vec4f( 0.7f, 0.7f, 0.7f, 0.5f ) ); + geom->setColorArray( colors ); + colors->setBinding( osg::Array::BIND_OVERALL ); + //geom->setColorBinding( osg::Geometry::BIND_OVERALL ); + + geom->setVertexArray( vertices ); + geom->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, vertices->size() ) ); + } + + return geode; + } + inline void WireFrameModeOn( osg::StateSet* state ) + { + osg::ref_ptr polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); + if( !polygon_mode ) + { + polygon_mode = new osg::PolygonMode(); + state->setAttribute( polygon_mode ); + } + polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE ); + } + inline void disableWireFrameOnText( osg::Node* node ) + { + if( node == nullptr ) + { + return; + } + + osg::Group* grp = dynamic_cast( node ); + if( grp ) + { + for( size_t i = 0; i < grp->getNumChildren(); ++i ) + { + osg::Node* child = grp->getChild( i ); + disableWireFrameOnText( child ); + } + } + else + { + osg::Geode* geode = dynamic_cast( node ); + if( geode ) + { + for( size_t i = 0; i < geode->getNumDrawables(); ++i ) + { + osg::Drawable* child = geode->getDrawable( i ); + + osgText::Text* txt = dynamic_cast( child ); + if( txt ) + { + osg::StateSet* state = txt->getOrCreateStateSet(); + + osg::PolygonMode* polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); + + if( !polygon_mode ) + { + polygon_mode = new osg::PolygonMode(); + state->setAttribute( polygon_mode ); + } + polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ); + } + } + } + } + } + inline void WireFrameModeOn( osg::Node* node ) + { + if( node == nullptr ) + { + return; + } + + osg::StateSet* state = node->getOrCreateStateSet(); + WireFrameModeOn( state ); + disableWireFrameOnText( node ); + } + inline void WireFrameModeOff( osg::StateSet* state ) + { + osg::ref_ptr polygon_mode = dynamic_cast( state->getAttribute( osg::StateAttribute::POLYGONMODE ) ); + + if( !polygon_mode ) + { + polygon_mode = new osg::PolygonMode(); + state->setAttribute( polygon_mode ); + } + polygon_mode->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL ); + } + inline void WireFrameModeOff( osg::Node *node ) + { + if( node == nullptr ) + { + return; + } + + osg::StateSet *state = node->getOrCreateStateSet(); + WireFrameModeOff( state ); + } + inline void HiddenLineModeOn( osg::Group* node ) + { + return; + osg::ref_ptr ss = node->getOrCreateStateSet(); + ss->setAttributeAndModes( new osg::ColorMask( false, false, false, false ), osg::StateAttribute::ON ); + ss->setBinName( "RenderBin" ); + ss->setBinNumber( 1 ); + ss->setRenderBinDetails( 1, "RenderBin" ); + + osg::ref_ptr node_lines = new osg::Group(); + // TODO: create lines + + ss = node_lines->getOrCreateStateSet(); + ss->setBinName( "RenderBin" ); + ss->setBinNumber( 2 ); + ss->setRenderBinDetails( 2, "RenderBin" ); + ss->setMode( GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON ); + osg::ref_ptr po = new osg::PolygonOffset( 10.0f, 10.0f ); + ss->setAttributeAndModes( po, osg::StateAttribute::ON ); + } + inline void cullFrontBack( bool front, bool back, osg::StateSet* stateset ) + { + if( front ) + { + if( back ) + { + // cull back and front + osg::ref_ptr cull = new osg::CullFace( osg::CullFace::FRONT_AND_BACK ); + stateset->setAttributeAndModes( cull.get(), osg::StateAttribute::ON ); + } + else + { + // cull back face off + osg::ref_ptr cull_back_off = new osg::CullFace( osg::CullFace::BACK ); + stateset->setAttributeAndModes( cull_back_off.get(), osg::StateAttribute::OFF ); + + // cull front face on + osg::ref_ptr cull_front_on = new osg::CullFace( osg::CullFace::FRONT ); + stateset->setAttributeAndModes( cull_front_on.get(), osg::StateAttribute::ON ); + } + } + else + { + if( back ) + { + // cull front face off + osg::ref_ptr cull_front_off = new osg::CullFace( osg::CullFace::FRONT ); + stateset->setAttributeAndModes( cull_front_off.get(), osg::StateAttribute::OFF ); + + // cull back face on + osg::ref_ptr cull_back_on = new osg::CullFace( osg::CullFace::BACK ); + stateset->setAttributeAndModes( cull_back_on.get(), osg::StateAttribute::ON ); + + } + else + { + // cull back and front off + osg::ref_ptr cull = new osg::CullFace( osg::CullFace::FRONT_AND_BACK ); + stateset->setAttributeAndModes( cull.get(), osg::StateAttribute::OFF ); + } + } + } + + inline bool hasTrianglesWithMaterial(osg::Node* node) + { + osg::ref_ptr mat; + osg::StateSet* stateset = node->getStateSet(); + if (stateset) + { + mat = dynamic_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); + } + + osg::Geometry* geom = dynamic_cast(node); + if (geom) + { + for (unsigned int ii = 0; ii < geom->getNumPrimitiveSets(); ++ii) + { + osg::PrimitiveSet* prim = geom->getPrimitiveSet(ii); + + if (prim->getMode() == osg::PrimitiveSet::TRIANGLES || prim->getMode() == osg::PrimitiveSet::QUADS) + { + if (mat) + { + return true; + } + } + } + } + + osg::Group* group = dynamic_cast(node); + if (group) + { + for (unsigned int ii = 0; ii < group->getNumChildren(); ++ii) + { + osg::Node* child_node = group->getChild(ii); + if (hasTrianglesWithMaterial(child_node)) + { + return true; + } + } + } + return false; + } + + inline void setMaterialAlpha( osg::Node* node, float alpha, bool create_material_if_not_existing ) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + if( stateset ) + { + osg::ref_ptr mat = dynamic_cast( stateset->getAttribute( osg::StateAttribute::MATERIAL ) ); + if( mat ) + { + osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK); + if (ambient.a() > alpha) + { + mat->setAlpha(osg::Material::FRONT_AND_BACK, alpha); + } + } + else if( create_material_if_not_existing ) + { + osg::ref_ptr mat = new osg::Material(); + mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.8f, 0.83f, 0.84f, alpha)); + mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.8f, 0.83f, 0.84f, alpha)); + stateset->setAttribute(mat, osg::StateAttribute::ON); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + } + + osg::Group* group = dynamic_cast( node ); + if( group ) + { + for( unsigned int ii = 0; ii < group->getNumChildren(); ++ii ) + { + osg::Node* child_node = group->getChild( ii ); + setMaterialAlpha( child_node, alpha, false ); + } + } + } + + inline void removeChildren( osg::Group* group ) + { + if( group ) + { + group->removeChildren( 0, group->getNumChildren() ); + } + } + + inline void clearAllChildNodes( osg::Group* group ) + { + int num_children = group->getNumChildren(); + for( int i = 0; i < num_children; ++i ) + { + osg::Node* node = group->getChild( i ); + osg::Group* child_group = dynamic_cast( node ); + if( child_group ) + { + osg::Geode* geode = dynamic_cast( child_group ); + if( geode ) + { + geode->removeDrawables( 0, geode->getNumDrawables() ); + + } + clearAllChildNodes( child_group ); + } + node->releaseGLObjects(); + if( node->getStateSet() ) + { + node->setStateSet( nullptr ); + } + } + group->removeChildren( 0, group->getNumChildren() ); + } + inline void removeDrawables( osg::Geode* geode ) + { + if( geode ) + { + geode->removeDrawables( 0, geode->getNumDrawables() ); + } + } + + inline void translateGroup( osg::Group* grp, const osg::Vec3d& trans, std::unordered_set& set_applied, double minTranslateLength ) + { + if (trans.length2() < minTranslateLength*minTranslateLength ) + { + return; + } + + osg::Vec3d currentTranslate = trans; + int num_children = grp->getNumChildren(); + for (int i = 0; i < num_children; ++i) + { + osg::Node* node = grp->getChild(i); + if (!node) + { + continue; + } + + osg::MatrixTransform* child_transform = dynamic_cast(node); + if (child_transform) + { + osg::Matrix matrix = child_transform->getMatrix(); + osg::Vec3d delta = trans - matrix.getTrans(); + + matrix.preMult(osg::Matrix::translate(trans)); + child_transform->setMatrix(matrix); + + osg::Vec3d matrix_translate2 = child_transform->getMatrix().getTrans(); + currentTranslate = osg::Vec3d(); + } + + osg::Group* child_group = dynamic_cast(node); + if (child_group) + { + translateGroup(child_group, currentTranslate, set_applied, minTranslateLength); + continue; + } + + osg::Geode* child_geode = dynamic_cast(node); + if (child_geode) + { + if (set_applied.find(child_geode) != set_applied.end()) + { + continue; + } + set_applied.insert(child_geode); + for (size_t ii_drawables = 0; ii_drawables < child_geode->getNumDrawables(); ++ii_drawables) + { + osg::Drawable* drawable = child_geode->getDrawable(ii_drawables); + + osg::Geometry* child_geometry = dynamic_cast(drawable); + if (!child_geometry) + { +#ifdef _DEBUG + std::cout << "!child_geometry" << std::endl; +#endif + continue; + } + osg::Array* vertices_array = child_geometry->getVertexArray(); + osg::Vec3Array* vertices_float = dynamic_cast(vertices_array); + + if (!vertices_float) + { +#ifdef _DEBUG + std::cout << "!vertices_float" << std::endl; +#endif + continue; + } + + for (osg::Vec3Array::iterator it_array = vertices_float->begin(); it_array != vertices_float->end(); ++it_array) + { + osg::Vec3f& vertex = (*it_array); + vertex = vertex + trans; + } + + vertices_float->dirty(); + child_geometry->dirtyBound(); + child_geometry->dirtyDisplayList(); + child_geode->dirtyBound(); + grp->dirtyBound(); + } + + continue; + } + + osg::Geometry* child_geometry = dynamic_cast(node); + if (child_geometry) + { + osg::Array* vertices_array = child_geometry->getVertexArray(); + osg::Vec3Array* vertices_float = dynamic_cast(vertices_array); + + if (!vertices_float) + { +#ifdef _DEBUG + std::cout << "!vertices_float" << std::endl; +#endif + continue; + } + + for (osg::Vec3Array::iterator it_array = vertices_float->begin(); it_array != vertices_float->end(); ++it_array) + { + osg::Vec3f& vertex = (*it_array); + vertex = vertex + trans; + } + + vertices_float->dirty(); + child_geometry->dirtyBound(); + child_geometry->dirtyDisplayList(); + continue; + } + +#ifdef _DEBUG + std::cout << __FUNC__ << ": unhandled node: " << node->className() << std::endl; +#endif + } + } +} diff --git a/IfcPlusPlus/src/ifcpp/geometry/SolidModelConverter.h b/IfcPlusPlus/src/ifcpp/geometry/SolidModelConverter.h index 0d7f9ae24..e5fce8dd7 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/SolidModelConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/SolidModelConverter.h @@ -118,7 +118,10 @@ class SolidModelConverter : public StatusCallback { convertIfcExtrudedAreaSolid( extruded_area, item_data_solid ); item_data->addItemData( item_data_solid ); - item_data->applyTransformToItem( swept_area_pos->m_matrix, eps, false); + if (swept_area_pos) + { + item_data->applyTransformToItem(swept_area_pos->m_matrix, eps, false); + } return; } @@ -830,6 +833,12 @@ class SolidModelConverter : public StatusCallback shared_ptr second_operand_data( new ItemShapeData() ); convertIfcBooleanOperand( ifc_second_operand, second_operand_data, first_operand_data ); + //glm::vec4 color(0.5, 0.5, 0.5, 1.); + //GeomDebugDump::dumpItemShapeInputData(first_operand_data, color); + //GeomDebugDump::dumpItemShapeInputData(second_operand_data, color); + //GeomDebugDump::moveOffset(1); + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) int tag = -1; std::string className = IFC4X3::EntityFactory::getStringForClassID(ifc_second_operand->classID()); if (dynamic_pointer_cast(ifc_second_operand)) @@ -844,6 +853,7 @@ class SolidModelConverter : public StatusCallback { tag = dynamic_pointer_cast(ifc_second_operand)->m_tag; } +#endif // for every first operand polyhedrons, apply all second operand polyhedrons std::vector > >& vec_first_operand_meshsets = first_operand_data->m_meshsets; @@ -868,6 +878,8 @@ class SolidModelConverter : public StatusCallback // copy also styles from operands, if any std::copy(first_operand_data->m_vec_styles.begin(), first_operand_data->m_vec_styles.end(), std::back_inserter(item_data->m_vec_styles)); + //GeomDebugDump::dumpItemShapeInputData(first_operand_data, color); + shared_ptr boolean_clipping_result = dynamic_pointer_cast( bool_result ); if( boolean_clipping_result ) { @@ -1214,16 +1226,23 @@ class SolidModelConverter : public StatusCallback carve::geom::plane<3> base_surface_plane; vec3 base_surface_position; shared_ptr base_position_transform; + shared_ptr base_position_rotate; if( base_surface_pos ) { m_curve_converter->getPlacementConverter()->getPlane( base_surface_pos, base_surface_plane, base_surface_position ); m_curve_converter->getPlacementConverter()->convertIfcAxis2Placement3D( base_surface_pos, base_position_transform ); + m_curve_converter->getPlacementConverter()->convertIfcAxis2Placement3D(base_surface_pos, base_position_rotate, true); } carve::math::Matrix base_position_matrix = carve::math::Matrix::IDENT(); if( base_position_transform ) { base_position_matrix = base_position_transform->m_matrix; } + carve::math::Matrix base_rotate_matrix = carve::math::Matrix::IDENT(); + if (base_position_rotate) + { + base_rotate_matrix = base_position_rotate->m_matrix; + } // If the agreement flag is TRUE, then the subset is the one the normal points away from bool agreement = half_space_solid->m_AgreementFlag->m_value; @@ -1298,15 +1317,10 @@ class SolidModelConverter : public StatusCallback if( other_operand ) { std::set setVisited; - other_operand->computeItemBoundingBox(bbox_other_operand, setVisited); + carve::math::Matrix matrixIdentity; + other_operand->computeItemBoundingBox(bbox_other_operand, /*matrixIdentity,*/ setVisited); extrusion_extent = bbox_other_operand.extent; - //double max_extent = std::max( aabb_extent.x, std::max( aabb_extent.y, aabb_extent.z ) ); - //if (std::abs(max_extent) > eps) - //{ - // extrusion_depth = 2.1 * max_extent; - //} - extrusion_depth = 2.1 * extrusion_extent.z; } @@ -1458,31 +1472,29 @@ class SolidModelConverter : public StatusCallback } } - - int var = 0; - if (var == 0) { - shared_ptr surface_item_data(new ItemShapeData()); - shared_ptr surface_proxy; - m_face_converter->convertIfcSurface(base_surface, surface_item_data, surface_proxy, extrusion_depth); - if (surface_item_data->m_polylines.size() > 0) + // project center of other operand onto plane, create box there + vec3 v; + vec3 poly_point = bbox_other_operand.pos; + const vec3& base_surface_normal = base_surface_plane.N; + double t; + carve::IntersectionClass intersect = carve::geom3d::rayPlaneIntersection(base_surface_plane, poly_point, poly_point + base_surface_normal, v, t, eps); + if (intersect > 0) { - shared_ptr& surface_data = surface_item_data->m_polylines[0]; - std::vector base_surface_points = surface_data->points; - - if (base_surface_points.size() != 4) - { - messageCallback("invalid IfcHalfSpaceSolid.BaseSurface", StatusCallback::MESSAGE_TYPE_ERROR, __FUNC__, polygonal_half_space.get()); - return; - } - - // If the agreement flag is TRUE, then the subset is the one the normal points away from - bool agreement = half_space_solid->m_AgreementFlag->m_value; - if (!agreement) + vec3 localZ = carve::geom::VECTOR(0, 0, 1); + localZ = base_rotate_matrix * localZ; + double dotProduct = dot(localZ, base_surface_normal); + + vec3 posX = carve::geom::VECTOR(extrusion_depth, 0, 0); + posX = base_rotate_matrix * posX; + vec3 posY = carve::geom::VECTOR(0, extrusion_depth, 0); + posY = base_rotate_matrix * posY; + + std::vector base_surface_points = { v + posX + posY, v - posX + posY, v - posX - posY, v + posX - posY }; + if (dotProduct < 0) { std::reverse(base_surface_points.begin(), base_surface_points.end()); } - vec3 base_surface_normal = GeomUtils::computePolygonNormal(base_surface_points, eps); vec3 half_space_extrusion_direction = -base_surface_normal; vec3 half_space_extrusion_vector = half_space_extrusion_direction * extrusion_depth; shared_ptr half_space_box_data(new carve::input::PolyhedronData()); @@ -1490,29 +1502,7 @@ class SolidModelConverter : public StatusCallback item_data->addOpenOrClosedPolyhedron(half_space_box_data, params); } } - else if (var == 1) - { - std::vector box_base_points; - box_base_points.push_back(base_position_matrix * carve::geom::VECTOR(extrusion_depth, extrusion_depth, 0.0)); - box_base_points.push_back(base_position_matrix * carve::geom::VECTOR(-extrusion_depth, extrusion_depth, 0.0)); - box_base_points.push_back(base_position_matrix * carve::geom::VECTOR(-extrusion_depth, -extrusion_depth, 0.0)); - box_base_points.push_back(base_position_matrix * carve::geom::VECTOR(extrusion_depth, -extrusion_depth, 0.0)); - - vec3 half_space_extrusion_direction = -base_surface_plane.N; - vec3 half_space_extrusion_vector = half_space_extrusion_direction * extrusion_depth; - - vec3 box_base_normal = GeomUtils::computePolygonNormal(box_base_points, eps); - double dot_normal = dot(box_base_normal, base_surface_plane.N); - if (dot_normal > 0) - { - std::reverse(box_base_points.begin(), box_base_points.end()); - } - - shared_ptr half_space_box_data(new carve::input::PolyhedronData()); - extrudeBox(box_base_points, half_space_extrusion_vector, half_space_box_data); - item_data->addOpenOrClosedPolyhedron(half_space_box_data, params); - } - + return; } } diff --git a/IfcPlusPlus/src/ifcpp/geometry/StylesConverter.h b/IfcPlusPlus/src/ifcpp/geometry/StylesConverter.h index 04b67836d..a7db3740b 100644 --- a/IfcPlusPlus/src/ifcpp/geometry/StylesConverter.h +++ b/IfcPlusPlus/src/ifcpp/geometry/StylesConverter.h @@ -1,848 +1,851 @@ -/* -*-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. -*/ - -#pragma once - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace IFC4X3; - -class StylesConverter : public StatusCallback -{ -protected: - std::map > m_map_ifc_styles; - std::mutex m_writelock_styles_converter; - std::mutex m_mutexSearch; - -public: - StylesConverter() - { - } - virtual ~StylesConverter() - { - } - - void clearStylesCache() - { - m_map_ifc_styles.clear(); - } - - static void convertIfcSpecularHighlightSelect(shared_ptr highlight_select, shared_ptr& style_data) - { - if (dynamic_pointer_cast(highlight_select)) - { - shared_ptr spec = dynamic_pointer_cast(highlight_select); - style_data->m_specular_exponent = spec->m_value; - } - else if (dynamic_pointer_cast(highlight_select)) - { - shared_ptr specular_roughness = dynamic_pointer_cast(highlight_select); - style_data->m_specular_roughness = specular_roughness->m_value; - } - } - - static void convertIfcColourRgb(shared_ptr color_rgb, glm::vec4& color) - { - if (color_rgb->m_Red) - { - color.x = (float)color_rgb->m_Red->m_value; - } - if (color_rgb->m_Green) - { - color.y = (float)color_rgb->m_Green->m_value; - } - if (color_rgb->m_Blue) - { - color.z = (float)color_rgb->m_Blue->m_value; - } - } - - static void convertIfcColourOrFactor(shared_ptr color_or_factor, glm::vec4& src_color, glm::vec4& target_color) - { - // TYPE IfcColourOrFactor = SELECT ( IfcNormalisedRatioMeasure, IfcColourRgb); - shared_ptr color_rgb = dynamic_pointer_cast(color_or_factor); - if (color_rgb) - { - convertIfcColourRgb(color_rgb, target_color); - return; - } - - shared_ptr ratio_measure = dynamic_pointer_cast(color_or_factor); - if (ratio_measure) - { - float factor = (float)ratio_measure->m_value; - target_color = glm::vec4(src_color.x * factor, src_color.y * factor, src_color.z * factor, src_color.w); - return; - } - } - - static void convertIfcColour(shared_ptr ifc_color_select, glm::vec4& color) - { - // IfcColour = SELECT ( IfcColourSpecification, IfcPreDefinedColour ); - shared_ptr color_spec = dynamic_pointer_cast(ifc_color_select); - if (color_spec) - { - // ENTITY IfcColourSpecification ABSTRACT SUPERTYPE OF(IfcColourRgb); - shared_ptr color_rgb = dynamic_pointer_cast(color_spec); - if (color_rgb) - { - convertIfcColourRgb(color_rgb, color); - } - return; - } - - shared_ptr predefined_color = dynamic_pointer_cast(ifc_color_select); - if (predefined_color) - { - // ENTITY IfcPreDefinedColour ABSTRACT SUPERTYPE OF(IfcDraughtingPreDefinedColour) - shared_ptr draughting_predefined_color = dynamic_pointer_cast(predefined_color); - if (draughting_predefined_color) - { - if (draughting_predefined_color->m_Name) - { - std::string predefined_name = draughting_predefined_color->m_Name->m_value; - if (std_iequal(predefined_name, "black")) color = glm::vec4(0.0, 0.0, 0.0, 1.0); - else if (std_iequal(predefined_name, "red")) color = glm::vec4(1.0, 0.0, 0.0, 1.0); - else if (std_iequal(predefined_name, "green")) color = glm::vec4(0.0, 1.0, 0.0, 1.0); - else if (std_iequal(predefined_name, "blue")) color = glm::vec4(0.0, 0.0, 1.0, 1.0); - else if (std_iequal(predefined_name, "yellow")) color = glm::vec4(1.0, 1.0, 0.0, 1.0); - else if (std_iequal(predefined_name, "magenta")) color = glm::vec4(1.0, 0.0, 1.0, 1.0); - else if (std_iequal(predefined_name, "cyan")) color = glm::vec4(0.0, 1.0, 1.0, 1.0); - else if (std_iequal(predefined_name, "white")) color = glm::vec4(1.0, 1.0, 1.0, 1.0); - } - } - return; - } - } - - static void convertIfcSurfaceStyleShading(shared_ptr surface_style_shading, shared_ptr& style_data) - { - if (surface_style_shading) - { - glm::vec4 surface_color(0.8, 0.82, 0.84, 1.0); - if (surface_style_shading->m_SurfaceColour) - { - shared_ptr surf_color = surface_style_shading->m_SurfaceColour; - convertIfcColourRgb(surf_color, surface_color); - } - - if (surface_color.x < 0.05 && surface_color.y < 0.05 && surface_color.z < 0.05) - { - surface_color = glm::vec4(0.11, 0.12, 0.13, surface_color.w); - } - - glm::vec4 ambient_color(surface_color); - glm::vec4 diffuse_color(surface_color); - glm::vec4 specular_color(surface_color); - double shininess = 35.f; - double alpha = surface_color.w; // 1=opaque, 0=transparent - bool set_transparent = false; - - shared_ptr surf_style_rendering = dynamic_pointer_cast(surface_style_shading); - if (surf_style_rendering) - { - if (surf_style_rendering->m_DiffuseColour) - { - shared_ptr color_or_factor = surf_style_rendering->m_DiffuseColour; - convertIfcColourOrFactor(color_or_factor, surface_color, diffuse_color); - } - - if (surf_style_rendering->m_SpecularColour) - { - shared_ptr ifc_specular_color = surf_style_rendering->m_SpecularColour; - convertIfcColourOrFactor(ifc_specular_color, surface_color, specular_color); - } - - if (surf_style_rendering->m_Transparency) - { - // in IFC 1 is transparent, 0 is opaque. if not given, the value 0 (opaque) is assumed - // in osg, 1 is opaque, 0 is transparent - alpha = 1.f - (float)surf_style_rendering->m_Transparency->m_value; - if (alpha < 0.1f) - { - alpha = 0.1f; - } - - if (alpha > 1.f) - { - alpha = 1.f; - } - - if (alpha < 0.99f) - { - set_transparent = true; - } - } - - if (surf_style_rendering->m_SpecularHighlight) - { - shared_ptr spec_highlight = surf_style_rendering->m_SpecularHighlight; - convertIfcSpecularHighlightSelect(spec_highlight, style_data); - shininess = style_data->m_specular_roughness * 128; - if (shininess <= 1.0) - { - shininess = 1.0; - } - } - } - - if (alpha < 0.1f) - { - alpha = 0.1f; - } - - style_data->m_color_ambient = glm::vec4(ambient_color.x * 0.8, ambient_color.y * 0.8, ambient_color.z * 0.8, alpha); - style_data->m_color_diffuse = glm::vec4(diffuse_color.x, diffuse_color.y, diffuse_color.z, alpha); - style_data->m_color_specular = glm::vec4(specular_color.x * 0.1, specular_color.y * 0.1, specular_color.z * 0.1, alpha); - - style_data->m_shininess = shininess; - style_data->m_set_transparent = set_transparent; - style_data->m_transparency = alpha; - style_data->m_complete = true; - style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; - return; - } - } - - void convertIfcPresentationStyle(shared_ptr presentation_style, shared_ptr& style_data) - { - int style_id = presentation_style->m_tag; - { - - auto it_find_existing_style = m_map_ifc_styles.find(style_id); - if (it_find_existing_style != m_map_ifc_styles.end()) - { - // use existing style - style_data = it_find_existing_style->second; - if (style_data->m_complete) - { - return; - } - } - else - { - if (!style_data) - { - style_data = shared_ptr(new StyleData(style_id)); - } - - std::lock_guard lock(m_writelock_styles_converter); - m_map_ifc_styles[style_id] = style_data; - } - } - - // ENTITY IfcPresentationStyle ABSTRACT SUPERTYPE OF(ONEOF(IfcCurveStyle, IfcFillAreaStyle, IfcSurfaceStyle, IfcSymbolStyle, IfcTextStyle)); - shared_ptr curve_style = dynamic_pointer_cast(presentation_style); - if (curve_style) - { - convertIfcCurveStyle(curve_style, style_data); - return; - } - - shared_ptr fill_area_style = dynamic_pointer_cast(presentation_style); - if (fill_area_style) - { - //std::vector > m_FillStyles; - //shared_ptr m_ModelOrDraughting; //optional - - style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; - - for (shared_ptr& fillStyle : fill_area_style->m_FillStyles) - { - // TYPE IfcFillStyleSelect = SELECT ( IfcFillAreaStyleHatching, IfcFillAreaStyleTiles, IfcExternallyDefinedHatchStyle, IfcColour); - - shared_ptr hatching = dynamic_pointer_cast(fillStyle); - if (hatching) - { - // attributes: - //shared_ptr m_HatchLineAppearance; - //shared_ptr m_StartOfNextHatchLine; - //shared_ptr m_PointOfReferenceHatchLine; //optional - //shared_ptr m_PatternStart; //optional - //shared_ptr m_HatchLineAngle; - - if (hatching->m_HatchLineAppearance) - { - convertIfcCurveStyle(hatching->m_HatchLineAppearance, style_data); - } - continue; - } - - shared_ptr tiles = dynamic_pointer_cast(fillStyle); - if (tiles) - { -#ifdef _DEBUG - std::cout << "IfcFillAreaStyleTiles not implemented" << std::endl; -#endif - continue; - } - - shared_ptr externalStyle = dynamic_pointer_cast(fillStyle); - if (externalStyle) - { -#ifdef _DEBUG - std::cout << "IfcExternallyDefinedHatchStyle not implemented" << std::endl; -#endif - continue; - } - - shared_ptr AreaColour = dynamic_pointer_cast(fillStyle); - if (AreaColour) - { - glm::vec4 color(0.2, 0.25, 0.3, 1.0); - convertIfcColour(AreaColour, color); - - if (color.x < 0.05 && color.y < 0.05 && color.z < 0.05) - { - color = glm::vec4(0.1, 0.125, 0.15, color.w); - } - - double shininess = 35.0; - style_data->m_color_ambient = glm::vec4(color.x * 0.8, color.y * 0.8, color.z * 0.8, color.w); - style_data->m_color_diffuse = glm::vec4(color.x, color.y, color.z, color.w); - style_data->m_color_specular = glm::vec4(color.x * 0.1, color.y * 0.1, color.z * 0.1, color.w); - style_data->m_shininess = shininess; - style_data->m_set_transparent = false; - style_data->m_complete = true; - continue; - } - } - - return; - } - - shared_ptr surface_style = dynamic_pointer_cast(presentation_style); - if (surface_style) - { - convertIfcSurfaceStyle(surface_style, style_data); - return; - } - - shared_ptr text_style = dynamic_pointer_cast(presentation_style); - if (text_style) - { - style_data->m_text_style = text_style; - style_data->m_complete = true; - return; - } - - return; - } - - void convertIfcCurveStyle(shared_ptr curve_style, shared_ptr& style_data) - { - if (!curve_style) - { - return; - } - int style_id = curve_style->m_tag; -#if ENABLE_STYLE_CACHING - auto it_find_existing_style = m_map_ifc_styles.find(style_id); - if (it_find_existing_style != m_map_ifc_styles.end()) - { - std::lock_guard lock(m_mutexSearch); - auto it_find_existing_style = m_map_ifc_styles.find(style_id); - style_data = it_find_existing_style->second; - if (style_data->m_complete) - { - return; - } - } - else -#endif - { - if (!style_data) - { - style_data = shared_ptr(new StyleData(style_id)); - } - - std::lock_guard lock(m_writelock_styles_converter); - m_map_ifc_styles[style_id] = style_data; - style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_CURVE; - } - - - //CurveFont : OPTIONAL IfcCurveFontOrScaledCurveFontSelect; - //CurveWidth : OPTIONAL IfcSizeSelect; - //CurveColour : OPTIONAL IfcColour; - - shared_ptr curve_color = curve_style->m_CurveColour; - if (curve_color) - { - glm::vec4 color(0.2, 0.25, 0.3, 1.0); - convertIfcColour(curve_color, color); - - if (color.x < 0.05 && color.y < 0.05 && color.z < 0.05) - { - color = glm::vec4(0.1, 0.125, 0.15, color.w); - } - - double shininess = 35.0; - style_data->m_color_ambient = glm::vec4(color.x, color.y, color.z, color.w); - style_data->m_color_diffuse = glm::vec4(color.x, color.y, color.z, color.w); - style_data->m_color_specular = glm::vec4(color.x * 0.1, color.y * 0.1, color.z * 0.1, color.w); - style_data->m_shininess = shininess; - style_data->m_set_transparent = false; - style_data->m_complete = true; - } - } - - void convertIfcMaterial(const shared_ptr& mat, std::vector >& vec_style_data) - { - if (!mat) - { - return; - } - - // IfcMaterialDefinition ----------------------------------------------------------- - // inverse attributes: - // std::vector > m_AssociatedTo_inverse; - // std::vector > m_HasExternalReferences_inverse; - // std::vector > m_HasProperties_inverse; - - // IfcMaterial ----------------------------------------------------------- - // attributes: - //shared_ptr& Name = mat->m_Name; - ////shared_ptr m_Description; //optional - ////shared_ptr m_Category; //optional - //// inverse attributes: - //std::vector > m_HasRepresentation_inverse; - //std::vector > m_IsRelatedWith_inverse; - //std::vector > m_RelatesTo_inverse; - - - for (size_t kk = 0; kk < mat->m_IsRelatedWith_inverse.size(); ++kk) - { - const weak_ptr& mat_is_related_weak = mat->m_IsRelatedWith_inverse[kk]; - - if (mat_is_related_weak.expired()) - { - continue; - } - - shared_ptr mat_is_related(mat_is_related_weak); - if (!mat_is_related) - { - continue; - } - } - - for (size_t kk = 0; kk < mat->m_HasRepresentation_inverse.size(); ++kk) - { - const weak_ptr& mat_def_representation_weak = mat->m_HasRepresentation_inverse[kk]; - - if (mat_def_representation_weak.expired()) - { - continue; - } - - shared_ptr mat_def_representation(mat_def_representation_weak); - if (!mat_def_representation) - { - continue; - } - std::vector >& mat_representations = mat_def_representation->m_Representations; - - for (const shared_ptr& mat_rep : mat_representations) - { - if (!mat_rep) - { - continue; - } - shared_ptr mat_style = dynamic_pointer_cast(mat_rep); - if (!mat_style) - { - continue; - } - for (const shared_ptr& mat_rep_item : mat_style->m_Items) - { - if (!mat_rep_item) - { - continue; - } - - shared_ptr styled_item = dynamic_pointer_cast(mat_rep_item); - if (styled_item) - { - //std::vector > m_Styles; - for (const shared_ptr& presentationStyle : styled_item->m_Styles) - { - if (!presentationStyle) - { - continue; - } - - shared_ptr style_data; - convertIfcPresentationStyle(presentationStyle, style_data); - if (style_data) - { - vec_style_data.push_back(style_data); - } - - - // ENTITY IfcPresentationStyle ABSTRACT SUPERTYPE OF (ONEOF (IfcCurveStyle ,IfcFillAreaStyle ,IfcSurfaceStyle ,IfcTextStyle)); - //shared_ptr style_data; - - //shared_ptr curveStyle = dynamic_pointer_cast(style); - //if( curveStyle ) - //{ - // convertIfcCurveStyle( curve_style, style_data ); - // if( style_data ) - // { - // vec_style_data.push_back( style_data ); - // } - // continue; - //} - - // shared_ptr surface_style = dynamic_pointer_cast(style_select); - // if( !surface_style ) - // { - // continue; - // } - // for( const shared_ptr& surface_style_select : surface_style->m_Styles ) - // { - // if( !surface_style_select ) - // { - // continue; - // } - // shared_ptr surface_style_shading = dynamic_pointer_cast(surface_style_select); - // if( surface_style_shading ) - // { - // const int style_id = surface_style->m_tag; - // shared_ptr style_data(new StyleData(style_id)); - // StylesConverter::convertIfcSurfaceStyleShading(surface_style_shading, style_data); - // vec_style_data.push_back(style_data); - // continue; - // } - // } - // } - //} - } - } - } - } - } - } - - void convertElementStyle(const shared_ptr& ifc_element, std::vector >& vec_style_data) - { - // handle assigned materials - std::vector >& vec_associates = ifc_element->m_HasAssociations_inverse; - - for (size_t ii = 0; ii < vec_associates.size(); ++ii) - { - shared_ptr rel_associates(vec_associates[ii]); - shared_ptr associated_material = dynamic_pointer_cast(rel_associates); - if (!associated_material) - { - continue; - } - - // TYPE IfcMaterialSelect = SELECT (IfcMaterialDefinition ,IfcMaterialList ,IfcMaterialUsageDefinition); - shared_ptr relatingMaterial = associated_material->m_RelatingMaterial; - - if (!relatingMaterial) - { - continue; - } - - shared_ptr material = dynamic_pointer_cast(relatingMaterial); - if (material) - { - convertIfcMaterial(material, vec_style_data); - } - - shared_ptr materialList = dynamic_pointer_cast(relatingMaterial); - if (materialList) - { - - for (size_t jj = 0; jj < materialList->m_Materials.size(); ++jj) - { - const shared_ptr& mat = materialList->m_Materials[jj]; - convertIfcMaterial(mat, vec_style_data); - } - - continue; - } - - shared_ptr material_layer_set_usage = dynamic_pointer_cast(relatingMaterial); - if (material_layer_set_usage) - { - if (!material_layer_set_usage->m_ForLayerSet) - { - continue; - } - - for (size_t jj = 0; jj < material_layer_set_usage->m_ForLayerSet->m_MaterialLayers.size(); ++jj) - { - const shared_ptr& material_layer = material_layer_set_usage->m_ForLayerSet->m_MaterialLayers[jj]; - if (!material_layer) - { - continue; - } - - const shared_ptr& mat = material_layer->m_Material; //optional - convertIfcMaterial(mat, vec_style_data); - } - } - } - } - - void convertIfcSurfaceStyle(shared_ptr surface_style, shared_ptr& style_data) - { - if (!surface_style) - { - return; - } - const int style_id = surface_style->m_tag; - - { - std::lock_guard lock(m_writelock_styles_converter); - auto it_find_existing_style = m_map_ifc_styles.find(style_id); - if (it_find_existing_style != m_map_ifc_styles.end()) - { - // todo: check if style compare is faster here - style_data = it_find_existing_style->second; - if (style_data->m_complete) - { - return; - } - } - else - { - if (!style_data) - { - style_data = shared_ptr(new StyleData(style_id)); - } - - m_map_ifc_styles[style_id] = style_data; - } - } - - style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; - - std::vector >& vec_styles = surface_style->m_Styles; - if (vec_styles.size() == 0) - { - return; - } - - for (size_t ii_styles = 0; ii_styles < vec_styles.size(); ++ii_styles) - { - shared_ptr surf_style_element_select = vec_styles[ii_styles]; - if (!surf_style_element_select) - { - continue; - } - // TYPE IfcSurfaceStyleElementSelect = SELECT (IfcExternallyDefinedSurfaceStyle ,IfcSurfaceStyleLighting ,IfcSurfaceStyleRefraction ,IfcSurfaceStyleShading ,IfcSurfaceStyleWithTextures); - shared_ptr surface_style_shading = dynamic_pointer_cast(surf_style_element_select); - if (surface_style_shading) - { - convertIfcSurfaceStyleShading(surface_style_shading, style_data); - continue; - } - - shared_ptr ext_surf_style = dynamic_pointer_cast(surf_style_element_select); - if (ext_surf_style) - { -#ifdef _DEBUG - std::cout << "IfcExternallyDefinedSurfaceStyle not implemented" << std::endl; -#endif - continue; - } - - shared_ptr style_lighting = dynamic_pointer_cast(surf_style_element_select); - if (style_lighting) - { -#ifdef _DEBUG - std::cout << "IfcSurfaceStyleLighting not implemented" << std::endl; -#endif - continue; - } - - shared_ptr style_refraction = dynamic_pointer_cast(surf_style_element_select); - if (style_refraction) - { -#ifdef _DEBUG - std::cout << "IfcSurfaceStyleRefraction not implemented" << std::endl; -#endif - continue; - } - - shared_ptr style_texture = dynamic_pointer_cast(surf_style_element_select); - if (style_texture) - { -#ifdef _DEBUG - std::cout << "IfcSurfaceStyleWithTextures not implemented" << std::endl; -#endif - continue; - } - } - } - - void convertRepresentationStyle(const shared_ptr& representation_item, std::vector >& vec_style_data) - { - std::vector >& vec_StyledByItem_inverse = representation_item->m_StyledByItem_inverse; - for (size_t i = 0; i < vec_StyledByItem_inverse.size(); ++i) - { - weak_ptr styled_item_weak = vec_StyledByItem_inverse[i]; - shared_ptr styled_item = shared_ptr(styled_item_weak); - convertIfcStyledItem(styled_item, vec_style_data); - } - } - - void convertIfcStyledItem(weak_ptr styled_item_weak, std::vector >& vec_style_data) - { - if (styled_item_weak.expired()) - { - return; - } - shared_ptr styled_item(styled_item_weak); - const int style_id = styled_item->m_tag; - - { - //std::lock_guard lock(m_writelock_styles_converter); - auto it_find_existing_style = m_map_ifc_styles.find(style_id); - if (it_find_existing_style != m_map_ifc_styles.end()) - { - vec_style_data.push_back(it_find_existing_style->second); - return; - } - } - - std::vector >& vec_style_assigns = styled_item->m_Styles; - for (size_t i_style_assign = 0; i_style_assign < vec_style_assigns.size(); ++i_style_assign) - { - shared_ptr presentationStyle = vec_style_assigns[i_style_assign]; - if (!presentationStyle) - { - continue; - } - - shared_ptr style_data; - convertIfcPresentationStyle(presentationStyle, style_data); - if (style_data) - { - vec_style_data.push_back(style_data); - } - continue; - } - } - - void convertIfcComplexPropertyColor(shared_ptr complex_property, glm::vec4& vec_color) - { - std::vector >& vec_HasProperties = complex_property->m_HasProperties; - if (!complex_property->m_UsageName) return; - if (vec_HasProperties.size() < 3) return; - std::string usage_name = complex_property->m_UsageName->m_value; - if (!std_iequal(usage_name.c_str(), "Color")) return; - - if (complex_property->m_HasProperties.size() > 2) - { - shared_ptr prop1 = dynamic_pointer_cast(complex_property->m_HasProperties[0]); - shared_ptr prop2 = dynamic_pointer_cast(complex_property->m_HasProperties[1]); - shared_ptr prop3 = dynamic_pointer_cast(complex_property->m_HasProperties[2]); - - if (prop1 && prop2 && prop3) - { - shared_ptr v1_select = prop1->m_NominalValue; - shared_ptr v2_select = prop2->m_NominalValue; - shared_ptr v3_select = prop3->m_NominalValue; - if (v1_select && v2_select && v3_select) - { - shared_ptr v1_int = dynamic_pointer_cast(v1_select); - shared_ptr v2_int = dynamic_pointer_cast(v2_select); - shared_ptr v3_int = dynamic_pointer_cast(v3_select); - - if (v1_int && v2_int && v3_int) - { - double r = v1_int->m_value / 255.0; - double g = v2_int->m_value / 255.0; - double b = v3_int->m_value / 255.0; - - if (r < 0.05 && g < 0.05 && b < 0.05) - { - r = 0.1; - g = 0.12; - b = 0.15; - } - vec_color.x = r; - vec_color.y = g; - vec_color.z = b; - vec_color.w = 1.0; - - //ScopedLock lock( m_writelock_styles_converter ); - //style_data->color_ambient.setColor( r, g, b, 1.f ); - //style_data->color_diffuse.setColor( r, g, b, 1.f ); - //style_data->color_specular.setColor( r, g, b, 1.f ); - //style_data->shininess = 35.f; - - //m_map_ifc_styles[complex_property_id] = style_data; - - return; - } - } - } - } - } -}; +/* -*-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. +*/ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace IFC4X3; + +class StylesConverter : public StatusCallback +{ +protected: + std::map > m_map_ifc_styles; + std::mutex m_writelock_styles_converter; + std::mutex m_mutexSearch; + +public: + StylesConverter() + { + } + virtual ~StylesConverter() + { + } + + void clearStylesCache() + { + m_map_ifc_styles.clear(); + } + + static void convertIfcSpecularHighlightSelect(shared_ptr highlight_select, shared_ptr& style_data) + { + if (dynamic_pointer_cast(highlight_select)) + { + shared_ptr spec = dynamic_pointer_cast(highlight_select); + style_data->m_specular_exponent = spec->m_value; + } + else if (dynamic_pointer_cast(highlight_select)) + { + shared_ptr specular_roughness = dynamic_pointer_cast(highlight_select); + style_data->m_specular_roughness = specular_roughness->m_value; + } + } + + static void convertIfcColourRgb(shared_ptr color_rgb, glm::vec4& color) + { + if (color_rgb->m_Red) + { + color.x = (float)color_rgb->m_Red->m_value; + } + if (color_rgb->m_Green) + { + color.y = (float)color_rgb->m_Green->m_value; + } + if (color_rgb->m_Blue) + { + color.z = (float)color_rgb->m_Blue->m_value; + } + } + + static void convertIfcColourOrFactor(shared_ptr color_or_factor, glm::vec4& src_color, glm::vec4& target_color) + { + // TYPE IfcColourOrFactor = SELECT ( IfcNormalisedRatioMeasure, IfcColourRgb); + shared_ptr color_rgb = dynamic_pointer_cast(color_or_factor); + if (color_rgb) + { + convertIfcColourRgb(color_rgb, target_color); + return; + } + + shared_ptr ratio_measure = dynamic_pointer_cast(color_or_factor); + if (ratio_measure) + { + float factor = (float)ratio_measure->m_value; + target_color = glm::vec4(src_color.x * factor, src_color.y * factor, src_color.z * factor, src_color.w); + return; + } + } + + static void convertIfcColour(shared_ptr ifc_color_select, glm::vec4& color) + { + // IfcColour = SELECT ( IfcColourSpecification, IfcPreDefinedColour ); + shared_ptr color_spec = dynamic_pointer_cast(ifc_color_select); + if (color_spec) + { + // ENTITY IfcColourSpecification ABSTRACT SUPERTYPE OF(IfcColourRgb); + shared_ptr color_rgb = dynamic_pointer_cast(color_spec); + if (color_rgb) + { + convertIfcColourRgb(color_rgb, color); + } + return; + } + + shared_ptr predefined_color = dynamic_pointer_cast(ifc_color_select); + if (predefined_color) + { + // ENTITY IfcPreDefinedColour ABSTRACT SUPERTYPE OF(IfcDraughtingPreDefinedColour) + shared_ptr draughting_predefined_color = dynamic_pointer_cast(predefined_color); + if (draughting_predefined_color) + { + if (draughting_predefined_color->m_Name) + { + std::string predefined_name = draughting_predefined_color->m_Name->m_value; + if (std_iequal(predefined_name, "black")) color = glm::vec4(0.0, 0.0, 0.0, 1.0); + else if (std_iequal(predefined_name, "red")) color = glm::vec4(1.0, 0.0, 0.0, 1.0); + else if (std_iequal(predefined_name, "green")) color = glm::vec4(0.0, 1.0, 0.0, 1.0); + else if (std_iequal(predefined_name, "blue")) color = glm::vec4(0.0, 0.0, 1.0, 1.0); + else if (std_iequal(predefined_name, "yellow")) color = glm::vec4(1.0, 1.0, 0.0, 1.0); + else if (std_iequal(predefined_name, "magenta")) color = glm::vec4(1.0, 0.0, 1.0, 1.0); + else if (std_iequal(predefined_name, "cyan")) color = glm::vec4(0.0, 1.0, 1.0, 1.0); + else if (std_iequal(predefined_name, "white")) color = glm::vec4(1.0, 1.0, 1.0, 1.0); + } + } + return; + } + } + + static void convertIfcSurfaceStyleShading(shared_ptr surface_style_shading, shared_ptr& style_data) + { + if (surface_style_shading) + { + glm::vec4 surface_color(0.8, 0.82, 0.84, 1.0); + if (surface_style_shading->m_SurfaceColour) + { + shared_ptr surf_color = surface_style_shading->m_SurfaceColour; + convertIfcColourRgb(surf_color, surface_color); + } + + if (surface_color.x < 0.05 && surface_color.y < 0.05 && surface_color.z < 0.05) + { + surface_color = glm::vec4(0.11, 0.12, 0.13, surface_color.w); + } + + glm::vec4 ambient_color(surface_color); + glm::vec4 diffuse_color(surface_color); + glm::vec4 specular_color(surface_color); + double shininess = 35.f; + double alpha = surface_color.w; // 1=opaque, 0=transparent + bool set_transparent = false; + + shared_ptr surf_style_rendering = dynamic_pointer_cast(surface_style_shading); + if (surf_style_rendering) + { + if (surf_style_rendering->m_DiffuseColour) + { + shared_ptr color_or_factor = surf_style_rendering->m_DiffuseColour; + convertIfcColourOrFactor(color_or_factor, surface_color, diffuse_color); + } + + if (surf_style_rendering->m_SpecularColour) + { + shared_ptr ifc_specular_color = surf_style_rendering->m_SpecularColour; + convertIfcColourOrFactor(ifc_specular_color, surface_color, specular_color); + } + + if (surf_style_rendering->m_Transparency) + { + // in IFC 1 is transparent, 0 is opaque. if not given, the value 0 (opaque) is assumed + // in osg, 1 is opaque, 0 is transparent + alpha = 1.f - (float)surf_style_rendering->m_Transparency->m_value; + if (alpha < 0.1f) + { + alpha = 0.1f; + } + + if (alpha > 1.f) + { + alpha = 1.f; + } + + if (alpha < 0.99f) + { + set_transparent = true; + } + } + + if (surf_style_rendering->m_SpecularHighlight) + { + shared_ptr spec_highlight = surf_style_rendering->m_SpecularHighlight; + convertIfcSpecularHighlightSelect(spec_highlight, style_data); + shininess = style_data->m_specular_roughness * 128; + if (shininess <= 1.0) + { + shininess = 1.0; + } + } + } + + if (alpha < 0.1f) + { + alpha = 0.1f; + } + + style_data->m_color_ambient = glm::vec4(ambient_color.x * 0.8, ambient_color.y * 0.8, ambient_color.z * 0.8, alpha); + style_data->m_color_diffuse = glm::vec4(diffuse_color.x, diffuse_color.y, diffuse_color.z, alpha); + style_data->m_color_specular = glm::vec4(specular_color.x * 0.1, specular_color.y * 0.1, specular_color.z * 0.1, alpha); + + style_data->m_shininess = shininess; + style_data->m_set_transparent = set_transparent; + style_data->m_transparency = alpha; + style_data->m_complete = true; + style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; + return; + } + } + + void convertIfcPresentationStyle(shared_ptr presentation_style, shared_ptr& style_data) + { + int style_id = presentation_style->m_tag; + { + + auto it_find_existing_style = m_map_ifc_styles.find(style_id); + if (it_find_existing_style != m_map_ifc_styles.end()) + { + // use existing style + style_data = it_find_existing_style->second; + if (style_data->m_complete) + { + return; + } + } + else + { + if (!style_data) + { + style_data = shared_ptr(new StyleData(style_id)); + } + + std::lock_guard lock(m_writelock_styles_converter); + m_map_ifc_styles[style_id] = style_data; + } + } + + // ENTITY IfcPresentationStyle ABSTRACT SUPERTYPE OF(ONEOF(IfcCurveStyle, IfcFillAreaStyle, IfcSurfaceStyle, IfcSymbolStyle, IfcTextStyle)); + shared_ptr curve_style = dynamic_pointer_cast(presentation_style); + if (curve_style) + { + convertIfcCurveStyle(curve_style, style_data); + return; + } + + shared_ptr fill_area_style = dynamic_pointer_cast(presentation_style); + if (fill_area_style) + { + //std::vector > m_FillStyles; + //shared_ptr m_ModelOrDraughting; //optional + + style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; + + for (shared_ptr& fillStyle : fill_area_style->m_FillStyles) + { + // TYPE IfcFillStyleSelect = SELECT ( IfcFillAreaStyleHatching, IfcFillAreaStyleTiles, IfcExternallyDefinedHatchStyle, IfcColour); + + shared_ptr hatching = dynamic_pointer_cast(fillStyle); + if (hatching) + { + // attributes: + //shared_ptr m_HatchLineAppearance; + //shared_ptr m_StartOfNextHatchLine; + //shared_ptr m_PointOfReferenceHatchLine; //optional + //shared_ptr m_PatternStart; //optional + //shared_ptr m_HatchLineAngle; + + if (hatching->m_HatchLineAppearance) + { + convertIfcCurveStyle(hatching->m_HatchLineAppearance, style_data); + } + continue; + } + + shared_ptr tiles = dynamic_pointer_cast(fillStyle); + if (tiles) + { +#ifdef _DEBUG + std::cout << "IfcFillAreaStyleTiles not implemented" << std::endl; +#endif + continue; + } + + shared_ptr externalStyle = dynamic_pointer_cast(fillStyle); + if (externalStyle) + { +#ifdef _DEBUG + std::cout << "IfcExternallyDefinedHatchStyle not implemented" << std::endl; +#endif + continue; + } + + shared_ptr AreaColour = dynamic_pointer_cast(fillStyle); + if (AreaColour) + { + glm::vec4 color(0.2, 0.25, 0.3, 1.0); + convertIfcColour(AreaColour, color); + + if (color.x < 0.05 && color.y < 0.05 && color.z < 0.05) + { + color = glm::vec4(0.1, 0.125, 0.15, color.w); + } + + double shininess = 35.0; + style_data->m_color_ambient = glm::vec4(color.x * 0.8, color.y * 0.8, color.z * 0.8, color.w); + style_data->m_color_diffuse = glm::vec4(color.x, color.y, color.z, color.w); + style_data->m_color_specular = glm::vec4(color.x * 0.1, color.y * 0.1, color.z * 0.1, color.w); + style_data->m_shininess = shininess; + style_data->m_set_transparent = false; + style_data->m_complete = true; + continue; + } + } + + return; + } + + shared_ptr surface_style = dynamic_pointer_cast(presentation_style); + if (surface_style) + { + convertIfcSurfaceStyle(surface_style, style_data); + return; + } + + shared_ptr text_style = dynamic_pointer_cast(presentation_style); + if (text_style) + { + style_data->m_text_style = text_style; + style_data->m_complete = true; + return; + } + + return; + } + + void convertIfcCurveStyle(shared_ptr curve_style, shared_ptr& style_data) + { + if (!curve_style) + { + return; + } + int style_id = curve_style->m_tag; +#if ENABLE_STYLE_CACHING + auto it_find_existing_style = m_map_ifc_styles.find(style_id); + if (it_find_existing_style != m_map_ifc_styles.end()) + { + std::lock_guard lock(m_mutexSearch); + auto it_find_existing_style = m_map_ifc_styles.find(style_id); + style_data = it_find_existing_style->second; + if (style_data->m_complete) + { + return; + } + } + else +#endif + { + if (!style_data) + { + style_data = shared_ptr(new StyleData(style_id)); + } + + std::lock_guard lock(m_writelock_styles_converter); + m_map_ifc_styles[style_id] = style_data; + style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_CURVE; + } + + + //CurveFont : OPTIONAL IfcCurveFontOrScaledCurveFontSelect; + //CurveWidth : OPTIONAL IfcSizeSelect; + //CurveColour : OPTIONAL IfcColour; + + shared_ptr curve_color = curve_style->m_CurveColour; + if (curve_color) + { + glm::vec4 color(0.2, 0.25, 0.3, 1.0); + convertIfcColour(curve_color, color); + + if (color.x < 0.05 && color.y < 0.05 && color.z < 0.05) + { + color = glm::vec4(0.1, 0.125, 0.15, color.w); + } + + double shininess = 35.0; + style_data->m_color_ambient = glm::vec4(color.x, color.y, color.z, color.w); + style_data->m_color_diffuse = glm::vec4(color.x, color.y, color.z, color.w); + style_data->m_color_specular = glm::vec4(color.x * 0.1, color.y * 0.1, color.z * 0.1, color.w); + style_data->m_shininess = shininess; + style_data->m_set_transparent = false; + style_data->m_complete = true; + } + } + + void convertIfcMaterial(const shared_ptr& mat, std::vector >& vec_style_data) + { + if (!mat) + { + return; + } + + // IfcMaterialDefinition ----------------------------------------------------------- + // inverse attributes: + // std::vector > m_AssociatedTo_inverse; + // std::vector > m_HasExternalReferences_inverse; + // std::vector > m_HasProperties_inverse; + + // IfcMaterial ----------------------------------------------------------- + // attributes: + //shared_ptr& Name = mat->m_Name; + ////shared_ptr m_Description; //optional + ////shared_ptr m_Category; //optional + //// inverse attributes: + //std::vector > m_HasRepresentation_inverse; + //std::vector > m_IsRelatedWith_inverse; + //std::vector > m_RelatesTo_inverse; + + + for (size_t kk = 0; kk < mat->m_IsRelatedWith_inverse.size(); ++kk) + { + const weak_ptr& mat_is_related_weak = mat->m_IsRelatedWith_inverse[kk]; + + if (mat_is_related_weak.expired()) + { + continue; + } + + shared_ptr mat_is_related(mat_is_related_weak); + if (!mat_is_related) + { + continue; + } + } + + for (size_t kk = 0; kk < mat->m_HasRepresentation_inverse.size(); ++kk) + { + const weak_ptr& mat_def_representation_weak = mat->m_HasRepresentation_inverse[kk]; + + if (mat_def_representation_weak.expired()) + { + continue; + } + + shared_ptr mat_def_representation(mat_def_representation_weak); + if (!mat_def_representation) + { + continue; + } + std::vector >& mat_representations = mat_def_representation->m_Representations; + + for (const shared_ptr& mat_rep : mat_representations) + { + if (!mat_rep) + { + continue; + } + shared_ptr mat_style = dynamic_pointer_cast(mat_rep); + if (!mat_style) + { + continue; + } + for (const shared_ptr& mat_rep_item : mat_style->m_Items) + { + if (!mat_rep_item) + { + continue; + } + + shared_ptr styled_item = dynamic_pointer_cast(mat_rep_item); + if (styled_item) + { + //std::vector > m_Styles; + for (const shared_ptr& presentationStyle : styled_item->m_Styles) + { + if (!presentationStyle) + { + continue; + } + + shared_ptr style_data; + convertIfcPresentationStyle(presentationStyle, style_data); + if (style_data) + { + vec_style_data.push_back(style_data); + } + + + // ENTITY IfcPresentationStyle ABSTRACT SUPERTYPE OF (ONEOF (IfcCurveStyle ,IfcFillAreaStyle ,IfcSurfaceStyle ,IfcTextStyle)); + //shared_ptr style_data; + + //shared_ptr curveStyle = dynamic_pointer_cast(style); + //if( curveStyle ) + //{ + // convertIfcCurveStyle( curve_style, style_data ); + // if( style_data ) + // { + // vec_style_data.push_back( style_data ); + // } + // continue; + //} + + // shared_ptr surface_style = dynamic_pointer_cast(style_select); + // if( !surface_style ) + // { + // continue; + // } + // for( const shared_ptr& surface_style_select : surface_style->m_Styles ) + // { + // if( !surface_style_select ) + // { + // continue; + // } + // shared_ptr surface_style_shading = dynamic_pointer_cast(surface_style_select); + // if( surface_style_shading ) + // { + // const int style_id = surface_style->m_tag; + // shared_ptr style_data(new StyleData(style_id)); + // StylesConverter::convertIfcSurfaceStyleShading(surface_style_shading, style_data); + // vec_style_data.push_back(style_data); + // continue; + // } + // } + // } + //} + } + } + } + } + } + } + + void convertElementStyle(const shared_ptr& ifc_element, std::vector >& vec_style_data) + { + // handle assigned materials + std::vector >& vec_associates = ifc_element->m_HasAssociations_inverse; + + for (size_t ii = 0; ii < vec_associates.size(); ++ii) + { + shared_ptr rel_associates(vec_associates[ii]); + shared_ptr associated_material = dynamic_pointer_cast(rel_associates); + if (!associated_material) + { + continue; + } + + // TYPE IfcMaterialSelect = SELECT (IfcMaterialDefinition ,IfcMaterialList ,IfcMaterialUsageDefinition); + shared_ptr relatingMaterial = associated_material->m_RelatingMaterial; + + if (!relatingMaterial) + { + continue; + } + + shared_ptr material = dynamic_pointer_cast(relatingMaterial); + if (material) + { + convertIfcMaterial(material, vec_style_data); + } + + shared_ptr materialList = dynamic_pointer_cast(relatingMaterial); + if (materialList) + { + + for (size_t jj = 0; jj < materialList->m_Materials.size(); ++jj) + { + const shared_ptr& mat = materialList->m_Materials[jj]; + convertIfcMaterial(mat, vec_style_data); + } + + continue; + } + + shared_ptr material_layer_set_usage = dynamic_pointer_cast(relatingMaterial); + if (material_layer_set_usage) + { + if (!material_layer_set_usage->m_ForLayerSet) + { + continue; + } + + for (size_t jj = 0; jj < material_layer_set_usage->m_ForLayerSet->m_MaterialLayers.size(); ++jj) + { + const shared_ptr& material_layer = material_layer_set_usage->m_ForLayerSet->m_MaterialLayers[jj]; + if (!material_layer) + { + continue; + } + + const shared_ptr& mat = material_layer->m_Material; //optional + convertIfcMaterial(mat, vec_style_data); + } + } + } + } + + void convertIfcSurfaceStyle(shared_ptr surface_style, shared_ptr& style_data) + { + if (!surface_style) + { + return; + } + const int style_id = surface_style->m_tag; + + { + std::lock_guard lock(m_writelock_styles_converter); + auto it_find_existing_style = m_map_ifc_styles.find(style_id); + if (it_find_existing_style != m_map_ifc_styles.end()) + { + // todo: check if style compare is faster here + style_data = it_find_existing_style->second; + if (style_data->m_complete) + { + return; + } + } + else + { + if (!style_data) + { + style_data = shared_ptr(new StyleData(style_id)); + } + + m_map_ifc_styles[style_id] = style_data; + } + } + + style_data->m_apply_to_geometry_type = StyleData::GEOM_TYPE_SURFACE; + + std::vector >& vec_styles = surface_style->m_Styles; + if (vec_styles.size() == 0) + { + return; + } + + for (size_t ii_styles = 0; ii_styles < vec_styles.size(); ++ii_styles) + { + shared_ptr surf_style_element_select = vec_styles[ii_styles]; + if (!surf_style_element_select) + { + continue; + } + // TYPE IfcSurfaceStyleElementSelect = SELECT (IfcExternallyDefinedSurfaceStyle ,IfcSurfaceStyleLighting ,IfcSurfaceStyleRefraction ,IfcSurfaceStyleShading ,IfcSurfaceStyleWithTextures); + shared_ptr surface_style_shading = dynamic_pointer_cast(surf_style_element_select); + if (surface_style_shading) + { + convertIfcSurfaceStyleShading(surface_style_shading, style_data); + continue; + } + + shared_ptr ext_surf_style = dynamic_pointer_cast(surf_style_element_select); + if (ext_surf_style) + { +#ifdef _DEBUG + std::cout << "IfcExternallyDefinedSurfaceStyle not implemented" << std::endl; +#endif + continue; + } + + shared_ptr style_lighting = dynamic_pointer_cast(surf_style_element_select); + if (style_lighting) + { +#ifdef _DEBUG + std::cout << "IfcSurfaceStyleLighting not implemented" << std::endl; +#endif + continue; + } + + shared_ptr style_refraction = dynamic_pointer_cast(surf_style_element_select); + if (style_refraction) + { +#ifdef _DEBUG + std::cout << "IfcSurfaceStyleRefraction not implemented" << std::endl; +#endif + continue; + } + + shared_ptr style_texture = dynamic_pointer_cast(surf_style_element_select); + if (style_texture) + { +#ifdef _DEBUG + std::cout << "IfcSurfaceStyleWithTextures not implemented" << std::endl; +#endif + continue; + } + } + } + + void convertRepresentationStyle(const shared_ptr& representation_item, std::vector >& vec_style_data) + { + std::vector >& vec_StyledByItem_inverse = representation_item->m_StyledByItem_inverse; + for (size_t i = 0; i < vec_StyledByItem_inverse.size(); ++i) + { + weak_ptr& styled_item_weak = vec_StyledByItem_inverse[i]; + if (!styled_item_weak.expired()) + { + shared_ptr styled_item = shared_ptr(styled_item_weak); + convertIfcStyledItem(styled_item, vec_style_data); + } + } + } + + void convertIfcStyledItem(weak_ptr styled_item_weak, std::vector >& vec_style_data) + { + if (styled_item_weak.expired()) + { + return; + } + shared_ptr styled_item(styled_item_weak); + const int style_id = styled_item->m_tag; + + { + //std::lock_guard lock(m_writelock_styles_converter); + auto it_find_existing_style = m_map_ifc_styles.find(style_id); + if (it_find_existing_style != m_map_ifc_styles.end()) + { + vec_style_data.push_back(it_find_existing_style->second); + return; + } + } + + std::vector >& vec_style_assigns = styled_item->m_Styles; + for (size_t i_style_assign = 0; i_style_assign < vec_style_assigns.size(); ++i_style_assign) + { + shared_ptr presentationStyle = vec_style_assigns[i_style_assign]; + if (!presentationStyle) + { + continue; + } + + shared_ptr style_data; + convertIfcPresentationStyle(presentationStyle, style_data); + if (style_data) + { + vec_style_data.push_back(style_data); + } + continue; + } + } + + void convertIfcComplexPropertyColor(shared_ptr complex_property, glm::vec4& vec_color) + { + std::vector >& vec_HasProperties = complex_property->m_HasProperties; + if (!complex_property->m_UsageName) return; + if (vec_HasProperties.size() < 3) return; + std::string usage_name = complex_property->m_UsageName->m_value; + if (!std_iequal(usage_name.c_str(), "Color")) return; + + if (complex_property->m_HasProperties.size() > 2) + { + shared_ptr prop1 = dynamic_pointer_cast(complex_property->m_HasProperties[0]); + shared_ptr prop2 = dynamic_pointer_cast(complex_property->m_HasProperties[1]); + shared_ptr prop3 = dynamic_pointer_cast(complex_property->m_HasProperties[2]); + + if (prop1 && prop2 && prop3) + { + shared_ptr v1_select = prop1->m_NominalValue; + shared_ptr v2_select = prop2->m_NominalValue; + shared_ptr v3_select = prop3->m_NominalValue; + if (v1_select && v2_select && v3_select) + { + shared_ptr v1_int = dynamic_pointer_cast(v1_select); + shared_ptr v2_int = dynamic_pointer_cast(v2_select); + shared_ptr v3_int = dynamic_pointer_cast(v3_select); + + if (v1_int && v2_int && v3_int) + { + double r = v1_int->m_value / 255.0; + double g = v2_int->m_value / 255.0; + double b = v3_int->m_value / 255.0; + + if (r < 0.05 && g < 0.05 && b < 0.05) + { + r = 0.1; + g = 0.12; + b = 0.15; + } + vec_color.x = r; + vec_color.y = g; + vec_color.z = b; + vec_color.w = 1.0; + + //ScopedLock lock( m_writelock_styles_converter ); + //style_data->color_ambient.setColor( r, g, b, 1.f ); + //style_data->color_diffuse.setColor( r, g, b, 1.f ); + //style_data->color_specular.setColor( r, g, b, 1.f ); + //style_data->shininess = 35.f; + + //m_map_ifc_styles[complex_property_id] = style_data; + + return; + } + } + } + } + } +}; diff --git a/IfcPlusPlus/src/ifcpp/model/AttributeObject.cpp b/IfcPlusPlus/src/ifcpp/model/AttributeObject.cpp index 145523e58..bfb1c74fb 100644 --- a/IfcPlusPlus/src/ifcpp/model/AttributeObject.cpp +++ b/IfcPlusPlus/src/ifcpp/model/AttributeObject.cpp @@ -1,9 +1,9 @@ -//#include "AttributeObject.h" - -//AttributeObject::AttributeObject() {} -//AttributeObject::AttributeObject(std::vector >& vec) { m_vec = vec; } -//AttributeObject::~AttributeObject() {} -////virtual const char* className() const { return "AttributeObject"; } -//shared_ptr AttributeObject::getDeepCopy() const { return shared_ptr(new AttributeObject()); } -// -//void AttributeObject::getStepParameter(std::stringstream& stream, bool is_select_type) const {} +//#include "AttributeObject.h" + +//AttributeObject::AttributeObject() {} +//AttributeObject::AttributeObject(std::vector >& vec) { m_vec = vec; } +//AttributeObject::~AttributeObject() {} +////virtual const char* className() const { return "AttributeObject"; } +//shared_ptr AttributeObject::getDeepCopy() const { return shared_ptr(new AttributeObject()); } +// +//void AttributeObject::getStepParameter(std::stringstream& stream, bool is_select_type) const {} diff --git a/IfcPlusPlus/src/ifcpp/model/AttributeObject.h b/IfcPlusPlus/src/ifcpp/model/AttributeObject.h index da852c4c2..1bca2db0e 100644 --- a/IfcPlusPlus/src/ifcpp/model/AttributeObject.h +++ b/IfcPlusPlus/src/ifcpp/model/AttributeObject.h @@ -1,101 +1,101 @@ -/* -*-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. -*/ - -#pragma once -#include -#include -#include "GlobalDefines.h" -#include "ifcpp/model/BasicTypes.h" -#include "ifcpp/model/BuildingObject.h" - -class AttributeObjectVector : public BuildingObject -{ -public: - AttributeObjectVector() = default; - AttributeObjectVector( std::vector >& vec ){ m_vec = vec; } - virtual ~AttributeObjectVector()= default; - uint32_t classID() const override { return 3667068888; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override {} - std::vector > m_vec; -}; - -class BoolAttribute : public BuildingObject -{ -public: - BoolAttribute()= default; - BoolAttribute( bool value ) : m_value( value ){} - virtual ~BoolAttribute()= default; - uint32_t classID() const override { return 164996446; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - bool m_value; -}; - -class LogicalAttribute : public BuildingObject -{ -public: - LogicalAttribute()= default; - LogicalAttribute( LogicalEnum value ) : m_value( value ){} - virtual ~LogicalAttribute()= default; - uint32_t classID() const override { return 1777643854; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - LogicalEnum m_value; -}; - -class IntegerAttribute : public BuildingObject -{ -public: - IntegerAttribute()= default; - IntegerAttribute( int value ) : m_value( value ){} - virtual ~IntegerAttribute()= default; - uint32_t classID() const override { return 2693262938; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - int m_value; -}; - -class RealAttribute : public BuildingObject -{ -public: - RealAttribute()= default; - RealAttribute( double value ) : m_value( value ){} - virtual ~RealAttribute()= default; - uint32_t classID() const override { return 93569251; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - double m_value; -}; - -class StringAttribute : public BuildingObject -{ -public: - StringAttribute()= default; - StringAttribute( std::string& value ) : m_value( value ){} - virtual ~StringAttribute()= default; - uint32_t classID() const override { return 308612603; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - std::string m_value; -}; - -class BinaryAttribute : public BuildingObject -{ -public: - BinaryAttribute()= default; - BinaryAttribute( const std::string& value ) { m_value = value.c_str(); } - BinaryAttribute( const char* value ) : m_value( value ) {} - virtual ~BinaryAttribute()= default; - uint32_t classID() const override { return 2341374947; } - void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} - const char* m_value; -}; +/* -*-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. +*/ + +#pragma once +#include +#include +#include "GlobalDefines.h" +#include "ifcpp/model/BasicTypes.h" +#include "ifcpp/model/BuildingObject.h" + +class AttributeObjectVector : public BuildingObject +{ +public: + AttributeObjectVector() = default; + AttributeObjectVector( std::vector >& vec ){ m_vec = vec; } + virtual ~AttributeObjectVector()= default; + uint32_t classID() const override { return 3667068888; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override {} + std::vector > m_vec; +}; + +class BoolAttribute : public BuildingObject +{ +public: + BoolAttribute()= default; + BoolAttribute( bool value ) : m_value( value ){} + virtual ~BoolAttribute()= default; + uint32_t classID() const override { return 164996446; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + bool m_value; +}; + +class LogicalAttribute : public BuildingObject +{ +public: + LogicalAttribute()= default; + LogicalAttribute( LogicalEnum value ) : m_value( value ){} + virtual ~LogicalAttribute()= default; + uint32_t classID() const override { return 1777643854; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + LogicalEnum m_value; +}; + +class IntegerAttribute : public BuildingObject +{ +public: + IntegerAttribute()= default; + IntegerAttribute( int value ) : m_value( value ){} + virtual ~IntegerAttribute()= default; + uint32_t classID() const override { return 2693262938; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + int m_value; +}; + +class RealAttribute : public BuildingObject +{ +public: + RealAttribute()= default; + RealAttribute( double value ) : m_value( value ){} + virtual ~RealAttribute()= default; + uint32_t classID() const override { return 93569251; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + double m_value; +}; + +class StringAttribute : public BuildingObject +{ +public: + StringAttribute()= default; + StringAttribute( std::string& value ) : m_value( value ){} + virtual ~StringAttribute()= default; + uint32_t classID() const override { return 308612603; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + std::string m_value; +}; + +class BinaryAttribute : public BuildingObject +{ +public: + BinaryAttribute()= default; + BinaryAttribute( const std::string& value ) { m_value = value.c_str(); } + BinaryAttribute( const char* value ) : m_value( value ) {} + virtual ~BinaryAttribute()= default; + uint32_t classID() const override { return 2341374947; } + void getStepParameter( std::stringstream& /*stream*/, bool /*is_select_type = false*/, size_t /*precision*/ ) const override{} + const char* m_value; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/BasicTypes.h b/IfcPlusPlus/src/ifcpp/model/BasicTypes.h index 1c4adb011..58e655d0c 100644 --- a/IfcPlusPlus/src/ifcpp/model/BasicTypes.h +++ b/IfcPlusPlus/src/ifcpp/model/BasicTypes.h @@ -1,87 +1,87 @@ -/* -*-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. -*/ - -#pragma once - -#if _MSC_VER >= 1600 - -#include -#if _MSC_VER < 1900 -using std::tr1::shared_ptr; -using std::tr1::weak_ptr; -using std::tr1::dynamic_pointer_cast; -using std::tr1::make_shared; -#else -using std::shared_ptr; -using std::weak_ptr; -using std::dynamic_pointer_cast; -using std::make_shared; -#endif - -#elif defined __clang__ -#include -using std::shared_ptr; -using std::weak_ptr; -using std::dynamic_pointer_cast; -using std::make_shared; - -#elif defined __GNUC__ && !defined(__FreeBSD__) -#if __GNUC__ < 5 -#include -using std::tr1::shared_ptr; -using std::tr1::weak_ptr; -using std::tr1::dynamic_pointer_cast; -using std::tr1::make_shared; -#else -#include -using std::shared_ptr; -using std::weak_ptr; -using std::dynamic_pointer_cast; -using std::make_shared; -#endif - -#define _stricmp strcasecmp - -#elif defined(__FreeBSD__) - -// Requires clang++ and libc++ -#include - -using std::shared_ptr; -using std::weak_ptr; -using std::dynamic_pointer_cast; -using std::make_shared; - -#define _stricmp strcasecmp - -#else - -#ifndef BOOST_SP_USE_QUICK_ALLOCATOR -#define BOOST_SP_USE_QUICK_ALLOCATOR -#endif -#ifndef BOOST_SP_NO_ATOMIC_ACCESS -#define BOOST_SP_NO_ATOMIC_ACCESS -#endif - -#include -#include -using boost::shared_ptr; -using boost::weak_ptr; -using boost::dynamic_pointer_cast; -using boost::make_shared; - -#endif +/* -*-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. +*/ + +#pragma once + +#if _MSC_VER >= 1600 + +#include +#if _MSC_VER < 1900 +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; +using std::tr1::dynamic_pointer_cast; +using std::tr1::make_shared; +#else +using std::shared_ptr; +using std::weak_ptr; +using std::dynamic_pointer_cast; +using std::make_shared; +#endif + +#elif defined __clang__ +#include +using std::shared_ptr; +using std::weak_ptr; +using std::dynamic_pointer_cast; +using std::make_shared; + +#elif defined __GNUC__ && !defined(__FreeBSD__) +#if __GNUC__ < 5 +#include +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; +using std::tr1::dynamic_pointer_cast; +using std::tr1::make_shared; +#else +#include +using std::shared_ptr; +using std::weak_ptr; +using std::dynamic_pointer_cast; +using std::make_shared; +#endif + +#define _stricmp strcasecmp + +#elif defined(__FreeBSD__) + +// Requires clang++ and libc++ +#include + +using std::shared_ptr; +using std::weak_ptr; +using std::dynamic_pointer_cast; +using std::make_shared; + +#define _stricmp strcasecmp + +#else + +#ifndef BOOST_SP_USE_QUICK_ALLOCATOR +#define BOOST_SP_USE_QUICK_ALLOCATOR +#endif +#ifndef BOOST_SP_NO_ATOMIC_ACCESS +#define BOOST_SP_NO_ATOMIC_ACCESS +#endif + +#include +#include +using boost::shared_ptr; +using boost::weak_ptr; +using boost::dynamic_pointer_cast; +using boost::make_shared; + +#endif diff --git a/IfcPlusPlus/src/ifcpp/model/BuildingException.h b/IfcPlusPlus/src/ifcpp/model/BuildingException.h index d6f29f0e9..0fbc5e24f 100644 --- a/IfcPlusPlus/src/ifcpp/model/BuildingException.h +++ b/IfcPlusPlus/src/ifcpp/model/BuildingException.h @@ -1,76 +1,76 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include -#include "GlobalDefines.h" - -#define __FUNC__ __FUNCTION__ -#define _func_ __FUNCTION__ - -class BuildingException : public std::exception -{ -public: - BuildingException( std::string reason ) - { - m_reason_str = reason; - } - - BuildingException( std::string reason, const char* function_name ) - { - m_reason_str.append(function_name); - if( !reason.empty() ) - { - m_reason_str.append( ": " ); - m_reason_str.append( reason.c_str() ); - } - } - - BuildingException(std::wstring reason) - { - std::wstring_convert, wchar_t> StringConverter; - std::string reason_str = StringConverter.to_bytes(reason); - m_reason_str.assign(reason_str.begin(), reason_str.end()); - } - - BuildingException(std::wstring reason, const char* function_name) - { - m_reason_str.append(function_name); - if( !reason.empty() ) - { - m_reason_str.append( ": " ); - - std::wstring_convert, wchar_t> StringConverter; - std::string reason_str = StringConverter.to_bytes(reason); - - m_reason_str.append( reason_str ); - } - } - - ~BuildingException() noexcept override = default; - - const char* what() const noexcept override - { - return m_reason_str.c_str(); - } - - std::string m_reason_str; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include +#include "GlobalDefines.h" + +#define __FUNC__ __FUNCTION__ +#define _func_ __FUNCTION__ + +class BuildingException : public std::exception +{ +public: + BuildingException( std::string reason ) + { + m_reason_str = reason; + } + + BuildingException( std::string reason, const char* function_name ) + { + m_reason_str.append(function_name); + if( !reason.empty() ) + { + m_reason_str.append( ": " ); + m_reason_str.append( reason.c_str() ); + } + } + + BuildingException(std::wstring reason) + { + std::wstring_convert, wchar_t> StringConverter; + std::string reason_str = StringConverter.to_bytes(reason); + m_reason_str.assign(reason_str.begin(), reason_str.end()); + } + + BuildingException(std::wstring reason, const char* function_name) + { + m_reason_str.append(function_name); + if( !reason.empty() ) + { + m_reason_str.append( ": " ); + + std::wstring_convert, wchar_t> StringConverter; + std::string reason_str = StringConverter.to_bytes(reason); + + m_reason_str.append( reason_str ); + } + } + + ~BuildingException() noexcept override = default; + + const char* what() const noexcept override + { + return m_reason_str.c_str(); + } + + std::string m_reason_str; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/BuildingGuid.h b/IfcPlusPlus/src/ifcpp/model/BuildingGuid.h index f3e25f0db..ee1581bcf 100644 --- a/IfcPlusPlus/src/ifcpp/model/BuildingGuid.h +++ b/IfcPlusPlus/src/ifcpp/model/BuildingGuid.h @@ -1,42 +1,42 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include "GlobalDefines.h" - -///@brief Creates a GUID string with 36 characters including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" -///@details These functions are non-template on purpose, to keep it simple and stupid, and to reduce compile time -IFCQUERY_EXPORT std::string createGUID32(); - -///@brief Compresses a GUID string -///@details Expects a string with exactly 36 characters including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" -///@returns an IFC GUID string with 22 characters, for example: "3n0m0Cc6L4xhvkpCU0k1GZ" -IFCQUERY_EXPORT std::string compressGUID(const std::string& in); - -///@brief Decompresses an IFC GUID string -///@details Expects a string with exactly 22 characters, for example "3n0m0Cc6L4xhvkpCU0k1GZ" -///@returns GUID string with 36 characters, including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" -IFCQUERY_EXPORT std::string decompressGUID(const std::string& in); - - -///@brief Create IFC GUID string with 22 characters, for example "3n0m0Cc6L4xhvkpCU0k1GZ" -///@details Use desired character type as template parameter - char or wchar_t. -///IFC uses a different base64 character set than RFC4648 - it starts with digits -///instead of uppercase letters and uses '_' and '$' as last two characters. +/* -*-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. +*/ + +#pragma once + +#include +#include "GlobalDefines.h" + +///@brief Creates a GUID string with 36 characters including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" +///@details These functions are non-template on purpose, to keep it simple and stupid, and to reduce compile time +IFCQUERY_EXPORT std::string createGUID32(); + +///@brief Compresses a GUID string +///@details Expects a string with exactly 36 characters including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" +///@returns an IFC GUID string with 22 characters, for example: "3n0m0Cc6L4xhvkpCU0k1GZ" +IFCQUERY_EXPORT std::string compressGUID(const std::string& in); + +///@brief Decompresses an IFC GUID string +///@details Expects a string with exactly 22 characters, for example "3n0m0Cc6L4xhvkpCU0k1GZ" +///@returns GUID string with 36 characters, including dashes, for example: "F103000C-9865-44EE-BE6E-CCC780B81423" +IFCQUERY_EXPORT std::string decompressGUID(const std::string& in); + + +///@brief Create IFC GUID string with 22 characters, for example "3n0m0Cc6L4xhvkpCU0k1GZ" +///@details Use desired character type as template parameter - char or wchar_t. +///IFC uses a different base64 character set than RFC4648 - it starts with digits +///instead of uppercase letters and uses '_' and '$' as last two characters. IFCQUERY_EXPORT std::string createBase64Uuid(); \ No newline at end of file diff --git a/IfcPlusPlus/src/ifcpp/model/BuildingModel.cpp b/IfcPlusPlus/src/ifcpp/model/BuildingModel.cpp index 437c45edb..29829d168 100644 --- a/IfcPlusPlus/src/ifcpp/model/BuildingModel.cpp +++ b/IfcPlusPlus/src/ifcpp/model/BuildingModel.cpp @@ -1,754 +1,754 @@ -/* -*-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. -*/ - -#pragma warning( disable: 4996 ) -#include -#include -#include - -#include "IfcApplication.h" -#include "IfcAxis2Placement.h" -#include "IfcAxis2Placement3D.h" -#include "IfcBuilding.h" -#include "IfcCartesianPoint.h" -#include "IfcChangeActionEnum.h" -#include "IfcConversionBasedUnit.h" -#include "IfcDimensionCount.h" -#include "IfcDimensionalExponents.h" -#include "IfcDirection.h" -#include "IfcElementAssembly.h" -#include "IfcGeometricRepresentationContext.h" -#include "IfcGloballyUniqueId.h" -#include "IfcIdentifier.h" -#include "IfcLabel.h" -#include "IfcLengthMeasure.h" -#include "IfcMeasureWithUnit.h" -#include "IfcOrganization.h" -#include "IfcOwnerHistory.h" -#include "IfcPerson.h" -#include "IfcPersonAndOrganization.h" -#include "IfcPlaneAngleMeasure.h" -#include "IfcProduct.h" -#include "IfcProject.h" -#include "IfcReal.h" -#include "IfcRelationship.h" -#include "IfcRelAggregates.h" -#include "IfcRelContainedInSpatialStructure.h" -#include "IfcSite.h" -#include "IfcSIUnit.h" -#include "IfcSIUnitName.h" -#include "IfcStyledItem.h" -#include "IfcUnit.h" -#include "IfcUnitAssignment.h" -#include "IfcUnitEnum.h" -#include "IfcValue.h" -#include "ifcpp/IFC4X3/EntityFactory.h" -#include "ifcpp/reader/ReaderUtil.h" -#include "ifcpp/writer/WriterUtil.h" - -#include "AttributeObject.h" -#include "BuildingGuid.h" -#include "BuildingException.h" -#include "BuildingModel.h" -#include "UnitConverter.h" - -using namespace IFC4X3; - -BuildingModel::BuildingModel() -{ - m_unit_converter = std::make_shared( ); - m_unit_converter->setMessageTarget( this ); - initFileHeader( "IfcPlusPlus-export.ifc", "IfcPlusPlus" ); -} - -BuildingModel::~BuildingModel()= default; - -std::string getIfcSchemaVersionString(BuildingModel::SchemaVersionEnum version) -{ - switch (version) - { - case BuildingModel::IFC2X: return "IFC2X"; - case BuildingModel::IFC2X2: return "IFC2X2"; - case BuildingModel::IFC2X3: return "IFC2X3"; - case BuildingModel::IFC2X4: return "IFC2X4"; - case BuildingModel::IFC4: return "IFC4"; - case BuildingModel::IFC4X1: return "IFC4X1"; - case BuildingModel::IFC4X3: return "IFC4X3"; - } - return "IFC_VERSION_UNDEFINED"; -}; - -std::string BuildingModel::getIfcSchemaVersionOfLoadedFile() -{ - return getIfcSchemaVersionString(m_ifc_schema_version_loaded_file); -} - -std::string BuildingModel::getIfcSchemaVersionCurrent() -{ - return getIfcSchemaVersionString(m_ifc_schema_version_current); -} - -void BuildingModel::initIfcModel() -{ - clearIfcModel(); - - shared_ptr project( new IfcProject( 1 ) ); - insertEntity(project); - - shared_ptr person( new IfcPerson() ); - person->m_FamilyName = std::make_shared( "FamilyName" ); - person->m_GivenName = std::make_shared( "GivenName" ); - insertEntity(person); - - shared_ptr org( new IfcOrganization() ); - org->m_Name = std::make_shared( "OrganizationName" ); - insertEntity(org); - - shared_ptr person_org( new IfcPersonAndOrganization() ); - person_org->m_ThePerson = person; - person_org->m_TheOrganization = org; - insertEntity(person_org); - - shared_ptr app( new IfcApplication() ); - app->m_ApplicationDeveloper = org; - app->m_Version = std::make_shared( "1.0" ); - app->m_ApplicationFullName = std::make_shared( "IfcPlusPlus" ); - app->m_ApplicationIdentifier = std::make_shared( "IfcPlusPlus" ); - insertEntity(app); - - shared_ptr point( new IfcCartesianPoint() ); - point->m_Coordinates[0] = 0.0; - point->m_Coordinates[1] = 0.0; - point->m_Coordinates[2] = 0.0; - //point->m_Coordinates.push_back( std::make_shared( 0.0 ) ); - //point->m_Coordinates.push_back( std::make_shared( 0.0 ) ); - insertEntity(point); - - shared_ptr axis_placement( new IfcAxis2Placement3D() ); - axis_placement->m_Location = point; - insertEntity(axis_placement); - - shared_ptr owner_history ( new IfcOwnerHistory() ); - owner_history->m_OwningUser = person_org; - owner_history->m_OwningApplication = app; - owner_history->m_ChangeAction = std::make_shared( IfcChangeActionEnum::ENUM_ADDED ); - insertEntity(owner_history); - - shared_ptr dim_exp( new IfcDimensionalExponents() ); - dim_exp->m_LengthExponent = 0; - dim_exp->m_MassExponent = 0; - dim_exp->m_TimeExponent = 0; - dim_exp->m_ElectricCurrentExponent = 0; - dim_exp->m_ThermodynamicTemperatureExponent = 0; - dim_exp->m_AmountOfSubstanceExponent = 0; - dim_exp->m_LuminousIntensityExponent = 0; - insertEntity(dim_exp); - - // length unit [m] - shared_ptr si_unit( new IfcSIUnit() ); - si_unit->m_UnitType = std::make_shared( IfcUnitEnum::ENUM_LENGTHUNIT ); - si_unit->m_Name = std::make_shared( IfcSIUnitName::ENUM_METRE ); - insertEntity(si_unit); - - // plane unit [rad] - shared_ptr plane_angle_unit( new IfcSIUnit() ); - plane_angle_unit->m_UnitType = std::make_shared( IfcUnitEnum::ENUM_PLANEANGLEUNIT ); - plane_angle_unit->m_Name = std::make_shared( IfcSIUnitName::ENUM_RADIAN ); - insertEntity(plane_angle_unit); - - // assign units - shared_ptr unit_assignment( new IfcUnitAssignment() ); - unit_assignment->m_Units.push_back( si_unit ); - unit_assignment->m_Units.push_back( plane_angle_unit ); - insertEntity(unit_assignment); - - project->m_GlobalId = std::make_shared( createBase64Uuid() ); - project->m_OwnerHistory = owner_history; - project->m_Name = std::make_shared( "IfcPlusPlus project" ); - project->m_UnitsInContext = unit_assignment; - - // create default IfcSite - shared_ptr site( new IfcSite() ); - site->m_GlobalId = std::make_shared( createBase64Uuid() ); - site->m_OwnerHistory = owner_history; - site->m_Name = std::make_shared( "Site" ); - insertEntity(site); - - shared_ptr rel_aggregates_site( new IfcRelAggregates() ); - rel_aggregates_site->m_RelatingObject = project; - insertEntity(rel_aggregates_site); - - // create default Building - shared_ptr building( new IfcBuilding() ); - building->m_GlobalId = std::make_shared( createBase64Uuid() ); - building->m_OwnerHistory = owner_history; - building->m_Name = std::make_shared( "Building" ); - insertEntity( building ); - - // set up world coordinate system - shared_ptr axis( new IfcDirection() ); - insertEntity(axis); - axis->m_DirectionRatios.push_back( std::make_shared( 1.0 ) ); - axis->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); - axis->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); - - shared_ptr ref_direction( new IfcDirection() ); - insertEntity(ref_direction); - ref_direction->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); - ref_direction->m_DirectionRatios.push_back( std::make_shared( 1.0 ) ); - ref_direction->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); - - shared_ptr location( new IfcCartesianPoint() ); - insertEntity(location); - location->m_Coordinates[0] = 0.0; - location->m_Coordinates[1] = 0.0; - location->m_Coordinates[2] = 0.0; - //location->m_Coordinates.push_back( std::make_shared(0.0 ) ); - //location->m_Coordinates.push_back( std::make_shared(0.0 ) ); - - shared_ptr world_coordinate_system( new IfcAxis2Placement3D() ); - insertEntity(world_coordinate_system); - world_coordinate_system->m_Location = location; - world_coordinate_system->m_Axis = axis; - world_coordinate_system->m_RefDirection = ref_direction; - - // 3d representation context - shared_ptr geom_context( new IfcGeometricRepresentationContext() ); - insertEntity(geom_context); - geom_context->m_CoordinateSpaceDimension = std::make_shared( 3 ); - geom_context->m_WorldCoordinateSystem = world_coordinate_system; - - updateCache(); -} - -void BuildingModel::initCopyIfcModel( const shared_ptr& other ) -{ - clearIfcModel(); - - shared_ptr project = other->getIfcProject(); - shared_ptr projectAsRoot(project); - - std::map > map_entities_copy; - map_entities_copy[projectAsRoot.get()] = projectAsRoot; - - collectDependentEntities( projectAsRoot, map_entities_copy, false); - - for( auto it = map_entities_copy.begin(); it != map_entities_copy.end(); ++it ) - { - shared_ptr bo = it->second; - shared_ptr entity = dynamic_pointer_cast(bo); - if( entity ) - { - insertEntity(entity); - } - } - - updateCache(); -} - -shared_ptr BuildingModel::getIfcProject() -{ - return m_ifc_project; -} -shared_ptr BuildingModel::getIfcGeometricRepresentationContext3D() -{ - return m_geom_context_3d; -} - -void BuildingModel::setIfcProject( shared_ptr project ) -{ - m_ifc_project = project; -} - -void BuildingModel::setIfcGeometricRepresentationContext3D( shared_ptr& context ) -{ - m_geom_context_3d = context; -} - -void BuildingModel::setUnitConverter( shared_ptr& uc ) -{ - m_unit_converter = uc; -} - -void BuildingModel::setMapIfcEntities( const std::map >& map ) -{ - clearIfcModel(); - m_map_entities.clear(); - m_map_entities = map; - updateCache(); - // todo: check model consistency -} - -void BuildingModel::insertEntity( shared_ptr e, bool overwrite_existing, bool warn_on_existing_entities ) -{ - if( !e ) - { - return; - } - int tag = e->m_tag; - if( tag < 0 ) - { - int next_unused_id = getMaxUsedEntityId() + 1; - e->m_tag = next_unused_id; - tag = next_unused_id; - } - - auto it_find = m_map_entities.find( tag ); - if( it_find != m_map_entities.end() ) - { - // key already exists - if( overwrite_existing ) - { - it_find->second = e; - } - else - { - if( warn_on_existing_entities ) - { - messageCallback( "Entity already in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); - } - } - } - else - { - // the key does not exist in the map - m_map_entities.insert( it_find, std::map >::value_type( tag, e ) ); - } -#ifdef _DEBUG - shared_ptr product = dynamic_pointer_cast( e ); - if( product ) - { - if( !product->m_GlobalId ) - { - messageCallback( "IfcProduct->m_GlobalId not set", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, product.get() ); - return; - } - if( product->m_GlobalId->m_value.length() < 22 ) - { - messageCallback( "IfcProduct->m_GlobalId.length() < 22", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, product.get() ); - } - } -#endif - - // TODO: if type is IfcRoot (or subtype), and GlobalID not set, create one -} - -void BuildingModel::removeEntity( shared_ptr e ) -{ - if( !e ) - { - messageCallback( "Entity not valid", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); - return; - } - int remove_id = e->m_tag; - auto it_find = m_map_entities.find(remove_id); - if( it_find == m_map_entities.end() ) - { - messageCallback( "Entity not found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); - return; - } - shared_ptr entity_found = it_find->second; - entity_found->unlinkFromInverseCounterparts(); - BuildingEntity* entity_remove_ptr = e.get(); - - if( entity_found.use_count() > 1 ) - { - // find references to this entity and remove them - std::vector > > vec_attributes_inverse; - entity_found->getAttributesInverse( vec_attributes_inverse ); - for(auto & ii : vec_attributes_inverse) - { - shared_ptr& attribute_inverse = ii.second; - - shared_ptr attribute_inverse_entity = dynamic_pointer_cast( attribute_inverse ); - if( attribute_inverse_entity ) - { - std::vector > > vec_attributes; - attribute_inverse_entity->getAttributes( vec_attributes ); - for( size_t jj = 0; jj < vec_attributes.size(); ++jj ) - { - shared_ptr& attribute = vec_attributes[jj].second; - if( attribute.get() == entity_remove_ptr ) - { - vec_attributes.erase( vec_attributes.begin() + jj ); - if( jj > 0 ) - { - --jj; - } - } - } - } - } - } - m_map_entities.erase( it_find ); -} - -void BuildingModel::removeEntity( int tag ) -{ - auto it_find = m_map_entities.find(tag); - if( it_find != m_map_entities.end() ) - { - shared_ptr entity_found = it_find->second; - removeEntity( entity_found ); - } -} - -int BuildingModel::getMaxUsedEntityId() -{ - if( m_map_entities.empty() ) - { - return 0; - } - // since it is an ordered map, we can just take the key of the last entry - int max_id = m_map_entities.rbegin()->first; - return max_id; -} - -void BuildingModel::removeUnreferencedEntities() -{ - for( auto it_entities = m_map_entities.begin(); it_entities != m_map_entities.end(); ) - { - shared_ptr entity = it_entities->second; - if( entity.use_count() < 2 ) - { - // if use_count is only 1, the entity is only referenced in m_map_entities, so no other entity or other object holds a shared_ptr - bool erase_entity = true; - shared_ptr ifc_relationship = dynamic_pointer_cast( entity ); - if( ifc_relationship ) - { - // relationship objects may only reference some entities while not being referenced itself - std::vector > > vec_attributes; - ifc_relationship->getAttributes( vec_attributes ); - if( vec_attributes.size() > 4 ) - { - // check if the object has relevant pointers - size_t num_relevant_attributes = 0; - // first 4 attributes are GlobalId, OwnerHistory, Name, Description - for( size_t ii = 4; ii < vec_attributes.size(); ++ii ) - { - shared_ptr attribute = vec_attributes[ii].second; - if( attribute ) - { - shared_ptr attribute_vec = dynamic_pointer_cast( attribute ); - if( attribute_vec ) - { - if( !attribute_vec->m_vec.empty() ) - { - ++num_relevant_attributes; - } - } - else - { - shared_ptr attribute_entity = dynamic_pointer_cast( attribute ); - if( attribute_entity ) - { - ++num_relevant_attributes; - } - } - } - } - if( num_relevant_attributes > 0 ) - { - erase_entity = false; - } - } - } - else - { - shared_ptr ifc_styled_item = dynamic_pointer_cast( entity ); - if( ifc_styled_item ) - { - if( ifc_styled_item->m_Item ) - { - erase_entity = false; - } - } - - } - if( erase_entity ) - { - entity->unlinkFromInverseCounterparts(); - auto erase_it = it_entities; -#ifdef _DEBUG - int tag = entity->m_tag; - const char* entity_className = IFC4X3::EntityFactory::getStringForClassID(entity->classID()); -#endif - ++it_entities; - // TODO: check here if it really has been removed - removeEntity( entity ); - } - else - { - ++it_entities; - } - } - else - { - ++it_entities; - } - } -} - -void BuildingModel::initFileHeader( const std::string& fileName, const std::string& generatingApplication ) -{ - m_file_name = fileName; - std::string filename_escaped = encodeStepString( fileName ); - std::stringstream strs; - strs << "HEADER;" << std::endl; - strs << "FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');" << std::endl; - strs << "FILE_NAME('" << filename_escaped.c_str() << "','"; - - //2011-04-21T14:25:12 - time_t rawtime; - struct tm* timeinfo; - char buffer[80]; - - time(&rawtime); - timeinfo = localtime(&rawtime); - - strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", timeinfo); - std::string str(buffer); - - strs << buffer; - strs << "',(''),('',''),'',' " << generatingApplication << "','');" << std::endl; - strs << "FILE_SCHEMA(('" << getIfcSchemaVersionCurrent() << "'));" << std::endl; - strs << "ENDSEC;" << std::endl; - - m_file_header = strs.str(); -} - -void BuildingModel::setFileHeader( const std::string& header ) -{ - m_file_header = header; -} - -void BuildingModel::setFileDescription( const std::string& description ) -{ - m_IFC_FILE_DESCRIPTION = description; -} - -void BuildingModel::setFileName( const std::string& name ) -{ - m_IFC_FILE_NAME = name; -} - -void BuildingModel::clearIfcModel() -{ - m_map_entities.clear(); - m_ifc_project.reset(); - m_geom_context_3d.reset(); - m_ifc_schema_version_current = IFC4X1; - m_ifc_schema_version_loaded_file = IFC4X1; - m_IFC_FILE_NAME = ""; - m_IFC_FILE_DESCRIPTION = ""; - m_file_header = ""; - m_unit_converter->resetUnitFactors(); -} - -void BuildingModel::resetIfcModel() -{ - clearIfcModel(); - initIfcModel(); - updateCache(); -} - -void BuildingModel::updateCache() -{ - bool found_project = false; - bool found_geom_context = false; - - shared_ptr keep_project = m_ifc_project; - m_ifc_project.reset(); - - // try to find IfcProject and IfcGeometricRepresentationContext - for( auto it=m_map_entities.begin(); it!=m_map_entities.end(); ++it ) - { - shared_ptr obj = it->second; - if( obj->classID() == IFC4X3::IFCPROJECT ) - { - if( m_ifc_project ) - { - messageCallback("More than one IfcProject in model", StatusCallback::MESSAGE_TYPE_ERROR, __FUNC__, m_ifc_project.get()); - } - m_ifc_project = dynamic_pointer_cast(obj); - - if( m_ifc_project ) - { - found_project = true; - if( found_geom_context ) - { - break; - } - } - else - { - std::cout << "BuildingModel::updateCache: IfcProject found but dynamic_cast failed. Is RTTI enabled?" << std::endl; - } - } - else if( obj->classID() == IFC4X3::IFCGEOMETRICREPRESENTATIONCONTEXT ) - { - shared_ptr context = dynamic_pointer_cast(obj); - if( context ) - { - if( context->m_CoordinateSpaceDimension ) - { - if( context->m_CoordinateSpaceDimension->m_value == 3 ) - { - m_geom_context_3d = context; - found_geom_context = true; - if( found_project ) - { - break; - } - } - } - } - } - } - - if( found_project ) - { - m_unit_converter->setIfcProject( m_ifc_project ); - } - else - { - std::cout << "BuildingModel::updateCache, IfcProject not found in model with " << m_map_entities.size() << " items" << std::endl; - } -} - -void BuildingModel::clearCache() -{ - m_ifc_project.reset(); - m_geom_context_3d.reset(); -} - -void BuildingModel::resolveInverseAttributes() -{ - for( auto it = m_map_entities.begin(); it != m_map_entities.end(); ++it ) - { - shared_ptr entity = it->second; - if( !entity ) - { - continue; - } - - entity->setInverseCounterparts( entity ); - } -} - -void BuildingModel::unsetInverseAttributes() -{ - for( auto it = m_map_entities.begin(); it != m_map_entities.end(); ++it ) - { - shared_ptr entity = it->second; - if( !entity ) - { - continue; - } - entity->unlinkFromInverseCounterparts(); - } -} - -void BuildingModel::collectDependentEntities( shared_ptr obj, std::map >& target_map, bool resolveInverseAttributes) -{ - if( !obj ) - { - return; - } - if( target_map.find( obj.get() ) != target_map.end() ) - { - //return; - } - - target_map[obj.get()] = obj; - - shared_ptr entity = dynamic_pointer_cast( obj ); - - if( entity ) - { - shared_ptr ele_assembly = dynamic_pointer_cast(entity); - if( ele_assembly ) - { - int assembly_id = ele_assembly->m_tag; - std::vector >& vec_is_decomposed_by = ele_assembly->m_IsDecomposedBy_inverse; - for( const auto& is_decomposed_weak_ptr : vec_is_decomposed_by ) - { - shared_ptr rel_dec; - if( is_decomposed_weak_ptr.expired() ) - { - continue; - } - shared_ptr is_decomposed_ptr(is_decomposed_weak_ptr); - if( is_decomposed_ptr ) - { - shared_ptr as_ifcpp_entity = is_decomposed_ptr; - collectDependentEntities(as_ifcpp_entity, target_map, resolveInverseAttributes); - } - } - } - - std::vector > > vec_attributes; - entity->getAttributes(vec_attributes); - - if( resolveInverseAttributes ) - { - entity->getAttributesInverse(vec_attributes); - } - - for( auto& vec_attribute : vec_attributes ) - { - shared_ptr& attribute = vec_attribute.second; - if( !attribute ) - { - // empty attribute - continue; - } - shared_ptr attribute_entity = dynamic_pointer_cast(attribute); - if( attribute_entity ) - { - if( target_map.find(attribute_entity.get()) == target_map.end() ) - { - target_map[attribute_entity.get()] = attribute_entity; - collectDependentEntities(attribute_entity, target_map, resolveInverseAttributes); - } - continue; - } - - shared_ptr attribute_object_vector = dynamic_pointer_cast(attribute); - if( attribute_object_vector ) - { - std::vector >& vec_of_attributes = attribute_object_vector->m_vec; - for( auto& attribute_object : vec_of_attributes ) - { - collectDependentEntities(attribute_object, target_map, resolveInverseAttributes); - } - continue; - } - } - return; - } - - shared_ptr attribute_object_vector = dynamic_pointer_cast(obj); - if( attribute_object_vector ) - { - std::vector >& vec_of_attributes = attribute_object_vector->m_vec; - for( auto& attribute_object : vec_of_attributes ) - { - collectDependentEntities(attribute_object, target_map, resolveInverseAttributes); - } - } -} +/* -*-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. +*/ + +#pragma warning( disable: 4996 ) +#include +#include +#include + +#include "IfcApplication.h" +#include "IfcAxis2Placement.h" +#include "IfcAxis2Placement3D.h" +#include "IfcBuilding.h" +#include "IfcCartesianPoint.h" +#include "IfcChangeActionEnum.h" +#include "IfcConversionBasedUnit.h" +#include "IfcDimensionCount.h" +#include "IfcDimensionalExponents.h" +#include "IfcDirection.h" +#include "IfcElementAssembly.h" +#include "IfcGeometricRepresentationContext.h" +#include "IfcGloballyUniqueId.h" +#include "IfcIdentifier.h" +#include "IfcLabel.h" +#include "IfcLengthMeasure.h" +#include "IfcMeasureWithUnit.h" +#include "IfcOrganization.h" +#include "IfcOwnerHistory.h" +#include "IfcPerson.h" +#include "IfcPersonAndOrganization.h" +#include "IfcPlaneAngleMeasure.h" +#include "IfcProduct.h" +#include "IfcProject.h" +#include "IfcReal.h" +#include "IfcRelationship.h" +#include "IfcRelAggregates.h" +#include "IfcRelContainedInSpatialStructure.h" +#include "IfcSite.h" +#include "IfcSIUnit.h" +#include "IfcSIUnitName.h" +#include "IfcStyledItem.h" +#include "IfcUnit.h" +#include "IfcUnitAssignment.h" +#include "IfcUnitEnum.h" +#include "IfcValue.h" +#include "ifcpp/IFC4X3/EntityFactory.h" +#include "ifcpp/reader/ReaderUtil.h" +#include "ifcpp/writer/WriterUtil.h" + +#include "AttributeObject.h" +#include "BuildingGuid.h" +#include "BuildingException.h" +#include "BuildingModel.h" +#include "UnitConverter.h" + +using namespace IFC4X3; + +BuildingModel::BuildingModel() +{ + m_unit_converter = std::make_shared( ); + m_unit_converter->setMessageTarget( this ); + initFileHeader( "IfcPlusPlus-export.ifc", "IfcPlusPlus" ); +} + +BuildingModel::~BuildingModel()= default; + +std::string getIfcSchemaVersionString(BuildingModel::SchemaVersionEnum version) +{ + switch (version) + { + case BuildingModel::IFC2X: return "IFC2X"; + case BuildingModel::IFC2X2: return "IFC2X2"; + case BuildingModel::IFC2X3: return "IFC2X3"; + case BuildingModel::IFC2X4: return "IFC2X4"; + case BuildingModel::IFC4: return "IFC4"; + case BuildingModel::IFC4X1: return "IFC4X1"; + case BuildingModel::IFC4X3: return "IFC4X3"; + } + return "IFC_VERSION_UNDEFINED"; +}; + +std::string BuildingModel::getIfcSchemaVersionOfLoadedFile() +{ + return getIfcSchemaVersionString(m_ifc_schema_version_loaded_file); +} + +std::string BuildingModel::getIfcSchemaVersionCurrent() +{ + return getIfcSchemaVersionString(m_ifc_schema_version_current); +} + +void BuildingModel::initIfcModel() +{ + clearIfcModel(); + + shared_ptr project( new IfcProject( 1 ) ); + insertEntity(project); + + shared_ptr person( new IfcPerson() ); + person->m_FamilyName = std::make_shared( "FamilyName" ); + person->m_GivenName = std::make_shared( "GivenName" ); + insertEntity(person); + + shared_ptr org( new IfcOrganization() ); + org->m_Name = std::make_shared( "OrganizationName" ); + insertEntity(org); + + shared_ptr person_org( new IfcPersonAndOrganization() ); + person_org->m_ThePerson = person; + person_org->m_TheOrganization = org; + insertEntity(person_org); + + shared_ptr app( new IfcApplication() ); + app->m_ApplicationDeveloper = org; + app->m_Version = std::make_shared( "1.0" ); + app->m_ApplicationFullName = std::make_shared( "IfcPlusPlus" ); + app->m_ApplicationIdentifier = std::make_shared( "IfcPlusPlus" ); + insertEntity(app); + + shared_ptr point( new IfcCartesianPoint() ); + point->m_Coordinates[0] = 0.0; + point->m_Coordinates[1] = 0.0; + point->m_Coordinates[2] = 0.0; + //point->m_Coordinates.push_back( std::make_shared( 0.0 ) ); + //point->m_Coordinates.push_back( std::make_shared( 0.0 ) ); + insertEntity(point); + + shared_ptr axis_placement( new IfcAxis2Placement3D() ); + axis_placement->m_Location = point; + insertEntity(axis_placement); + + shared_ptr owner_history ( new IfcOwnerHistory() ); + owner_history->m_OwningUser = person_org; + owner_history->m_OwningApplication = app; + owner_history->m_ChangeAction = std::make_shared( IfcChangeActionEnum::ENUM_ADDED ); + insertEntity(owner_history); + + shared_ptr dim_exp( new IfcDimensionalExponents() ); + dim_exp->m_LengthExponent = 0; + dim_exp->m_MassExponent = 0; + dim_exp->m_TimeExponent = 0; + dim_exp->m_ElectricCurrentExponent = 0; + dim_exp->m_ThermodynamicTemperatureExponent = 0; + dim_exp->m_AmountOfSubstanceExponent = 0; + dim_exp->m_LuminousIntensityExponent = 0; + insertEntity(dim_exp); + + // length unit [m] + shared_ptr si_unit( new IfcSIUnit() ); + si_unit->m_UnitType = std::make_shared( IfcUnitEnum::ENUM_LENGTHUNIT ); + si_unit->m_Name = std::make_shared( IfcSIUnitName::ENUM_METRE ); + insertEntity(si_unit); + + // plane unit [rad] + shared_ptr plane_angle_unit( new IfcSIUnit() ); + plane_angle_unit->m_UnitType = std::make_shared( IfcUnitEnum::ENUM_PLANEANGLEUNIT ); + plane_angle_unit->m_Name = std::make_shared( IfcSIUnitName::ENUM_RADIAN ); + insertEntity(plane_angle_unit); + + // assign units + shared_ptr unit_assignment( new IfcUnitAssignment() ); + unit_assignment->m_Units.push_back( si_unit ); + unit_assignment->m_Units.push_back( plane_angle_unit ); + insertEntity(unit_assignment); + + project->m_GlobalId = std::make_shared( createBase64Uuid() ); + project->m_OwnerHistory = owner_history; + project->m_Name = std::make_shared( "IfcPlusPlus project" ); + project->m_UnitsInContext = unit_assignment; + + // create default IfcSite + shared_ptr site( new IfcSite() ); + site->m_GlobalId = std::make_shared( createBase64Uuid() ); + site->m_OwnerHistory = owner_history; + site->m_Name = std::make_shared( "Site" ); + insertEntity(site); + + shared_ptr rel_aggregates_site( new IfcRelAggregates() ); + rel_aggregates_site->m_RelatingObject = project; + insertEntity(rel_aggregates_site); + + // create default Building + shared_ptr building( new IfcBuilding() ); + building->m_GlobalId = std::make_shared( createBase64Uuid() ); + building->m_OwnerHistory = owner_history; + building->m_Name = std::make_shared( "Building" ); + insertEntity( building ); + + // set up world coordinate system + shared_ptr axis( new IfcDirection() ); + insertEntity(axis); + axis->m_DirectionRatios.push_back( std::make_shared( 1.0 ) ); + axis->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); + axis->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); + + shared_ptr ref_direction( new IfcDirection() ); + insertEntity(ref_direction); + ref_direction->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); + ref_direction->m_DirectionRatios.push_back( std::make_shared( 1.0 ) ); + ref_direction->m_DirectionRatios.push_back( std::make_shared( 0.0 ) ); + + shared_ptr location( new IfcCartesianPoint() ); + insertEntity(location); + location->m_Coordinates[0] = 0.0; + location->m_Coordinates[1] = 0.0; + location->m_Coordinates[2] = 0.0; + //location->m_Coordinates.push_back( std::make_shared(0.0 ) ); + //location->m_Coordinates.push_back( std::make_shared(0.0 ) ); + + shared_ptr world_coordinate_system( new IfcAxis2Placement3D() ); + insertEntity(world_coordinate_system); + world_coordinate_system->m_Location = location; + world_coordinate_system->m_Axis = axis; + world_coordinate_system->m_RefDirection = ref_direction; + + // 3d representation context + shared_ptr geom_context( new IfcGeometricRepresentationContext() ); + insertEntity(geom_context); + geom_context->m_CoordinateSpaceDimension = std::make_shared( 3 ); + geom_context->m_WorldCoordinateSystem = world_coordinate_system; + + updateCache(); +} + +void BuildingModel::initCopyIfcModel( const shared_ptr& other ) +{ + clearIfcModel(); + + shared_ptr project = other->getIfcProject(); + shared_ptr projectAsRoot(project); + + std::map > map_entities_copy; + map_entities_copy[projectAsRoot.get()] = projectAsRoot; + + collectDependentEntities( projectAsRoot, map_entities_copy, false); + + for( auto it = map_entities_copy.begin(); it != map_entities_copy.end(); ++it ) + { + shared_ptr bo = it->second; + shared_ptr entity = dynamic_pointer_cast(bo); + if( entity ) + { + insertEntity(entity); + } + } + + updateCache(); +} + +shared_ptr BuildingModel::getIfcProject() +{ + return m_ifc_project; +} +shared_ptr BuildingModel::getIfcGeometricRepresentationContext3D() +{ + return m_geom_context_3d; +} + +void BuildingModel::setIfcProject( shared_ptr project ) +{ + m_ifc_project = project; +} + +void BuildingModel::setIfcGeometricRepresentationContext3D( shared_ptr& context ) +{ + m_geom_context_3d = context; +} + +void BuildingModel::setUnitConverter( shared_ptr& uc ) +{ + m_unit_converter = uc; +} + +void BuildingModel::setMapIfcEntities( const std::map >& map ) +{ + clearIfcModel(); + m_map_entities.clear(); + m_map_entities = map; + updateCache(); + // todo: check model consistency +} + +void BuildingModel::insertEntity( shared_ptr e, bool overwrite_existing, bool warn_on_existing_entities ) +{ + if( !e ) + { + return; + } + int tag = e->m_tag; + if( tag < 0 ) + { + int next_unused_id = getMaxUsedEntityId() + 1; + e->m_tag = next_unused_id; + tag = next_unused_id; + } + + auto it_find = m_map_entities.find( tag ); + if( it_find != m_map_entities.end() ) + { + // key already exists + if( overwrite_existing ) + { + it_find->second = e; + } + else + { + if( warn_on_existing_entities ) + { + messageCallback( "Entity already in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); + } + } + } + else + { + // the key does not exist in the map + m_map_entities.insert( it_find, std::map >::value_type( tag, e ) ); + } +#ifdef _DEBUG + shared_ptr product = dynamic_pointer_cast( e ); + if( product ) + { + if( !product->m_GlobalId ) + { + messageCallback( "IfcProduct->m_GlobalId not set", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, product.get() ); + return; + } + if( product->m_GlobalId->m_value.length() < 22 ) + { + messageCallback( "IfcProduct->m_GlobalId.length() < 22", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, product.get() ); + } + } +#endif + + // TODO: if type is IfcRoot (or subtype), and GlobalID not set, create one +} + +void BuildingModel::removeEntity( shared_ptr e ) +{ + if( !e ) + { + messageCallback( "Entity not valid", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); + return; + } + int remove_id = e->m_tag; + auto it_find = m_map_entities.find(remove_id); + if( it_find == m_map_entities.end() ) + { + messageCallback( "Entity not found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__, e.get() ); + return; + } + shared_ptr entity_found = it_find->second; + entity_found->unlinkFromInverseCounterparts(); + BuildingEntity* entity_remove_ptr = e.get(); + + if( entity_found.use_count() > 1 ) + { + // find references to this entity and remove them + std::vector > > vec_attributes_inverse; + entity_found->getAttributesInverse( vec_attributes_inverse ); + for(auto & ii : vec_attributes_inverse) + { + shared_ptr& attribute_inverse = ii.second; + + shared_ptr attribute_inverse_entity = dynamic_pointer_cast( attribute_inverse ); + if( attribute_inverse_entity ) + { + std::vector > > vec_attributes; + attribute_inverse_entity->getAttributes( vec_attributes ); + for( size_t jj = 0; jj < vec_attributes.size(); ++jj ) + { + shared_ptr& attribute = vec_attributes[jj].second; + if( attribute.get() == entity_remove_ptr ) + { + vec_attributes.erase( vec_attributes.begin() + jj ); + if( jj > 0 ) + { + --jj; + } + } + } + } + } + } + m_map_entities.erase( it_find ); +} + +void BuildingModel::removeEntity( int tag ) +{ + auto it_find = m_map_entities.find(tag); + if( it_find != m_map_entities.end() ) + { + shared_ptr entity_found = it_find->second; + removeEntity( entity_found ); + } +} + +int BuildingModel::getMaxUsedEntityId() +{ + if( m_map_entities.empty() ) + { + return 0; + } + // since it is an ordered map, we can just take the key of the last entry + int max_id = m_map_entities.rbegin()->first; + return max_id; +} + +void BuildingModel::removeUnreferencedEntities() +{ + for( auto it_entities = m_map_entities.begin(); it_entities != m_map_entities.end(); ) + { + shared_ptr entity = it_entities->second; + if( entity.use_count() < 2 ) + { + // if use_count is only 1, the entity is only referenced in m_map_entities, so no other entity or other object holds a shared_ptr + bool erase_entity = true; + shared_ptr ifc_relationship = dynamic_pointer_cast( entity ); + if( ifc_relationship ) + { + // relationship objects may only reference some entities while not being referenced itself + std::vector > > vec_attributes; + ifc_relationship->getAttributes( vec_attributes ); + if( vec_attributes.size() > 4 ) + { + // check if the object has relevant pointers + size_t num_relevant_attributes = 0; + // first 4 attributes are GlobalId, OwnerHistory, Name, Description + for( size_t ii = 4; ii < vec_attributes.size(); ++ii ) + { + shared_ptr attribute = vec_attributes[ii].second; + if( attribute ) + { + shared_ptr attribute_vec = dynamic_pointer_cast( attribute ); + if( attribute_vec ) + { + if( !attribute_vec->m_vec.empty() ) + { + ++num_relevant_attributes; + } + } + else + { + shared_ptr attribute_entity = dynamic_pointer_cast( attribute ); + if( attribute_entity ) + { + ++num_relevant_attributes; + } + } + } + } + if( num_relevant_attributes > 0 ) + { + erase_entity = false; + } + } + } + else + { + shared_ptr ifc_styled_item = dynamic_pointer_cast( entity ); + if( ifc_styled_item ) + { + if( ifc_styled_item->m_Item ) + { + erase_entity = false; + } + } + + } + if( erase_entity ) + { + entity->unlinkFromInverseCounterparts(); + auto erase_it = it_entities; +#ifdef _DEBUG + int tag = entity->m_tag; + const char* entity_className = IFC4X3::EntityFactory::getStringForClassID(entity->classID()); +#endif + ++it_entities; + // TODO: check here if it really has been removed + removeEntity( entity ); + } + else + { + ++it_entities; + } + } + else + { + ++it_entities; + } + } +} + +void BuildingModel::initFileHeader( const std::string& fileName, const std::string& generatingApplication ) +{ + m_file_name = fileName; + std::string filename_escaped = encodeStepString( fileName ); + std::stringstream strs; + strs << "HEADER;" << std::endl; + strs << "FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');" << std::endl; + strs << "FILE_NAME('" << filename_escaped.c_str() << "','"; + + //2011-04-21T14:25:12 + time_t rawtime; + struct tm* timeinfo; + char buffer[80]; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", timeinfo); + std::string str(buffer); + + strs << buffer; + strs << "',(''),('',''),'',' " << generatingApplication << "','');" << std::endl; + strs << "FILE_SCHEMA(('" << getIfcSchemaVersionCurrent() << "'));" << std::endl; + strs << "ENDSEC;" << std::endl; + + m_file_header = strs.str(); +} + +void BuildingModel::setFileHeader( const std::string& header ) +{ + m_file_header = header; +} + +void BuildingModel::setFileDescription( const std::string& description ) +{ + m_IFC_FILE_DESCRIPTION = description; +} + +void BuildingModel::setFileName( const std::string& name ) +{ + m_IFC_FILE_NAME = name; +} + +void BuildingModel::clearIfcModel() +{ + m_map_entities.clear(); + m_ifc_project.reset(); + m_geom_context_3d.reset(); + m_ifc_schema_version_current = IFC4X1; + m_ifc_schema_version_loaded_file = IFC4X1; + m_IFC_FILE_NAME = ""; + m_IFC_FILE_DESCRIPTION = ""; + m_file_header = ""; + m_unit_converter->resetUnitFactors(); +} + +void BuildingModel::resetIfcModel() +{ + clearIfcModel(); + initIfcModel(); + updateCache(); +} + +void BuildingModel::updateCache() +{ + bool found_project = false; + bool found_geom_context = false; + + shared_ptr keep_project = m_ifc_project; + m_ifc_project.reset(); + + // try to find IfcProject and IfcGeometricRepresentationContext + for( auto it=m_map_entities.begin(); it!=m_map_entities.end(); ++it ) + { + shared_ptr obj = it->second; + if( obj->classID() == IFC4X3::IFCPROJECT ) + { + if( m_ifc_project ) + { + messageCallback("More than one IfcProject in model", StatusCallback::MESSAGE_TYPE_ERROR, __FUNC__, m_ifc_project.get()); + } + m_ifc_project = dynamic_pointer_cast(obj); + + if( m_ifc_project ) + { + found_project = true; + if( found_geom_context ) + { + break; + } + } + else + { + std::cout << "BuildingModel::updateCache: IfcProject found but dynamic_cast failed. Is RTTI enabled?" << std::endl; + } + } + else if( obj->classID() == IFC4X3::IFCGEOMETRICREPRESENTATIONCONTEXT ) + { + shared_ptr context = dynamic_pointer_cast(obj); + if( context ) + { + if( context->m_CoordinateSpaceDimension ) + { + if( context->m_CoordinateSpaceDimension->m_value == 3 ) + { + m_geom_context_3d = context; + found_geom_context = true; + if( found_project ) + { + break; + } + } + } + } + } + } + + if( found_project ) + { + m_unit_converter->setIfcProject( m_ifc_project ); + } + else + { + std::cout << "BuildingModel::updateCache, IfcProject not found in model with " << m_map_entities.size() << " items" << std::endl; + } +} + +void BuildingModel::clearCache() +{ + m_ifc_project.reset(); + m_geom_context_3d.reset(); +} + +void BuildingModel::resolveInverseAttributes() +{ + for( auto it = m_map_entities.begin(); it != m_map_entities.end(); ++it ) + { + shared_ptr entity = it->second; + if( !entity ) + { + continue; + } + + entity->setInverseCounterparts( entity ); + } +} + +void BuildingModel::unsetInverseAttributes() +{ + for( auto it = m_map_entities.begin(); it != m_map_entities.end(); ++it ) + { + shared_ptr entity = it->second; + if( !entity ) + { + continue; + } + entity->unlinkFromInverseCounterparts(); + } +} + +void BuildingModel::collectDependentEntities( shared_ptr obj, std::map >& target_map, bool resolveInverseAttributes) +{ + if( !obj ) + { + return; + } + if( target_map.find( obj.get() ) != target_map.end() ) + { + //return; + } + + target_map[obj.get()] = obj; + + shared_ptr entity = dynamic_pointer_cast( obj ); + + if( entity ) + { + shared_ptr ele_assembly = dynamic_pointer_cast(entity); + if( ele_assembly ) + { + int assembly_id = ele_assembly->m_tag; + std::vector >& vec_is_decomposed_by = ele_assembly->m_IsDecomposedBy_inverse; + for( const auto& is_decomposed_weak_ptr : vec_is_decomposed_by ) + { + shared_ptr rel_dec; + if( is_decomposed_weak_ptr.expired() ) + { + continue; + } + shared_ptr is_decomposed_ptr(is_decomposed_weak_ptr); + if( is_decomposed_ptr ) + { + shared_ptr as_ifcpp_entity = is_decomposed_ptr; + collectDependentEntities(as_ifcpp_entity, target_map, resolveInverseAttributes); + } + } + } + + std::vector > > vec_attributes; + entity->getAttributes(vec_attributes); + + if( resolveInverseAttributes ) + { + entity->getAttributesInverse(vec_attributes); + } + + for( auto& vec_attribute : vec_attributes ) + { + shared_ptr& attribute = vec_attribute.second; + if( !attribute ) + { + // empty attribute + continue; + } + shared_ptr attribute_entity = dynamic_pointer_cast(attribute); + if( attribute_entity ) + { + if( target_map.find(attribute_entity.get()) == target_map.end() ) + { + target_map[attribute_entity.get()] = attribute_entity; + collectDependentEntities(attribute_entity, target_map, resolveInverseAttributes); + } + continue; + } + + shared_ptr attribute_object_vector = dynamic_pointer_cast(attribute); + if( attribute_object_vector ) + { + std::vector >& vec_of_attributes = attribute_object_vector->m_vec; + for( auto& attribute_object : vec_of_attributes ) + { + collectDependentEntities(attribute_object, target_map, resolveInverseAttributes); + } + continue; + } + } + return; + } + + shared_ptr attribute_object_vector = dynamic_pointer_cast(obj); + if( attribute_object_vector ) + { + std::vector >& vec_of_attributes = attribute_object_vector->m_vec; + for( auto& attribute_object : vec_of_attributes ) + { + collectDependentEntities(attribute_object, target_map, resolveInverseAttributes); + } + } +} diff --git a/IfcPlusPlus/src/ifcpp/model/BuildingModel.h b/IfcPlusPlus/src/ifcpp/model/BuildingModel.h index 2e0bf10af..016380777 100644 --- a/IfcPlusPlus/src/ifcpp/model/BuildingModel.h +++ b/IfcPlusPlus/src/ifcpp/model/BuildingModel.h @@ -1,108 +1,108 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include "BasicTypes.h" -#include "StatusCallback.h" - -class BuildingObject; -class BuildingEntity; -class UnitConverter; -namespace IFC4X3 -{ - class IfcProject; - class IfcGeometricRepresentationContext; -} - -class IFCQUERY_EXPORT BuildingModel : public StatusCallback -{ -public: - BuildingModel(); - ~BuildingModel() override; - - enum SchemaVersionEnum { IFC_VERSION_UNDEFINED, IFC_VERSION_UNKNOWN, IFC2X, IFC2X2, IFC2X3, IFC2X4, IFC4, IFC4X1, IFC4X3 }; - std::map >& getMapIfcEntities() { return m_map_entities; } - void setMapIfcEntities(const std::map >& map); - void insertEntity(shared_ptr e, bool overwrite_existing = false, bool warn_on_existing_entities = true); - void removeEntity(shared_ptr e); - void removeEntity(int tag); - void removeUnreferencedEntities(); - - /*! \brief Method getMaxUsedEntityId. Return the highest entity id in the model. */ - int getMaxUsedEntityId(); - shared_ptr getIfcProject(); - shared_ptr getIfcGeometricRepresentationContext3D(); - shared_ptr& getUnitConverter() { return m_unit_converter; } - - /*! \brief Method getIfcSchemaVersion. Returns the IFC version of the loaded file */ - SchemaVersionEnum& getIfcSchemaVersionEnumOfLoadedFile() { return m_ifc_schema_version_loaded_file; } - std::string getIfcSchemaVersionOfLoadedFile(); - - /*! \brief Method getIfcSchemaVersionCurrent. Returns the IFC version after loading. It is the newest implemented IFC version. IFC version of the loaded file may be older */ - SchemaVersionEnum& getIfcSchemaVersionEnumCurrent() { return m_ifc_schema_version_current; } - std::string getIfcSchemaVersionCurrent(); - - const std::string& getFileHeader() { return m_file_header; } - const std::string& getFileDescription() { return m_IFC_FILE_DESCRIPTION; } - const std::string& getFileName() { return m_IFC_FILE_NAME; } - - void setFileHeader(const std::string& header); - void setFileDescription(const std::string& description); - void setFileName(const std::string& name); - void setIfcProject(shared_ptr project); - void setUnitConverter(shared_ptr& uc); - void setIfcGeometricRepresentationContext3D(shared_ptr& context); - void resolveInverseAttributes(); - void unsetInverseAttributes(); - void clearIfcModel(); - void initIfcModel(); - void initCopyIfcModel(const shared_ptr& other); - void resetIfcModel(); - void updateCache(); - void clearCache(); - void initFileHeader(const std::string& fileName, const std::string& generatingApplication); - static void collectDependentEntities(shared_ptr entity, std::map >& target_map, bool resolveInverseAttributes); - - void cancelLoading() - { - m_cancelLoading = true; - } - bool isLoadingCancelled() - { - return m_cancelLoading; - } - - friend class ReaderSTEP; - friend class ReaderXML; - -private: - std::map > m_map_entities; - shared_ptr m_ifc_project; - shared_ptr m_geom_context_3d; - shared_ptr m_unit_converter; - std::string m_file_name; - std::string m_file_header; - std::string m_IFC_FILE_DESCRIPTION; - std::string m_IFC_FILE_NAME; - SchemaVersionEnum m_ifc_schema_version_loaded_file = IFC4X3; - SchemaVersionEnum m_ifc_schema_version_current = IFC4X3; - bool m_cancelLoading = false; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include "BasicTypes.h" +#include "StatusCallback.h" + +class BuildingObject; +class BuildingEntity; +class UnitConverter; +namespace IFC4X3 +{ + class IfcProject; + class IfcGeometricRepresentationContext; +} + +class IFCQUERY_EXPORT BuildingModel : public StatusCallback +{ +public: + BuildingModel(); + ~BuildingModel() override; + + enum SchemaVersionEnum { IFC_VERSION_UNDEFINED, IFC_VERSION_UNKNOWN, IFC2X, IFC2X2, IFC2X3, IFC2X4, IFC4, IFC4X1, IFC4X3 }; + std::map >& getMapIfcEntities() { return m_map_entities; } + void setMapIfcEntities(const std::map >& map); + void insertEntity(shared_ptr e, bool overwrite_existing = false, bool warn_on_existing_entities = true); + void removeEntity(shared_ptr e); + void removeEntity(int tag); + void removeUnreferencedEntities(); + + /*! \brief Method getMaxUsedEntityId. Return the highest entity id in the model. */ + int getMaxUsedEntityId(); + shared_ptr getIfcProject(); + shared_ptr getIfcGeometricRepresentationContext3D(); + shared_ptr& getUnitConverter() { return m_unit_converter; } + + /*! \brief Method getIfcSchemaVersion. Returns the IFC version of the loaded file */ + SchemaVersionEnum& getIfcSchemaVersionEnumOfLoadedFile() { return m_ifc_schema_version_loaded_file; } + std::string getIfcSchemaVersionOfLoadedFile(); + + /*! \brief Method getIfcSchemaVersionCurrent. Returns the IFC version after loading. It is the newest implemented IFC version. IFC version of the loaded file may be older */ + SchemaVersionEnum& getIfcSchemaVersionEnumCurrent() { return m_ifc_schema_version_current; } + std::string getIfcSchemaVersionCurrent(); + + const std::string& getFileHeader() { return m_file_header; } + const std::string& getFileDescription() { return m_IFC_FILE_DESCRIPTION; } + const std::string& getFileName() { return m_IFC_FILE_NAME; } + + void setFileHeader(const std::string& header); + void setFileDescription(const std::string& description); + void setFileName(const std::string& name); + void setIfcProject(shared_ptr project); + void setUnitConverter(shared_ptr& uc); + void setIfcGeometricRepresentationContext3D(shared_ptr& context); + void resolveInverseAttributes(); + void unsetInverseAttributes(); + void clearIfcModel(); + void initIfcModel(); + void initCopyIfcModel(const shared_ptr& other); + void resetIfcModel(); + void updateCache(); + void clearCache(); + void initFileHeader(const std::string& fileName, const std::string& generatingApplication); + static void collectDependentEntities(shared_ptr entity, std::map >& target_map, bool resolveInverseAttributes); + + void cancelLoading() + { + m_cancelLoading = true; + } + bool isLoadingCancelled() + { + return m_cancelLoading; + } + + friend class ReaderSTEP; + friend class ReaderXML; + +private: + std::map > m_map_entities; + shared_ptr m_ifc_project; + shared_ptr m_geom_context_3d; + shared_ptr m_unit_converter; + std::string m_file_name; + std::string m_file_header; + std::string m_IFC_FILE_DESCRIPTION; + std::string m_IFC_FILE_NAME; + SchemaVersionEnum m_ifc_schema_version_loaded_file = IFC4X3; + SchemaVersionEnum m_ifc_schema_version_current = IFC4X3; + bool m_cancelLoading = false; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/BuildingObject.h b/IfcPlusPlus/src/ifcpp/model/BuildingObject.h index 96621d158..226ae6cec 100644 --- a/IfcPlusPlus/src/ifcpp/model/BuildingObject.h +++ b/IfcPlusPlus/src/ifcpp/model/BuildingObject.h @@ -1,77 +1,77 @@ -/* -*-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. -*/ - -#pragma once - -#include "GlobalDefines.h" -#include -#include -#include -#include -#include -#include "ifcpp/model/BasicTypes.h" - -enum LogicalEnum { LOGICAL_TRUE, LOGICAL_FALSE, LOGICAL_UNKNOWN }; - -class IFCQUERY_EXPORT BuildingObject -{ -public: - virtual uint32_t classID() const = 0; - virtual void getStepParameter( std::stringstream& stream, bool is_select_type, size_t precision ) const = 0; -}; - -// ENTITY -class IFCQUERY_EXPORT BuildingEntity : virtual public BuildingObject -{ -public: - BuildingEntity() : m_tag(-1) - { - } - - BuildingEntity( int tag ) : m_tag(tag) - { - } - - virtual ~BuildingEntity() - { - } - virtual uint32_t classID() const override = 0; - - /** \brief Appends a line in STEP format to stream, including all attributes. */ - virtual void getStepLine(std::stringstream& stream, size_t precision) const = 0; - - /** \brief Reads all attributes from args. References to other entities are taken from map_entities. */ - virtual void readStepArguments(const std::vector& args, const std::map >& map_entities, std::stringstream& errorStream) = 0; - - /** \brief Number of attributes, including inherited attributes, without inverse attributes */ - virtual uint8_t getNumAttributes() const = 0; - - /** \brief Adds all attributes (including inherited attributes) with name and value to vec_attributes. Single attributes can be accessed directly, without this method.*/ - virtual void getAttributes(std::vector > >& vec_attributes) const = 0; - - /** \brief Same as getAttributes, but for inverse attributes.*/ - virtual void getAttributesInverse(std::vector > >& map_attributes) const = 0; - - /** \brief If there is a reference from object a to object b, and b has an inverse reference to a, the inverse reference is established here.*/ - virtual void setInverseCounterparts(shared_ptr ptr_self) = 0; - - /** \brief Removes the inverse reference established in setInverseCounterparts.*/ - virtual void unlinkFromInverseCounterparts() {} - - /// Entity ID (same as STEP ID) - int m_tag; -}; +/* -*-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. +*/ + +#pragma once + +#include "GlobalDefines.h" +#include +#include +#include +#include +#include +#include "ifcpp/model/BasicTypes.h" + +enum LogicalEnum { LOGICAL_TRUE, LOGICAL_FALSE, LOGICAL_UNKNOWN }; + +class IFCQUERY_EXPORT BuildingObject +{ +public: + virtual uint32_t classID() const = 0; + virtual void getStepParameter( std::stringstream& stream, bool is_select_type, size_t precision ) const = 0; +}; + +// ENTITY +class IFCQUERY_EXPORT BuildingEntity : virtual public BuildingObject +{ +public: + BuildingEntity() : m_tag(-1) + { + } + + BuildingEntity( int tag ) : m_tag(tag) + { + } + + virtual ~BuildingEntity() + { + } + virtual uint32_t classID() const override = 0; + + /** \brief Appends a line in STEP format to stream, including all attributes. */ + virtual void getStepLine(std::stringstream& stream, size_t precision) const = 0; + + /** \brief Reads all attributes from args. References to other entities are taken from map_entities. */ + virtual void readStepArguments(const std::vector& args, const std::map >& map_entities, std::stringstream& errorStream) = 0; + + /** \brief Number of attributes, including inherited attributes, without inverse attributes */ + virtual uint8_t getNumAttributes() const = 0; + + /** \brief Adds all attributes (including inherited attributes) with name and value to vec_attributes. Single attributes can be accessed directly, without this method.*/ + virtual void getAttributes(std::vector > >& vec_attributes) const = 0; + + /** \brief Same as getAttributes, but for inverse attributes.*/ + virtual void getAttributesInverse(std::vector > >& map_attributes) const = 0; + + /** \brief If there is a reference from object a to object b, and b has an inverse reference to a, the inverse reference is established here.*/ + virtual void setInverseCounterparts(shared_ptr ptr_self) = 0; + + /** \brief Removes the inverse reference established in setInverseCounterparts.*/ + virtual void unlinkFromInverseCounterparts() {} + + /// Entity ID (same as STEP ID) + int m_tag; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/GlobalDefines.h b/IfcPlusPlus/src/ifcpp/model/GlobalDefines.h index 2523a5aa4..3f402251d 100644 --- a/IfcPlusPlus/src/ifcpp/model/GlobalDefines.h +++ b/IfcPlusPlus/src/ifcpp/model/GlobalDefines.h @@ -1,41 +1,41 @@ -/* -*-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. -*/ - -#pragma once - -#pragma warning (disable: 4250 4251 4996) -#ifndef BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE - #define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE -#endif - -// IFCQUERY_LIB needs to be defined in the settings of the library (IfcPlusPlus). Do not define IFCQUERY_LIB in your application though - -#if defined(_MSC_VER) - #ifdef IFCQUERY_LIB - #define IFCQUERY_EXPORT __declspec(dllexport) - #else - #ifdef IFCQUERY_STATIC_LIB - #define IFCQUERY_EXPORT - #else - #define IFCQUERY_EXPORT __declspec(dllimport) - #endif - #endif -#endif - -#ifndef IFCQUERY_EXPORT -#define IFCQUERY_EXPORT // Define empty if not necessary -#endif +/* -*-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. +*/ + +#pragma once + +#pragma warning (disable: 4250 4251 4996) +#ifndef BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE + #define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE +#endif + +// IFCQUERY_LIB needs to be defined in the settings of the library (IfcPlusPlus). Do not define IFCQUERY_LIB in your application though + +#if defined(_MSC_VER) + #ifdef IFCQUERY_LIB + #define IFCQUERY_EXPORT __declspec(dllexport) + #else + #ifdef IFCQUERY_STATIC_LIB + #define IFCQUERY_EXPORT + #else + #define IFCQUERY_EXPORT __declspec(dllimport) + #endif + #endif +#endif + +#ifndef IFCQUERY_EXPORT +#define IFCQUERY_EXPORT // Define empty if not necessary +#endif diff --git a/IfcPlusPlus/src/ifcpp/model/StatusCallback.h b/IfcPlusPlus/src/ifcpp/model/StatusCallback.h index d79955779..c8ca0ba0f 100644 --- a/IfcPlusPlus/src/ifcpp/model/StatusCallback.h +++ b/IfcPlusPlus/src/ifcpp/model/StatusCallback.h @@ -1,208 +1,208 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include -#include - -#include "BasicTypes.h" -#include "GlobalDefines.h" - -class BuildingObject; - -class IFCQUERY_EXPORT StatusCallback -{ -public: - enum MessageType - { - MESSAGE_TYPE_UNKNOWN, - MESSAGE_TYPE_GENERAL_MESSAGE, - MESSAGE_TYPE_PROGRESS_VALUE, //\brief Progress mechanism to update progress bar or similar. - MESSAGE_TYPE_PROGRESS_TEXT, //\brief Progress mechanism to update text in progress bar or similar. - MESSAGE_TYPE_MINOR_WARNING, - MESSAGE_TYPE_WARNING, - MESSAGE_TYPE_ERROR, - MESSAGE_TYPE_CLEAR_MESSAGES, - MESSAGE_TYPE_CANCELED - }; - /*\class Message - \brief Combines all information about a status message, being it a general message, a warning, error, or a notification about a progress (for example during reading of a file). - */ - class Message - { - public: - //\brief Default constructor. - Message() - { - m_message_type = MessageType::MESSAGE_TYPE_UNKNOWN; - m_reporting_function = ""; - m_entity = nullptr; - m_progress_value = -1; - } - - std::string m_message_text; // Message text. - MessageType m_message_type; // Type of message (warning, error etc.). - const char* m_reporting_function; // Function name where the message is sent from. You can use the __FUNC__ macro from BuildingException.h. - BuildingObject* m_entity; // IFC entity in case the message applies to a certain entity. - - double m_progress_value; // Value of progress [0...1]. If negative value is given, the progress itself is ignored, for example when only the progress text is updated. - std::string m_progress_type; // Type of progress, for example "parse", "geometry". - std::string m_progress_text; // A text that describes the current actions. It can be used for example to set a text on the progress bar. - }; - - StatusCallback() = default; - virtual ~StatusCallback() = default; - - using MessageCallbackType = std::function)>; - using CancelCallbackType = std::function; - - //\brief error callback mechanism to show messages in gui - void setMessageCallBack(const MessageCallbackType& cb) - { - m_func_call_on_message = cb; - } - - virtual void setMessageTarget(StatusCallback* other) - { - m_redirect_target = other; - } - - virtual void unsetMessageCallBack() - { - m_func_call_on_message = nullptr; - } - - virtual void setCancelCheck(const CancelCallbackType& cb) - { - m_func_check_cancel = cb; - } - - virtual void unsetCancelCheck() - { - m_func_check_cancel = nullptr; - } - - //\brief trigger the callback to pass a message, warning, or error, for example to store in a logfile - virtual void messageCallback(shared_ptr m) - { - if (m_redirect_target) - { - m_redirect_target->messageCallback(m); - return; - } - -#ifdef _DEBUG - if (!m_func_call_on_message ) - { - if (m) - { - switch (m->m_message_type) - { - case MESSAGE_TYPE_UNKNOWN: - case MESSAGE_TYPE_GENERAL_MESSAGE: - case MESSAGE_TYPE_MINOR_WARNING: - case MESSAGE_TYPE_WARNING: - case MESSAGE_TYPE_ERROR: - std::wcout << L"messageCallback not set. Lost message: " << m->m_message_text.c_str() << std::endl; - break; - } - } - } - - if (m->m_message_type == MESSAGE_TYPE_ERROR) - { - std::wcout << L"error: " << m->m_message_text.c_str() << std::endl; - } -#endif - - if (m_func_call_on_message) - { - std::lock_guard lock(m_writelock); - m_func_call_on_message(m); - } - } - - //\brief check if cancellation has been requested. - virtual bool isCanceled() - { - if (m_redirect_target) - { - return m_redirect_target->isCanceled(); - } - - if (m_func_check_cancel) - { - std::lock_guard lock(m_writelock); - return m_func_check_cancel(); - } - - return false; - } - - virtual void messageCallback(const std::string& message_text, MessageType type, const char* reporting_function, BuildingObject* entity = nullptr) - { - shared_ptr message(new Message()); - message->m_message_text.assign(message_text.begin(), message_text.end()); - message->m_message_type = type; - message->m_reporting_function = reporting_function; - message->m_entity = entity; - messageCallback(message); - } - virtual void progressValueCallback(double progress_value, const std::string& progress_type) - { - shared_ptr progress_message(new Message()); - progress_message->m_message_type = MessageType::MESSAGE_TYPE_PROGRESS_VALUE; - progress_message->m_progress_value = progress_value; - progress_message->m_progress_type.assign(progress_type); - messageCallback(progress_message); - } - virtual void progressTextCallback(const std::string& progress_text) - { - shared_ptr progress_message(new Message()); - progress_message->m_message_type = MessageType::MESSAGE_TYPE_PROGRESS_TEXT; - progress_message->m_progress_value = -1; - progress_message->m_progress_text.assign(progress_text); - messageCallback(progress_message); - } - virtual void clearMessagesCallback() - { - shared_ptr progress_message(new Message()); - progress_message->m_message_type = MessageType::MESSAGE_TYPE_CLEAR_MESSAGES; - messageCallback(progress_message); - } - virtual void canceledCallback() - { - shared_ptr canceled_message(new Message()); - canceled_message->m_message_type = MessageType::MESSAGE_TYPE_CANCELED; - messageCallback(canceled_message); - } - -protected: - //\brief Message callback function - MessageCallbackType m_func_call_on_message; - - //\brief Cancel callback - CancelCallbackType m_func_check_cancel; - - StatusCallback* m_redirect_target = nullptr; - - std::mutex m_writelock; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "BasicTypes.h" +#include "GlobalDefines.h" + +class BuildingObject; + +class IFCQUERY_EXPORT StatusCallback +{ +public: + enum MessageType + { + MESSAGE_TYPE_UNKNOWN, + MESSAGE_TYPE_GENERAL_MESSAGE, + MESSAGE_TYPE_PROGRESS_VALUE, //\brief Progress mechanism to update progress bar or similar. + MESSAGE_TYPE_PROGRESS_TEXT, //\brief Progress mechanism to update text in progress bar or similar. + MESSAGE_TYPE_MINOR_WARNING, + MESSAGE_TYPE_WARNING, + MESSAGE_TYPE_ERROR, + MESSAGE_TYPE_CLEAR_MESSAGES, + MESSAGE_TYPE_CANCELED + }; + /*\class Message + \brief Combines all information about a status message, being it a general message, a warning, error, or a notification about a progress (for example during reading of a file). + */ + class Message + { + public: + //\brief Default constructor. + Message() + { + m_message_type = MessageType::MESSAGE_TYPE_UNKNOWN; + m_reporting_function = ""; + m_entity = nullptr; + m_progress_value = -1; + } + + std::string m_message_text; // Message text. + MessageType m_message_type; // Type of message (warning, error etc.). + const char* m_reporting_function; // Function name where the message is sent from. You can use the __FUNC__ macro from BuildingException.h. + BuildingObject* m_entity; // IFC entity in case the message applies to a certain entity. + + double m_progress_value; // Value of progress [0...1]. If negative value is given, the progress itself is ignored, for example when only the progress text is updated. + std::string m_progress_type; // Type of progress, for example "parse", "geometry". + std::string m_progress_text; // A text that describes the current actions. It can be used for example to set a text on the progress bar. + }; + + StatusCallback() = default; + virtual ~StatusCallback() = default; + + using MessageCallbackType = std::function)>; + using CancelCallbackType = std::function; + + //\brief error callback mechanism to show messages in gui + void setMessageCallBack(const MessageCallbackType& cb) + { + m_func_call_on_message = cb; + } + + virtual void setMessageTarget(StatusCallback* other) + { + m_redirect_target = other; + } + + virtual void unsetMessageCallBack() + { + m_func_call_on_message = nullptr; + } + + virtual void setCancelCheck(const CancelCallbackType& cb) + { + m_func_check_cancel = cb; + } + + virtual void unsetCancelCheck() + { + m_func_check_cancel = nullptr; + } + + //\brief trigger the callback to pass a message, warning, or error, for example to store in a logfile + virtual void messageCallback(shared_ptr m) + { + if (m_redirect_target) + { + m_redirect_target->messageCallback(m); + return; + } + +#ifdef _DEBUG + if (!m_func_call_on_message ) + { + if (m) + { + switch (m->m_message_type) + { + case MESSAGE_TYPE_UNKNOWN: + case MESSAGE_TYPE_GENERAL_MESSAGE: + case MESSAGE_TYPE_MINOR_WARNING: + case MESSAGE_TYPE_WARNING: + case MESSAGE_TYPE_ERROR: + std::wcout << L"messageCallback not set. Lost message: " << m->m_message_text.c_str() << std::endl; + break; + } + } + } + + if (m->m_message_type == MESSAGE_TYPE_ERROR) + { + std::wcout << L"error: " << m->m_message_text.c_str() << std::endl; + } +#endif + + if (m_func_call_on_message) + { + std::lock_guard lock(m_writelock); + m_func_call_on_message(m); + } + } + + //\brief check if cancellation has been requested. + virtual bool isCanceled() + { + if (m_redirect_target) + { + return m_redirect_target->isCanceled(); + } + + if (m_func_check_cancel) + { + std::lock_guard lock(m_writelock); + return m_func_check_cancel(); + } + + return false; + } + + virtual void messageCallback(const std::string& message_text, MessageType type, const char* reporting_function, BuildingObject* entity = nullptr) + { + shared_ptr message(new Message()); + message->m_message_text.assign(message_text.begin(), message_text.end()); + message->m_message_type = type; + message->m_reporting_function = reporting_function; + message->m_entity = entity; + messageCallback(message); + } + virtual void progressValueCallback(double progress_value, const std::string& progress_type) + { + shared_ptr progress_message(new Message()); + progress_message->m_message_type = MessageType::MESSAGE_TYPE_PROGRESS_VALUE; + progress_message->m_progress_value = progress_value; + progress_message->m_progress_type.assign(progress_type); + messageCallback(progress_message); + } + virtual void progressTextCallback(const std::string& progress_text) + { + shared_ptr progress_message(new Message()); + progress_message->m_message_type = MessageType::MESSAGE_TYPE_PROGRESS_TEXT; + progress_message->m_progress_value = -1; + progress_message->m_progress_text.assign(progress_text); + messageCallback(progress_message); + } + virtual void clearMessagesCallback() + { + shared_ptr progress_message(new Message()); + progress_message->m_message_type = MessageType::MESSAGE_TYPE_CLEAR_MESSAGES; + messageCallback(progress_message); + } + virtual void canceledCallback() + { + shared_ptr canceled_message(new Message()); + canceled_message->m_message_type = MessageType::MESSAGE_TYPE_CANCELED; + messageCallback(canceled_message); + } + +protected: + //\brief Message callback function + MessageCallbackType m_func_call_on_message; + + //\brief Cancel callback + CancelCallbackType m_func_check_cancel; + + StatusCallback* m_redirect_target = nullptr; + + std::mutex m_writelock; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/UnitConverter.cpp b/IfcPlusPlus/src/ifcpp/model/UnitConverter.cpp index 85c09a585..222904e47 100644 --- a/IfcPlusPlus/src/ifcpp/model/UnitConverter.cpp +++ b/IfcPlusPlus/src/ifcpp/model/UnitConverter.cpp @@ -1,495 +1,495 @@ -/* -*-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. -*/ - -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ifcpp/reader/ReaderUtil.h" -#include "UnitConverter.h" - -using namespace IFC4X3; - -UnitConverter::UnitConverter() -{ - resetComplete(); - m_plane_angle_factor = M_PI/180.0; -} - -void UnitConverter::resetUnitFactors() -{ - m_loaded_prefix.reset(); - m_length_unit_factor = 1.0; - m_plane_angle_factor = M_PI / 180.0; - if( m_angular_unit == RADIANT ) - { - m_plane_angle_factor = 1.0; // radian - } - else if( m_angular_unit == DEGREE ) - { - m_plane_angle_factor = M_PI/180.0; // 360 degrees - } - else if( m_angular_unit == GON ) - { - m_plane_angle_factor = M_PI/200.0; // 400 gon - } -} -void UnitConverter::resetComplete() -{ - m_angular_unit = UNDEFINED; - m_length_unit_found = false; - m_loaded_units.clear(); - resetUnitFactors(); -} - -void UnitConverter::setAngleUnit(AngularUnit unit) -{ - m_angular_unit = unit; - if( m_angular_unit == RADIANT ) - { - m_plane_angle_factor = 1.0; // radian - } - else if( m_angular_unit == DEGREE ) - { - m_plane_angle_factor = M_PI / 180.0; // 360 degrees - } - else if( m_angular_unit == GON ) - { - m_plane_angle_factor = M_PI / 200.0; // 400 gon - } - else - { - messageCallback("Could not set angular unit", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__); - } -} - -std::string UnitConverter::getUnitLabel(IFC4X3::IfcUnitEnum::IfcUnitEnumEnum unitEnum) -{ - double unit_factor = 1.0; - std::string unit_label = ""; - - auto itFind = m_loaded_units.find(unitEnum); - if( itFind != m_loaded_units.end() ) - { - shared_ptr si_unit = itFind->second; - if( si_unit ) - { - if( si_unit->m_Prefix ) - { - switch( si_unit->m_Prefix->m_enum ) - { - case IfcSIPrefix::ENUM_EXA: - { - unit_factor = 1E18; - unit_label = "exa_"; - break; - } - case IfcSIPrefix::ENUM_PETA: - { - unit_factor = 1E15; - unit_label = "peta_"; - break; - } - case IfcSIPrefix::ENUM_TERA: - { - unit_factor = 1E12; - unit_label = "tera_"; - break; - } - case IfcSIPrefix::ENUM_GIGA: - { - unit_factor = 1E9; - unit_label = "giga_"; - break; - } - case IfcSIPrefix::ENUM_MEGA: - { - unit_factor = 1E6; - unit_label = "mega_"; - break; - } - case IfcSIPrefix::ENUM_KILO: - { - unit_factor = 1E3; - unit_label = "kilo_"; - break; - } - case IfcSIPrefix::ENUM_HECTO: - { - unit_factor = 1E2; - unit_label = "hecto_m"; - break; - } - case IfcSIPrefix::ENUM_DECA: - { - unit_factor = 1E1; - unit_label = ""; - break; - } - case IfcSIPrefix::ENUM_DECI: - { - unit_factor = 1E-1; - unit_label = "deci_"; - break; - } - case IfcSIPrefix::ENUM_CENTI: - { - unit_factor = 1E-2; - unit_label = "centi_"; - break; - } - case IfcSIPrefix::ENUM_MILLI: - { - unit_factor = 1E-3; - unit_label = "milli_"; - break; - } - case IfcSIPrefix::ENUM_MICRO: - { - unit_factor = 1E-6; - unit_label = "micro_"; - break; - } - case IfcSIPrefix::ENUM_NANO: - { - unit_factor = 1E-9; - unit_label = "nano_"; - break; - } - case IfcSIPrefix::ENUM_PICO: - { - unit_factor = 1E-12; - unit_label = "pico_"; - break; - } - case IfcSIPrefix::ENUM_FEMTO: - { - unit_factor = 1E-15; - unit_label = "femto_"; - break; - } - case IfcSIPrefix::ENUM_ATTO: - { - unit_factor = 1E-18; - unit_label = "atto_"; - break; - } - } - } - - shared_ptr unit_type = si_unit->m_UnitType; - if( unit_type ) - { - if( unit_type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) - { - unit_label += "m"; - - if( si_unit->m_Prefix ) - { - if( si_unit->m_Prefix->m_enum == IfcSIPrefix::ENUM_MILLI ) - { - unit_label = "mm"; - } - } - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_AREAUNIT ) - { - unit_label += "m2"; - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_VOLUMEUNIT ) - { - unit_label += "m3"; - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_MASSUNIT ) - { - unit_label += "gram"; - } - } - } - } - return unit_label; -} - -void UnitConverter::setIfcProject( shared_ptr project ) -{ - resetComplete(); - - if( !project->m_UnitsInContext ) - { - messageCallback( "IfcProject.UnitsInContext not defined", StatusCallback::MESSAGE_TYPE_WARNING, "" ); - return; - } - - shared_ptr unit_assignment = project->m_UnitsInContext; - std::vector >& vec_units = unit_assignment->m_Units; - for(auto unit : vec_units) - { - if( !unit ) - { - continue; - } - shared_ptr si_unit = dynamic_pointer_cast( unit ); - if( si_unit ) - { - // example: #10 = IFCSIUNIT( *, .LENGTHUNIT., $, .METRE. ); - // shared_ptr m_Dimensions; - // shared_ptr m_UnitType; - // shared_ptr m_Prefix; //optional - // shared_ptr m_Name; - - shared_ptr unit_type = si_unit->m_UnitType; - shared_ptr unit_name = si_unit->m_Name; - - if( unit_type ) - { - m_loaded_units[unit_type->m_enum] = si_unit; - if( unit_type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) - { - if( unit_name ) - { - if( unit_name->m_enum == IfcSIUnitName::ENUM_METRE ) - { - m_length_unit_factor = 1.0; - m_length_unit_found = true; - //unit_label = "m"; - } - } - - if( si_unit->m_Prefix ) - { - m_loaded_prefix = si_unit->m_Prefix; - m_length_unit_found = true; - - switch( m_loaded_prefix->m_enum ) - { - case IfcSIPrefix::ENUM_EXA: - { - m_length_unit_factor = 1E18; - break; - } - case IfcSIPrefix::ENUM_PETA: - { - m_length_unit_factor = 1E15; - break; - } - case IfcSIPrefix::ENUM_TERA: - { - m_length_unit_factor = 1E12; - break; - } - case IfcSIPrefix::ENUM_GIGA: - { - m_length_unit_factor = 1E9; - break; - } - case IfcSIPrefix::ENUM_MEGA: - { - m_length_unit_factor = 1E6; - break; - } - case IfcSIPrefix::ENUM_KILO: - { - m_length_unit_factor = 1E3; - break; - } - case IfcSIPrefix::ENUM_HECTO: - { - m_length_unit_factor = 1E2; - break; - } - case IfcSIPrefix::ENUM_DECA: - { - m_length_unit_factor = 1E1; - break; - } - case IfcSIPrefix::ENUM_DECI: - { - m_length_unit_factor = 1E-1; - break; - } - case IfcSIPrefix::ENUM_CENTI: - { - m_length_unit_factor = 1E-2; - break; - } - case IfcSIPrefix::ENUM_MILLI: - { - m_length_unit_factor = 1E-3; - break; - } - case IfcSIPrefix::ENUM_MICRO: - { - m_length_unit_factor = 1E-6; - break; - } - case IfcSIPrefix::ENUM_NANO: - { - m_length_unit_factor = 1E-9; - break; - } - case IfcSIPrefix::ENUM_PICO: - { - m_length_unit_factor = 1E-12; - break; - } - case IfcSIPrefix::ENUM_FEMTO: - { - m_length_unit_factor = 1E-15; - break; - } - case IfcSIPrefix::ENUM_ATTO: - { - m_length_unit_factor = 1E-18; - break; - } - - default: - m_length_unit_found = false; - } - - } - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_AREAUNIT ) - { - // TODO: implement - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_VOLUMEUNIT ) - { - // TODO: implement - } - else if( unit_type->m_enum == IfcUnitEnum::ENUM_PLANEANGLEUNIT ) - { - if( unit_name ) - { - if( unit_name->m_enum == IfcSIUnitName::ENUM_RADIAN ) - { - m_angular_unit = RADIANT; - m_plane_angle_factor = 1.0; - } - } - } - } - continue; - } - - shared_ptr conversion_based_unit = dynamic_pointer_cast( unit ); - if( conversion_based_unit ) - { - shared_ptr conversion_factor = conversion_based_unit->m_ConversionFactor; - if( conversion_factor ) - { - - shared_ptr unit_component = conversion_factor->m_UnitComponent; - if( unit_component ) - { - shared_ptr unit_component_si = dynamic_pointer_cast( unit_component ); - if( unit_component_si ) - { - shared_ptr type = unit_component_si->m_UnitType; - if( type ) - { - if( type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) - { - shared_ptr length_value_select = conversion_factor->m_ValueComponent; - if( length_value_select ) - { - shared_ptr ratio_measure = dynamic_pointer_cast( length_value_select ); - if( ratio_measure ) - { - //typedef double IfcRatioMeasure; - - m_length_unit_factor = ratio_measure->m_value; - m_length_unit_found = true; - } - shared_ptr length_measure = dynamic_pointer_cast( length_value_select ); - if( length_measure ) - { - m_length_unit_factor = length_measure->m_value; - m_length_unit_found = true; - } - } - - if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "INCH" ) ) - { - m_length_unit_factor = 25.4*0.001; // 1 unit is 25.4 mm - m_length_unit_found = true; - } - else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "FOOT" ) ) - { - m_length_unit_factor = 304.8*0.001; // 1 unit is 304.8 mm - m_length_unit_found = true; - } - else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "YARD" ) ) - { - m_length_unit_factor = 914*0.001; // 1 unit is 914 mm - m_length_unit_found = true; - } - else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "MILE" ) ) - { - m_length_unit_factor = 1609*0.001; // 1 unit is 1609 mm - m_length_unit_found = true; - } - - } - else if( type->m_enum == IfcUnitEnum::ENUM_PLANEANGLEUNIT ) - { - shared_ptr plane_angle_value = conversion_factor->m_ValueComponent; - if( plane_angle_value ) - { - if( dynamic_pointer_cast( plane_angle_value ) ) - { - shared_ptr plane_angle_measure = dynamic_pointer_cast( plane_angle_value ); - m_plane_angle_factor = plane_angle_measure->m_value; - m_angular_unit = CONVERSION_BASED; - } - else if( dynamic_pointer_cast( plane_angle_value ) ) - { - shared_ptr plane_angle_measure = dynamic_pointer_cast( plane_angle_value ); - m_plane_angle_factor = plane_angle_measure->m_value; - m_angular_unit = CONVERSION_BASED; - } - else if( conversion_based_unit->m_Name ) - { - if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "DEGREE" ) ) - { - m_angular_unit = DEGREE; - m_plane_angle_factor = M_PI / 180.0; - } - } - } - } - } - } - } - } - } - } -} +/* -*-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. +*/ + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifcpp/reader/ReaderUtil.h" +#include "UnitConverter.h" + +using namespace IFC4X3; + +UnitConverter::UnitConverter() +{ + resetComplete(); + m_plane_angle_factor = M_PI/180.0; +} + +void UnitConverter::resetUnitFactors() +{ + m_loaded_prefix.reset(); + m_length_unit_factor = 1.0; + m_plane_angle_factor = M_PI / 180.0; + if( m_angular_unit == RADIANT ) + { + m_plane_angle_factor = 1.0; // radian + } + else if( m_angular_unit == DEGREE ) + { + m_plane_angle_factor = M_PI/180.0; // 360 degrees + } + else if( m_angular_unit == GON ) + { + m_plane_angle_factor = M_PI/200.0; // 400 gon + } +} +void UnitConverter::resetComplete() +{ + m_angular_unit = UNDEFINED; + m_length_unit_found = false; + m_loaded_units.clear(); + resetUnitFactors(); +} + +void UnitConverter::setAngleUnit(AngularUnit unit) +{ + m_angular_unit = unit; + if( m_angular_unit == RADIANT ) + { + m_plane_angle_factor = 1.0; // radian + } + else if( m_angular_unit == DEGREE ) + { + m_plane_angle_factor = M_PI / 180.0; // 360 degrees + } + else if( m_angular_unit == GON ) + { + m_plane_angle_factor = M_PI / 200.0; // 400 gon + } + else + { + messageCallback("Could not set angular unit", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__); + } +} + +std::string UnitConverter::getUnitLabel(IFC4X3::IfcUnitEnum::IfcUnitEnumEnum unitEnum) +{ + double unit_factor = 1.0; + std::string unit_label = ""; + + auto itFind = m_loaded_units.find(unitEnum); + if( itFind != m_loaded_units.end() ) + { + shared_ptr si_unit = itFind->second; + if( si_unit ) + { + if( si_unit->m_Prefix ) + { + switch( si_unit->m_Prefix->m_enum ) + { + case IfcSIPrefix::ENUM_EXA: + { + unit_factor = 1E18; + unit_label = "exa_"; + break; + } + case IfcSIPrefix::ENUM_PETA: + { + unit_factor = 1E15; + unit_label = "peta_"; + break; + } + case IfcSIPrefix::ENUM_TERA: + { + unit_factor = 1E12; + unit_label = "tera_"; + break; + } + case IfcSIPrefix::ENUM_GIGA: + { + unit_factor = 1E9; + unit_label = "giga_"; + break; + } + case IfcSIPrefix::ENUM_MEGA: + { + unit_factor = 1E6; + unit_label = "mega_"; + break; + } + case IfcSIPrefix::ENUM_KILO: + { + unit_factor = 1E3; + unit_label = "kilo_"; + break; + } + case IfcSIPrefix::ENUM_HECTO: + { + unit_factor = 1E2; + unit_label = "hecto_m"; + break; + } + case IfcSIPrefix::ENUM_DECA: + { + unit_factor = 1E1; + unit_label = ""; + break; + } + case IfcSIPrefix::ENUM_DECI: + { + unit_factor = 1E-1; + unit_label = "deci_"; + break; + } + case IfcSIPrefix::ENUM_CENTI: + { + unit_factor = 1E-2; + unit_label = "centi_"; + break; + } + case IfcSIPrefix::ENUM_MILLI: + { + unit_factor = 1E-3; + unit_label = "milli_"; + break; + } + case IfcSIPrefix::ENUM_MICRO: + { + unit_factor = 1E-6; + unit_label = "micro_"; + break; + } + case IfcSIPrefix::ENUM_NANO: + { + unit_factor = 1E-9; + unit_label = "nano_"; + break; + } + case IfcSIPrefix::ENUM_PICO: + { + unit_factor = 1E-12; + unit_label = "pico_"; + break; + } + case IfcSIPrefix::ENUM_FEMTO: + { + unit_factor = 1E-15; + unit_label = "femto_"; + break; + } + case IfcSIPrefix::ENUM_ATTO: + { + unit_factor = 1E-18; + unit_label = "atto_"; + break; + } + } + } + + shared_ptr unit_type = si_unit->m_UnitType; + if( unit_type ) + { + if( unit_type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) + { + unit_label += "m"; + + if( si_unit->m_Prefix ) + { + if( si_unit->m_Prefix->m_enum == IfcSIPrefix::ENUM_MILLI ) + { + unit_label = "mm"; + } + } + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_AREAUNIT ) + { + unit_label += "m2"; + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_VOLUMEUNIT ) + { + unit_label += "m3"; + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_MASSUNIT ) + { + unit_label += "gram"; + } + } + } + } + return unit_label; +} + +void UnitConverter::setIfcProject( shared_ptr project ) +{ + resetComplete(); + + if( !project->m_UnitsInContext ) + { + messageCallback( "IfcProject.UnitsInContext not defined", StatusCallback::MESSAGE_TYPE_WARNING, "" ); + return; + } + + shared_ptr unit_assignment = project->m_UnitsInContext; + std::vector >& vec_units = unit_assignment->m_Units; + for(auto unit : vec_units) + { + if( !unit ) + { + continue; + } + shared_ptr si_unit = dynamic_pointer_cast( unit ); + if( si_unit ) + { + // example: #10 = IFCSIUNIT( *, .LENGTHUNIT., $, .METRE. ); + // shared_ptr m_Dimensions; + // shared_ptr m_UnitType; + // shared_ptr m_Prefix; //optional + // shared_ptr m_Name; + + shared_ptr unit_type = si_unit->m_UnitType; + shared_ptr unit_name = si_unit->m_Name; + + if( unit_type ) + { + m_loaded_units[unit_type->m_enum] = si_unit; + if( unit_type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) + { + if( unit_name ) + { + if( unit_name->m_enum == IfcSIUnitName::ENUM_METRE ) + { + m_length_unit_factor = 1.0; + m_length_unit_found = true; + //unit_label = "m"; + } + } + + if( si_unit->m_Prefix ) + { + m_loaded_prefix = si_unit->m_Prefix; + m_length_unit_found = true; + + switch( m_loaded_prefix->m_enum ) + { + case IfcSIPrefix::ENUM_EXA: + { + m_length_unit_factor = 1E18; + break; + } + case IfcSIPrefix::ENUM_PETA: + { + m_length_unit_factor = 1E15; + break; + } + case IfcSIPrefix::ENUM_TERA: + { + m_length_unit_factor = 1E12; + break; + } + case IfcSIPrefix::ENUM_GIGA: + { + m_length_unit_factor = 1E9; + break; + } + case IfcSIPrefix::ENUM_MEGA: + { + m_length_unit_factor = 1E6; + break; + } + case IfcSIPrefix::ENUM_KILO: + { + m_length_unit_factor = 1E3; + break; + } + case IfcSIPrefix::ENUM_HECTO: + { + m_length_unit_factor = 1E2; + break; + } + case IfcSIPrefix::ENUM_DECA: + { + m_length_unit_factor = 1E1; + break; + } + case IfcSIPrefix::ENUM_DECI: + { + m_length_unit_factor = 1E-1; + break; + } + case IfcSIPrefix::ENUM_CENTI: + { + m_length_unit_factor = 1E-2; + break; + } + case IfcSIPrefix::ENUM_MILLI: + { + m_length_unit_factor = 1E-3; + break; + } + case IfcSIPrefix::ENUM_MICRO: + { + m_length_unit_factor = 1E-6; + break; + } + case IfcSIPrefix::ENUM_NANO: + { + m_length_unit_factor = 1E-9; + break; + } + case IfcSIPrefix::ENUM_PICO: + { + m_length_unit_factor = 1E-12; + break; + } + case IfcSIPrefix::ENUM_FEMTO: + { + m_length_unit_factor = 1E-15; + break; + } + case IfcSIPrefix::ENUM_ATTO: + { + m_length_unit_factor = 1E-18; + break; + } + + default: + m_length_unit_found = false; + } + + } + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_AREAUNIT ) + { + // TODO: implement + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_VOLUMEUNIT ) + { + // TODO: implement + } + else if( unit_type->m_enum == IfcUnitEnum::ENUM_PLANEANGLEUNIT ) + { + if( unit_name ) + { + if( unit_name->m_enum == IfcSIUnitName::ENUM_RADIAN ) + { + m_angular_unit = RADIANT; + m_plane_angle_factor = 1.0; + } + } + } + } + continue; + } + + shared_ptr conversion_based_unit = dynamic_pointer_cast( unit ); + if( conversion_based_unit ) + { + shared_ptr conversion_factor = conversion_based_unit->m_ConversionFactor; + if( conversion_factor ) + { + + shared_ptr unit_component = conversion_factor->m_UnitComponent; + if( unit_component ) + { + shared_ptr unit_component_si = dynamic_pointer_cast( unit_component ); + if( unit_component_si ) + { + shared_ptr type = unit_component_si->m_UnitType; + if( type ) + { + if( type->m_enum == IfcUnitEnum::ENUM_LENGTHUNIT ) + { + shared_ptr length_value_select = conversion_factor->m_ValueComponent; + if( length_value_select ) + { + shared_ptr ratio_measure = dynamic_pointer_cast( length_value_select ); + if( ratio_measure ) + { + //typedef double IfcRatioMeasure; + + m_length_unit_factor = ratio_measure->m_value; + m_length_unit_found = true; + } + shared_ptr length_measure = dynamic_pointer_cast( length_value_select ); + if( length_measure ) + { + m_length_unit_factor = length_measure->m_value; + m_length_unit_found = true; + } + } + + if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "INCH" ) ) + { + m_length_unit_factor = 25.4*0.001; // 1 unit is 25.4 mm + m_length_unit_found = true; + } + else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "FOOT" ) ) + { + m_length_unit_factor = 304.8*0.001; // 1 unit is 304.8 mm + m_length_unit_found = true; + } + else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "YARD" ) ) + { + m_length_unit_factor = 914*0.001; // 1 unit is 914 mm + m_length_unit_found = true; + } + else if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "MILE" ) ) + { + m_length_unit_factor = 1609*0.001; // 1 unit is 1609 mm + m_length_unit_found = true; + } + + } + else if( type->m_enum == IfcUnitEnum::ENUM_PLANEANGLEUNIT ) + { + shared_ptr plane_angle_value = conversion_factor->m_ValueComponent; + if( plane_angle_value ) + { + if( dynamic_pointer_cast( plane_angle_value ) ) + { + shared_ptr plane_angle_measure = dynamic_pointer_cast( plane_angle_value ); + m_plane_angle_factor = plane_angle_measure->m_value; + m_angular_unit = CONVERSION_BASED; + } + else if( dynamic_pointer_cast( plane_angle_value ) ) + { + shared_ptr plane_angle_measure = dynamic_pointer_cast( plane_angle_value ); + m_plane_angle_factor = plane_angle_measure->m_value; + m_angular_unit = CONVERSION_BASED; + } + else if( conversion_based_unit->m_Name ) + { + if( std_iequal( conversion_based_unit->m_Name->m_value.c_str(), "DEGREE" ) ) + { + m_angular_unit = DEGREE; + m_plane_angle_factor = M_PI / 180.0; + } + } + } + } + } + } + } + } + } + } +} diff --git a/IfcPlusPlus/src/ifcpp/model/UnitConverter.h b/IfcPlusPlus/src/ifcpp/model/UnitConverter.h index 791d6df78..867dae97a 100644 --- a/IfcPlusPlus/src/ifcpp/model/UnitConverter.h +++ b/IfcPlusPlus/src/ifcpp/model/UnitConverter.h @@ -1,88 +1,88 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include "BasicTypes.h" -#include "StatusCallback.h" -#include "BuildingException.h" -#include "ifcpp/IFC4X3/include/IfcProject.h" -#include "ifcpp/IFC4X3/include/IfcSIPrefix.h" -#include "ifcpp/IFC4X3/include/IfcSIUnit.h" -#include "ifcpp/IFC4X3/include/IfcSIUnitName.h" -#include "ifcpp/IFC4X3/include/IfcUnitEnum.h" - -//\brief class to convert values from different units into meter and radian -class IFCQUERY_EXPORT UnitConverter : public StatusCallback -{ -public: - enum AngularUnit { UNDEFINED, RADIANT, DEGREE, GON, CONVERSION_BASED }; - UnitConverter(); - ~UnitConverter() override = default; - void setIfcProject( shared_ptr project); - void setLengthInMeterFactor( double factor ) - { - m_length_unit_factor = factor; - m_length_unit_found = true; - } - double getLengthInMeterFactor() - { - if( !m_length_unit_found ) - { - messageCallback( "No length unit definition found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__ ); - } - - return m_length_unit_factor * m_custom_length_factor; - } - - double getCustomLengthFactor() - { - return m_custom_length_factor; - } - void setCustomLengthFactor( double custom_factor ) - { - m_custom_length_factor = custom_factor; - } - - void setAngleUnit(AngularUnit unit); - double getAngleInRadiantFactor() - { - if( m_angular_unit == UNDEFINED ) - { - messageCallback( "No plane angle unit definition found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__ ); - } - return m_plane_angle_factor; - } - AngularUnit getAngularUnit() { return m_angular_unit; } - shared_ptr& getLoadedPrefix() { return m_loaded_prefix; } - - std::string getUnitLabel(IFC4X3::IfcUnitEnum::IfcUnitEnumEnum); - std::map >& getLoadedUnits() { return m_loaded_units; } - - void resetUnitFactors(); - void resetComplete(); - -protected: - shared_ptr m_loaded_prefix; - std::map > m_loaded_units; - double m_length_unit_factor = 1.0; - double m_custom_length_factor = 1.0; - bool m_length_unit_found = false; - double m_plane_angle_factor = 1.0; - AngularUnit m_angular_unit; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include "BasicTypes.h" +#include "StatusCallback.h" +#include "BuildingException.h" +#include "ifcpp/IFC4X3/include/IfcProject.h" +#include "ifcpp/IFC4X3/include/IfcSIPrefix.h" +#include "ifcpp/IFC4X3/include/IfcSIUnit.h" +#include "ifcpp/IFC4X3/include/IfcSIUnitName.h" +#include "ifcpp/IFC4X3/include/IfcUnitEnum.h" + +//\brief class to convert values from different units into meter and radian +class IFCQUERY_EXPORT UnitConverter : public StatusCallback +{ +public: + enum AngularUnit { UNDEFINED, RADIANT, DEGREE, GON, CONVERSION_BASED }; + UnitConverter(); + ~UnitConverter() override = default; + void setIfcProject( shared_ptr project); + void setLengthInMeterFactor( double factor ) + { + m_length_unit_factor = factor; + m_length_unit_found = true; + } + double getLengthInMeterFactor() + { + if( !m_length_unit_found ) + { + messageCallback( "No length unit definition found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__ ); + } + + return m_length_unit_factor * m_custom_length_factor; + } + + double getCustomLengthFactor() + { + return m_custom_length_factor; + } + void setCustomLengthFactor( double custom_factor ) + { + m_custom_length_factor = custom_factor; + } + + void setAngleUnit(AngularUnit unit); + double getAngleInRadiantFactor() + { + if( m_angular_unit == UNDEFINED ) + { + messageCallback( "No plane angle unit definition found in model", StatusCallback::MESSAGE_TYPE_WARNING, __FUNC__ ); + } + return m_plane_angle_factor; + } + AngularUnit getAngularUnit() { return m_angular_unit; } + shared_ptr& getLoadedPrefix() { return m_loaded_prefix; } + + std::string getUnitLabel(IFC4X3::IfcUnitEnum::IfcUnitEnumEnum); + std::map >& getLoadedUnits() { return m_loaded_units; } + + void resetUnitFactors(); + void resetComplete(); + +protected: + shared_ptr m_loaded_prefix; + std::map > m_loaded_units; + double m_length_unit_factor = 1.0; + double m_custom_length_factor = 1.0; + bool m_length_unit_found = false; + double m_plane_angle_factor = 1.0; + AngularUnit m_angular_unit; +}; diff --git a/IfcPlusPlus/src/ifcpp/model/UnknownEntityException.h b/IfcPlusPlus/src/ifcpp/model/UnknownEntityException.h index 1fff88710..edaff9ba5 100644 --- a/IfcPlusPlus/src/ifcpp/model/UnknownEntityException.h +++ b/IfcPlusPlus/src/ifcpp/model/UnknownEntityException.h @@ -1,30 +1,30 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include - -class UnknownEntityException : public std::exception -{ -public: - UnknownEntityException( std::string keyword ) { m_keyword = keyword; } - ~UnknownEntityException() = default; - virtual const char* what() const throw() { return m_keyword.c_str(); } - std::string m_keyword; -}; +/* -*-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. +*/ + +#pragma once + +#include +#include + +class UnknownEntityException : public std::exception +{ +public: + UnknownEntityException( std::string keyword ) { m_keyword = keyword; } + ~UnknownEntityException() = default; + virtual const char* what() const throw() { return m_keyword.c_str(); } + std::string m_keyword; +}; diff --git a/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.cpp b/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.cpp index 749c08b00..988ca22fd 100644 --- a/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.cpp +++ b/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.cpp @@ -899,7 +899,7 @@ void ReaderSTEP::readData(std::istream& read_in, std::streampos file_size, share if (entity) // skip aborted entities { - map_entities.insert(std::make_pair(entity->m_tag, entity)); + map_entities[entity->m_tag] = entity; } } diff --git a/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.h b/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.h index 0337136e5..03d85846b 100644 --- a/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.h +++ b/IfcPlusPlus/src/ifcpp/reader/ReaderSTEP.h @@ -1,40 +1,40 @@ -/* -*-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. -*/ - -#pragma once - -#include "ifcpp/model/BasicTypes.h" -#include "ifcpp/model/BuildingObject.h" -#include "ifcpp/model/BuildingModel.h" -#include "ifcpp/model/StatusCallback.h" - -class IFCQUERY_EXPORT ReaderSTEP : public StatusCallback -{ -public: - ReaderSTEP(); - ~ReaderSTEP() override; - void readHeader( std::istream& in, shared_ptr& target_model ); - void readData( std::istream& in, std::streampos file_end_pos, shared_ptr& model ); - - /*\brief Opens the given file, reads the content, and puts the entities into target_model. - \param[in] file_path Absolute path of the file to read. - **/ - void loadModelFromFile( const std::string& filePath, shared_ptr& targetModel ); - void loadModelFromStream( std::istream& content, std::streampos file_end_pos, shared_ptr& targetModel ); - void readSingleStepLine( const std::string& line, std::pair >& target_read_object ); - void readEntityArguments( std::vector > >& vec_entities, const std::map >& map, shared_ptr& targetModel ); -}; +/* -*-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. +*/ + +#pragma once + +#include "ifcpp/model/BasicTypes.h" +#include "ifcpp/model/BuildingObject.h" +#include "ifcpp/model/BuildingModel.h" +#include "ifcpp/model/StatusCallback.h" + +class IFCQUERY_EXPORT ReaderSTEP : public StatusCallback +{ +public: + ReaderSTEP(); + ~ReaderSTEP() override; + void readHeader( std::istream& in, shared_ptr& target_model ); + void readData( std::istream& in, std::streampos file_end_pos, shared_ptr& model ); + + /*\brief Opens the given file, reads the content, and puts the entities into target_model. + \param[in] file_path Absolute path of the file to read. + **/ + void loadModelFromFile( const std::string& filePath, shared_ptr& targetModel ); + void loadModelFromStream( std::istream& content, std::streampos file_end_pos, shared_ptr& targetModel ); + void readSingleStepLine( const std::string& line, std::pair >& target_read_object ); + void readEntityArguments( std::vector > >& vec_entities, const std::map >& map, shared_ptr& targetModel ); +}; diff --git a/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.cpp b/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.cpp index 582bca0fb..ff1819906 100644 --- a/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.cpp +++ b/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.cpp @@ -1,1547 +1,1547 @@ -/* -*-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. -*/ - -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include - -#include "ifcpp/model/BuildingException.h" -#include "ReaderUtil.h" - -#ifndef CP_UTF8 -#define CP_UTF8 65001 -#endif -#ifdef _MSC_VER -#include -#else -#include -#include -#endif -#include "utf8.h" -//#pragma execution_character_set("utf-8") - -template -static T convertToHex(unsigned char mc) -{ - if (mc >= '0' && mc <= '9') - { - return static_cast(mc) - static_cast('0'); - } - else if (mc >= 'A' && mc <= 'F') - { - return 10 + static_cast(mc) - static_cast('A'); - } - else if (mc >= 'a' && mc <= 'f') - { - return 10 + static_cast(mc) - static_cast('a'); - } - - return 0; -} - -//static char Hex2Char(unsigned char h1, unsigned char h2) -//{ -// // Combine the Unicode values into a single wchar_t -// char returnValue = (convertToHex(h1) << 4) + convertToHex(h2); -// return (returnValue); -//} - -std::string wstring2string(const std::wstring& wstr) -{ - if (wstr.empty()) return std::string(); - -#ifdef _MSC_VER - int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); - std::string strTo(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); - return strTo; -#else - - try - { - std::wstring_convert, wchar_t> StringConverter; - return StringConverter.to_bytes(wstr); - } - catch (...) - { - std::cout << "std::use_facet failed" << std::endl; - } - - return ""; -#endif -} - -std::wstring string2wstring(const std::string& inputString) -{ - if (inputString.empty()) return std::wstring(); - - try - { -#ifdef _MSC_VER - std::wstringstream ws; - ws << inputString.c_str(); - std::wstring result = ws.str(); - return result; -#else - std::wstring_convert> conv; - return conv.from_bytes(inputString); -#endif - } - catch (...) - { - return std::filesystem::path(inputString).wstring(); - } -} - -void checkOpeningClosingParenthesis(const char* ch_check) -{ - int num_opening = 0; - int num_closing = 0; - while (*ch_check != '\0') - { - if (*ch_check == '(') - { - ++num_opening; - } - else if (*ch_check == ')') - { - ++num_closing; - } - else if (*ch_check == '\'') - { - findEndOfString(ch_check); - continue; - } - ++ch_check; - } - if (num_opening != num_closing) - { - std::stringstream err; - err << "checkOpeningClosingParenthesis: num_opening != num_closing " << std::endl; - throw BuildingException(err.str(), __FUNC__); - } -} - -std::istream& bufferedGetline(std::istream& inputStream, std::string& lineOut) -{ - lineOut.clear(); - std::istream::sentry se(inputStream, true); - std::streambuf* sb = inputStream.rdbuf(); - - // std::getline does not work with all line endings, reads complete file instead. - // Handle \n (unix), \r\n (windows), \r (mac) line endings here - while (true) - { - int c = sb->sbumpc(); - switch (c) - { - case '\n': - return inputStream; - case '\r': - if (sb->sgetc() == '\n') - { - sb->sbumpc(); - } - return inputStream; - case std::streambuf::traits_type::eof(): - // in case the last line has no line ending - if (lineOut.empty()) - { - inputStream.setstate(std::ios::eofbit); - } - return inputStream; - default: - lineOut += (char)c; - } - } -} - -std::istream& bufferedGetStepLine(std::istream& inputStream, std::string& lineOut) -{ - lineOut.clear(); - std::istream::sentry se(inputStream, true); - std::streambuf* sb = inputStream.rdbuf(); - bool inString = false; - -#ifdef _DEBUG - std::string string40; -#endif - - // std::getline does not work with all line endings, reads complete file instead. - // Handle \n (unix), \r\n (windows), \r (mac) line endings here - while (true) - { - int c = sb->sbumpc(); // sbumpc: character at the current position and advances the current position to the next character -#ifdef _DEBUG - std::string charAsString; - charAsString += ((char)c); - string40 += charAsString; -#endif - switch (c) - { - case ';': - { - if (!inString) - { - int nextChar = sb->sgetc(); - if (isspace(nextChar)) - { - sb->sbumpc(); // sbumpc: character at the current position and advances the current position to the next character - } - return inputStream; - } - lineOut += (char)c; - continue; - } - case '\'': - { - inString = !inString; - lineOut += (char)c; - continue; - } - case '/': - if (!inString) - { - int nextChar = sb->sgetc(); -#ifdef _DEBUG - std::string charAsString2; - charAsString2 += ((char)nextChar); -#endif - if (nextChar == '*') - { -#ifdef _DEBUG - std::string commentIgnored = "/*"; -#endif - sb->sbumpc(); - - // continue till end of /* */ comment - bool inMultiLineComment = true; - while (inMultiLineComment) - { - int c2 = sb->sbumpc(); - if (c2 == '*') - { - char c3 = sb->sgetc(); - if (c3 == '/') - { - sb->sbumpc(); - inMultiLineComment = false; - - // skip whitespaces - while (true) - { - char c4 = sb->sgetc(); - if (isspace(c4)) - { - sb->sbumpc(); - } - else if (c4 == std::streambuf::traits_type::eof()) - { - if (lineOut.empty()) - { - inputStream.setstate(std::ios::eofbit); - } - return inputStream; - } - else - { - break; - } - } - - break; - } - } - else if (c2 == std::streambuf::traits_type::eof()) - { - // in case the last line has no line ending - if (lineOut.empty()) - { - inputStream.setstate(std::ios::eofbit); - } - return inputStream; - } -#ifdef _DEBUG - commentIgnored += c2; -#endif - } - continue; - } - } - lineOut += (char)c; - continue; - - case '\n': - continue; - - case '\r': - continue; - - case std::streambuf::traits_type::eof(): - // in case the last line has no line ending - if (lineOut.empty()) - { - inputStream.setstate(std::ios::eofbit); - } - return inputStream; - default: - lineOut += (char)c; - } - } -} - -void findLeadingTrailingParanthesis(char* ch, char*& pos_opening, char*& pos_closing) -{ - short num_opening = 0; - while (*ch != '\0') - { - if (*ch == '\'') - { - ++ch; - // beginning of string, continue to end - while (*ch != '\0') - { - if (*ch == '\'') - { - break; - } - ++ch; - } - ++ch; - continue; - } - - if (*ch == '(') - { - if (num_opening == 0) - { - pos_opening = ch; - } - ++num_opening; - } - else if (*ch == ')') - { - --num_opening; - if (num_opening == 0) - { - pos_closing = ch; - } - } - ++ch; - } -} - -bool findEndOfStepLine(char* ch, char*& pos_end) -{ - short num_opening = 0; - while (*ch != '\0') - { - if (*ch == '\'') - { - ++ch; - // beginning of string, continue to end - while (*ch != '\0') - { - if (*ch == '\'') - { - break; - } - ++ch; - } - if (*ch == '\0') - { - // end of string inside comment, so not a valid end of STEP line - return false; - } - ++ch; - continue; - } - - if (*ch == '(') - { - ++num_opening; - } - else if (*ch == ')') - { - --num_opening; - if (num_opening == 0) - { - while (isspace(*ch)) { ++ch; } - - if (*ch == ';') - { - pos_end = ch; - return true; - } - } - } - else if (*ch == ';') - { - if (num_opening == 0) - { - pos_end = ch; - return true; - } - } - ++ch; - } - return false; -} - -void tokenizeList(std::string& list_str, std::vector& list_items) -{ - if (list_str.empty()) - { - return; - } - char* stream_pos = const_cast(list_str.c_str()); - char* last_token = stream_pos; - int numNestedLists = 0; - while (*stream_pos != '\0') - { - if (*stream_pos == '(') - { - ++stream_pos; - ++numNestedLists; - continue; - } - else if (*stream_pos == ')') - { - ++stream_pos; - --numNestedLists; - continue; - } - else if (*stream_pos == '\'') - { - ++stream_pos; - // beginning of string, continue to end - while (*stream_pos != '\0') - { - if (*stream_pos == '\'') - { - break; - } - ++stream_pos; - } - ++stream_pos; - continue; - } - - if (*stream_pos == ',' && numNestedLists == 0) - { - std::string item(last_token, stream_pos - last_token); - list_items.push_back(item); - - ++stream_pos; - while (isspace(*stream_pos)) { ++stream_pos; } - last_token = stream_pos; - if (*stream_pos == '\0') - { - throw BuildingException("tokenizeList: *stream_pos == '\0'", __FUNC__); - } - continue; - } - ++stream_pos; - } - // pick up last element - if (last_token != nullptr) - { - if (last_token != stream_pos) - { - std::string item(last_token, stream_pos - last_token); - list_items.push_back(item); - } - } -} - -void tokenizeEntityList(std::string& list_str, std::vector& list_items) -{ - if (list_str.empty()) - { - return; - } - char* stream_pos = const_cast(list_str.c_str()); - while (*stream_pos != '\0') - { - // skip whitespace - while (isspace(*stream_pos)) { ++stream_pos; } - - if (*stream_pos == '#') - { - ++stream_pos; - // beginning of id - char* begin_id = stream_pos; - - // proceed until end of integer - ++stream_pos; - while (*stream_pos != '\0') - { - if (isdigit(*stream_pos)) - { - ++stream_pos; - } - else - { - break; - } - } - const int id = std::stoi(std::string(begin_id, stream_pos - begin_id)); - list_items.push_back(id); - } - else if (*stream_pos == '$') - { - // empty - } - else - { - std::stringstream err; - err << "tokenizeEntityList: unexpected argument: " << list_str.c_str() << std::endl; - throw BuildingException(err.str(), __FUNC__); - } - - while (isspace(*stream_pos)) - { - ++stream_pos; - } - if (*stream_pos == ',') - { - ++stream_pos; - //last_token = stream_pos; - continue; - } - else - { - break; - } - } -} - -void readIntegerList(const std::string& str, std::vector& vec) -{ - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == '(') - { - ++i; - last_token = i; - break; - } - ++i; - } - - while (i < argsize) - { - if (ch[i] == ',') - { - size_t str_length = i - last_token; - if (str_length > 0) - { - vec.push_back(std::stoi(str.substr(last_token, i - last_token))); - } - last_token = i + 1; - } - else if (ch[i] == ')') - { - size_t str_length = i - last_token; - if (str_length > 0) - { - vec.push_back(std::stoi(str.substr(last_token, i - last_token))); - } - return; - } - ++i; - } -} - -void readIntegerList2D(const std::string& str, std::vector >& vec) -{ - // ((1,2,4),(3,23,039),(938,3,-3,6)) - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t num_par_open = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == ',') - { - if (num_par_open == 1) - { - std::vector inner_vec; - vec.push_back(inner_vec); - readIntegerList(str.substr(last_token, i - last_token), inner_vec); - last_token = i + 1; - } - } - else if (ch[i] == '(') - { - ++num_par_open; - } - else if (ch[i] == ')') - { - --num_par_open; - if (num_par_open == 0) - { - std::vector inner_vec; - vec.push_back(inner_vec); - readIntegerList(str.substr(last_token, i - last_token), inner_vec); - return; - } - } - ++i; - } -} - -void readRealList(const std::string& str, std::vector& vec) -{ - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == '(') - { - ++i; - last_token = i; - break; - } - ++i; - } - - while (i < argsize) - { - if (ch[i] == ',') - { - vec.push_back(std::stod(str.substr(last_token, i - last_token))); - last_token = i + 1; - } - else if (ch[i] == ')') - { - vec.push_back(std::stod(str.substr(last_token, i - last_token))); - return; - } - ++i; - } -} - -void readRealArray(const std::string& str, double(&vec)[3], short int& size) -{ - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == '(') - { - ++i; - last_token = i; - break; - } - ++i; - } - short idx = 0; - while (i < argsize) - { - if (ch[i] == ',') - { - if (idx < 3) - { - vec[idx] = std::stod(str.substr(last_token, i - last_token)); - } - ++idx; - last_token = i + 1; - } - else if (ch[i] == ')') - { - if (idx < 3) - { - vec[idx] = std::stod(str.substr(last_token, i - last_token)); - } - size = idx + 1; - return; - } - ++i; - } -} - -void readRealList2D(const std::string& str, std::vector >& vec) -{ - // ((1.6,2.0,4.9382),(3.78,23.34,039.938367),(938.034,3.0,-3.45,6.9182)) - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t num_par_open = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == ',') - { - if (num_par_open == 1) - { - std::vector inner_vec; - vec.push_back(inner_vec); - readRealList(str.substr(last_token, i - last_token), vec.back()); - last_token = i; - } - } - else if (ch[i] == '(') - { - ++num_par_open; - last_token = i; - } - else if (ch[i] == ')') - { - --num_par_open; - if (num_par_open == 0) - { - std::vector inner_vec; - vec.push_back(inner_vec); - readRealList(str.substr(last_token, i - last_token), vec.back()); - return; - } - } - ++i; - } -} - -void readRealList3D(const std::string& str, std::vector > >& vec) -{ - // ((1.6,2.0,4.9382),(3.78,23.34,039.938367),(938.034,3.0,-3.45,6.9182)) - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t num_par_open = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == ',') - { - if (num_par_open == 1) - { - std::vector > inner_vec; - vec.push_back(inner_vec); - readRealList2D(str.substr(last_token, i - last_token), vec.back()); - last_token = i; - } - } - else if (ch[i] == '(') - { - ++num_par_open; - last_token = i; - } - else if (ch[i] == ')') - { - --num_par_open; - if (num_par_open == 0) - { - std::vector > inner_vec; - vec.push_back(inner_vec); - readRealList2D(str.substr(last_token, i - last_token), vec.back()); - return; - } - } - ++i; - } -} - -void readBinary(const std::string& str, std::string& target) -{ - target = str; -} - -void readBinaryString(const std::string& attribute_value, std::string& target) -{ - if (attribute_value.size() < 2) - { - target = attribute_value; - return; - } - if (attribute_value[0] == '"' && attribute_value[attribute_value.size() - 1] == '"') - { - target = attribute_value.substr(1, attribute_value.size() - 2); - } -} - -void readBinaryList(const std::string& str, std::vector& vec) -{ - readStringList(str, vec); -} - -void readStringList(const std::string& str, std::vector& vec) -{ - const char* ch = str.c_str(); - const size_t argsize = str.size(); - if (argsize == 0) - { - return; - } - size_t i = 0; - size_t last_token = 0; - while (i < argsize) - { - if (ch[i] == '(') - { - ++i; - last_token = i; - break; - } - ++i; - } - while (i < argsize) - { - if (ch[i] == ',') - { - vec.push_back(str.substr(last_token, i - last_token)); - last_token = i + 1; - } - else if (ch[i] == ')') - { - vec.push_back(str.substr(last_token, i - last_token)); - return; - } - ++i; - } -} - -void findEndOfString(const char*& stream_pos) -{ - ++stream_pos; - const char* pos_begin = stream_pos; - - // beginning of string, continue to end - while (*stream_pos != '\0') - { - if (*stream_pos == '\\') - { - if (*(stream_pos + 1) == 'X') - { - if (*(stream_pos + 2) == '0' || *(stream_pos + 2) == '2' || *(stream_pos + 2) == '4') - { - if (*(stream_pos + 3) == '\\') - { - // ISO 10646 encoding, continue - stream_pos += 4; - continue; - } - } - } - - if (*(stream_pos + 1) == '\\') - { - // we have a double backslash, so just continue - ++stream_pos; - ++stream_pos; - continue; - } - if (*(stream_pos + 1) == '\'') - { - // quote is escaped - ++stream_pos; - ++stream_pos; - continue; - } - } - - if (*stream_pos == '\'') - { - if (*(stream_pos + 1) == '\'') - { - // two single quotes in string - if (stream_pos != pos_begin) - { - ++stream_pos; - ++stream_pos; - continue; - } - } - ++stream_pos; - - // end of string - break; - } - ++stream_pos; - } -} - -static char16_t checkAndConvertAppleEncoding(char16_t input) -{ - if (input >= 0x80 && input <= 0xFF) - { - switch (input) - { - case 0x80: return 196; - case 0x81: return 197; - case 0x82: return 199; - case 0x83: return 201; - case 0x84: return 209; - case 0x85: return 214; - case 0x86: return 220; - case 0x87: return 225; - case 0x88: return 224; - case 0x89: return 226; - case 0x8A: return 228; - case 0x8B: return 227; - case 0x8C: return 229; - case 0x8D: return 231; - case 0x8E: return 233; - case 0x8F: return 232; - case 0x90: return 234; - case 0x91: return 235; - case 0x92: return 237; - case 0x93: return 236; - case 0x94: return 238; - case 0x95: return 239; - case 0x96: return 241; - case 0x97: return 243; - case 0x98: return 242; - case 0x99: return 244; - case 0x9A: return 246; - case 0x9B: return 245; - case 0x9C: return 250; - case 0x9D: return 249; - case 0x9E: return 251; - case 0x9F: return 252; - case 0xA0: return 8224; - case 0xA1: return 176; - case 0xA2: return 162; - case 0xA3: return 163; - case 0xA4: return 167; - case 0xA5: return 8226; - case 0xA6: return 182; - case 0xA7: return 223; - case 0xA8: return 174; - case 0xA9: return 169; - case 0xAA: return 8482; - case 0xAB: return 180; - case 0xAC: return 168; - case 0xAD: return 8800; - case 0xAE: return 198; - case 0xAF: return 216; - case 0xB0: return 8734; - case 0xB1: return 177; - case 0xB2: return 8804; - case 0xB3: return 8805; - case 0xB4: return 165; - case 0xB5: return 181; - case 0xB6: return 8706; - case 0xB7: return 8721; - case 0xB8: return 8719; - case 0xB9: return 960; - case 0xBA: return 8747; - case 0xBB: return 170; - case 0xBC: return 186; - case 0xBD: return 937; - case 0xBE: return 230; - case 0xBF: return 248; - case 0xC0: return 191; - case 0xC1: return 161; - case 0xC2: return 172; - case 0xC3: return 8730; - case 0xC4: return 402; - case 0xC5: return 8776; - case 0xC6: return 8710; - case 0xC7: return 171; - case 0xC8: return 187; - case 0xC9: return 8230; - case 0xCA: return 160; - case 0xCB: return 192; - case 0xCC: return 195; - case 0xCD: return 213; - case 0xCE: return 338; - case 0xCF: return 339; - case 0xD0: return 8211; - case 0xD1: return 8212; - case 0xD2: return 8220; - case 0xD3: return 8221; - case 0xD4: return 8216; - case 0xD5: return 8217; - case 0xD6: return 247; - case 0xD7: return 9674; - case 0xD8: return 255; - case 0xD9: return 376; - case 0xDA: return 8260; - case 0xDB: return 8364; - case 0xDC: return 8249; - case 0xDD: return 8250; - case 0xDE: return 64257; - case 0xDF: return 64258; - case 0xE0: return 8225; - case 0xE1: return 183; - case 0xE2: return 8218; - case 0xE3: return 8222; - case 0xE4: return 8240; - case 0xE5: return 194; - case 0xE6: return 202; - case 0xE7: return 193; - case 0xE8: return 203; - case 0xE9: return 200; - case 0xEA: return 205; - case 0xEB: return 206; - case 0xEC: return 207; - case 0xED: return 204; - case 0xEE: return 211; - case 0xEF: return 212; - case 0xF0: return 63743; - case 0xF1: return 210; - case 0xF2: return 218; - case 0xF3: return 219; - case 0xF4: return 217; - case 0xF5: return 305; - case 0xF6: return 710; - case 0xF7: return 732; - case 0xF8: return 175; - case 0xF9: return 728; - case 0xFA: return 729; - case 0xFB: return 730; - case 0xFC: return 184; - case 0xFD: return 733; - case 0xFE: return 731; - case 0xFF: return 711; - } - } - return input; -} - -void decodeArgumentString(const std::string& argument_str, std::string& arg_out) -{ - const size_t arg_length = argument_str.length(); - if (arg_length == 0) - { - return; - } - - std::string arg_str_new; - - char* stream_pos = const_cast(argument_str.c_str()); // ascii characters from STEP file - while (*stream_pos != '\0') - { - if (*stream_pos == '\\') - { - char c1 = *(stream_pos + 1); - if (c1 == 'S') - { - // we have \S - char c2 = *(stream_pos + 2); - if (c2 == '\\') - { - // we have '\S\', for example 'Heizk\S\vrper' - char c3 = *(stream_pos + 3); - if (c3 != '\0') - { - char c4 = *(stream_pos + 4); - if (c4 == '\\') - { - // we have '\S\ . \' - char c5 = *(stream_pos + 5); - if (c5 == 'S') - { - if (*(stream_pos + 6) == '\\') - { - if (*(stream_pos + 7) != '\0') - { - char first = c3; - char second = *(stream_pos + 7); - char append_char = char(125 + first + second); - arg_str_new += append_char; - stream_pos += 8; - continue; - } - } - } - else if (c5 == 'Q') - { - if (*(stream_pos + 6) == '\\') - { - if (*(stream_pos + 7) != '\0') - { - char first = c3; - char second = *(stream_pos + 7); - char append_char = char(125 + first + second); - arg_str_new += append_char; - stream_pos += 8; - continue; - } - } - } - } - else - { - // next characters code value v shall be interpreted as v + 128 - char first = c3; - char append_char = char(128 + first); - uint8_t charAsUint = append_char; - arg_str_new.push_back(0xc0 | charAsUint >> 6); - arg_str_new.push_back(0x80 | (charAsUint & 0x3f)); - - stream_pos += 4; - continue; - } - } - } - } - else if (c1 == 'X') - { - char c2 = *(stream_pos + 2); - if (c2 == '\\') - { - // we have \\X\\Unicode code points - - char codePoint1 = *(stream_pos + 3); - char codePoint2 = *(stream_pos + 4); - codePoint1 = convertToHex(codePoint1); - codePoint2 = convertToHex(codePoint2); - - // Combine the Unicode values into a single char - char combined[2]; - combined[0] = (codePoint1 << 4) | codePoint2; - combined[1] = 0; - char16_t* combined16 = reinterpret_cast(combined); - if (combined16[0] >= 0x80 && combined16[0] <= 0x9F) - { - combined16[0] = checkAndConvertAppleEncoding(combined16[0]); - } - std::u16string u16str(combined16, 1); - std::wstring_convert, char16_t> convert; - std::string utf8 = convert.to_bytes(u16str); - - arg_str_new += utf8; - stream_pos += 5; - continue; - } - else if (c2 == '0') - { - if (*(stream_pos + 3) == '\\') - { - stream_pos += 4; - continue; - } - } - else if (c2 == '2') - { - if (*(stream_pos + 3) == '\\') - { - // we have \X2\Unicode code points - // for example pot\X2\00EA\X0\ncia - - // the following sequence of multiples of four hexadecimal characters shall be interpreted as encoding the - // two-octet representation of characters from the BMP in ISO 10646 - - stream_pos += 4; - std::vector utf16Characters; - do - { - char codePoint1 = *(stream_pos + 0); - - if (codePoint1 == '\\') - { - char c1 = *(stream_pos + 1); - char c2 = *(stream_pos + 2); - char c3 = *(stream_pos + 3); - if (c1 == 'X' && c2 == '0' && c3 == '\\') - { - stream_pos += 4; - break; - } - else - { - // unexpected sequence - arg_out = argument_str; - return; - } - } - - char codePoint2 = *(stream_pos + 1); - char codePoint3 = *(stream_pos + 2); - char codePoint4 = *(stream_pos + 3); - - char c1 = (convertToHex(codePoint1) << 4) | convertToHex(codePoint2); - char c2 = (convertToHex(codePoint3) << 4) | convertToHex(codePoint4); - - utf16Characters.push_back(c2); - utf16Characters.push_back(c1); - - stream_pos += 4; - - } while ((*stream_pos != '\0')); - - std::u16string u16str(reinterpret_cast(&utf16Characters[0]), utf16Characters.size() / 2); - std::wstring_convert, char16_t> convert; - std::string utf8 = convert.to_bytes(u16str); - arg_str_new += utf8; - } - continue; - } - } - else if (c1 == 'N') - { - if (*(stream_pos + 2) == '\\') - { - arg_str_new.append("\n"); - stream_pos += 3; - continue; - } - } - } - - char current_char = *stream_pos; - arg_str_new += current_char; - ++stream_pos; - } - - arg_out = arg_str_new; -} - -void decodeArgumentStrings( const std::vector& entity_arguments, std::vector& args_out) -{ - for (auto& argument_str : entity_arguments) - { - const size_t arg_length = argument_str.length(); - if (arg_length == 0) - { - continue; - } - - std::string decoded; - decodeArgumentString(argument_str, decoded); - args_out.push_back(decoded); - } -} - -void readBool(const std::string& attribute_value, bool& target) -{ - if (std_iequal(attribute_value, ".F.")) - { - target = false; - } - else if (std_iequal(attribute_value, ".T.")) - { - target = true; - } -} - -void readLogical(const std::string& attribute_value, LogicalEnum& target) -{ - if (std_iequal(attribute_value, ".F.")) - { - target = LOGICAL_FALSE; - } - else if (std_iequal(attribute_value, ".T.")) - { - target = LOGICAL_TRUE; - } - else if (std_iequal(attribute_value, ".U.")) - { - target = LOGICAL_UNKNOWN; - } -} - -void readInteger(const std::string& attribute_value, int& target) -{ - target = std::stoi(attribute_value); -} - -void readIntegerValue(const std::string& str, int& int_value) -{ - if (str.compare("$") == 0) - { - int_value = std::numeric_limits::quiet_NaN(); - } - else if (str.compare("*") == 0) - { - int_value = std::numeric_limits::quiet_NaN(); - } - else - { - int_value = std::stoi(str); - } -} - -void readReal(const std::string& attribute_value, double& target) -{ - target = std::stod(attribute_value); -} - -void readString(const std::string& attribute_value, std::string& target) -{ - if (attribute_value.size() < 2) - { - target = attribute_value; - return; - } - if (attribute_value[0] == '\'' && attribute_value[attribute_value.size() - 1] == '\'') - { - target = attribute_value.substr(1, attribute_value.size() - 2); - } - else - { - target = attribute_value; - } -} - -void addArgument(const char* stream_pos, const char*& last_token, std::vector& entity_arguments) -{ - if (*last_token == ',') - { - ++last_token; - } - - const char* begin_arg = last_token; - - // skip whitespace - while (isspace(*begin_arg)) - { - ++begin_arg; - } - - int remaining_size = static_cast(stream_pos - begin_arg); - if (remaining_size > 0) - { - const char* end_arg = stream_pos - 1; - entity_arguments.emplace_back(begin_arg, end_arg - begin_arg + 1); - } - last_token = stream_pos; -} - -//\brief split one string into a vector of argument strings -// caution: when using threads, this method runs in parallel threads -void tokenizeEntityArguments(const std::string& argument_str, std::vector& entity_arguments) -{ - if (argument_str.size() == 0) - { - return; - } - const char* stream_pos = argument_str.c_str(); - int num_open_braces = 0; - const char* last_token = stream_pos; - - while (*stream_pos != '\0') - { - if (*stream_pos == '\'') - { - findEndOfString(stream_pos); - continue; - } - - if (*stream_pos == '(') - { - ++num_open_braces; - } - else if (*stream_pos == ',') - { - if (num_open_braces == 0) - { - const char* last_token_check = last_token; - addArgument(stream_pos, last_token, entity_arguments); - } - } - else if (*stream_pos == ')') - { - --num_open_braces; - if (num_open_braces == 0) - { - ++stream_pos; - addArgument(stream_pos, last_token, entity_arguments); - - const char* stream_pos_begin = argument_str.c_str(); - size_t readCount = stream_pos - stream_pos_begin; - - if (readCount == argument_str.size()) - { - break; - } - } - } - ++stream_pos; - } - - if (*last_token != *stream_pos) - { - if (*last_token != '\0') - { - addArgument(stream_pos, last_token, entity_arguments); - } - } -} - -//\brief split one string into a vector of argument strings -// caution: when using threads, this method runs in parallel threads -void tokenizeInlineArgument(std::string arg, std::string& keyword, std::string& inline_arg) -{ - if (arg.empty()) - { - throw BuildingException("arg.size() == 0", __FUNC__); - } - if (arg[0] == '$') - { - return; - } - if (arg[0] == '*') - { - return; - } - if (arg[0] == '#') - { - throw BuildingException("tokenizeInlineArgument: argument begins with #, so it is not inline", __FUNC__); - } - - char* stream_pos = const_cast(arg.c_str()); - while (isspace(*stream_pos)) { ++stream_pos; } - - char* begin_keyword = stream_pos; - while (isalnum(*stream_pos)) { ++stream_pos; } - - // get type name - std::string key(begin_keyword, stream_pos - begin_keyword); - - if (key.empty()) - { - // single argument, for example .T. - inline_arg = arg; - return; - } - - // proceed to '(' - int numOpenBraces = 0; - if (*stream_pos == '(') - { - ++stream_pos; - ++numOpenBraces; - } - else - { - while (*stream_pos != '\0') - { - if (*stream_pos == '(') - { - ++stream_pos; - ++numOpenBraces; - break; - } - ++stream_pos; - } - } - - // proceed to ')' - std::string inline_argument; - char* inline_argument_begin = stream_pos; - - while (*stream_pos != '\0') - { - if (*stream_pos == '\'') - { - ++stream_pos; - // inside string - while (*stream_pos != '\0') - { - if (*stream_pos == '\'') - { - // check if tick is escaped - char* tick_pos = stream_pos; - bool tick_escaped = false; - while (tick_pos != begin_keyword) - { - --tick_pos; - if (*tick_pos == '\\') - { - tick_escaped = !tick_escaped; - continue; - } - break; - } - if (tick_escaped) - { - ++stream_pos; - continue; - } - // else tick marks the end of argument - break; - } - ++stream_pos; - } - } - - if (*stream_pos == '(') - { - ++numOpenBraces; - ++stream_pos; - continue; - } - - if (*stream_pos == ')') - { - --numOpenBraces; - // skip whitespace - while (isspace(*inline_argument_begin)) - { - ++inline_argument_begin; - } - if (numOpenBraces == 0) - { - char* end_arg = stream_pos - 1; - inline_argument = std::string(inline_argument_begin, end_arg - inline_argument_begin + 1); - break; - } - } - ++stream_pos; - } - - std::transform(key.begin(), key.end(), key.begin(), [](char c) {return static_cast(std::toupper(c)); }); - keyword = key; - inline_arg = inline_argument; -} - -bool std_iequal(const std::string& a, const std::string& b) -{ - if (a.size() == b.size()) - { - return std::equal(a.begin(), a.end(), b.begin(), [](const char l, const char r) { return std::toupper(l) == std::toupper(r); }); - } - return false; -} +/* -*-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. +*/ + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include + +#include "ifcpp/model/BuildingException.h" +#include "ReaderUtil.h" + +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif +#ifdef _MSC_VER +#include +#else +#include +#include +#endif +#include "utf8.h" +//#pragma execution_character_set("utf-8") + +template +static T convertToHex(unsigned char mc) +{ + if (mc >= '0' && mc <= '9') + { + return static_cast(mc) - static_cast('0'); + } + else if (mc >= 'A' && mc <= 'F') + { + return 10 + static_cast(mc) - static_cast('A'); + } + else if (mc >= 'a' && mc <= 'f') + { + return 10 + static_cast(mc) - static_cast('a'); + } + + return 0; +} + +//static char Hex2Char(unsigned char h1, unsigned char h2) +//{ +// // Combine the Unicode values into a single wchar_t +// char returnValue = (convertToHex(h1) << 4) + convertToHex(h2); +// return (returnValue); +//} + +std::string wstring2string(const std::wstring& wstr) +{ + if (wstr.empty()) return std::string(); + +#ifdef _MSC_VER + int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +#else + + try + { + std::wstring_convert, wchar_t> StringConverter; + return StringConverter.to_bytes(wstr); + } + catch (...) + { + std::cout << "std::use_facet failed" << std::endl; + } + + return ""; +#endif +} + +std::wstring string2wstring(const std::string& inputString) +{ + if (inputString.empty()) return std::wstring(); + + try + { +#ifdef _MSC_VER + std::wstringstream ws; + ws << inputString.c_str(); + std::wstring result = ws.str(); + return result; +#else + std::wstring_convert> conv; + return conv.from_bytes(inputString); +#endif + } + catch (...) + { + return std::filesystem::path(inputString).wstring(); + } +} + +void checkOpeningClosingParenthesis(const char* ch_check) +{ + int num_opening = 0; + int num_closing = 0; + while (*ch_check != '\0') + { + if (*ch_check == '(') + { + ++num_opening; + } + else if (*ch_check == ')') + { + ++num_closing; + } + else if (*ch_check == '\'') + { + findEndOfString(ch_check); + continue; + } + ++ch_check; + } + if (num_opening != num_closing) + { + std::stringstream err; + err << "checkOpeningClosingParenthesis: num_opening != num_closing " << std::endl; + throw BuildingException(err.str(), __FUNC__); + } +} + +std::istream& bufferedGetline(std::istream& inputStream, std::string& lineOut) +{ + lineOut.clear(); + std::istream::sentry se(inputStream, true); + std::streambuf* sb = inputStream.rdbuf(); + + // std::getline does not work with all line endings, reads complete file instead. + // Handle \n (unix), \r\n (windows), \r (mac) line endings here + while (true) + { + int c = sb->sbumpc(); + switch (c) + { + case '\n': + return inputStream; + case '\r': + if (sb->sgetc() == '\n') + { + sb->sbumpc(); + } + return inputStream; + case std::streambuf::traits_type::eof(): + // in case the last line has no line ending + if (lineOut.empty()) + { + inputStream.setstate(std::ios::eofbit); + } + return inputStream; + default: + lineOut += (char)c; + } + } +} + +std::istream& bufferedGetStepLine(std::istream& inputStream, std::string& lineOut) +{ + lineOut.clear(); + std::istream::sentry se(inputStream, true); + std::streambuf* sb = inputStream.rdbuf(); + bool inString = false; + +#ifdef _DEBUG + std::string string40; +#endif + + // std::getline does not work with all line endings, reads complete file instead. + // Handle \n (unix), \r\n (windows), \r (mac) line endings here + while (true) + { + int c = sb->sbumpc(); // sbumpc: character at the current position and advances the current position to the next character +#ifdef _DEBUG + std::string charAsString; + charAsString += ((char)c); + string40 += charAsString; +#endif + switch (c) + { + case ';': + { + if (!inString) + { + int nextChar = sb->sgetc(); + if (isspace(nextChar)) + { + sb->sbumpc(); // sbumpc: character at the current position and advances the current position to the next character + } + return inputStream; + } + lineOut += (char)c; + continue; + } + case '\'': + { + inString = !inString; + lineOut += (char)c; + continue; + } + case '/': + if (!inString) + { + int nextChar = sb->sgetc(); +#ifdef _DEBUG + std::string charAsString2; + charAsString2 += ((char)nextChar); +#endif + if (nextChar == '*') + { +#ifdef _DEBUG + std::string commentIgnored = "/*"; +#endif + sb->sbumpc(); + + // continue till end of /* */ comment + bool inMultiLineComment = true; + while (inMultiLineComment) + { + int c2 = sb->sbumpc(); + if (c2 == '*') + { + char c3 = sb->sgetc(); + if (c3 == '/') + { + sb->sbumpc(); + inMultiLineComment = false; + + // skip whitespaces + while (true) + { + char c4 = sb->sgetc(); + if (isspace(c4)) + { + sb->sbumpc(); + } + else if (c4 == std::streambuf::traits_type::eof()) + { + if (lineOut.empty()) + { + inputStream.setstate(std::ios::eofbit); + } + return inputStream; + } + else + { + break; + } + } + + break; + } + } + else if (c2 == std::streambuf::traits_type::eof()) + { + // in case the last line has no line ending + if (lineOut.empty()) + { + inputStream.setstate(std::ios::eofbit); + } + return inputStream; + } +#ifdef _DEBUG + commentIgnored += c2; +#endif + } + continue; + } + } + lineOut += (char)c; + continue; + + case '\n': + continue; + + case '\r': + continue; + + case std::streambuf::traits_type::eof(): + // in case the last line has no line ending + if (lineOut.empty()) + { + inputStream.setstate(std::ios::eofbit); + } + return inputStream; + default: + lineOut += (char)c; + } + } +} + +void findLeadingTrailingParanthesis(char* ch, char*& pos_opening, char*& pos_closing) +{ + short num_opening = 0; + while (*ch != '\0') + { + if (*ch == '\'') + { + ++ch; + // beginning of string, continue to end + while (*ch != '\0') + { + if (*ch == '\'') + { + break; + } + ++ch; + } + ++ch; + continue; + } + + if (*ch == '(') + { + if (num_opening == 0) + { + pos_opening = ch; + } + ++num_opening; + } + else if (*ch == ')') + { + --num_opening; + if (num_opening == 0) + { + pos_closing = ch; + } + } + ++ch; + } +} + +bool findEndOfStepLine(char* ch, char*& pos_end) +{ + short num_opening = 0; + while (*ch != '\0') + { + if (*ch == '\'') + { + ++ch; + // beginning of string, continue to end + while (*ch != '\0') + { + if (*ch == '\'') + { + break; + } + ++ch; + } + if (*ch == '\0') + { + // end of string inside comment, so not a valid end of STEP line + return false; + } + ++ch; + continue; + } + + if (*ch == '(') + { + ++num_opening; + } + else if (*ch == ')') + { + --num_opening; + if (num_opening == 0) + { + while (isspace(*ch)) { ++ch; } + + if (*ch == ';') + { + pos_end = ch; + return true; + } + } + } + else if (*ch == ';') + { + if (num_opening == 0) + { + pos_end = ch; + return true; + } + } + ++ch; + } + return false; +} + +void tokenizeList(std::string& list_str, std::vector& list_items) +{ + if (list_str.empty()) + { + return; + } + char* stream_pos = const_cast(list_str.c_str()); + char* last_token = stream_pos; + int numNestedLists = 0; + while (*stream_pos != '\0') + { + if (*stream_pos == '(') + { + ++stream_pos; + ++numNestedLists; + continue; + } + else if (*stream_pos == ')') + { + ++stream_pos; + --numNestedLists; + continue; + } + else if (*stream_pos == '\'') + { + ++stream_pos; + // beginning of string, continue to end + while (*stream_pos != '\0') + { + if (*stream_pos == '\'') + { + break; + } + ++stream_pos; + } + ++stream_pos; + continue; + } + + if (*stream_pos == ',' && numNestedLists == 0) + { + std::string item(last_token, stream_pos - last_token); + list_items.push_back(item); + + ++stream_pos; + while (isspace(*stream_pos)) { ++stream_pos; } + last_token = stream_pos; + if (*stream_pos == '\0') + { + throw BuildingException("tokenizeList: *stream_pos == '\0'", __FUNC__); + } + continue; + } + ++stream_pos; + } + // pick up last element + if (last_token != nullptr) + { + if (last_token != stream_pos) + { + std::string item(last_token, stream_pos - last_token); + list_items.push_back(item); + } + } +} + +void tokenizeEntityList(std::string& list_str, std::vector& list_items) +{ + if (list_str.empty()) + { + return; + } + char* stream_pos = const_cast(list_str.c_str()); + while (*stream_pos != '\0') + { + // skip whitespace + while (isspace(*stream_pos)) { ++stream_pos; } + + if (*stream_pos == '#') + { + ++stream_pos; + // beginning of id + char* begin_id = stream_pos; + + // proceed until end of integer + ++stream_pos; + while (*stream_pos != '\0') + { + if (isdigit(*stream_pos)) + { + ++stream_pos; + } + else + { + break; + } + } + const int id = std::stoi(std::string(begin_id, stream_pos - begin_id)); + list_items.push_back(id); + } + else if (*stream_pos == '$') + { + // empty + } + else + { + std::stringstream err; + err << "tokenizeEntityList: unexpected argument: " << list_str.c_str() << std::endl; + throw BuildingException(err.str(), __FUNC__); + } + + while (isspace(*stream_pos)) + { + ++stream_pos; + } + if (*stream_pos == ',') + { + ++stream_pos; + //last_token = stream_pos; + continue; + } + else + { + break; + } + } +} + +void readIntegerList(const std::string& str, std::vector& vec) +{ + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == '(') + { + ++i; + last_token = i; + break; + } + ++i; + } + + while (i < argsize) + { + if (ch[i] == ',') + { + size_t str_length = i - last_token; + if (str_length > 0) + { + vec.push_back(std::stoi(str.substr(last_token, i - last_token))); + } + last_token = i + 1; + } + else if (ch[i] == ')') + { + size_t str_length = i - last_token; + if (str_length > 0) + { + vec.push_back(std::stoi(str.substr(last_token, i - last_token))); + } + return; + } + ++i; + } +} + +void readIntegerList2D(const std::string& str, std::vector >& vec) +{ + // ((1,2,4),(3,23,039),(938,3,-3,6)) + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t num_par_open = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == ',') + { + if (num_par_open == 1) + { + std::vector inner_vec; + vec.push_back(inner_vec); + readIntegerList(str.substr(last_token, i - last_token), inner_vec); + last_token = i + 1; + } + } + else if (ch[i] == '(') + { + ++num_par_open; + } + else if (ch[i] == ')') + { + --num_par_open; + if (num_par_open == 0) + { + std::vector inner_vec; + vec.push_back(inner_vec); + readIntegerList(str.substr(last_token, i - last_token), inner_vec); + return; + } + } + ++i; + } +} + +void readRealList(const std::string& str, std::vector& vec) +{ + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == '(') + { + ++i; + last_token = i; + break; + } + ++i; + } + + while (i < argsize) + { + if (ch[i] == ',') + { + vec.push_back(std::stod(str.substr(last_token, i - last_token))); + last_token = i + 1; + } + else if (ch[i] == ')') + { + vec.push_back(std::stod(str.substr(last_token, i - last_token))); + return; + } + ++i; + } +} + +void readRealArray(const std::string& str, double(&vec)[3], short int& size) +{ + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == '(') + { + ++i; + last_token = i; + break; + } + ++i; + } + short idx = 0; + while (i < argsize) + { + if (ch[i] == ',') + { + if (idx < 3) + { + vec[idx] = std::stod(str.substr(last_token, i - last_token)); + } + ++idx; + last_token = i + 1; + } + else if (ch[i] == ')') + { + if (idx < 3) + { + vec[idx] = std::stod(str.substr(last_token, i - last_token)); + } + size = idx + 1; + return; + } + ++i; + } +} + +void readRealList2D(const std::string& str, std::vector >& vec) +{ + // ((1.6,2.0,4.9382),(3.78,23.34,039.938367),(938.034,3.0,-3.45,6.9182)) + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t num_par_open = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == ',') + { + if (num_par_open == 1) + { + std::vector inner_vec; + vec.push_back(inner_vec); + readRealList(str.substr(last_token, i - last_token), vec.back()); + last_token = i; + } + } + else if (ch[i] == '(') + { + ++num_par_open; + last_token = i; + } + else if (ch[i] == ')') + { + --num_par_open; + if (num_par_open == 0) + { + std::vector inner_vec; + vec.push_back(inner_vec); + readRealList(str.substr(last_token, i - last_token), vec.back()); + return; + } + } + ++i; + } +} + +void readRealList3D(const std::string& str, std::vector > >& vec) +{ + // ((1.6,2.0,4.9382),(3.78,23.34,039.938367),(938.034,3.0,-3.45,6.9182)) + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t num_par_open = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == ',') + { + if (num_par_open == 1) + { + std::vector > inner_vec; + vec.push_back(inner_vec); + readRealList2D(str.substr(last_token, i - last_token), vec.back()); + last_token = i; + } + } + else if (ch[i] == '(') + { + ++num_par_open; + last_token = i; + } + else if (ch[i] == ')') + { + --num_par_open; + if (num_par_open == 0) + { + std::vector > inner_vec; + vec.push_back(inner_vec); + readRealList2D(str.substr(last_token, i - last_token), vec.back()); + return; + } + } + ++i; + } +} + +void readBinary(const std::string& str, std::string& target) +{ + target = str; +} + +void readBinaryString(const std::string& attribute_value, std::string& target) +{ + if (attribute_value.size() < 2) + { + target = attribute_value; + return; + } + if (attribute_value[0] == '"' && attribute_value[attribute_value.size() - 1] == '"') + { + target = attribute_value.substr(1, attribute_value.size() - 2); + } +} + +void readBinaryList(const std::string& str, std::vector& vec) +{ + readStringList(str, vec); +} + +void readStringList(const std::string& str, std::vector& vec) +{ + const char* ch = str.c_str(); + const size_t argsize = str.size(); + if (argsize == 0) + { + return; + } + size_t i = 0; + size_t last_token = 0; + while (i < argsize) + { + if (ch[i] == '(') + { + ++i; + last_token = i; + break; + } + ++i; + } + while (i < argsize) + { + if (ch[i] == ',') + { + vec.push_back(str.substr(last_token, i - last_token)); + last_token = i + 1; + } + else if (ch[i] == ')') + { + vec.push_back(str.substr(last_token, i - last_token)); + return; + } + ++i; + } +} + +void findEndOfString(const char*& stream_pos) +{ + ++stream_pos; + const char* pos_begin = stream_pos; + + // beginning of string, continue to end + while (*stream_pos != '\0') + { + if (*stream_pos == '\\') + { + if (*(stream_pos + 1) == 'X') + { + if (*(stream_pos + 2) == '0' || *(stream_pos + 2) == '2' || *(stream_pos + 2) == '4') + { + if (*(stream_pos + 3) == '\\') + { + // ISO 10646 encoding, continue + stream_pos += 4; + continue; + } + } + } + + if (*(stream_pos + 1) == '\\') + { + // we have a double backslash, so just continue + ++stream_pos; + ++stream_pos; + continue; + } + if (*(stream_pos + 1) == '\'') + { + // quote is escaped + ++stream_pos; + ++stream_pos; + continue; + } + } + + if (*stream_pos == '\'') + { + if (*(stream_pos + 1) == '\'') + { + // two single quotes in string + if (stream_pos != pos_begin) + { + ++stream_pos; + ++stream_pos; + continue; + } + } + ++stream_pos; + + // end of string + break; + } + ++stream_pos; + } +} + +static char16_t checkAndConvertAppleEncoding(char16_t input) +{ + if (input >= 0x80 && input <= 0xFF) + { + switch (input) + { + case 0x80: return 196; + case 0x81: return 197; + case 0x82: return 199; + case 0x83: return 201; + case 0x84: return 209; + case 0x85: return 214; + case 0x86: return 220; + case 0x87: return 225; + case 0x88: return 224; + case 0x89: return 226; + case 0x8A: return 228; + case 0x8B: return 227; + case 0x8C: return 229; + case 0x8D: return 231; + case 0x8E: return 233; + case 0x8F: return 232; + case 0x90: return 234; + case 0x91: return 235; + case 0x92: return 237; + case 0x93: return 236; + case 0x94: return 238; + case 0x95: return 239; + case 0x96: return 241; + case 0x97: return 243; + case 0x98: return 242; + case 0x99: return 244; + case 0x9A: return 246; + case 0x9B: return 245; + case 0x9C: return 250; + case 0x9D: return 249; + case 0x9E: return 251; + case 0x9F: return 252; + case 0xA0: return 8224; + case 0xA1: return 176; + case 0xA2: return 162; + case 0xA3: return 163; + case 0xA4: return 167; + case 0xA5: return 8226; + case 0xA6: return 182; + case 0xA7: return 223; + case 0xA8: return 174; + case 0xA9: return 169; + case 0xAA: return 8482; + case 0xAB: return 180; + case 0xAC: return 168; + case 0xAD: return 8800; + case 0xAE: return 198; + case 0xAF: return 216; + case 0xB0: return 8734; + case 0xB1: return 177; + case 0xB2: return 8804; + case 0xB3: return 8805; + case 0xB4: return 165; + case 0xB5: return 181; + case 0xB6: return 8706; + case 0xB7: return 8721; + case 0xB8: return 8719; + case 0xB9: return 960; + case 0xBA: return 8747; + case 0xBB: return 170; + case 0xBC: return 186; + case 0xBD: return 937; + case 0xBE: return 230; + case 0xBF: return 248; + case 0xC0: return 191; + case 0xC1: return 161; + case 0xC2: return 172; + case 0xC3: return 8730; + case 0xC4: return 402; + case 0xC5: return 8776; + case 0xC6: return 8710; + case 0xC7: return 171; + case 0xC8: return 187; + case 0xC9: return 8230; + case 0xCA: return 160; + case 0xCB: return 192; + case 0xCC: return 195; + case 0xCD: return 213; + case 0xCE: return 338; + case 0xCF: return 339; + case 0xD0: return 8211; + case 0xD1: return 8212; + case 0xD2: return 8220; + case 0xD3: return 8221; + case 0xD4: return 8216; + case 0xD5: return 8217; + case 0xD6: return 247; + case 0xD7: return 9674; + case 0xD8: return 255; + case 0xD9: return 376; + case 0xDA: return 8260; + case 0xDB: return 8364; + case 0xDC: return 8249; + case 0xDD: return 8250; + case 0xDE: return 64257; + case 0xDF: return 64258; + case 0xE0: return 8225; + case 0xE1: return 183; + case 0xE2: return 8218; + case 0xE3: return 8222; + case 0xE4: return 8240; + case 0xE5: return 194; + case 0xE6: return 202; + case 0xE7: return 193; + case 0xE8: return 203; + case 0xE9: return 200; + case 0xEA: return 205; + case 0xEB: return 206; + case 0xEC: return 207; + case 0xED: return 204; + case 0xEE: return 211; + case 0xEF: return 212; + case 0xF0: return 63743; + case 0xF1: return 210; + case 0xF2: return 218; + case 0xF3: return 219; + case 0xF4: return 217; + case 0xF5: return 305; + case 0xF6: return 710; + case 0xF7: return 732; + case 0xF8: return 175; + case 0xF9: return 728; + case 0xFA: return 729; + case 0xFB: return 730; + case 0xFC: return 184; + case 0xFD: return 733; + case 0xFE: return 731; + case 0xFF: return 711; + } + } + return input; +} + +void decodeArgumentString(const std::string& argument_str, std::string& arg_out) +{ + const size_t arg_length = argument_str.length(); + if (arg_length == 0) + { + return; + } + + std::string arg_str_new; + + char* stream_pos = const_cast(argument_str.c_str()); // ascii characters from STEP file + while (*stream_pos != '\0') + { + if (*stream_pos == '\\') + { + char c1 = *(stream_pos + 1); + if (c1 == 'S') + { + // we have \S + char c2 = *(stream_pos + 2); + if (c2 == '\\') + { + // we have '\S\', for example 'Heizk\S\vrper' + char c3 = *(stream_pos + 3); + if (c3 != '\0') + { + char c4 = *(stream_pos + 4); + if (c4 == '\\') + { + // we have '\S\ . \' + char c5 = *(stream_pos + 5); + if (c5 == 'S') + { + if (*(stream_pos + 6) == '\\') + { + if (*(stream_pos + 7) != '\0') + { + char first = c3; + char second = *(stream_pos + 7); + char append_char = char(125 + first + second); + arg_str_new += append_char; + stream_pos += 8; + continue; + } + } + } + else if (c5 == 'Q') + { + if (*(stream_pos + 6) == '\\') + { + if (*(stream_pos + 7) != '\0') + { + char first = c3; + char second = *(stream_pos + 7); + char append_char = char(125 + first + second); + arg_str_new += append_char; + stream_pos += 8; + continue; + } + } + } + } + else + { + // next characters code value v shall be interpreted as v + 128 + char first = c3; + char append_char = char(128 + first); + uint8_t charAsUint = append_char; + arg_str_new.push_back(0xc0 | charAsUint >> 6); + arg_str_new.push_back(0x80 | (charAsUint & 0x3f)); + + stream_pos += 4; + continue; + } + } + } + } + else if (c1 == 'X') + { + char c2 = *(stream_pos + 2); + if (c2 == '\\') + { + // we have \\X\\Unicode code points + + char codePoint1 = *(stream_pos + 3); + char codePoint2 = *(stream_pos + 4); + codePoint1 = convertToHex(codePoint1); + codePoint2 = convertToHex(codePoint2); + + // Combine the Unicode values into a single char + char combined[2]; + combined[0] = (codePoint1 << 4) | codePoint2; + combined[1] = 0; + char16_t* combined16 = reinterpret_cast(combined); + if (combined16[0] >= 0x80 && combined16[0] <= 0x9F) + { + combined16[0] = checkAndConvertAppleEncoding(combined16[0]); + } + std::u16string u16str(combined16, 1); + std::wstring_convert, char16_t> convert; + std::string utf8 = convert.to_bytes(u16str); + + arg_str_new += utf8; + stream_pos += 5; + continue; + } + else if (c2 == '0') + { + if (*(stream_pos + 3) == '\\') + { + stream_pos += 4; + continue; + } + } + else if (c2 == '2') + { + if (*(stream_pos + 3) == '\\') + { + // we have \X2\Unicode code points + // for example pot\X2\00EA\X0\ncia + + // the following sequence of multiples of four hexadecimal characters shall be interpreted as encoding the + // two-octet representation of characters from the BMP in ISO 10646 + + stream_pos += 4; + std::vector utf16Characters; + do + { + char codePoint1 = *(stream_pos + 0); + + if (codePoint1 == '\\') + { + char c1 = *(stream_pos + 1); + char c2 = *(stream_pos + 2); + char c3 = *(stream_pos + 3); + if (c1 == 'X' && c2 == '0' && c3 == '\\') + { + stream_pos += 4; + break; + } + else + { + // unexpected sequence + arg_out = argument_str; + return; + } + } + + char codePoint2 = *(stream_pos + 1); + char codePoint3 = *(stream_pos + 2); + char codePoint4 = *(stream_pos + 3); + + char c1 = (convertToHex(codePoint1) << 4) | convertToHex(codePoint2); + char c2 = (convertToHex(codePoint3) << 4) | convertToHex(codePoint4); + + utf16Characters.push_back(c2); + utf16Characters.push_back(c1); + + stream_pos += 4; + + } while ((*stream_pos != '\0')); + + std::u16string u16str(reinterpret_cast(&utf16Characters[0]), utf16Characters.size() / 2); + std::wstring_convert, char16_t> convert; + std::string utf8 = convert.to_bytes(u16str); + arg_str_new += utf8; + } + continue; + } + } + else if (c1 == 'N') + { + if (*(stream_pos + 2) == '\\') + { + arg_str_new.append("\n"); + stream_pos += 3; + continue; + } + } + } + + char current_char = *stream_pos; + arg_str_new += current_char; + ++stream_pos; + } + + arg_out = arg_str_new; +} + +void decodeArgumentStrings( const std::vector& entity_arguments, std::vector& args_out) +{ + for (auto& argument_str : entity_arguments) + { + const size_t arg_length = argument_str.length(); + if (arg_length == 0) + { + continue; + } + + std::string decoded; + decodeArgumentString(argument_str, decoded); + args_out.push_back(decoded); + } +} + +void readBool(const std::string& attribute_value, bool& target) +{ + if (std_iequal(attribute_value, ".F.")) + { + target = false; + } + else if (std_iequal(attribute_value, ".T.")) + { + target = true; + } +} + +void readLogical(const std::string& attribute_value, LogicalEnum& target) +{ + if (std_iequal(attribute_value, ".F.")) + { + target = LOGICAL_FALSE; + } + else if (std_iequal(attribute_value, ".T.")) + { + target = LOGICAL_TRUE; + } + else if (std_iequal(attribute_value, ".U.")) + { + target = LOGICAL_UNKNOWN; + } +} + +void readInteger(const std::string& attribute_value, int& target) +{ + target = std::stoi(attribute_value); +} + +void readIntegerValue(const std::string& str, int& int_value) +{ + if (str.compare("$") == 0) + { + int_value = std::numeric_limits::quiet_NaN(); + } + else if (str.compare("*") == 0) + { + int_value = std::numeric_limits::quiet_NaN(); + } + else + { + int_value = std::stoi(str); + } +} + +void readReal(const std::string& attribute_value, double& target) +{ + target = std::stod(attribute_value); +} + +void readString(const std::string& attribute_value, std::string& target) +{ + if (attribute_value.size() < 2) + { + target = attribute_value; + return; + } + if (attribute_value[0] == '\'' && attribute_value[attribute_value.size() - 1] == '\'') + { + target = attribute_value.substr(1, attribute_value.size() - 2); + } + else + { + target = attribute_value; + } +} + +void addArgument(const char* stream_pos, const char*& last_token, std::vector& entity_arguments) +{ + if (*last_token == ',') + { + ++last_token; + } + + const char* begin_arg = last_token; + + // skip whitespace + while (isspace(*begin_arg)) + { + ++begin_arg; + } + + int remaining_size = static_cast(stream_pos - begin_arg); + if (remaining_size > 0) + { + const char* end_arg = stream_pos - 1; + entity_arguments.emplace_back(begin_arg, end_arg - begin_arg + 1); + } + last_token = stream_pos; +} + +//\brief split one string into a vector of argument strings +// caution: when using threads, this method runs in parallel threads +void tokenizeEntityArguments(const std::string& argument_str, std::vector& entity_arguments) +{ + if (argument_str.size() == 0) + { + return; + } + const char* stream_pos = argument_str.c_str(); + int num_open_braces = 0; + const char* last_token = stream_pos; + + while (*stream_pos != '\0') + { + if (*stream_pos == '\'') + { + findEndOfString(stream_pos); + continue; + } + + if (*stream_pos == '(') + { + ++num_open_braces; + } + else if (*stream_pos == ',') + { + if (num_open_braces == 0) + { + const char* last_token_check = last_token; + addArgument(stream_pos, last_token, entity_arguments); + } + } + else if (*stream_pos == ')') + { + --num_open_braces; + if (num_open_braces == 0) + { + ++stream_pos; + addArgument(stream_pos, last_token, entity_arguments); + + const char* stream_pos_begin = argument_str.c_str(); + size_t readCount = stream_pos - stream_pos_begin; + + if (readCount == argument_str.size()) + { + break; + } + } + } + ++stream_pos; + } + + if (*last_token != *stream_pos) + { + if (*last_token != '\0') + { + addArgument(stream_pos, last_token, entity_arguments); + } + } +} + +//\brief split one string into a vector of argument strings +// caution: when using threads, this method runs in parallel threads +void tokenizeInlineArgument(std::string arg, std::string& keyword, std::string& inline_arg) +{ + if (arg.empty()) + { + throw BuildingException("arg.size() == 0", __FUNC__); + } + if (arg[0] == '$') + { + return; + } + if (arg[0] == '*') + { + return; + } + if (arg[0] == '#') + { + throw BuildingException("tokenizeInlineArgument: argument begins with #, so it is not inline", __FUNC__); + } + + char* stream_pos = const_cast(arg.c_str()); + while (isspace(*stream_pos)) { ++stream_pos; } + + char* begin_keyword = stream_pos; + while (isalnum(*stream_pos)) { ++stream_pos; } + + // get type name + std::string key(begin_keyword, stream_pos - begin_keyword); + + if (key.empty()) + { + // single argument, for example .T. + inline_arg = arg; + return; + } + + // proceed to '(' + int numOpenBraces = 0; + if (*stream_pos == '(') + { + ++stream_pos; + ++numOpenBraces; + } + else + { + while (*stream_pos != '\0') + { + if (*stream_pos == '(') + { + ++stream_pos; + ++numOpenBraces; + break; + } + ++stream_pos; + } + } + + // proceed to ')' + std::string inline_argument; + char* inline_argument_begin = stream_pos; + + while (*stream_pos != '\0') + { + if (*stream_pos == '\'') + { + ++stream_pos; + // inside string + while (*stream_pos != '\0') + { + if (*stream_pos == '\'') + { + // check if tick is escaped + char* tick_pos = stream_pos; + bool tick_escaped = false; + while (tick_pos != begin_keyword) + { + --tick_pos; + if (*tick_pos == '\\') + { + tick_escaped = !tick_escaped; + continue; + } + break; + } + if (tick_escaped) + { + ++stream_pos; + continue; + } + // else tick marks the end of argument + break; + } + ++stream_pos; + } + } + + if (*stream_pos == '(') + { + ++numOpenBraces; + ++stream_pos; + continue; + } + + if (*stream_pos == ')') + { + --numOpenBraces; + // skip whitespace + while (isspace(*inline_argument_begin)) + { + ++inline_argument_begin; + } + if (numOpenBraces == 0) + { + char* end_arg = stream_pos - 1; + inline_argument = std::string(inline_argument_begin, end_arg - inline_argument_begin + 1); + break; + } + } + ++stream_pos; + } + + std::transform(key.begin(), key.end(), key.begin(), [](char c) {return static_cast(std::toupper(c)); }); + keyword = key; + inline_arg = inline_argument; +} + +bool std_iequal(const std::string& a, const std::string& b) +{ + if (a.size() == b.size()) + { + return std::equal(a.begin(), a.end(), b.begin(), [](const char l, const char r) { return std::toupper(l) == std::toupper(r); }); + } + return false; +} diff --git a/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.h b/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.h index 399e963f7..e744c8193 100644 --- a/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.h +++ b/IfcPlusPlus/src/ifcpp/reader/ReaderUtil.h @@ -1,765 +1,765 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ifcpp/model/BasicTypes.h" -#include "ifcpp/model/BuildingException.h" -#include "ifcpp/model/BuildingObject.h" -#include "ifcpp/IFC4X3/TypeFactory.h" - -#ifdef _MSC_VER -#include -#include -#pragma warning(disable : 4996) -#endif - -void readBoolList( const std::string& str, std::vector& vec ); -void readIntegerList( const std::string& str, std::vector& vec ); -void readIntegerList2D( const std::string& str, std::vector >& vec ); -void readIntegerList3D( const std::string& str, std::vector > >& vec ); -void readRealList( const std::string& str, std::vector& vec ); -void readRealArray( const std::string& str, double (&vec)[3], short int& size ); -void readRealList2D( const std::string& str, std::vector >& vec ); -void readRealList3D( const std::string& str, std::vector > >& vec ); -void readBinary( const std::string& str, std::string& target ); -void readBinaryString(const std::string& attribute_value, std::string& target); -void readBinaryList( const std::string& str, std::vector& vec ); -void readStringList( const std::string& str, std::vector& vec ); - -IFCQUERY_EXPORT void tokenizeEntityArguments( const std::string& argument_str, std::vector& entity_arguments ); -IFCQUERY_EXPORT void tokenizeEntityArguments( const std::string& argument_str, std::vector& entity_arguments ); -void tokenizeInlineArgument(std::string arg, std::string& keyword, std::string& inline_arg); -void tokenizeList( std::string& list_str, std::vector& list_items ); -void tokenizeEntityList( std::string& list_str, std::vector& list_items ); -void findLeadingTrailingParanthesis(char* ch, char*& pos_opening, char*& pos_closing); -void findEndOfString(const char*& stream_pos); -bool findEndOfStepLine(char* ch, char*& pos_end); -void checkOpeningClosingParenthesis(const char* ch_check); -std::istream& bufferedGetline(std::istream& is, std::string& t); -std::istream& bufferedGetStepLine(std::istream& inputStream, std::string& lineOut); - -IFCQUERY_EXPORT std::string wstring2string(const std::wstring& str); -IFCQUERY_EXPORT std::wstring string2wstring(const std::string& inputString); -IFCQUERY_EXPORT bool std_iequal(const std::string& a, const std::string& b); - -inline std::string getFileExtension(std::string path) -{ -#ifdef _MSC_VER - return std::filesystem::path(string2wstring(path)).extension().string(); -#else - return std::filesystem::path(path).extension().string(); -#endif -} - -IFCQUERY_EXPORT void decodeArgumentString(const std::string& argument_str, std::string& arg_out); -IFCQUERY_EXPORT void decodeArgumentStrings( const std::vector& entity_arguments, std::vector& args_out ); - -void readBool(const std::string& attribute_value, bool& target); -void readLogical(const std::string& attribute_value, LogicalEnum& target); -void readInteger(const std::string& attribute_value, int& target); -void readIntegerValue(const std::string& str, int& int_value); -void readReal(const std::string& attribute_value, double& target); -void readString(const std::string& attribute_value, std::string& target); - -template -void readTypeOfIntegerList( const std::string& str, std::vector >& target_vec ) -{ - // example: (38,12,4) - char const* ch = str.c_str(); - char const* last_token = nullptr; - - // ignore leading space or opening parenthesis - while( *ch != '\0' ) - { - if( *ch == '(' ) - { - checkOpeningClosingParenthesis( ch ); - ++ch; - last_token = ch; - break; - } - else if( isspace( *ch ) ) { ++ch; } - else { break; } - } - - while( *ch != '\0' ) - { - if( isspace(*ch) ) - { - ++ch; - continue; - } - - while( *ch != ',' && *ch != '\0' && *ch != ')' ) - { - ++ch; - } - - if( last_token != nullptr ) - { - size_t length_str = ch - last_token; - if( length_str > 0 ) - { - std::string int_str(last_token, length_str); - int int_value = 0; - try - { - int_value = std::stoi(int_str); - } - catch( std::exception& ) - { -#ifdef _DEBUG - std::cout << "bad number: " << int_str << std::endl; -#endif - } - target_vec.push_back(shared_ptr(new T(int_value))); - } - } - - if( *ch == '\0' ) - { - break; - } - - if( *ch == ')' ) - { - break; - } -#ifdef _DEBUG - if( *ch != ',' ) - { - std::cout << __FUNC__ << ": *ch != ','" << std::endl; - } -#endif - - ++ch; - last_token = ch; - } -} - -template -void readTypeOfIntegerList2D( const std::string& str, std::vector > >& target_vec ) -{ - // example: ((8,12,4),(58,10,34),(18,10,4)) - char const* ch = str.c_str(); - - const size_t argsize = str.size(); - if( argsize == 0 ) - { - return; - } - - //Optional lists can be represented by a $ sign - if( ch[0] == '$' ) - return; - - if( ch[0] != '(' ) - { - throw BuildingException( "string does not start with (", __FUNC__ ); - } - size_t i = 0; - size_t last_token = 1; - int num_par_open = 0; - while( i -void readTypeOfRealList( const std::string& str, std::vector >& target_vec ) -{ - // example: (.38,12.0,.04) - char const* ch = str.c_str(); - char const* last_token = nullptr; - - // ignore leading space or opening parenthesis - while( *ch != '\0' ) - { - if( *ch == '(' ) - { - checkOpeningClosingParenthesis( ch ); - ++ch; - last_token = ch; - break; - } - else if( isspace( *ch ) ) { ++ch; } - else { break; } - } - - while( *ch != '\0' ) - { - if( isspace(*ch) ) - { - ++ch; - continue; - } - - while( *ch != ',' && *ch != '\0' && *ch != ')' ) - { - ++ch; - } - - if( last_token != nullptr ) - { - size_t length_str = ch - last_token; - if( length_str > 0 ) - { - std::string double_str(last_token, length_str); - double real_value = 0; - try - { - real_value = std::stod(double_str); - } - catch( std::exception& ) - { - - } - - target_vec.push_back(shared_ptr(new T(real_value))); - } - } - - if( *ch == '\0' ) - { - break; - } - - if( *ch == ')' ) - { - break; - } -#ifdef _DEBUG - if( *ch != ',' ) - { - std::cout << __FUNC__ << ": *ch != ','" << std::endl; - } -#endif - - ++ch; - last_token = ch; - } -} - -template -void readTypeOfRealList2D( const std::string& str, std::vector > >& target_vec ) -{ - // example: ((.38,12.0,.04),(.38,1.0,346.0),(1.8,1.0,.04)) - char const* ch = str.c_str(); - - const size_t argsize = str.size(); - if( argsize == 0 ) - { - return; - } - - //Optional lists can be represented by a $ sign - if( ch[0] == '$' ) - return; - - if( ch[0] != '(' ) - { - throw BuildingException( "string does not start with (", __FUNC__ ); - } - size_t i=0; - size_t last_token = 1; - int num_par_open = 0; - while( i -void readTypeOfStringList( const char* str, std::vector >& target_vec ) -{ - // example: ('Tahoma') - const char* ch = str; - const char* last_token = nullptr; - - // ignore leading space or opening parenthesis - while( *ch != '\0' ) - { - if( *ch == '(' ) - { - checkOpeningClosingParenthesis( ch ); - ++ch; - last_token = ch; - break; - } - else if( isspace( *ch ) ) { ++ch; } - else { break; } - } - - while( *ch != '\0' ) - { - if( isspace( *ch ) ) - { - // ignore leading space characters - ++ch; - continue; - } - - if ( *ch == '\'' ) - findEndOfString( ch ); - - while( *ch != ',' && *ch != '\0' && *ch != ')' ) - { - ++ch; - } - - if( last_token != nullptr ) - { - size_t length_str = ch - last_token; - if( length_str > 0 ) - { - std::string str_value( last_token, length_str ); - if (length_str > 1) - { - if (str_value.front() == '\'' && str_value.back() == '\'') - { - // "'Tahoma'" -> "Tahoma" - str_value = str_value.substr(1, str_value.size() - 2); - } - } - target_vec.push_back( shared_ptr( new T( str_value ) ) ); - } - } - - if( *ch == '\0' ) - { - break; - } - - if( *ch == ')' ) - { - break; - } -#ifdef _DEBUG - if( *ch != ',' ) - { - std::cout << __FUNC__ << ": *ch != ','" << std::endl; - } -#endif - - ++ch; - last_token = ch; - } -} - -template -void readTypeOfStringList( const std::string& str, std::vector >& target_vec ) -{ - char* ch = (char*)str.c_str(); - readTypeOfStringList( ch, target_vec ); -} - -template -void readEntityReference( const std::string& str, shared_ptr& target, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - if( str.length() == 0) - { - return; - } - if( str.at(0) == '#' ) - { - int tag = std::stoi( str.substr( 1 ) ); - std::map >::const_iterator it_entity = mapEntities.find( tag ); - if( it_entity != mapEntities.end() ) - { - shared_ptr found_obj = it_entity->second; - target = dynamic_pointer_cast(found_obj); - } - else - { - errorStream << "object with id " << tag << " not found" << std::endl; - } - } - else if( str.compare("$")==0 ) - { - - } - else if( str.compare("*")==0 ) - { - - } - else - { - errorStream << __FUNC__ << ": unexpected argument" << std::endl; - } -} - -template -void readTypeList( const std::string arg_complete, std::vector >& vec, const std::map >& mapEntities ) -{ - // example: (IFCPARAMETERVALUE(0.5),*,IFCPARAMETERVALUE(2.0)) - char* pos_opening = nullptr; - char* pos_closing = nullptr; - char* ch = (char*)arg_complete.c_str(); - findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); - if( pos_opening == nullptr || pos_closing == nullptr ) - { - if( arg_complete.compare("$") == 0 ) - { - // empty list - return; - } - std::stringstream err; - err << "num_opening != num_closing : " << arg_complete.c_str() << std::endl; - throw BuildingException( err.str(), __FUNC__ ); - } - std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); - std::vector list_items; - tokenizeList( arg, list_items ); - - for( size_t i=0; i type_obj = T::createObjectFromSTEP( item, mapEntities ); - if( type_obj ) - { - vec.push_back( type_obj ); - } - } -} - -template -void readSelectType( const std::string& item, shared_ptr& result, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - char* ch = (char*)item.c_str(); - if( *ch == '#' ) - { - ++ch; - const int id = std::stoi( ch ); - auto it_entity = mapEntities.find( id ); - if( it_entity != mapEntities.end() ) - { - shared_ptr found_obj = it_entity->second; - result = dynamic_pointer_cast(found_obj); - } - return; - } - - // could be type like IFCPARAMETERVALUE(90) - std::string type_name; - std::string inline_arg; - tokenizeInlineArgument( item, type_name, inline_arg ); - - if(type_name.size() == 0 ) - { - return; - } - - std::transform(type_name.begin(), type_name.end(), type_name.begin(), [](char c) {return std::toupper(c); }); - - shared_ptr type_instance = IFC4X3::TypeFactory::createTypeObject(type_name.c_str(), inline_arg, mapEntities, errorStream ); - if( type_instance ) - { - result = dynamic_pointer_cast(type_instance); - return; - } - - errorStream << "unhandled select argument: " << item << " in function readSelectType" << std::endl; -} - -template -void readSelectList( const std::string& arg_complete, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - // example: (#287,#291,#295,#299) or (IfcLabel('label'),'',IfcLengthMeasure(2.0),#299) - char* pos_opening = nullptr; - char* pos_closing = nullptr; - char* ch = (char*)arg_complete.c_str(); - findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); - if( pos_opening == nullptr || pos_closing == nullptr ) - { - if( arg_complete.compare("$") == 0 ) - { - // empty list - return; - } - std::stringstream err; - err << "num_opening != num_closing : " << arg_complete.c_str() << std::endl; - throw BuildingException( err.str().c_str(), __FUNC__ ); - } - std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); - std::vector list_items; - tokenizeList( arg, list_items ); - - for( size_t i=0; i select_object; - try - { - readSelectType( item, select_object, mapEntities, errorStream ); - } - catch( BuildingException& e ) - { - errorStream << e.what(); - } - if( select_object ) - { - vec.push_back( select_object ); - } - } -} - -template -void readEntityReferenceList( const char* arg_complete, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - // example: (#287,#291,#295,#299) - char* pos_opening = nullptr; - char* pos_closing = nullptr; - char* ch = (char*)arg_complete; - findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); - if( pos_opening == nullptr || pos_closing == nullptr ) - { - if( arg_complete != nullptr ) - { - if( *arg_complete == '$' ) - { - // empty list - return; - } - } - errorStream << "num_opening != num_closing " << std::endl; - return; - } - std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); - std::vector list_items; - tokenizeEntityList( arg, list_items ); - std::vector vec_not_found; - std::map >::const_iterator it_entity; - for( size_t i=0; i found_obj = it_entity->second; - vec.push_back( dynamic_pointer_cast(found_obj) ); - } - else - { - vec_not_found.push_back( id ); - } - } - - // in case there are unresolved references - if( vec_not_found.size() > 0 ) - { - errorStream << "object with id "; - - for( size_t i=0; i -void readEntityReferenceList( const std::string& str, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - // example: (#287,#291,#295,#299) - char* ch = (char*)str.c_str(); - readEntityReferenceList( ch, vec, mapEntities, errorStream ); -} - -template -void readEntityReferenceList2D( const std::string& str, std::vector > >& vec, const std::map >& mapEntities, std::stringstream& errorStream) -{ - // example: ((#287,#291,#295,#299),(#287,#291,#295,#299)) - char* ch = (char*)str.c_str(); - - const size_t argsize = str.size(); - if( argsize == 0 ) - { - return; - } - - char* last_token = ch; - int num_par_open = 0; - while( *ch != '\0' ) - { - if( *ch == ')' ) - { - --num_par_open; - if( num_par_open == 0 ) - { - // last list - vec.resize(vec.size()+1); - readEntityReferenceList( std::string( last_token, ch-last_token ), vec.back(), mapEntities, errorStream ); - return; - } - } - else if( *ch == '(' ) - { - ++num_par_open; - if( num_par_open == 1 ) - { - last_token = ch+1; - } - } - else if( *ch == ',' ) - { - if( num_par_open == 1 ) - { - vec.resize(vec.size()+1); - readEntityReferenceList( std::string( last_token, ch-last_token ), vec.back(), mapEntities, errorStream ); - last_token = ch+1; - } - } - ++ch; - } - // no closing parenthesis found - std::stringstream err; - err << "no closing parenthesis found: " << str << std::endl; - throw BuildingException( err.str(), __FUNC__ ); -} - -template -void readEntityReferenceList3D( const std::string& str, std::vector > > >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) -{ - // example: (((#287,#291,#295,#299),(#287,#291,#295,#299)),((#287,#291,#295,#299),(#287,#291,#295,#299))) - const size_t argsize = str.size(); - if( argsize < 8 ) - { - return; - } - - int num_par_open = 0; - char* ch = (char*)str.c_str(); - char* last_token = ch; - - while( *ch != '\0' ) - { - if( *ch == ')' ) - { - --num_par_open; - if( num_par_open == 0 ) - { - // last list - vec.resize(vec.size()+1); - std::string inner_argument( last_token, ch-last_token ); - readEntityReferenceList2D( inner_argument, vec.back(), mapEntities, errorStream ); - return; - } - } - else if( *ch == '(' ) - { - ++num_par_open; - if( num_par_open == 1 ) - { - last_token = ch+1; - } - } - else if( *ch == ',' ) - { - if( num_par_open == 1 ) - { - vec.resize(vec.size()+1); - std::string inner_argument( last_token, ch-last_token ); - readEntityReferenceList2D( inner_argument, vec.back(), mapEntities, errorStream ); - last_token = ch+1; - } - } - ++ch; - } - // no closing parenthesis found - std::stringstream err; - err << "no closing parenthesis found: " << str << std::endl; - throw BuildingException( err.str(), __FUNC__ ); -} +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ifcpp/model/BasicTypes.h" +#include "ifcpp/model/BuildingException.h" +#include "ifcpp/model/BuildingObject.h" +#include "ifcpp/IFC4X3/TypeFactory.h" + +#ifdef _MSC_VER +#include +#include +#pragma warning(disable : 4996) +#endif + +void readBoolList( const std::string& str, std::vector& vec ); +void readIntegerList( const std::string& str, std::vector& vec ); +void readIntegerList2D( const std::string& str, std::vector >& vec ); +void readIntegerList3D( const std::string& str, std::vector > >& vec ); +void readRealList( const std::string& str, std::vector& vec ); +void readRealArray( const std::string& str, double (&vec)[3], short int& size ); +void readRealList2D( const std::string& str, std::vector >& vec ); +void readRealList3D( const std::string& str, std::vector > >& vec ); +void readBinary( const std::string& str, std::string& target ); +void readBinaryString(const std::string& attribute_value, std::string& target); +void readBinaryList( const std::string& str, std::vector& vec ); +void readStringList( const std::string& str, std::vector& vec ); + +IFCQUERY_EXPORT void tokenizeEntityArguments( const std::string& argument_str, std::vector& entity_arguments ); +IFCQUERY_EXPORT void tokenizeEntityArguments( const std::string& argument_str, std::vector& entity_arguments ); +void tokenizeInlineArgument(std::string arg, std::string& keyword, std::string& inline_arg); +void tokenizeList( std::string& list_str, std::vector& list_items ); +void tokenizeEntityList( std::string& list_str, std::vector& list_items ); +void findLeadingTrailingParanthesis(char* ch, char*& pos_opening, char*& pos_closing); +void findEndOfString(const char*& stream_pos); +bool findEndOfStepLine(char* ch, char*& pos_end); +void checkOpeningClosingParenthesis(const char* ch_check); +std::istream& bufferedGetline(std::istream& is, std::string& t); +std::istream& bufferedGetStepLine(std::istream& inputStream, std::string& lineOut); + +IFCQUERY_EXPORT std::string wstring2string(const std::wstring& str); +IFCQUERY_EXPORT std::wstring string2wstring(const std::string& inputString); +IFCQUERY_EXPORT bool std_iequal(const std::string& a, const std::string& b); + +inline std::string getFileExtension(std::string path) +{ +#ifdef _MSC_VER + return std::filesystem::path(string2wstring(path)).extension().string(); +#else + return std::filesystem::path(path).extension().string(); +#endif +} + +IFCQUERY_EXPORT void decodeArgumentString(const std::string& argument_str, std::string& arg_out); +IFCQUERY_EXPORT void decodeArgumentStrings( const std::vector& entity_arguments, std::vector& args_out ); + +void readBool(const std::string& attribute_value, bool& target); +void readLogical(const std::string& attribute_value, LogicalEnum& target); +void readInteger(const std::string& attribute_value, int& target); +void readIntegerValue(const std::string& str, int& int_value); +void readReal(const std::string& attribute_value, double& target); +void readString(const std::string& attribute_value, std::string& target); + +template +void readTypeOfIntegerList( const std::string& str, std::vector >& target_vec ) +{ + // example: (38,12,4) + char const* ch = str.c_str(); + char const* last_token = nullptr; + + // ignore leading space or opening parenthesis + while( *ch != '\0' ) + { + if( *ch == '(' ) + { + checkOpeningClosingParenthesis( ch ); + ++ch; + last_token = ch; + break; + } + else if( isspace( *ch ) ) { ++ch; } + else { break; } + } + + while( *ch != '\0' ) + { + if( isspace(*ch) ) + { + ++ch; + continue; + } + + while( *ch != ',' && *ch != '\0' && *ch != ')' ) + { + ++ch; + } + + if( last_token != nullptr ) + { + size_t length_str = ch - last_token; + if( length_str > 0 ) + { + std::string int_str(last_token, length_str); + int int_value = 0; + try + { + int_value = std::stoi(int_str); + } + catch( std::exception& ) + { +#ifdef _DEBUG + std::cout << "bad number: " << int_str << std::endl; +#endif + } + target_vec.push_back(shared_ptr(new T(int_value))); + } + } + + if( *ch == '\0' ) + { + break; + } + + if( *ch == ')' ) + { + break; + } +#ifdef _DEBUG + if( *ch != ',' ) + { + std::cout << __FUNC__ << ": *ch != ','" << std::endl; + } +#endif + + ++ch; + last_token = ch; + } +} + +template +void readTypeOfIntegerList2D( const std::string& str, std::vector > >& target_vec ) +{ + // example: ((8,12,4),(58,10,34),(18,10,4)) + char const* ch = str.c_str(); + + const size_t argsize = str.size(); + if( argsize == 0 ) + { + return; + } + + //Optional lists can be represented by a $ sign + if( ch[0] == '$' ) + return; + + if( ch[0] != '(' ) + { + throw BuildingException( "string does not start with (", __FUNC__ ); + } + size_t i = 0; + size_t last_token = 1; + int num_par_open = 0; + while( i +void readTypeOfRealList( const std::string& str, std::vector >& target_vec ) +{ + // example: (.38,12.0,.04) + char const* ch = str.c_str(); + char const* last_token = nullptr; + + // ignore leading space or opening parenthesis + while( *ch != '\0' ) + { + if( *ch == '(' ) + { + checkOpeningClosingParenthesis( ch ); + ++ch; + last_token = ch; + break; + } + else if( isspace( *ch ) ) { ++ch; } + else { break; } + } + + while( *ch != '\0' ) + { + if( isspace(*ch) ) + { + ++ch; + continue; + } + + while( *ch != ',' && *ch != '\0' && *ch != ')' ) + { + ++ch; + } + + if( last_token != nullptr ) + { + size_t length_str = ch - last_token; + if( length_str > 0 ) + { + std::string double_str(last_token, length_str); + double real_value = 0; + try + { + real_value = std::stod(double_str); + } + catch( std::exception& ) + { + + } + + target_vec.push_back(shared_ptr(new T(real_value))); + } + } + + if( *ch == '\0' ) + { + break; + } + + if( *ch == ')' ) + { + break; + } +#ifdef _DEBUG + if( *ch != ',' ) + { + std::cout << __FUNC__ << ": *ch != ','" << std::endl; + } +#endif + + ++ch; + last_token = ch; + } +} + +template +void readTypeOfRealList2D( const std::string& str, std::vector > >& target_vec ) +{ + // example: ((.38,12.0,.04),(.38,1.0,346.0),(1.8,1.0,.04)) + char const* ch = str.c_str(); + + const size_t argsize = str.size(); + if( argsize == 0 ) + { + return; + } + + //Optional lists can be represented by a $ sign + if( ch[0] == '$' ) + return; + + if( ch[0] != '(' ) + { + throw BuildingException( "string does not start with (", __FUNC__ ); + } + size_t i=0; + size_t last_token = 1; + int num_par_open = 0; + while( i +void readTypeOfStringList( const char* str, std::vector >& target_vec ) +{ + // example: ('Tahoma') + const char* ch = str; + const char* last_token = nullptr; + + // ignore leading space or opening parenthesis + while( *ch != '\0' ) + { + if( *ch == '(' ) + { + checkOpeningClosingParenthesis( ch ); + ++ch; + last_token = ch; + break; + } + else if( isspace( *ch ) ) { ++ch; } + else { break; } + } + + while( *ch != '\0' ) + { + if( isspace( *ch ) ) + { + // ignore leading space characters + ++ch; + continue; + } + + if ( *ch == '\'' ) + findEndOfString( ch ); + + while( *ch != ',' && *ch != '\0' && *ch != ')' ) + { + ++ch; + } + + if( last_token != nullptr ) + { + size_t length_str = ch - last_token; + if( length_str > 0 ) + { + std::string str_value( last_token, length_str ); + if (length_str > 1) + { + if (str_value.front() == '\'' && str_value.back() == '\'') + { + // "'Tahoma'" -> "Tahoma" + str_value = str_value.substr(1, str_value.size() - 2); + } + } + target_vec.push_back( shared_ptr( new T( str_value ) ) ); + } + } + + if( *ch == '\0' ) + { + break; + } + + if( *ch == ')' ) + { + break; + } +#ifdef _DEBUG + if( *ch != ',' ) + { + std::cout << __FUNC__ << ": *ch != ','" << std::endl; + } +#endif + + ++ch; + last_token = ch; + } +} + +template +void readTypeOfStringList( const std::string& str, std::vector >& target_vec ) +{ + char* ch = (char*)str.c_str(); + readTypeOfStringList( ch, target_vec ); +} + +template +void readEntityReference( const std::string& str, shared_ptr& target, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + if( str.length() == 0) + { + return; + } + if( str.at(0) == '#' ) + { + int tag = std::stoi( str.substr( 1 ) ); + std::map >::const_iterator it_entity = mapEntities.find( tag ); + if( it_entity != mapEntities.end() ) + { + shared_ptr found_obj = it_entity->second; + target = dynamic_pointer_cast(found_obj); + } + else + { + errorStream << "object with id " << tag << " not found" << std::endl; + } + } + else if( str.compare("$")==0 ) + { + + } + else if( str.compare("*")==0 ) + { + + } + else + { + errorStream << __FUNC__ << ": unexpected argument" << std::endl; + } +} + +template +void readTypeList( const std::string arg_complete, std::vector >& vec, const std::map >& mapEntities ) +{ + // example: (IFCPARAMETERVALUE(0.5),*,IFCPARAMETERVALUE(2.0)) + char* pos_opening = nullptr; + char* pos_closing = nullptr; + char* ch = (char*)arg_complete.c_str(); + findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); + if( pos_opening == nullptr || pos_closing == nullptr ) + { + if( arg_complete.compare("$") == 0 ) + { + // empty list + return; + } + std::stringstream err; + err << "num_opening != num_closing : " << arg_complete.c_str() << std::endl; + throw BuildingException( err.str(), __FUNC__ ); + } + std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); + std::vector list_items; + tokenizeList( arg, list_items ); + + for( size_t i=0; i type_obj = T::createObjectFromSTEP( item, mapEntities ); + if( type_obj ) + { + vec.push_back( type_obj ); + } + } +} + +template +void readSelectType( const std::string& item, shared_ptr& result, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + char* ch = (char*)item.c_str(); + if( *ch == '#' ) + { + ++ch; + const int id = std::stoi( ch ); + auto it_entity = mapEntities.find( id ); + if( it_entity != mapEntities.end() ) + { + shared_ptr found_obj = it_entity->second; + result = dynamic_pointer_cast(found_obj); + } + return; + } + + // could be type like IFCPARAMETERVALUE(90) + std::string type_name; + std::string inline_arg; + tokenizeInlineArgument( item, type_name, inline_arg ); + + if(type_name.size() == 0 ) + { + return; + } + + std::transform(type_name.begin(), type_name.end(), type_name.begin(), [](char c) {return std::toupper(c); }); + + shared_ptr type_instance = IFC4X3::TypeFactory::createTypeObject(type_name.c_str(), inline_arg, mapEntities, errorStream ); + if( type_instance ) + { + result = dynamic_pointer_cast(type_instance); + return; + } + + errorStream << "unhandled select argument: " << item << " in function readSelectType" << std::endl; +} + +template +void readSelectList( const std::string& arg_complete, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + // example: (#287,#291,#295,#299) or (IfcLabel('label'),'',IfcLengthMeasure(2.0),#299) + char* pos_opening = nullptr; + char* pos_closing = nullptr; + char* ch = (char*)arg_complete.c_str(); + findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); + if( pos_opening == nullptr || pos_closing == nullptr ) + { + if( arg_complete.compare("$") == 0 ) + { + // empty list + return; + } + std::stringstream err; + err << "num_opening != num_closing : " << arg_complete.c_str() << std::endl; + throw BuildingException( err.str().c_str(), __FUNC__ ); + } + std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); + std::vector list_items; + tokenizeList( arg, list_items ); + + for( size_t i=0; i select_object; + try + { + readSelectType( item, select_object, mapEntities, errorStream ); + } + catch( BuildingException& e ) + { + errorStream << e.what(); + } + if( select_object ) + { + vec.push_back( select_object ); + } + } +} + +template +void readEntityReferenceList( const char* arg_complete, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + // example: (#287,#291,#295,#299) + char* pos_opening = nullptr; + char* pos_closing = nullptr; + char* ch = (char*)arg_complete; + findLeadingTrailingParanthesis( ch, pos_opening, pos_closing ); + if( pos_opening == nullptr || pos_closing == nullptr ) + { + if( arg_complete != nullptr ) + { + if( *arg_complete == '$' ) + { + // empty list + return; + } + } + errorStream << "num_opening != num_closing " << std::endl; + return; + } + std::string arg( pos_opening+1, pos_closing-pos_opening-1 ); + std::vector list_items; + tokenizeEntityList( arg, list_items ); + std::vector vec_not_found; + std::map >::const_iterator it_entity; + for( size_t i=0; i found_obj = it_entity->second; + vec.push_back( dynamic_pointer_cast(found_obj) ); + } + else + { + vec_not_found.push_back( id ); + } + } + + // in case there are unresolved references + if( vec_not_found.size() > 0 ) + { + errorStream << "object with id "; + + for( size_t i=0; i +void readEntityReferenceList( const std::string& str, std::vector >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + // example: (#287,#291,#295,#299) + char* ch = (char*)str.c_str(); + readEntityReferenceList( ch, vec, mapEntities, errorStream ); +} + +template +void readEntityReferenceList2D( const std::string& str, std::vector > >& vec, const std::map >& mapEntities, std::stringstream& errorStream) +{ + // example: ((#287,#291,#295,#299),(#287,#291,#295,#299)) + char* ch = (char*)str.c_str(); + + const size_t argsize = str.size(); + if( argsize == 0 ) + { + return; + } + + char* last_token = ch; + int num_par_open = 0; + while( *ch != '\0' ) + { + if( *ch == ')' ) + { + --num_par_open; + if( num_par_open == 0 ) + { + // last list + vec.resize(vec.size()+1); + readEntityReferenceList( std::string( last_token, ch-last_token ), vec.back(), mapEntities, errorStream ); + return; + } + } + else if( *ch == '(' ) + { + ++num_par_open; + if( num_par_open == 1 ) + { + last_token = ch+1; + } + } + else if( *ch == ',' ) + { + if( num_par_open == 1 ) + { + vec.resize(vec.size()+1); + readEntityReferenceList( std::string( last_token, ch-last_token ), vec.back(), mapEntities, errorStream ); + last_token = ch+1; + } + } + ++ch; + } + // no closing parenthesis found + std::stringstream err; + err << "no closing parenthesis found: " << str << std::endl; + throw BuildingException( err.str(), __FUNC__ ); +} + +template +void readEntityReferenceList3D( const std::string& str, std::vector > > >& vec, const std::map >& mapEntities, std::stringstream& errorStream ) +{ + // example: (((#287,#291,#295,#299),(#287,#291,#295,#299)),((#287,#291,#295,#299),(#287,#291,#295,#299))) + const size_t argsize = str.size(); + if( argsize < 8 ) + { + return; + } + + int num_par_open = 0; + char* ch = (char*)str.c_str(); + char* last_token = ch; + + while( *ch != '\0' ) + { + if( *ch == ')' ) + { + --num_par_open; + if( num_par_open == 0 ) + { + // last list + vec.resize(vec.size()+1); + std::string inner_argument( last_token, ch-last_token ); + readEntityReferenceList2D( inner_argument, vec.back(), mapEntities, errorStream ); + return; + } + } + else if( *ch == '(' ) + { + ++num_par_open; + if( num_par_open == 1 ) + { + last_token = ch+1; + } + } + else if( *ch == ',' ) + { + if( num_par_open == 1 ) + { + vec.resize(vec.size()+1); + std::string inner_argument( last_token, ch-last_token ); + readEntityReferenceList2D( inner_argument, vec.back(), mapEntities, errorStream ); + last_token = ch+1; + } + } + ++ch; + } + // no closing parenthesis found + std::stringstream err; + err << "no closing parenthesis found: " << str << std::endl; + throw BuildingException( err.str(), __FUNC__ ); +} diff --git a/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.cpp b/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.cpp index 84be79244..a3d7a13b9 100644 --- a/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.cpp +++ b/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.cpp @@ -1,108 +1,108 @@ -/* -*-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 -#include -#include - - -#include "ifcpp/model/AttributeObject.h" -#include "ifcpp/model/BasicTypes.h" -#include "ifcpp/model/BuildingObject.h" -#include "ifcpp/model/BuildingModel.h" -#include "ifcpp/writer/WriterUtil.h" -#include "IfcProduct.h" -#include "IfcProject.h" -#include "ifcpp/writer/WriterSTEP.h" -#include "ifcpp/reader/ReaderUtil.h" -#include "ifcpp/IFC4X3/EntityFactory.h" - -//#define EXTERNAL_WRITE_METHODS - -void WriterSTEP::writeModelToStream(std::stringstream& stream, shared_ptr model) -{ - //imbue C locale to always use dots as decimal separator - stream.imbue(std::locale("C")); - - const std::string& file_header_str = model->getFileHeader(); - stream << "ISO-10303-21;\n"; - stream << file_header_str.c_str(); - stream << "DATA;\n"; - stream << std::setprecision(m_writeNumberPrecision); - stream << std::setiosflags(std::ios::showpoint); - stream << std::fixed; - - const std::map >& mapEntities = model->getMapIfcEntities(); - std::vector, std::string>> entityDataStrings; - for (auto entity : mapEntities) - { - entityDataStrings.push_back(std::tuple, std::string>(entity.first, entity.second, "")); - } - - std::mutex mutexProgress; - auto t_start = std::chrono::high_resolution_clock::now(); - std::atomic counter = 0; - size_t numEntities = entityDataStrings.size(); - std::for_each(std::execution::par, entityDataStrings.begin(), entityDataStrings.end(), [&, this](std::tuple, std::string>& entityDataForOutput) { - shared_ptr obj = std::get<1>(entityDataForOutput); - if (obj.use_count() < 2) - { - // entity is referenced only in model map, not by other entities - if (!dynamic_pointer_cast(obj) && !dynamic_pointer_cast(obj)) - { - return; - } - } - std::stringstream tmpStream; -#ifdef EXTERNAL_WRITE_METHODS - getStepLine(obj, tmpStream); -#else - obj->getStepLine(tmpStream, m_writeNumberPrecision); -#endif - tmpStream << std::endl; - std::get<2>(entityDataForOutput) = tmpStream.str(); - - counter.fetch_add(1); - int currentCount = counter.load(); - if (currentCount % 60 == 0) - { - auto t_now = std::chrono::high_resolution_clock::now(); - double elapsed_time_ms = std::chrono::duration(t_now - t_start).count(); - - if (elapsed_time_ms > 1000) - { - t_start = t_now; - const std::lock_guard lock(mutexProgress); - double progress = 0.1 + 0.8 * (currentCount / double(numEntities)); - progressValueCallback(progress, "parse"); - } - } - }); - - for (auto line : entityDataStrings) - { - stream << std::get<2>(line); - } - - stream << "ENDSEC;\n"; - stream << "END-ISO-10303-21; \n"; -} +/* -*-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 +#include +#include + + +#include "ifcpp/model/AttributeObject.h" +#include "ifcpp/model/BasicTypes.h" +#include "ifcpp/model/BuildingObject.h" +#include "ifcpp/model/BuildingModel.h" +#include "ifcpp/writer/WriterUtil.h" +#include "IfcProduct.h" +#include "IfcProject.h" +#include "ifcpp/writer/WriterSTEP.h" +#include "ifcpp/reader/ReaderUtil.h" +#include "ifcpp/IFC4X3/EntityFactory.h" + +//#define EXTERNAL_WRITE_METHODS + +void WriterSTEP::writeModelToStream(std::stringstream& stream, shared_ptr model) +{ + //imbue C locale to always use dots as decimal separator + stream.imbue(std::locale("C")); + + const std::string& file_header_str = model->getFileHeader(); + stream << "ISO-10303-21;\n"; + stream << file_header_str.c_str(); + stream << "DATA;\n"; + stream << std::setprecision(m_writeNumberPrecision); + stream << std::setiosflags(std::ios::showpoint); + stream << std::fixed; + + const std::map >& mapEntities = model->getMapIfcEntities(); + std::vector, std::string>> entityDataStrings; + for (auto entity : mapEntities) + { + entityDataStrings.push_back(std::tuple, std::string>(entity.first, entity.second, "")); + } + + std::mutex mutexProgress; + auto t_start = std::chrono::high_resolution_clock::now(); + std::atomic counter = 0; + size_t numEntities = entityDataStrings.size(); + std::for_each(std::execution::par, entityDataStrings.begin(), entityDataStrings.end(), [&, this](std::tuple, std::string>& entityDataForOutput) { + shared_ptr obj = std::get<1>(entityDataForOutput); + if (obj.use_count() < 2) + { + // entity is referenced only in model map, not by other entities + if (!dynamic_pointer_cast(obj) && !dynamic_pointer_cast(obj)) + { + return; + } + } + std::stringstream tmpStream; +#ifdef EXTERNAL_WRITE_METHODS + getStepLine(obj, tmpStream); +#else + obj->getStepLine(tmpStream, m_writeNumberPrecision); +#endif + tmpStream << std::endl; + std::get<2>(entityDataForOutput) = tmpStream.str(); + + counter.fetch_add(1); + int currentCount = counter.load(); + if (currentCount % 60 == 0) + { + auto t_now = std::chrono::high_resolution_clock::now(); + double elapsed_time_ms = std::chrono::duration(t_now - t_start).count(); + + if (elapsed_time_ms > 1000) + { + t_start = t_now; + const std::lock_guard lock(mutexProgress); + double progress = 0.1 + 0.8 * (currentCount / double(numEntities)); + progressValueCallback(progress, "parse"); + } + } + }); + + for (auto line : entityDataStrings) + { + stream << std::get<2>(line); + } + + stream << "ENDSEC;\n"; + stream << "END-ISO-10303-21; \n"; +} diff --git a/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.h b/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.h index bd50f4408..77688d27d 100644 --- a/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.h +++ b/IfcPlusPlus/src/ifcpp/writer/WriterSTEP.h @@ -1,31 +1,31 @@ -/* -*-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. -*/ - -#pragma once - -#include "ifcpp/model/BuildingModel.h" -#include "ifcpp/model/StatusCallback.h" - -class IFCQUERY_EXPORT WriterSTEP : public StatusCallback -{ -public: - WriterSTEP() = default; - ~WriterSTEP() = default; - virtual void writeModelToStream( std::stringstream& stream, shared_ptr model ); - - size_t m_writeNumberPrecision = 15; -}; +/* -*-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. +*/ + +#pragma once + +#include "ifcpp/model/BuildingModel.h" +#include "ifcpp/model/StatusCallback.h" + +class IFCQUERY_EXPORT WriterSTEP : public StatusCallback +{ +public: + WriterSTEP() = default; + ~WriterSTEP() = default; + virtual void writeModelToStream( std::stringstream& stream, shared_ptr model ); + + size_t m_writeNumberPrecision = 15; +}; diff --git a/IfcPlusPlus/src/ifcpp/writer/WriterUtil.cpp b/IfcPlusPlus/src/ifcpp/writer/WriterUtil.cpp index 1b4e8fd47..5026e42e1 100644 --- a/IfcPlusPlus/src/ifcpp/writer/WriterUtil.cpp +++ b/IfcPlusPlus/src/ifcpp/writer/WriterUtil.cpp @@ -1,312 +1,312 @@ -/* -*-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 "ifcpp/model/GlobalDefines.h" -#include "ifcpp/model/BuildingObject.h" -#include "WriterUtil.h" - -void appendRealWithoutTrailingZeros(std::stringstream& stream, const double number, size_t precision) -{ - std::ostringstream temp; - temp.imbue(std::locale("C")); - temp.precision(precision); - temp << std::fixed << number; - std::string str = temp.str(); - size_t pos_dot = str.find_last_of('.'); - if (pos_dot != std::string::npos) - { - // 1.000 -> 1. - size_t pos_last_non_zero = str.find_last_not_of('0'); - if (pos_last_non_zero != std::string::npos && pos_last_non_zero >= pos_dot) - { - str.erase(pos_last_non_zero + 1, std::string::npos); - } - } - stream << str; -} - -void writeIntList(std::stringstream& stream, const std::vector& vec) -{ - // example: (3,23,039) - if (vec.size() == 0) - { - stream << "$"; - return; - } - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - if (ii > 0) - { - stream << ","; - } - - stream << vec[ii]; - } - stream << ")"; -} - -void writeIntList2D(std::stringstream& stream, const std::vector >& vec) -{ - // example: ((1,2,4),(3,23,039),(938,3,-3,6)) - if (vec.size() == 0) - { - stream << "$"; - return; - } - - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - const std::vector& inner_vec = vec[ii]; - if (ii > 0) - { - stream << ","; - } - writeIntList(stream, inner_vec); - } - stream << ")"; -} - -void writeIntList3D(std::stringstream& stream, const std::vector > >& vec) -{ - // example: ((1,2,4),(3,23,39),(938,3,-35,6)) - if (vec.size() == 0) - { - stream << "$"; - return; - } - - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - const std::vector >& inner_vec = vec[ii]; - if (ii > 0) - { - stream << ","; - } - writeIntList2D(stream, inner_vec); - } - stream << ")"; -} - -void writeRealList(std::stringstream& stream, const std::vector& vec, bool optionalAttribute, size_t precision) -{ - // example: (3,23,039) - if (vec.size() == 0) - { - if (optionalAttribute) - { - stream << "$"; - } - else - { - stream << "()"; - } - return; - } - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - if (ii > 0) - { - stream << ","; - } - - appendRealWithoutTrailingZeros(stream, vec[ii], precision); - } - stream << ")"; -} - -void writeRealArray3(std::stringstream& stream, const double (&vec)[3], bool optionalAttribute, short int size, size_t precision) -{ - // example: (3,23,039) - if (size == 0) - { - if (optionalAttribute) - { - stream << "$"; - } - else - { - stream << "()"; - } - - - return; - } - stream << "("; - for (size_t ii = 0; ii < size; ++ii) - { - if (ii > 0) - { - stream << ","; - } - - appendRealWithoutTrailingZeros(stream, vec[ii], precision); - } - stream << ")"; -} - -void writeRealList2D(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision) -{ - // example: ((1,2,4),(3,23,039),(938,3,-3,6)) - if (vec.size() == 0) - { - //stream << "$"; - if (optionalAttribute) - { - stream << "$"; - } - else - { - stream << "()"; - } - return; - } - - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - const std::vector& inner_vec = vec[ii]; - if (ii > 0) - { - stream << ","; - } - writeRealList(stream, inner_vec, false, precision); - } - stream << ")"; -} - -void writeRealList3D(std::stringstream& stream, const std::vector > >& vec, size_t precision) -{ - // example: ((1,2,4),(3,23,39),(938,3,-35,6)) - if (vec.size() == 0) - { - stream << "$"; - return; - } - - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - const std::vector >& inner_vec = vec[ii]; - if (ii > 0) - { - stream << ","; - } - writeRealList2D(stream, inner_vec, false, precision); - } - stream << ")"; -} - -void writeStepParameterDouble( double value, std::stringstream& stream, const std::string& classIDstr, bool is_select_type, size_t precision ) -{ - if( std::isnan(value) ) - { - stream << "$"; - return; - } - - if( is_select_type ) - { - //std::string classIDstr = IFC4X3::EntityFactory::getStringForClassID(classID); - //std::transform(classIDstr.begin(), classIDstr.end(), classIDstr.begin(), [](char c) {return std::toupper(c); }); - stream << classIDstr << "("; // for example "IFCLABEL("; - } - //stream << "'" << encodeStepString( m_value ) << "'"; - appendRealWithoutTrailingZeros( stream, value, precision ); - if( is_select_type ) { stream << ")"; } -} - -std::string encodeStepString( const std::string& str ) -{ - char* stream_pos = const_cast(str.c_str()); - std::string result_str; - std::string beginUnicodeTag = "\\X2\\"; - std::string endUnicodeTag = "\\X0\\"; - bool hasOpenedUnicodeTag = false; - - auto closeUnicodeBlockIfOpened = [&] - { - if( hasOpenedUnicodeTag ) - { - result_str += endUnicodeTag; - hasOpenedUnicodeTag = false; - } - }; - - while( *stream_pos != '\0' ) - { - char append_char = *stream_pos; - if( append_char == 10 ) - { - closeUnicodeBlockIfOpened(); - // encode new line - result_str.append( "\\X\\0A" ); - } - else if( append_char == 13 ) - { - closeUnicodeBlockIfOpened(); - // encode carriage return - result_str.append( "\\X\\0D" ); - } - else if( append_char == 39 ) - { - closeUnicodeBlockIfOpened(); - // encode apostrophe - result_str.append( "\\X\\27" ); - } - else if( append_char == 92 ) - { - closeUnicodeBlockIfOpened(); - // encode backslash - result_str.append( "\\\\" ); - } - else if( append_char > 0 && append_char < 128 ) - { - closeUnicodeBlockIfOpened(); - result_str.push_back( static_cast(append_char) ); - } - else - { - unsigned char value = static_cast(append_char); - char temporary[8]; - sprintf( temporary, "%04X", value ); - - if( !hasOpenedUnicodeTag ) - { - result_str += beginUnicodeTag; - hasOpenedUnicodeTag = true; - } - - result_str.push_back( temporary[0] ); - result_str.push_back( temporary[1] ); - result_str.push_back( temporary[2] ); - result_str.push_back( temporary[3] ); - } - ++stream_pos; - } - - closeUnicodeBlockIfOpened(); - return result_str; -} +/* -*-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 "ifcpp/model/GlobalDefines.h" +#include "ifcpp/model/BuildingObject.h" +#include "WriterUtil.h" + +void appendRealWithoutTrailingZeros(std::stringstream& stream, const double number, size_t precision) +{ + std::ostringstream temp; + temp.imbue(std::locale("C")); + temp.precision(precision); + temp << std::fixed << number; + std::string str = temp.str(); + size_t pos_dot = str.find_last_of('.'); + if (pos_dot != std::string::npos) + { + // 1.000 -> 1. + size_t pos_last_non_zero = str.find_last_not_of('0'); + if (pos_last_non_zero != std::string::npos && pos_last_non_zero >= pos_dot) + { + str.erase(pos_last_non_zero + 1, std::string::npos); + } + } + stream << str; +} + +void writeIntList(std::stringstream& stream, const std::vector& vec) +{ + // example: (3,23,039) + if (vec.size() == 0) + { + stream << "$"; + return; + } + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + if (ii > 0) + { + stream << ","; + } + + stream << vec[ii]; + } + stream << ")"; +} + +void writeIntList2D(std::stringstream& stream, const std::vector >& vec) +{ + // example: ((1,2,4),(3,23,039),(938,3,-3,6)) + if (vec.size() == 0) + { + stream << "$"; + return; + } + + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + const std::vector& inner_vec = vec[ii]; + if (ii > 0) + { + stream << ","; + } + writeIntList(stream, inner_vec); + } + stream << ")"; +} + +void writeIntList3D(std::stringstream& stream, const std::vector > >& vec) +{ + // example: ((1,2,4),(3,23,39),(938,3,-35,6)) + if (vec.size() == 0) + { + stream << "$"; + return; + } + + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + const std::vector >& inner_vec = vec[ii]; + if (ii > 0) + { + stream << ","; + } + writeIntList2D(stream, inner_vec); + } + stream << ")"; +} + +void writeRealList(std::stringstream& stream, const std::vector& vec, bool optionalAttribute, size_t precision) +{ + // example: (3,23,039) + if (vec.size() == 0) + { + if (optionalAttribute) + { + stream << "$"; + } + else + { + stream << "()"; + } + return; + } + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + if (ii > 0) + { + stream << ","; + } + + appendRealWithoutTrailingZeros(stream, vec[ii], precision); + } + stream << ")"; +} + +void writeRealArray3(std::stringstream& stream, const double (&vec)[3], bool optionalAttribute, short int size, size_t precision) +{ + // example: (3,23,039) + if (size == 0) + { + if (optionalAttribute) + { + stream << "$"; + } + else + { + stream << "()"; + } + + + return; + } + stream << "("; + for (size_t ii = 0; ii < size; ++ii) + { + if (ii > 0) + { + stream << ","; + } + + appendRealWithoutTrailingZeros(stream, vec[ii], precision); + } + stream << ")"; +} + +void writeRealList2D(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision) +{ + // example: ((1,2,4),(3,23,039),(938,3,-3,6)) + if (vec.size() == 0) + { + //stream << "$"; + if (optionalAttribute) + { + stream << "$"; + } + else + { + stream << "()"; + } + return; + } + + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + const std::vector& inner_vec = vec[ii]; + if (ii > 0) + { + stream << ","; + } + writeRealList(stream, inner_vec, false, precision); + } + stream << ")"; +} + +void writeRealList3D(std::stringstream& stream, const std::vector > >& vec, size_t precision) +{ + // example: ((1,2,4),(3,23,39),(938,3,-35,6)) + if (vec.size() == 0) + { + stream << "$"; + return; + } + + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + const std::vector >& inner_vec = vec[ii]; + if (ii > 0) + { + stream << ","; + } + writeRealList2D(stream, inner_vec, false, precision); + } + stream << ")"; +} + +void writeStepParameterDouble( double value, std::stringstream& stream, const std::string& classIDstr, bool is_select_type, size_t precision ) +{ + if( std::isnan(value) ) + { + stream << "$"; + return; + } + + if( is_select_type ) + { + //std::string classIDstr = IFC4X3::EntityFactory::getStringForClassID(classID); + //std::transform(classIDstr.begin(), classIDstr.end(), classIDstr.begin(), [](char c) {return std::toupper(c); }); + stream << classIDstr << "("; // for example "IFCLABEL("; + } + //stream << "'" << encodeStepString( m_value ) << "'"; + appendRealWithoutTrailingZeros( stream, value, precision ); + if( is_select_type ) { stream << ")"; } +} + +std::string encodeStepString( const std::string& str ) +{ + char* stream_pos = const_cast(str.c_str()); + std::string result_str; + std::string beginUnicodeTag = "\\X2\\"; + std::string endUnicodeTag = "\\X0\\"; + bool hasOpenedUnicodeTag = false; + + auto closeUnicodeBlockIfOpened = [&] + { + if( hasOpenedUnicodeTag ) + { + result_str += endUnicodeTag; + hasOpenedUnicodeTag = false; + } + }; + + while( *stream_pos != '\0' ) + { + char append_char = *stream_pos; + if( append_char == 10 ) + { + closeUnicodeBlockIfOpened(); + // encode new line + result_str.append( "\\X\\0A" ); + } + else if( append_char == 13 ) + { + closeUnicodeBlockIfOpened(); + // encode carriage return + result_str.append( "\\X\\0D" ); + } + else if( append_char == 39 ) + { + closeUnicodeBlockIfOpened(); + // encode apostrophe + result_str.append( "\\X\\27" ); + } + else if( append_char == 92 ) + { + closeUnicodeBlockIfOpened(); + // encode backslash + result_str.append( "\\\\" ); + } + else if( append_char > 0 && append_char < 128 ) + { + closeUnicodeBlockIfOpened(); + result_str.push_back( static_cast(append_char) ); + } + else + { + unsigned char value = static_cast(append_char); + char temporary[8]; + sprintf( temporary, "%04X", value ); + + if( !hasOpenedUnicodeTag ) + { + result_str += beginUnicodeTag; + hasOpenedUnicodeTag = true; + } + + result_str.push_back( temporary[0] ); + result_str.push_back( temporary[1] ); + result_str.push_back( temporary[2] ); + result_str.push_back( temporary[3] ); + } + ++stream_pos; + } + + closeUnicodeBlockIfOpened(); + return result_str; +} diff --git a/IfcPlusPlus/src/ifcpp/writer/WriterUtil.h b/IfcPlusPlus/src/ifcpp/writer/WriterUtil.h index 517c8f4fc..cae29ac44 100644 --- a/IfcPlusPlus/src/ifcpp/writer/WriterUtil.h +++ b/IfcPlusPlus/src/ifcpp/writer/WriterUtil.h @@ -1,235 +1,235 @@ -/* -*-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. -*/ - -#pragma once - -#include -#include -#include -#include -#include "ifcpp/model/GlobalDefines.h" -#include "ifcpp/model/BuildingObject.h" -#include "ifcpp/model/BasicTypes.h" - -IFCQUERY_EXPORT std::string encodeStepString(const std::string& str); - -IFCQUERY_EXPORT void appendRealWithoutTrailingZeros(std::stringstream& stream, const double number, size_t precision); -void writeRealList(std::stringstream& stream, const std::vector& vec, bool optionalAttribute, size_t precision); -void writeRealArray3(std::stringstream& stream, const double (&vec)[3], bool optionalAttribute, short int size, size_t precision); -void writeRealList2D(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision); -void writeRealList3D(std::stringstream& stream, const std::vector > >& vec, size_t precision); -void writeIntList(std::stringstream& stream, const std::vector& vec); -void writeIntList2D(std::stringstream& stream, const std::vector >& vec); -void writeIntList3D(std::stringstream& stream, const std::vector > >& vec); -void writeStepParameterDouble(double value, std::stringstream& stream, const std::string& classIDstr, bool is_select_type, size_t precision); - - -template -void writeTypeOfIntList( std::stringstream& stream, const std::vector >& vec, bool optionalAttribute ) -{ - // example: (38,12,4) - if( vec.size() == 0 ) - { - if (optionalAttribute) - { - stream << "$"; - } - else - { - stream << "()"; - } - return; - } - stream << "("; - for( size_t ii = 0; ii < vec.size(); ++ii ) - { - if( ii > 0 ) - { - stream << ","; - } - - stream << vec[ii]->m_value; - } - stream << ")"; -} -template -void writeTypeOfRealList(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision) -{ - // example: (38.5, -1.2, 4.0) - if (vec.size() == 0) - { - if (optionalAttribute) - { - stream << "$"; - } - else - { - stream << "()"; - } - return; - } - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - if (ii > 0) - { - stream << ","; - } - - appendRealWithoutTrailingZeros(stream, vec[ii]->m_value, precision); - } - stream << ")"; -} - -template -void writeTypeOfIntList2D( std::stringstream& stream, const std::vector > >& vec, bool optionalAttribute, size_t precision) -{ - // example: ((38,12,4),(38,1,346),(1,1,0)) - if( vec.size() == 0 ) - { - stream << "$"; - return; - } - stream << "("; - for( size_t ii = 0; ii < vec.size(); ++ii ) - { - const std::vector >& inner_vec = vec[ii]; - - if( ii > 0 ) - { - stream << ","; - } - writeTypeOfIntList( stream, inner_vec, optionalAttribute ); - } - stream << ")"; -} - -template -void writeTypeOfRealList2D(std::stringstream& stream, const std::vector > >& vec, bool optionalAttribute, size_t precision) -{ - // example: ((.38,12.0,.04),(.38,1.0,346.0),(1.8,1.0,.04)) - if (vec.size() == 0) - { - stream << "$"; - return; - } - stream << "("; - for (size_t ii = 0; ii < vec.size(); ++ii) - { - const std::vector >& inner_vec = vec[ii]; - - if (ii > 0) - { - stream << ","; - } - writeTypeOfRealList(stream, inner_vec, optionalAttribute, precision); - } - stream << ")"; -} - -template -void writeEntityList( std::stringstream& stream, const std::vector >& vec ) -{ - // example: (#287,#291,$,#299) - if( vec.size() == 0 ) - { - stream << "$"; - return; - } - stream << "("; - for( size_t ii = 0; ii < vec.size(); ++ii ) - { - if( ii > 0 ) - { - stream << ","; - } - - const shared_ptr& entity = vec[ii]; - if( entity ) - { - stream << "#" << entity->m_tag; - } - else - { - stream << "$"; - } - } - stream << ")"; -} - -template -void writeEntityList2D( std::stringstream& stream, const std::vector > >& vec ) -{ - // example: ((#287,#291,$,#299),(#287,#291,$,#299)) - if( vec.size() == 0 ) - { - stream << "$"; - return; - } - - stream << "("; - for( size_t ii = 0; ii < vec.size(); ++ii ) - { - const std::vector >& inner_vec = vec[ii]; - if( ii > 0 ) - { - stream << ","; - } - writeEntityList( stream, inner_vec ); - } - stream << ")"; -} - -template -void writeEntityList3D( std::stringstream& stream, const std::vector > > >& vec ) -{ - // example: (((#287,#291,$,#299),(#287,#291,$,#299)),((#287,#291,$,#299),(#287,#291,$,#299))) - if( vec.size() == 0 ) - { - stream << "$"; - return; - } - - stream << "("; - for( size_t ii = 0; ii < vec.size(); ++ii ) - { - const std::vector > >& inner_vec = vec[ii]; - - if( ii > 0 ) - { - stream << ","; - } - writeEntityList2D( stream, inner_vec ); - } - stream << ")"; -} - -inline void writeStringList( std::wstringstream& stream, const std::vector& vec_strings ) -{ - // example: ("str1","str2","str3") - stream << "("; - for( size_t ii = 0; ii < vec_strings.size(); ++ii ) - { - const std::wstring& str = vec_strings[ii]; - if( ii > 0 ) - { - stream << ","; - } - stream << str.c_str(); - } - stream << ")"; -} +/* -*-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. +*/ + +#pragma once + +#include +#include +#include +#include +#include "ifcpp/model/GlobalDefines.h" +#include "ifcpp/model/BuildingObject.h" +#include "ifcpp/model/BasicTypes.h" + +IFCQUERY_EXPORT std::string encodeStepString(const std::string& str); + +IFCQUERY_EXPORT void appendRealWithoutTrailingZeros(std::stringstream& stream, const double number, size_t precision); +void writeRealList(std::stringstream& stream, const std::vector& vec, bool optionalAttribute, size_t precision); +void writeRealArray3(std::stringstream& stream, const double (&vec)[3], bool optionalAttribute, short int size, size_t precision); +void writeRealList2D(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision); +void writeRealList3D(std::stringstream& stream, const std::vector > >& vec, size_t precision); +void writeIntList(std::stringstream& stream, const std::vector& vec); +void writeIntList2D(std::stringstream& stream, const std::vector >& vec); +void writeIntList3D(std::stringstream& stream, const std::vector > >& vec); +void writeStepParameterDouble(double value, std::stringstream& stream, const std::string& classIDstr, bool is_select_type, size_t precision); + + +template +void writeTypeOfIntList( std::stringstream& stream, const std::vector >& vec, bool optionalAttribute ) +{ + // example: (38,12,4) + if( vec.size() == 0 ) + { + if (optionalAttribute) + { + stream << "$"; + } + else + { + stream << "()"; + } + return; + } + stream << "("; + for( size_t ii = 0; ii < vec.size(); ++ii ) + { + if( ii > 0 ) + { + stream << ","; + } + + stream << vec[ii]->m_value; + } + stream << ")"; +} +template +void writeTypeOfRealList(std::stringstream& stream, const std::vector >& vec, bool optionalAttribute, size_t precision) +{ + // example: (38.5, -1.2, 4.0) + if (vec.size() == 0) + { + if (optionalAttribute) + { + stream << "$"; + } + else + { + stream << "()"; + } + return; + } + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + if (ii > 0) + { + stream << ","; + } + + appendRealWithoutTrailingZeros(stream, vec[ii]->m_value, precision); + } + stream << ")"; +} + +template +void writeTypeOfIntList2D( std::stringstream& stream, const std::vector > >& vec, bool optionalAttribute, size_t precision) +{ + // example: ((38,12,4),(38,1,346),(1,1,0)) + if( vec.size() == 0 ) + { + stream << "$"; + return; + } + stream << "("; + for( size_t ii = 0; ii < vec.size(); ++ii ) + { + const std::vector >& inner_vec = vec[ii]; + + if( ii > 0 ) + { + stream << ","; + } + writeTypeOfIntList( stream, inner_vec, optionalAttribute ); + } + stream << ")"; +} + +template +void writeTypeOfRealList2D(std::stringstream& stream, const std::vector > >& vec, bool optionalAttribute, size_t precision) +{ + // example: ((.38,12.0,.04),(.38,1.0,346.0),(1.8,1.0,.04)) + if (vec.size() == 0) + { + stream << "$"; + return; + } + stream << "("; + for (size_t ii = 0; ii < vec.size(); ++ii) + { + const std::vector >& inner_vec = vec[ii]; + + if (ii > 0) + { + stream << ","; + } + writeTypeOfRealList(stream, inner_vec, optionalAttribute, precision); + } + stream << ")"; +} + +template +void writeEntityList( std::stringstream& stream, const std::vector >& vec ) +{ + // example: (#287,#291,$,#299) + if( vec.size() == 0 ) + { + stream << "$"; + return; + } + stream << "("; + for( size_t ii = 0; ii < vec.size(); ++ii ) + { + if( ii > 0 ) + { + stream << ","; + } + + const shared_ptr& entity = vec[ii]; + if( entity ) + { + stream << "#" << entity->m_tag; + } + else + { + stream << "$"; + } + } + stream << ")"; +} + +template +void writeEntityList2D( std::stringstream& stream, const std::vector > >& vec ) +{ + // example: ((#287,#291,$,#299),(#287,#291,$,#299)) + if( vec.size() == 0 ) + { + stream << "$"; + return; + } + + stream << "("; + for( size_t ii = 0; ii < vec.size(); ++ii ) + { + const std::vector >& inner_vec = vec[ii]; + if( ii > 0 ) + { + stream << ","; + } + writeEntityList( stream, inner_vec ); + } + stream << ")"; +} + +template +void writeEntityList3D( std::stringstream& stream, const std::vector > > >& vec ) +{ + // example: (((#287,#291,$,#299),(#287,#291,$,#299)),((#287,#291,$,#299),(#287,#291,$,#299))) + if( vec.size() == 0 ) + { + stream << "$"; + return; + } + + stream << "("; + for( size_t ii = 0; ii < vec.size(); ++ii ) + { + const std::vector > >& inner_vec = vec[ii]; + + if( ii > 0 ) + { + stream << ","; + } + writeEntityList2D( stream, inner_vec ); + } + stream << ")"; +} + +inline void writeStringList( std::wstringstream& stream, const std::vector& vec_strings ) +{ + // example: ("str1","str2","str3") + stream << "("; + for( size_t ii = 0; ii < vec_strings.size(); ++ii ) + { + const std::wstring& str = vec_strings[ii]; + if( ii > 0 ) + { + stream << ","; + } + stream << str.c_str(); + } + stream << ")"; +} diff --git a/examples/SimpleViewerExampleQt/Resources/styles.css b/examples/SimpleViewerExampleQt/Resources/styles.css index 4af40dae1..e1d43148f 100644 --- a/examples/SimpleViewerExampleQt/Resources/styles.css +++ b/examples/SimpleViewerExampleQt/Resources/styles.css @@ -3,8 +3,8 @@ QFrame { padding: 0px; } -QWidget#OpenFileWidget { background-color: rgba(240,240,240,0.5); border: 1px solid #cecece; margin: 10px;} -QWidget#SettingsWidget { background-color: rgba(240,240,240,0.5); border: 1px solid #cecece;} +QWidget#OpenFileWidget { background-color: rgba(240,240,240,0.7); border: 1px solid #cecece; margin: 10px;} +QWidget#SettingsWidget { background-color: rgba(240,240,240,0.7); border: 1px solid #cecece;} QStatusBar { height:20px;max-height: 24px;} diff --git a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.sln b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.sln index 2b800a566..000b7aa2b 100644 --- a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.sln +++ b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2043 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34408.163 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleViewerExampleQt", "SimpleViewerExampleQt.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" EndProject @@ -11,6 +11,8 @@ Global Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 + ReleaseWithDebugInfo|x64 = ReleaseWithDebugInfo|x64 + ReleaseWithDebugInfo|x86 = ReleaseWithDebugInfo|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64 @@ -21,6 +23,10 @@ Global {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64 {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.ActiveCfg = Release|Win32 {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.Build.0 = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.ReleaseWithDebugInfo|x64.ActiveCfg = ReleaseWithDebugInfo|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.ReleaseWithDebugInfo|x64.Build.0 = ReleaseWithDebugInfo|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.ReleaseWithDebugInfo|x86.ActiveCfg = ReleaseWithDebugInfo|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.ReleaseWithDebugInfo|x86.Build.0 = ReleaseWithDebugInfo|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj index fa03b4cf2..2b7455564 100644 --- a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj +++ b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj @@ -9,6 +9,14 @@ Debug x64 + + ReleaseWithDebugInfo + Win32 + + + ReleaseWithDebugInfo + x64 + Release Win32 @@ -36,6 +44,12 @@ Unicode true + + Application + v142 + Unicode + true + Application v143 @@ -48,6 +62,12 @@ Unicode true + + Application + v143 + Unicode + true + @@ -60,12 +80,18 @@ + + + + + + @@ -79,10 +105,24 @@ true true + + bin\ + bin\$(Configuration)\ + false + true + true + true + true + $(ProjectName)rd + bin32\ bin32\$(Configuration)\ + + bin32\ + bin32\$(Configuration)\ + bin\ bin\$(Configuration)\ @@ -97,10 +137,18 @@ $(DefaultQtVersion) core;opengl;openglwidgets;gui;widgets + + $(DefaultQtVersion) + core;opengl;openglwidgets;gui;widgets + 5.7.1 core;xml;opengl;network;gui;widgets;printsupport + + 5.7.1 + core;xml;opengl;network;gui;widgets;printsupport + $(DefaultQtVersion) core;opengl;gui;widgets;openglwidgets @@ -163,7 +211,7 @@ true 4189;4505;4005 /bigobj %(AdditionalOptions) - stdcpp17 + stdcpp20 true @@ -228,11 +276,47 @@ qrc_%(Filename).cpp + + + CARVE_SYSTEM_BOOST;UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;%(PreprocessorDefinitions) + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.\..\Carve\src;.\..\Carve\src\include;.\..\Carve\src\common;.\..\IfcPlusPlus\src;.\..\IfcPlusPlusGeometry\src;$(OSG_DIR)\include;.\src;.;%(AdditionalIncludeDirectories) + + + MultiThreadedDLL + true + Speed + true + true + true + true + + + Console + $(OutDir)\$(ProjectName).exe + ..\Carve\bin32;..\IfcPlusPlus\bin32;$(OSG_DIR)\lib;$(OSG_DIR)\..\freetype\2.5.3\objs\win32\vc2010;%(AdditionalLibraryDirectories) + false + OpenThreads.lib;osg.lib;osgDB.lib;osgUtil.lib;osgGA.lib;osgViewer.lib;osgText.lib;osgFx.lib;osgQt.lib;freetype253.lib;IfcPlusPlus.lib;Carve.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies) + + + Moc'ing %(Identity)... + output + .\GeneratedFiles\$(ConfigurationName) + moc_%(Filename).cpp + + + %(Filename) + default + true + Rcc'ing %(Identity)... + .\GeneratedFiles + qrc_%(Filename).cpp + + UNICODE;WIN32;WIN64;NDEBUG;QT_NO_DEBUG;IFCQUERY_STATIC_LIB;%(PreprocessorDefinitions) .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;../../IfcPlusPlus/src/external;../../IfcPlusPlus/src/external/Carve/include;../../IfcPlusPlus/src/external/Carve/src;../../IfcPlusPlus/src/external/Carve/src/include;../../IfcPlusPlus/src/external/Carve/src/common;../../IfcPlusPlus/src/external/Carve/build/src;.;.\src;..\..\IfcPlusPlus\src;..\..\IfcPlusPlus\src\ifcpp\IFC4X3\include;..\..\IfcPlusPlus\src\external;..\..\IfcPlusPlus\src\external\glm;$(OSG_DIR)\include;$(OSG_DIR)\build\include;%(AdditionalIncludeDirectories) - ProgramDatabase + None MultiThreadedDLL true true @@ -250,7 +334,7 @@ false Default false - stdcpp17 + stdcpp20 true 4305;4005 @@ -258,6 +342,64 @@ Windows $(OutDir)\$(ProjectName).exe ..\..\IfcPlusPlus\bin;$(OSG_DIR)\lib;$(OSG_DIR)\build\lib;$(FREETYPE_DIR)\objs\vc2010\x64;%(AdditionalLibraryDirectories) + false + osg.lib;OpenThreads.lib;osgDB.lib;osgFX.lib;osgGA.lib;osgText.lib;osgUtil.lib;osgViewer.lib;IfcPlusPlus.lib;%(AdditionalDependencies) + Default + + + + + true + + + + + + + Moc'ing %(Identity)... + output + .\GeneratedFiles\$(ConfigurationName) + moc_%(Filename).cpp + + + %(Filename) + default + true + Rcc'ing %(Identity)... + .\GeneratedFiles + qrc_%(Filename).cpp + + + + + UNICODE;WIN32;WIN64;NDEBUG;QT_NO_DEBUG;IFCQUERY_STATIC_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;../../IfcPlusPlus/src/external;../../IfcPlusPlus/src/external/Carve/include;../../IfcPlusPlus/src/external/Carve/src;../../IfcPlusPlus/src/external/Carve/src/include;../../IfcPlusPlus/src/external/Carve/src/common;../../IfcPlusPlus/src/external/Carve/build/src;.;.\src;..\..\IfcPlusPlus\src;..\..\IfcPlusPlus\src\ifcpp\IFC4X3\include;..\..\IfcPlusPlus\src\external;..\..\IfcPlusPlus\src\external\glm;$(OSG_DIR)\include;$(OSG_DIR)\build\include;%(AdditionalIncludeDirectories) + ProgramDatabase + MultiThreadedDLL + true + true + Speed + true + true + true + false + true + MaxSpeed + true + + + Default + false + Default + false + stdcpp20 + true + 4305;4005 + + + Windows + $(OutDir)\$(TargetName).exe + ..\..\IfcPlusPlus\bin;$(OSG_DIR)\lib;$(OSG_DIR)\build\lib;$(FREETYPE_DIR)\objs\vc2010\x64;%(AdditionalLibraryDirectories) true osg.lib;OpenThreads.lib;osgDB.lib;osgFX.lib;osgGA.lib;osgText.lib;osgUtil.lib;osgViewer.lib;IfcPlusPlus.lib;%(AdditionalDependencies) Default @@ -314,7 +456,9 @@ true true true + true true + true @@ -322,37 +466,49 @@ true true true + true true + true true true true + true true + true true true true + true true + true true true true + true true + true true true true + true true + true true true true + true true + true diff --git a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user index c086fa79b..62ddec22b 100644 --- a/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user +++ b/examples/SimpleViewerExampleQt/SimpleViewerExampleQt.vcxproj.user @@ -11,22 +11,35 @@ WindowsLocalDebugger PATH=$(QTDIR)\bin%3b$(IFCPP_DIR)\IfcPlusPlus\bin%3b$(QTDIR)\bin%3b$(OSG_DIR)\build\bin%3b$(PATH) + + WindowsLocalDebugger + PATH=$(QTDIR)\bin%3b$(IFCPP_DIR)\IfcPlusPlus\bin%3b$(QTDIR)\bin%3b$(OSG_DIR)\build\bin%3b$(PATH) + PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH) PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH) + + PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH) + - 2024-01-26T15:10:49.9598175Z + 2024-03-20T09:31:00.6834341Z - 2024-01-26T15:10:50.0702063Z + 2024-03-20T09:31:01.1689286Z - 2024-01-26T15:10:50.1968036Z + 2024-03-20T09:31:01.3289091Z + + + 2024-03-20T09:31:00.3503804Z - 2024-01-26T15:10:50.2919150Z + 2024-03-20T09:31:01.5408142Z + + + 2024-03-20T09:31:00.5066279Z \ No newline at end of file diff --git a/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.cpp b/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.cpp index 08fb27b80..7a5ea4442 100644 --- a/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.cpp +++ b/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.cpp @@ -18,11 +18,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OU #pragma warning( disable: 4996 ) #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include @@ -134,6 +135,15 @@ void OpenFileWidget::closeEvent( QCloseEvent* ) settings.setValue("IOsplitterSizes", m_io_splitter->saveState()); } +void OpenFileWidget::paintEvent(QPaintEvent* event) +{ + QColor backgroundColor = palette().light().color(); + backgroundColor.setAlpha(100); + QPainter customPainter(this); + customPainter.fillRect(rect(), backgroundColor); + QWidget::paintEvent(event); +} + void OpenFileWidget::messageTarget( void* ptr, shared_ptr m ) { OpenFileWidget* myself = (OpenFileWidget*)ptr; @@ -223,7 +233,7 @@ void OpenFileWidget::loadIfcFile( QString& path_in ) { // redirect message callbacks m_system->m_ifc_model = shared_ptr(new BuildingModel()); - m_system->m_geometry_converter = make_shared(m_system->m_ifc_model); + m_system->m_geometry_converter = make_shared(m_system->m_ifc_model, m_system->m_geom_settings); m_system->m_geometry_converter->setMessageCallBack(std::bind(&OpenFileWidget::messageTarget, this, std::placeholders::_1)); m_stopSignals = false; @@ -285,6 +295,7 @@ void OpenFileWidget::loadIfcFile( QString& path_in ) m_stopSignals = true; return; } + setEnabled(false); // first remove previously loaded geometry from scenegraph osg::ref_ptr model_switch = m_system->getViewController()->getModelNode(); @@ -305,7 +316,7 @@ void OpenFileWidget::loadIfcFile( QString& path_in ) step_reader->loadModelFromFile(path_str, geometry_converter->getBuildingModel()); // convert IFC geometric representations into Carve geometry - geometry_converter->setCsgEps(1.5e-08 * geometry_converter->getBuildingModel()->getUnitConverter()->getLengthInMeterFactor()); + geometry_converter->setCsgEps(1.5e-08); geometry_converter->convertGeometry(); // convert Carve geometry to OSG @@ -346,6 +357,9 @@ void OpenFileWidget::loadIfcFile( QString& path_in ) } } } + + geometry_converter->clearIfcRepresentationsInModel( true, true, false ); + geometry_converter->clearInputCache(); } catch( BuildingException& e ) { @@ -362,6 +376,7 @@ void OpenFileWidget::loadIfcFile( QString& path_in ) m_system->notifyModelLoadingDone(); progressValue( 1.0, "" ); + setEnabled(true); } void OpenFileWidget::txtOut( QString txt ) @@ -479,7 +494,7 @@ void OpenFileWidget::slotWriteFileClicked() } shared_ptr geom_converter = m_system->getGeometryConverter(); - geom_converter->setGeomSettings(m_system->getGeometrySettings()); + geom_converter->setGeomSettings( m_system->getGeometrySettings() ); shared_ptr& model = geom_converter->getBuildingModel(); std::string applicationName = "IfcPlusPlus"; model->initFileHeader(path_std, applicationName); diff --git a/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.h b/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.h index eb6c7c9d8..44fe4a970 100644 --- a/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.h +++ b/examples/SimpleViewerExampleQt/src/gui/OpenFileWidget.h @@ -46,6 +46,7 @@ class OpenFileWidget : public QWidget static void messageTarget( void* obj_ptr, shared_ptr t ); void closeEvent( QCloseEvent *event ); + void paintEvent(QPaintEvent *event); void txtOut( QString txt ); void txtOutWarning( QString txt );