From 8dca84dd07fb47706daea54fa9f272005b90c925 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 8 Oct 2021 15:38:46 +0200 Subject: [PATCH] Geometry caching --- src/ifcconvert/IfcConvert.cpp | 25 +- src/ifcgeom/IfcGeom.h | 14 +- src/ifcgeom/IfcGeomFunctions.cpp | 30 +- src/ifcgeom/IfcGeomIteratorImplementation.h | 70 +- src/ifcgeom/IfcGeomRenderStyles.cpp | 30 +- src/ifcgeom/IfcGeomRepresentation.h | 33 +- src/ifcgeom/IfcGeomShapes.cpp | 18 +- src/ifcgeom/IfcGeomTree.h | 2 +- src/ifcgeom/IfcRegister.cpp | 2 +- src/ifcgeom/IfcRepresentationShapeItem.h | 11 +- src/ifcgeom_schema_agnostic/IfcGeomIterator.h | 2 + .../IfcGeomMaterial.cpp | 21 +- src/ifcgeom_schema_agnostic/IfcGeomMaterial.h | 10 +- .../IfcGeomRenderStyles.h | 2 +- .../IteratorImplementation.h | 5 + src/ifcgeom_schema_agnostic/SurfaceStyle.cpp | 91 ++- src/serializers/GeometrySerializer.h | 1 - src/serializers/HdfSerializer.cpp | 630 ++++++++++++++++-- src/serializers/HdfSerializer.h | 75 ++- src/serializers/SvgSerializer.cpp | 2 + 20 files changed, 867 insertions(+), 207 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index cd4397493a..3e1ea2eb8f 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -209,6 +209,7 @@ int main(int argc, char** argv) { path_t filter_filename; path_t default_material_filename; path_t log_file; + path_t cache_file; std::string log_format; po::options_description generic_options("Command line options"); @@ -218,6 +219,9 @@ int main(int argc, char** argv) { ("version", "display version information") ("verbose,v", po::value(&vcounter)->zero_tokens(), "more verbose log messages") ("quiet,q", "less status and progress output") +#ifdef WITH_HDF5 + ("cache", "cache geometry creation. Use --cache-file to specify cache file path.") +#endif ("stderr-progress", "output progress to stderr stream") ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") ("no-progress", "suppress possible progress bar type of prints that use carriage return") @@ -230,8 +234,12 @@ int main(int argc, char** argv) { ("mmap", "use memory-mapped file for input") #endif ("input-file", new po::typed_value(0), "input IFC file") - ("output-file", new po::typed_value(0), "output geometry file"); - + ("output-file", new po::typed_value(0), "output geometry file") +#ifdef WITH_HDF5 + ("cache-file", new po::typed_value(&cache_file), "geometry cache file") +#endif + ; + po::options_description ifc_options("IFC options"); ifc_options.add_options() ("calculate-quantities", "Calculate or fix the physical quantity definitions " @@ -680,6 +688,7 @@ int main(int argc, char** argv) { STP = IfcUtil::path::from_utf8(".stp"), IGS = IfcUtil::path::from_utf8(".igs"), SVG = IfcUtil::path::from_utf8(".svg"), + CACHE = IfcUtil::path::from_utf8(".cache"), HDF = IfcUtil::path::from_utf8(".h5"), XML = IfcUtil::path::from_utf8(".xml"), IFC = IfcUtil::path::from_utf8(".ifc"); @@ -969,6 +978,18 @@ int main(int argc, char** argv) { } IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs, num_threads); + +#ifdef WITH_HDF5 + std::unique_ptr cache; + if (vmap.count("cache-file") || vmap.count("cache")) { + if (!vmap.count("cache-file")) { + cache_file = input_filename + CACHE + HDF; + } + cache.reset(new HdfSerializer(IfcUtil::path::to_utf8(cache_file), settings)); + context_iterator.set_cache(cache.get()); + } +#endif + if (!context_iterator.initialize()) { /// @todo It would be nice to know and print separate error prints for a case where we found no entities /// and for a case we found no entities that satisfy our filtering criteria. diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 72c1245d73..69187f9e8b 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -288,9 +288,9 @@ private: MAKE_TYPE_NAME(Cache) cache; #endif - std::map style_cache; + std::map> style_cache; - const SurfaceStyle* internalize_surface_style(const std::pair& shading_style); + std::shared_ptr internalize_surface_style(const std::pair& shading_style); public: MAKE_TYPE_NAME(Kernel)() @@ -364,9 +364,9 @@ public: bool convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes); void assert_closed_wire(TopoDS_Wire& wire); - bool convert_layerset(const IfcSchema::IfcProduct*, std::vector&, std::vector&, std::vector&); - bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector&, const std::vector&, IfcRepresentationShapeItems&); - bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector >&, const std::vector&, IfcRepresentationShapeItems&); + bool convert_layerset(const IfcSchema::IfcProduct*, std::vector&, std::vector>&, std::vector&); + bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector&, const std::vector>&, IfcRepresentationShapeItems&); + bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector >&, const std::vector>&, IfcRepresentationShapeItems&); bool fold_layers(const IfcSchema::IfcWall*, const IfcRepresentationShapeItems&, const std::vector&, const std::vector&, std::vector< std::vector >&); bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&); @@ -443,8 +443,8 @@ public: const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct*); IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation); IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation*); - const SurfaceStyle* get_style(const IfcSchema::IfcRepresentationItem*); - const SurfaceStyle* get_style(const IfcSchema::IfcMaterial*); + std::shared_ptr get_style(const IfcSchema::IfcRepresentationItem*); + std::shared_ptr get_style(const IfcSchema::IfcMaterial*); template std::pair _get_surface_style(const IfcSchema::IfcStyledItem* si) { std::vector prs_styles; diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 0dd73a5c49..97db9bd92a 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -907,7 +907,7 @@ bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, cons } } - cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), it3->Placement(), entity_shape, &it3->Style())); + cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), it3->Placement(), entity_shape, it3->StylePtr())); } return true; @@ -1123,7 +1123,7 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, continue; } - cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), result, &it3->Style())); + cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), result, it3->StylePtr())); // For manifold first operands we're not even going to try if processing // as loose faces gives a better result. @@ -1848,7 +1848,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc std::vector thickness; std::vector layers; std::vector< std::vector > folded_layers; - std::vector styles; + std::vector> styles; if (convert_layerset(product, layers, styles, thickness)) { IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); @@ -1889,7 +1889,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc const IfcSchema::IfcMaterial* single_material = get_single_material_association(product); if (single_material) { - const IfcGeom::SurfaceStyle* s = get_style(single_material); + auto s = get_style(single_material); for (IfcGeom::IfcRepresentationShapeItems::iterator it = shapes.begin(); it != shapes.end(); ++it) { if (!it->hasStyle() && s) { it->setStyle(s); @@ -1917,8 +1917,8 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc for (auto& s : shapes) { if (s.hasStyle()) { for (auto& p : style_cache) { - if (&p.second == &s.Style()) { - p.second.Transparency() = settings.force_space_transparency(); + if (p.second == s.StylePtr()) { + std::const_pointer_cast(p.second)->Transparency() = settings.force_space_transparency(); } } } @@ -2303,7 +2303,7 @@ std::pair IfcGeom::Kernel::initializeUnits(IfcSchema::IfcUn return std::pair(unit_name, unit_magnitude); } -bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std::vector& surfaces, std::vector& styles, std::vector& thicknesses) { +bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std::vector& surfaces, std::vector>& styles, std::vector& thicknesses) { IfcSchema::IfcMaterialLayerSetUsage* usage = 0; Handle_Geom_Surface reference_surface; @@ -3063,7 +3063,7 @@ namespace { #endif } -bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& items, const std::vector< std::vector >& surfaces, const std::vector& styles, IfcRepresentationShapeItems& result) { +bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& items, const std::vector< std::vector >& surfaces, const std::vector>& styles, IfcRepresentationShapeItems& result) { Bnd_Box bb; TopoDS_Shape input; flatten_shape_list(items, input, false); @@ -3148,8 +3148,8 @@ bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& i for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { TopoDS_Shape a,b; if (split_solid_by_shell(it->Shape(), shells.First(), a, b)) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, styles[0] ? styles[0] : &it->Style())); - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, styles[1] ? styles[1] : &it->Style())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr())); } else { continue; } @@ -3168,7 +3168,7 @@ bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& i std::vector slices; if (split(*this, it->Shape(), shells, getValue(GV_PRECISION), slices) && slices.size() == styles.size()) { for (size_t i = 0; i < slices.size(); ++i) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], styles[i] ? styles[i] : &it->Style())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr())); } } else { return false; @@ -3181,7 +3181,7 @@ bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& i } -bool IfcGeom::Kernel::apply_layerset(const IfcRepresentationShapeItems& items, const std::vector& surfaces, const std::vector& styles, IfcRepresentationShapeItems& result) { +bool IfcGeom::Kernel::apply_layerset(const IfcRepresentationShapeItems& items, const std::vector& surfaces, const std::vector>& styles, IfcRepresentationShapeItems& result) { if (surfaces.size() < 3) { return false; @@ -3191,8 +3191,8 @@ bool IfcGeom::Kernel::apply_layerset(const IfcRepresentationShapeItems& items, c for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { TopoDS_Shape a,b; if (split_solid_by_surface(it->Shape(), surfaces[1], a, b)) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, styles[0] ? styles[0] : &it->Style())); - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, styles[1] ? styles[1] : &it->Style())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr())); } else { continue; } @@ -3259,7 +3259,7 @@ bool IfcGeom::Kernel::apply_layerset(const IfcRepresentationShapeItems& items, c std::vector slices; if (split(*this, it->Shape(), operands, getValue(GV_PRECISION), slices) && slices.size() == styles.size()) { for (size_t i = 0; i < slices.size(); ++i) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], styles[i] ? styles[i] : &it->Style())); + result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr())); } } else { return false; diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 676db8f5de..f2f32a0fb8 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -777,11 +777,25 @@ namespace IfcGeom { Logger::SetProduct(product); + bool read_from_cache = false; BRepElement* element; - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - element = kernel.create_brep_for_representation_and_product(settings, representation, product); - } else { - element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model); + +#ifdef WITH_HDF5 + if (cache_) { + auto from_cache = cache_->read(*ifc_file, product->GlobalId(), representation->data().id()); + if (from_cache) { + read_from_cache = true; + element = (BRepElement*) from_cache; + } + } +#endif + + if (!read_from_cache) { + if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { + element = kernel.create_brep_for_representation_and_product(settings, representation, product); + } else { + element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model); + } } Logger::SetProduct(boost::none); @@ -791,6 +805,12 @@ namespace IfcGeom { continue; } +#ifdef WITH_HDF5 + if (cache_ && !read_from_cache) { + cache_->write(element); + } +#endif + return element; } } @@ -1003,14 +1023,42 @@ namespace IfcGeom { Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); } } else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) { - try { - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - next_triangulation = new TriangulationElement(*next_shape_model); - } else { - next_triangulation = new TriangulationElement(*next_shape_model, current_triangulation->geometry_pointer()); + + bool read_from_cache = false; + +#ifdef WITH_HDF5 + if (cache_) { + // the part before the hyphen is the representation id + auto gid2 = next_shape_model->geometry().id(); + auto hyphen = gid2.find("-"); + if (hyphen != std::string::npos) { + gid2 = gid2.substr(0, hyphen); + } + + auto from_cache = cache_->read(*ifc_file, next_shape_model->guid(), boost::lexical_cast(gid2), HdfSerializer::READ_TRIANGULATION); + if (from_cache) { + read_from_cache = true; + next_triangulation = (TriangulationElement*)from_cache; + } + } +#endif + + if (!read_from_cache) { + try { + if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { + next_triangulation = new TriangulationElement(*next_shape_model); + } else { + next_triangulation = new TriangulationElement(*next_shape_model, current_triangulation->geometry_pointer()); + } + +#ifdef WITH_HDF5 + if (cache_) { + cache_->write(next_triangulation); + } +#endif + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); } - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); } } } diff --git a/src/ifcgeom/IfcGeomRenderStyles.cpp b/src/ifcgeom/IfcGeomRenderStyles.cpp index 131b297b4d..0644ca1cc6 100644 --- a/src/ifcgeom/IfcGeomRenderStyles.cpp +++ b/src/ifcgeom/IfcGeomRenderStyles.cpp @@ -56,25 +56,31 @@ namespace { #define Kernel MAKE_TYPE_NAME(Kernel) -const IfcGeom::SurfaceStyle* IfcGeom::Kernel::internalize_surface_style(const std::pair& shading_styles) { +std::shared_ptr IfcGeom::Kernel::internalize_surface_style(const std::pair& shading_styles) { if (shading_styles.second == 0) { return 0; } int surface_style_id = shading_styles.first->data().id(); - std::map::const_iterator it = style_cache.find(surface_style_id); + auto it = style_cache.find(surface_style_id); if (it != style_cache.end()) { - return &(it->second); + return it->second; } - SurfaceStyle surface_style; - + + IfcSchema::IfcSurfaceStyle* style = shading_styles.first->as(); IfcSchema::IfcSurfaceStyleShading* shading = shading_styles.second->as(); + std::shared_ptr surface_style_ptr; + if (style->Name()) { - surface_style = SurfaceStyle(surface_style_id, *style->Name()); + surface_style_ptr.reset(new SurfaceStyle(surface_style_id, *style->Name())); } else { - surface_style = SurfaceStyle(surface_style_id); + surface_style_ptr.reset(new SurfaceStyle(surface_style_id)); } + + std::shared_ptr surface_style_ptr_const = std::const_pointer_cast(surface_style_ptr); + SurfaceStyle& surface_style = *surface_style_ptr; + double rgb[3]; if (process_colour(shading->SurfaceColour(), rgb)) { surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2])); @@ -113,14 +119,14 @@ const IfcGeom::SurfaceStyle* IfcGeom::Kernel::internalize_surface_style(const st surface_style.Transparency().reset(d); } } - return &(style_cache[surface_style_id] = surface_style); + return style_cache[surface_style_id] = surface_style_ptr_const; } -const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationItem* item) { +std::shared_ptr IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationItem* item) { return internalize_surface_style(get_surface_style(item)); } -const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcMaterial* material) { +std::shared_ptr IfcGeom::Kernel::get_style(const IfcSchema::IfcMaterial* material) { IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation(); for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) { IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations(); @@ -135,6 +141,6 @@ const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcMate } } } - IfcGeom::SurfaceStyle material_style = IfcGeom::SurfaceStyle(material->data().id(), material->Name()); - return &(style_cache[material->data().id()] = material_style); + auto material_style = std::make_shared(material->data().id(), material->Name()); + return style_cache[material->data().id()] = material_style; } diff --git a/src/ifcgeom/IfcGeomRepresentation.h b/src/ifcgeom/IfcGeomRepresentation.h index 93dd1fae4f..f0d3ce6b2e 100644 --- a/src/ifcgeom/IfcGeomRepresentation.h +++ b/src/ifcgeom/IfcGeomRepresentation.h @@ -121,6 +121,11 @@ namespace IfcGeom { size_t weld_offset_; VertexKeyMap welds; + // when read from serialization, the element needs to take ownership of the styles, + // the material vector is constructor off of this. + // @todo this can be improved + std::vector> styles_; + public: const std::string& id() const { return id_; } const std::vector& verts() const { return _verts; } @@ -144,7 +149,7 @@ namespace IfcGeom { int surface_style_id = -1; if (iit->hasStyle()) { - Material adapter(&iit->Style()); + Material adapter(iit->StylePtr()); std::vector::const_iterator jt = std::find(_materials.begin(), _materials.end(), adapter); if (jt == _materials.end()) { surface_style_id = (int)_materials.size(); @@ -349,6 +354,32 @@ namespace IfcGeom { BRepTools::Clean(s); } } + + Triangulation( + ElementSettings settings, + const std::string& id, + const std::vector& verts, + const std::vector& faces, + const std::vector& edges, + const std::vector& normals, + const std::vector& uvs, + const std::vector& material_ids, + const std::vector>& styles) + : Representation(settings) + , id_(id) + , _verts(verts) + , _faces(faces) + , _edges(edges) + , _normals(normals) + , uvs_(uvs) + , _material_ids(material_ids) + , styles_(styles) + { + for (auto& s : styles_) { + _materials.push_back(IfcGeom::Material(s)); + } + } + virtual ~Triangulation() {} /// Generates UVs for a single mesh using box projection. diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index a784586db2..917abc3cf2 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -434,9 +434,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_S bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, IfcRepresentationShapeItems& shape) { TopoDS_Shape s; - const SurfaceStyle* collective_style = get_style(l); + auto collective_style = get_style(l); if (convert_shape(l->Outer(),s) ) { - const SurfaceStyle* indiv_style = get_style(l->Outer()); + auto indiv_style = get_style(l->Outer()); IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) { @@ -466,10 +466,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, IfcRepre bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { bool part_success = false; IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); - const SurfaceStyle* collective_style = get_style(l); + auto collective_style = get_style(l); for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { TopoDS_Shape s; - const SurfaceStyle* shell_style = get_style(*it); + auto shell_style = get_style(*it); if (convert_shape(*it,s)) { shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, shell_style ? shell_style : collective_style)); part_success |= true; @@ -527,10 +527,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { aggregate_of_instance::ptr shells = l->SbsmBoundary(); - const SurfaceStyle* collective_style = get_style(l); + auto collective_style = get_style(l); for( aggregate_of_instance::it it = shells->begin(); it != shells->end(); ++ it ) { TopoDS_Shape s; - const SurfaceStyle* shell_style = 0; + decltype(collective_style) shell_style; if ((*it)->declaration().is(IfcSchema::IfcRepresentationItem::Class())) { shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); } @@ -845,7 +845,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcMappedItem* l, IfcRepresentati } gtrsf.Multiply(trsf); - const IfcGeom::SurfaceStyle* mapped_item_style = get_style(l); + auto mapped_item_style = get_style(l); const size_t previous_size = shapes.size(); bool b = convert_shapes(map->MappedRepresentation(), shapes); @@ -893,7 +893,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresenta aggregate_of_instance::ptr elements = l->Elements(); if ( !elements->size() ) return false; bool part_succes = false; - const IfcGeom::SurfaceStyle* parent_style = get_style(l); + auto parent_style = get_style(l); for (aggregate_of_instance::it it = elements->begin(); it != elements->end(); ++it) { auto element = *it; TopoDS_Shape s; @@ -917,7 +917,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresenta } part_succes = true; - const IfcGeom::SurfaceStyle* style = 0; + decltype(parent_style) style = 0; if (element->declaration().is(IfcSchema::IfcPoint::Class())) { style = get_style((IfcSchema::IfcPoint*) element); } diff --git a/src/ifcgeom/IfcGeomTree.h b/src/ifcgeom/IfcGeomTree.h index dd86cc6ebb..593a8dae16 100644 --- a/src/ifcgeom/IfcGeomTree.h +++ b/src/ifcgeom/IfcGeomTree.h @@ -338,7 +338,7 @@ namespace IfcGeom { for (; it.More(); it.Next(), ++git) { std::unique_ptr adaptor; if (git->hasStyle()) { - adaptor.reset(new Material(&git->Style())); + adaptor.reset(new Material(git->StylePtr())); } else { adaptor.reset(new Material(IfcGeom::get_default_style(elem->type()))); } diff --git a/src/ifcgeom/IfcRegister.cpp b/src/ifcgeom/IfcRegister.cpp index e91f93e569..1902e3cb1d 100644 --- a/src/ifcgeom/IfcRegister.cpp +++ b/src/ifcgeom/IfcRegister.cpp @@ -30,7 +30,7 @@ bool IfcGeom::Kernel::convert_shapes(const IfcBaseInterface* l, IfcRepresentatio if (shape_type(l) != ST_SHAPELIST) { TopoDS_Shape shp; if (convert_shape(l, shp)) { - const IfcGeom::SurfaceStyle* style = nullptr; + std::shared_ptr style; if (l->as()) { style = get_style(l->as()); } diff --git a/src/ifcgeom/IfcRepresentationShapeItem.h b/src/ifcgeom/IfcRepresentationShapeItem.h index 588e85a916..2087ed7876 100644 --- a/src/ifcgeom/IfcRepresentationShapeItem.h +++ b/src/ifcgeom/IfcRepresentationShapeItem.h @@ -31,13 +31,13 @@ namespace IfcGeom { int id; gp_GTrsf placement; TopoDS_Shape shape; - const SurfaceStyle* style; + std::shared_ptr style; public: - IfcRepresentationShapeItem(int id, const gp_GTrsf& placement, const TopoDS_Shape& shape, const SurfaceStyle* style) + IfcRepresentationShapeItem(int id, const gp_GTrsf& placement, const TopoDS_Shape& shape, std::shared_ptr style) : id(id), placement(placement), shape(shape), style(style) {} IfcRepresentationShapeItem(int id, const gp_GTrsf& placement, const TopoDS_Shape& shape) : id(id), placement(placement), shape(shape), style(0) {} - IfcRepresentationShapeItem(int id, const TopoDS_Shape& shape, const SurfaceStyle* style) + IfcRepresentationShapeItem(int id, const TopoDS_Shape& shape, std::shared_ptr style) : id(id), shape(shape), style(style) {} IfcRepresentationShapeItem(int id, const TopoDS_Shape& shape) : id(id), shape(shape), style(0) {} @@ -45,9 +45,10 @@ namespace IfcGeom { void prepend(const gp_GTrsf& trsf) { placement.PreMultiply(trsf); } const TopoDS_Shape& Shape() const { return shape; } const gp_GTrsf& Placement() const { return placement; } - bool hasStyle() const { return style != 0; } + bool hasStyle() const { return !!style; } const SurfaceStyle& Style() const { return *style; } - void setStyle(const SurfaceStyle* newStyle) { style = newStyle; } + const std::shared_ptr StylePtr() const { return style; } + void setStyle(std::shared_ptr newStyle) { style = newStyle; } int ItemId() const { return id; } }; typedef std::vector IfcRepresentationShapeItems; diff --git a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h index 1ce8a47922..6d08b35b04 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h @@ -137,6 +137,8 @@ namespace IfcGeom { const Element* get_object(int id) { return implementation_->get_object(id); } IfcUtil::IfcBaseClass* create() { return implementation_->create(); } + + void set_cache(HdfSerializer* cache) { return implementation_->set_cache(cache); } }; } diff --git a/src/ifcgeom_schema_agnostic/IfcGeomMaterial.cpp b/src/ifcgeom_schema_agnostic/IfcGeomMaterial.cpp index 3593ed7673..12083e77a6 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomMaterial.cpp +++ b/src/ifcgeom_schema_agnostic/IfcGeomMaterial.cpp @@ -20,16 +20,23 @@ #include "IfcGeomMaterial.h" static double black[3] = {0.,0.,0.}; +static const std::string no_name = ""; -IfcGeom::Material::Material(const IfcGeom::SurfaceStyle* style) : style(style) {} -bool IfcGeom::Material::hasDiffuse() const { return style->Diffuse() ? true : false; } -bool IfcGeom::Material::hasSpecular() const { return style->Specular() ? true : false; } -bool IfcGeom::Material::hasTransparency() const { return style->Transparency() ? true : false; } -bool IfcGeom::Material::hasSpecularity() const { return style->Specularity() ? true : false; } +IfcGeom::Material::Material() {} +IfcGeom::Material::Material(const std::shared_ptr& style) : style(style) {} + +bool IfcGeom::Material::hasDiffuse() const { return style && style->Diffuse() ? true : false; } +bool IfcGeom::Material::hasSpecular() const { return style && style->Specular() ? true : false; } +bool IfcGeom::Material::hasTransparency() const { return style && style->Transparency() ? true : false; } +bool IfcGeom::Material::hasSpecularity() const { return style && style->Specularity() ? true : false; } const double* IfcGeom::Material::diffuse() const { if (hasDiffuse()) return &((*style->Diffuse()).R()); else return black; } const double* IfcGeom::Material::specular() const { if (hasSpecular()) return &((*style->Specular()).R()); else return black; } double IfcGeom::Material::transparency() const { if (hasTransparency()) return *style->Transparency(); else return 0; } double IfcGeom::Material::specularity() const { if (hasSpecularity()) return *style->Specularity(); else return 0; } -const std::string &IfcGeom::Material::name() const { return style->Name(); } -const std::string &IfcGeom::Material::original_name() const { return style->original_name(); } +const std::string &IfcGeom::Material::name() const { return style ? style->Name() : no_name; } +const std::string &IfcGeom::Material::original_name() const { return style ? style->original_name() : no_name; } bool IfcGeom::Material::operator==(const IfcGeom::Material& other) const { return style == other.style; } + +const IfcGeom::SurfaceStyle& IfcGeom::Material::get_style() const { + return *style; +} diff --git a/src/ifcgeom_schema_agnostic/IfcGeomMaterial.h b/src/ifcgeom_schema_agnostic/IfcGeomMaterial.h index 8614ae20d3..531be4ef46 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomMaterial.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomMaterial.h @@ -28,11 +28,11 @@ namespace IfcGeom { class IFC_GEOM_API Material { private: - const IfcGeom::SurfaceStyle* style; + std::shared_ptr style; public: - explicit Material(const IfcGeom::SurfaceStyle* style = 0); // TODO default constructor for vector? - // Material(const Material& other); - // Material& operator=(const Material& other); + Material(); + explicit Material(const std::shared_ptr&); + bool hasDiffuse() const; bool hasSpecular() const; bool hasTransparency() const; @@ -44,6 +44,8 @@ namespace IfcGeom { const std::string &name() const; const std::string &original_name() const; bool operator==(const Material& other) const; + + const IfcGeom::SurfaceStyle& get_style() const; }; } diff --git a/src/ifcgeom_schema_agnostic/IfcGeomRenderStyles.h b/src/ifcgeom_schema_agnostic/IfcGeomRenderStyles.h index 4412e5d84d..dc4cf8f74e 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomRenderStyles.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomRenderStyles.h @@ -99,7 +99,7 @@ namespace IfcGeom { boost::optional& Id() { return id; } }; - IFC_GEOM_API const SurfaceStyle* get_default_style(const std::string& ifc_type); + IFC_GEOM_API std::shared_ptr get_default_style(const std::string& ifc_type); IFC_GEOM_API SurfaceStyle& update_default_style(const std::string& ifc_type); diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.h b/src/ifcgeom_schema_agnostic/IteratorImplementation.h index 35293cd59b..b384e9eb1e 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.h +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.h @@ -4,6 +4,7 @@ #include "../ifcgeom_schema_agnostic/IfcGeomFilter.h" #include "../ifcparse/IfcFile.h" #include "../ifcgeom/IfcGeomIteratorSettings.h" +#include "../serializers/HdfSerializer.h" #include @@ -34,7 +35,11 @@ IteratorFactoryImplementation& iterator_implementations(); namespace IfcGeom { class IteratorImplementation { + protected: + HdfSerializer* cache_ = nullptr; public: + void set_cache(HdfSerializer* cache) { cache_ = cache; } + virtual bool initialize() = 0; virtual void compute_bounds(bool with_geometry) = 0; virtual const gp_XYZ& bounds_min() const = 0; diff --git a/src/ifcgeom_schema_agnostic/SurfaceStyle.cpp b/src/ifcgeom_schema_agnostic/SurfaceStyle.cpp index 8484d3fb36..f1344e89f6 100644 --- a/src/ifcgeom_schema_agnostic/SurfaceStyle.cpp +++ b/src/ifcgeom_schema_agnostic/SurfaceStyle.cpp @@ -8,48 +8,48 @@ namespace pt = boost::property_tree; -static std::map default_materials; -static IfcGeom::SurfaceStyle default_material; +static std::map> default_materials; +static std::shared_ptr default_material; static bool default_materials_initialized = false; void InitDefaultMaterials() { - default_materials.insert(std::make_pair("IfcSite", IfcGeom::SurfaceStyle("IfcSite"))); - default_materials["IfcSite"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.65)); + default_materials.insert(std::make_pair("IfcSite", std::make_shared("IfcSite"))); + default_materials["IfcSite"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.65)); - default_materials.insert(std::make_pair("IfcSlab", IfcGeom::SurfaceStyle("IfcSlab"))); - default_materials["IfcSlab"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.4, 0.4, 0.4)); + default_materials.insert(std::make_pair("IfcSlab", std::make_shared("IfcSlab"))); + default_materials["IfcSlab"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.4, 0.4, 0.4)); - default_materials.insert(std::make_pair("IfcWallStandardCase", IfcGeom::SurfaceStyle("IfcWallStandardCase"))); - default_materials["IfcWallStandardCase"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); + default_materials.insert(std::make_pair("IfcWallStandardCase", std::make_shared("IfcWallStandardCase"))); + default_materials["IfcWallStandardCase"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); - default_materials.insert(std::make_pair("IfcWall", IfcGeom::SurfaceStyle("IfcWall"))); - default_materials["IfcWall"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); + default_materials.insert(std::make_pair("IfcWall", std::make_shared("IfcWall"))); + default_materials["IfcWall"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); - default_materials.insert(std::make_pair("IfcWindow", IfcGeom::SurfaceStyle("IfcWindow"))); - default_materials["IfcWindow"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.75)); - default_materials["IfcWindow"].Transparency().reset(0.3); + default_materials.insert(std::make_pair("IfcWindow", std::make_shared("IfcWindow"))); + default_materials["IfcWindow"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.75)); + default_materials["IfcWindow"]->Transparency().reset(0.3); - default_materials.insert(std::make_pair("IfcDoor", IfcGeom::SurfaceStyle("IfcDoor"))); - default_materials["IfcDoor"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.55, 0.3, 0.15)); + default_materials.insert(std::make_pair("IfcDoor", std::make_shared("IfcDoor"))); + default_materials["IfcDoor"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.55, 0.3, 0.15)); - default_materials.insert(std::make_pair("IfcBeam", IfcGeom::SurfaceStyle("IfcBeam"))); - default_materials["IfcBeam"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.7, 0.7)); + default_materials.insert(std::make_pair("IfcBeam", std::make_shared("IfcBeam"))); + default_materials["IfcBeam"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.7, 0.7)); - default_materials.insert(std::make_pair("IfcRailing", IfcGeom::SurfaceStyle("IfcRailing"))); - default_materials["IfcRailing"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); + default_materials.insert(std::make_pair("IfcRailing", std::make_shared("IfcRailing"))); + default_materials["IfcRailing"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); - default_materials.insert(std::make_pair("IfcMember", IfcGeom::SurfaceStyle("IfcMember"))); - default_materials["IfcMember"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); + default_materials.insert(std::make_pair("IfcMember", std::make_shared("IfcMember"))); + default_materials["IfcMember"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); - default_materials.insert(std::make_pair("IfcPlate", IfcGeom::SurfaceStyle("IfcPlate"))); - default_materials["IfcPlate"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.8, 0.8, 0.8)); + default_materials.insert(std::make_pair("IfcPlate", std::make_shared("IfcPlate"))); + default_materials["IfcPlate"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.8, 0.8, 0.8)); - default_materials.insert(std::make_pair("IfcSpace", IfcGeom::SurfaceStyle("IfcSpace"))); - default_materials["IfcWindow"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.75, 0.8)); - default_materials["IfcWindow"].Transparency().reset(0.8); + default_materials.insert(std::make_pair("IfcSpace", std::make_shared("IfcSpace"))); + default_materials["IfcWindow"]->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.75, 0.8)); + default_materials["IfcWindow"]->Transparency().reset(0.8); - default_material = IfcGeom::SurfaceStyle("DefaultMaterial"); - default_material.Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.7, 0.7, 0.7)); + default_material = std::make_shared("DefaultMaterial"); + default_material->Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.7, 0.7, 0.7)); default_materials_initialized = true; } @@ -83,58 +83,49 @@ void IfcGeom::set_default_style_file(const std::string& json_file) { for (pt::ptree::value_type &material_pair : root) { std::string name = material_pair.first; - default_materials.insert(std::make_pair(name, IfcGeom::SurfaceStyle(name))); + default_materials.insert(std::make_pair(name, std::make_shared(name))); pt::ptree material = material_pair.second; boost::optional diffuse = material.get_child_optional("diffuse"); - default_materials[name].Diffuse() = read_colour_component(diffuse); + default_materials[name]->Diffuse() = read_colour_component(diffuse); boost::optional specular = material.get_child_optional("specular"); - default_materials[name].Specular() = read_colour_component(specular); + default_materials[name]->Specular() = read_colour_component(specular); if (material.get_child_optional("specular-roughness")) { - default_materials[name].Specularity().reset(1.0 / material.get("specular-roughness")); + default_materials[name]->Specularity().reset(1.0 / material.get("specular-roughness")); } if (material.get_child_optional("transparency")) { - default_materials[name].Transparency() = material.get("transparency"); + default_materials[name]->Transparency() = material.get("transparency"); } } // Is "*" present? If yes, remove it and make it the default style. - std::map::const_iterator it = default_materials.find("*"); + auto it = default_materials.find("*"); if (it != default_materials.end()) { - IfcGeom::SurfaceStyle star = it->second; - default_material.Diffuse() = star.Diffuse(); - default_material.Specular() = star.Specular(); - default_material.Specularity() = star.Specularity(); - default_material.Transparency() = star.Transparency(); + default_material = it->second; default_materials.erase(it); } } -const IfcGeom::SurfaceStyle* IfcGeom::get_default_style(const std::string& s) { +std::shared_ptr IfcGeom::get_default_style(const std::string& s) { static std::mutex m; std::lock_guard lk(m); if (!default_materials_initialized) InitDefaultMaterials(); - std::map::const_iterator it = default_materials.find(s); + auto it = default_materials.find(s); if (it == default_materials.end()) { - default_materials.insert(std::make_pair(s, IfcGeom::SurfaceStyle(s))); - default_materials[s].Diffuse() = default_material.Diffuse(); - default_materials[s].Specular() = default_material.Specular(); - default_materials[s].Specularity() = default_material.Specularity(); - default_materials[s].Transparency() = default_material.Transparency(); + default_materials.insert(std::make_pair(s, default_material)); it = default_materials.find(s); } - const IfcGeom::SurfaceStyle& surface_style = it->second; - return &surface_style; + return it->second; } IfcGeom::SurfaceStyle& IfcGeom::update_default_style(const std::string& s) { if (!default_materials_initialized) InitDefaultMaterials(); - std::map::iterator it = default_materials.find(s); + auto it = default_materials.find(s); if (it == default_materials.end()) { throw std::runtime_error("No style registered for " + s); } - return it->second; + return *it->second; } diff --git a/src/serializers/GeometrySerializer.h b/src/serializers/GeometrySerializer.h index cd5017c10a..ad531f8120 100644 --- a/src/serializers/GeometrySerializer.h +++ b/src/serializers/GeometrySerializer.h @@ -21,7 +21,6 @@ #define GEOMETRYSERIALIZER_H #include "../serializers/Serializer.h" -#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h" #include "../ifcgeom/IfcGeomElement.h" class SerializerSettings : public IfcGeom::IteratorSettings diff --git a/src/serializers/HdfSerializer.cpp b/src/serializers/HdfSerializer.cpp index 9c7be031f1..5542a0da6e 100644 --- a/src/serializers/HdfSerializer.cpp +++ b/src/serializers/HdfSerializer.cpp @@ -25,106 +25,594 @@ #include "../ifcparse/utils.h" +#include +#include #include + #include +#include +#include +#ifdef USE_BINARY +#define write_shape write_binary +#define read_shape read_binary +#else +#define write_shape write_text +#define read_shape read_text +#endif +herr_t print_stack(hid_t, void*) { + /* + // For debugging: when using IfcConvert on Windows with wcout, + // it's difficult to get console output of HDF5 stack traces. + auto f = fopen("temp.txt", "w"); + H5Eprint(estack, f); + fclose(f); + */ + return 0; +} HdfSerializer::HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings) : GeometrySerializer(settings) , hdf_filename(hdf_filename) - , DATASET_NAME_POSITIONS("Positions") - , DATASET_NAME_NORMALS("Normals") - , DATASET_NAME_INDICES("Indices") - , DATASET_NAME_OCCT("OCCT Text") - + , settings_(settings) { - guids = {}; + H5E_auto2_t fn = &print_stack; + H5::Exception::setAutoPrint(fn, nullptr); + + try { + file = H5::H5File(hdf_filename, H5F_ACC_RDWR | H5F_ACC_CREAT); + } catch (H5::Exception&) { + file = H5::H5File(hdf_filename, H5F_ACC_TRUNC); + } + + str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + +#ifdef USE_BINARY + auto uint_type = H5::PredType::NATIVE_UINT8; + shape_type = H5::VarLenType(&uint_type); +#else + shape_type = str_type; +#endif + + hsize_t dims_3[1]{ 3 }; + double3 = H5::ArrayType(H5::PredType::NATIVE_DOUBLE, 1, dims_3); + + style_compound = H5::CompType(sizeof(surface_style_serialization)); + style_compound.insertMember("name", HOFFSET(surface_style_serialization, name), str_type); + style_compound.insertMember("original_name", HOFFSET(surface_style_serialization, original_name), str_type); + style_compound.insertMember("id", HOFFSET(surface_style_serialization, id), H5::PredType::NATIVE_INT); + style_compound.insertMember("diffuse", HOFFSET(surface_style_serialization, diffuse), double3); + style_compound.insertMember("specular", HOFFSET(surface_style_serialization, specular), double3); + style_compound.insertMember("transparency", HOFFSET(surface_style_serialization, transparency), H5::PredType::NATIVE_DOUBLE); + style_compound.insertMember("specularity", HOFFSET(surface_style_serialization, specularity), H5::PredType::NATIVE_DOUBLE); + + hsize_t dims_4x4[2]{ 4, 4 }; + double4x4 = H5::ArrayType(H5::PredType::NATIVE_DOUBLE, 2, dims_4x4); + + compound = H5::CompType(sizeof(brep_element)); + compound.insertMember("id", HOFFSET(brep_element, id), H5::PredType::NATIVE_INT); + compound.insertMember("matrix", HOFFSET(brep_element, matrix), double4x4); + compound.insertMember("shape_serialization", HOFFSET(brep_element, shape_serialization), shape_type); + compound.insertMember("surface_style_id", HOFFSET(brep_element, surface_style), style_compound); } - bool HdfSerializer::ready() { - //todo: check whether the file exists return true; } void HdfSerializer::writeHeader() { - const H5std_string FILE_NAME(hdf_filename); - file = H5::H5File(FILE_NAME, H5F_ACC_TRUNC); +} +namespace { + template + H5::DataType h5_datatype_for_cpp(); + + template <> + H5::DataType h5_datatype_for_cpp() { + return H5::PredType::NATIVE_INT; + } + + template <> + H5::DataType h5_datatype_for_cpp() { + return H5::PredType::NATIVE_DOUBLE; + } + + template <> + H5::DataType h5_datatype_for_cpp() { + return H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); + } + + template + void do_read(H5::Attribute& attr, T& val) { + attr.read(h5_datatype_for_cpp(), &val); + } + + template <> + void do_read(H5::Attribute& attr, std::string& val) { + attr.read(h5_datatype_for_cpp(), val); + } + + template + T read_scalar_attribute(H5::H5Object& l, const std::string& name) { + auto attr = l.openAttribute(name); + auto space = attr.getSpace(); + int rank = space.getSimpleExtentNdims(); + // A scalar dataspace, H5S_SCALAR, has a single element, though that + // element may be of a complex datatype, such as a compound or array + // datatype. By convention, the rank of a scalar dataspace is always + // 0 (zero); + if (rank != 0) { + throw std::runtime_error("Invalid"); + } + T val; + do_read(attr, val); + return val; + } +} + +#include + +namespace { + // https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Part/App/TopoShape.cpp + TopoDS_Shape read_binary(const hvl_t& vlen) { + std::string s((char*)vlen.p, (size_t)vlen.len); + std::istringstream str(s); + BinTools_ShapeSet theShapeSet; + theShapeSet.Read(str); + Standard_Integer shapeId = 0, locId = 0, orient = 0; + BinTools::GetInteger(str, shapeId); + if (shapeId <= 0 || shapeId > theShapeSet.NbShapes()) { + throw std::runtime_error(""); + } + + BinTools::GetInteger(str, locId); + BinTools::GetInteger(str, orient); + TopAbs_Orientation anOrient = static_cast(orient); + + TopoDS_Shape shp = theShapeSet.Shape(shapeId); + shp.Location(theShapeSet.Locations().Location(locId)); + shp.Orientation(anOrient); + + return shp; + } + + // https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Part/App/TopoShape.cpp + void write_binary(TopoDS_Shape shp, std::string& s) { + std::ostringstream out; + + BinTools_ShapeSet theShapeSet; + + Standard_Integer shapeId = theShapeSet.Add(shp); + Standard_Integer locId = theShapeSet.Locations().Index(shp.Location()); + Standard_Integer orient = static_cast(shp.Orientation()); + + theShapeSet.Write(out); + BinTools::PutInteger(out, shapeId); + BinTools::PutInteger(out, locId); + BinTools::PutInteger(out, orient); + + s = out.str(); + } + + TopoDS_Shape read_text(const std::string& s) { + std::stringstream stream(s); + BRep_Builder B; + TopoDS_Shape shp; + BRepTools::Read(shp, stream, B); + return shp; + } + + void write_text(TopoDS_Shape shp, std::string& out) { + std::stringstream sstream; + BRepTools::Write(shp, sstream); + out = sstream.str(); + } +} + +namespace { + template + std::vector read_dataset(const H5::Group& group, const std::string& name) { + auto ds = group.openDataSet(name); + auto space = ds.getSpace(); + int rank = space.getSimpleExtentNdims(); + std::vector dims(rank); + space.getSimpleExtentDims(dims.data(), NULL); + const hsize_t total = std::accumulate(dims.begin(), dims.end(), 1U, std::multiplies()); + std::vector result(total); + ds.read(result.data(), h5_datatype_for_cpp()); + return result; + } +} + +void HdfSerializer::read_surface_style(surface_style_serialization& s, std::shared_ptr& style_ptr) { + if (strlen(s.name) || s.id) { + if (strlen(s.name) && s.id) { + style_ptr = std::make_shared(s.id, s.name); + } else if (strlen(s.name)) { + style_ptr = std::make_shared(s.name); + } else if (s.id) { + style_ptr = std::make_shared(s.id); + } + auto& gss = *style_ptr; + + if (s.diffuse[0] == s.diffuse[0]) { + gss.Diffuse().emplace(s.diffuse[0], s.diffuse[1], s.diffuse[2]); + } + if (s.specular[0] == s.specular[0]) { + gss.Specular().emplace(s.specular[0], s.specular[1], s.specular[2]); + } + if (s.transparency == s.transparency) { + gss.Transparency() = s.transparency; + } + if (s.specularity == s.specularity) { + gss.Specularity() = s.specularity; + } + } + +} + + +const IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& guid, unsigned int representation_id, read_type rt) { + if (!H5Lexists(file.getId(), guid.c_str(), H5P_DEFAULT)) { + return nullptr; + } + + auto representation_id_str = std::to_string(representation_id); + + auto element_group = file.openGroup(guid); + if (!H5Lexists(element_group.getId(), representation_id_str.c_str(), H5P_DEFAULT)) { + return nullptr; + } + + int id = read_scalar_attribute(element_group, "id"); + int parent_id = read_scalar_attribute(element_group, "parent_id"); + std::string type = read_scalar_attribute(element_group, "type"); + std::string name = read_scalar_attribute(element_group, "name"); + std::string context = read_scalar_attribute(element_group, "context"); + std::string unique_id = read_scalar_attribute(element_group, "unique_id"); + + gp_Trsf trsf; + auto placeds = element_group.openDataSet(DATASET_NAME_PLACEMENT); + double m44[4][4]; + placeds.read(m44, H5::PredType::NATIVE_DOUBLE); + trsf.SetValues( + m44[0][0], m44[0][1], m44[0][2], m44[0][3], + m44[1][0], m44[1][1], m44[1][2], m44[1][3], + m44[2][0], m44[2][1], m44[2][2], m44[2][3] + ); + + auto representation_group = element_group.openGroup(std::to_string(representation_id)); + std::string geom_id = read_scalar_attribute(representation_group, "geom_id"); + + IfcGeom::ElementSettings element_settings(settings_, f.getUnit("LENGTHUNIT").second, type); + auto inst = f.instance_by_id(id)->as(); + + if (rt == READ_BREP) { + auto brepDataset = representation_group.openDataSet(DATASET_NAME_OCCT); + + std::vector parts; + { + auto space = brepDataset.getSpace(); + int rank = space.getSimpleExtentNdims(); + + if (rank != 1) { + return nullptr; + } + + std::vector dims(rank); + space.getSimpleExtentDims(dims.data(), NULL); + + parts.resize(dims[0]); + brepDataset.read(parts.data(), compound); + } + + IfcGeom::IfcRepresentationShapeItems shapes; + for (auto& part : parts) { + TopoDS_Shape shp = read_shape(part.shape_serialization); + + gp_GTrsf trsf(gp_Mat( + part.matrix[0][0], part.matrix[0][1], part.matrix[0][2], + part.matrix[1][0], part.matrix[1][1], part.matrix[1][2], + part.matrix[2][0], part.matrix[2][1], part.matrix[2][2] + ), gp_XYZ( + part.matrix[3][0], part.matrix[3][1], part.matrix[3][2] + )); + + std::shared_ptr style_ptr; + read_surface_style(part.surface_style, style_ptr); + + shapes.push_back(IfcGeom::IfcRepresentationShapeItem(part.id, trsf, shp, style_ptr)); + } + + auto geometry = boost::shared_ptr(new IfcGeom::Representation::BRep(element_settings, geom_id, shapes)); + + return new IfcGeom::BRepElement(id, parent_id, name, type, guid, context, trsf, geometry, inst); + } else { + + H5::Group meshGroup; + try { + meshGroup = representation_group.openGroup(GROUP_NAME_MESH); + } catch (H5::Exception&) { + return nullptr; + } + + auto verts = read_dataset(meshGroup, DATASET_NAME_POSITIONS); + auto faces = read_dataset(meshGroup, DATASET_NAME_INDICES); + auto edges = read_dataset(meshGroup, DATASET_NAME_EDGES); + auto normals = read_dataset(meshGroup, DATASET_NAME_NORMALS); + auto uvcoords = read_dataset(meshGroup, DATASET_NAME_UVCOORDS); + auto material_ids = read_dataset(meshGroup, DATASET_NAME_MATERIAL_IDS); + + std::vector surface_styles; + + { + auto ds = meshGroup.openDataSet(DATASET_NAME_MATERIALS); + auto space = ds.getSpace(); + int rank = space.getSimpleExtentNdims(); + + if (rank != 1) { + return nullptr; + } + + std::vector dims(rank); + space.getSimpleExtentDims(dims.data(), NULL); + + surface_styles.resize(dims[0]); + ds.read(surface_styles.data(), style_compound); + } + + std::vector> surface_style_ptrs(surface_styles.size()); + + for (size_t i = 0; i < surface_styles.size(); ++i) { + read_surface_style(surface_styles[i], surface_style_ptrs[i]); + } + + auto rep = boost::shared_ptr(new IfcGeom::Representation::Triangulation( + element_settings, + geom_id, + verts, + faces, + edges, + normals, + uvcoords, + material_ids, + surface_style_ptrs + )); + + return new IfcGeom::TriangulationElement( + IfcGeom::Element( + element_settings, + id, + parent_id, + name, + type, + guid, + context, + trsf, + inst + ), + rep + ); + } +} + +namespace { + std::array, 4> gtrsf_to_matrix(const gp_GTrsf& trsf) { + std::array, 4> arr; + + for (int i = 1; i < 5; ++i) { + for (int j = 1; j < 4; ++j) { + arr[i-1][j-1] = trsf.Value(j, i); + } + arr[i - 1][3] = i == 4 ? 1.0 : 0.0; + } + + return arr; + } +} + +H5::Group HdfSerializer::write(const IfcGeom::Element* o) { + try { + return file.openGroup(o->guid()); + } catch (H5::Exception&) {} + + H5::Group element_group = file.createGroup(o->guid()); + + typedef std::string const & (IfcGeom::Element::*string_member_fun)(void) const; + typedef int (IfcGeom::Element::*int_member_fun)(void) const; + + static const std::vector> data_pairs_string = { + {"type", &IfcGeom::Element::type}, + {"name", &IfcGeom::Element::name }, + {"guid", &IfcGeom::Element::guid }, + {"context", &IfcGeom::Element::context }, + {"unique_id", &IfcGeom::Element::unique_id } + }; + + static const std::vector> data_pairs_int = { + {"id", &IfcGeom::Element::id}, + {"parent_id", &IfcGeom::Element::parent_id }, + }; + + H5::DataSpace attrdspace(H5S_SCALAR); + + for (auto& p : data_pairs_string) { + H5::Attribute att = element_group.createAttribute(p.first, str_type, attrdspace); + att.write(str_type, ((*o).*(p.second))()); + } + + for (auto& p : data_pairs_int) { + H5::Attribute att = element_group.createAttribute(p.first, H5::PredType::NATIVE_INT, attrdspace); + int value = ((*o).*(p.second))(); + att.write(H5::PredType::NATIVE_INT, &value); + } + + hsize_t dims_4x4[2]{ 4, 4 }; + H5::DataSpace dataspace_4x4(2, dims_4x4); + + auto placement_dataset = element_group.createDataSet(DATASET_NAME_PLACEMENT, H5::PredType::NATIVE_DOUBLE, dataspace_4x4); + const std::vector& m43 = o->transformation().matrix().data(); + double m44[4][4] = { + { m43[0], m43[3], m43[6], m43[9] }, + { m43[1], m43[4], m43[7], m43[10] }, + { m43[2], m43[5], m43[8], m43[11] }, + { 0, 0, 0, 1 } + }; + placement_dataset.write(m44, H5::PredType::NATIVE_DOUBLE); + + return element_group; +} + +H5::Group HdfSerializer::createRepresentationGroup(const H5::Group& element_group, const std::string& gid) { + // the part before the hyphen is the representation id + auto gid2 = gid; + auto hyphen = gid2.find("-"); + if (hyphen != std::string::npos) { + gid2 = gid2.substr(0, hyphen); + } + + H5::Group representation_group; + try { + representation_group = element_group.openGroup(gid2); + } catch (H5::Exception&) { + representation_group = element_group.createGroup(gid2); + + H5::DataSpace attrdspace(H5S_SCALAR); + { + H5::Attribute att = representation_group.createAttribute("geom_id", str_type, attrdspace); + std::string value = gid; + att.write(str_type, value); + } + } + return representation_group; +} + +void HdfSerializer::write_style(surface_style_serialization& data, const IfcGeom::SurfaceStyle& s) { + data.name = s.Name().c_str(); + data.original_name = s.original_name().c_str(); + data.id = s.Id().get_value_or(0); + if (s.Diffuse()) { + data.diffuse[0] = s.Diffuse()->R(); + data.diffuse[1] = s.Diffuse()->G(); + data.diffuse[2] = s.Diffuse()->B(); + } + if (s.Specular()) { + data.specular[0] = s.Specular()->R(); + data.specular[1] = s.Specular()->G(); + data.specular[2] = s.Specular()->B(); + } + if (s.Transparency()) { + data.transparency = *s.Transparency(); + } + if (s.Specularity()) { + data.specularity = *s.Specularity(); + } } void HdfSerializer::write(const IfcGeom::BRepElement* o) { + static auto nan = std::numeric_limits::quiet_NaN(); - std::string guid = o->guid(); + auto element_group = write((const IfcGeom::Element*)o); - H5::Group elementGroup; + H5::Group representation_group = createRepresentationGroup(element_group, o->geometry().id()); - H5::Group meshGroup; - H5::DataSet positionsDataset; - H5::DataSet normalsDataset; - H5::DataSet indicesDataset; + std::list brep_strings; + size_t num_parts = std::distance(o->geometry().begin(), o->geometry().end()); + brep_element* parts = new brep_element[num_parts]; + size_t i = 0; + for (auto it = o->geometry().begin(); it != o->geometry().end(); ++it, ++i) { + parts[i].id = it->ItemId(); + std::array, 4> arr = gtrsf_to_matrix(it->Placement()); + for (int j = 0; j < 4; ++j) { + std::copy(arr[j].begin(), arr[j].end(), parts[i].matrix[j]); + } - H5::Group OCCTGroup; - H5::DataSet OCCTDataset; - - const IfcGeom::Representation::BRep& brepmesh = o->geometry(); - const IfcGeom::Representation::Serialization serialization(brepmesh); - std::string brep_data = serialization.brep_data(); - - const IfcGeom::TriangulationElement triangular_element(*o); - const IfcGeom::Representation::Triangulation& mesh = triangular_element.geometry(); - const int vcount = (int)mesh.verts().size() / 3; - const int fcount = (int)mesh.faces().size() / 3; - const bool isyup = settings().get(SerializerSettings::USE_Y_UP); - - std::string value = o->type(); - - if (fcount > 0) { - - guids.insert(guid); - elementGroup = file.createGroup(guid); - - meshGroup = elementGroup.createGroup("Triangle Mesh"); - OCCTGroup = elementGroup.createGroup("OCCT Data"); - - H5::StrType str_type(0, H5T_VARIABLE); - H5:: DataSpace attrdspace(H5S_SCALAR); - H5::Attribute att = elementGroup.createAttribute("IFC entity type", str_type, attrdspace); - att.write(str_type, value); - - const int RANK = 2; - hsize_t dimsf[2]; - dimsf[0] = vcount; - dimsf[1] = 3; - H5::DataSpace dataspace(RANK, dimsf); - - hsize_t dimsfaces[2]; - dimsfaces[0] = fcount; - dimsfaces[1] = 3; - H5::DataSpace face_dataspace(RANK, dimsfaces); - - const int RANK_OCCT = 1; - hsize_t dimsocct[2]; - dimsocct[0] = 1; - dimsfaces[1] = 1; - H5::DataSpace occt_dataspace(RANK_OCCT, dimsocct); - OCCTDataset = OCCTGroup.createDataSet(DATASET_NAME_OCCT, str_type, occt_dataspace); - OCCTDataset.write(brep_data, str_type); - - indicesDataset = meshGroup.createDataSet(DATASET_NAME_INDICES, H5::PredType::NATIVE_INT, face_dataspace); - positionsDataset = meshGroup.createDataSet(DATASET_NAME_POSITIONS, H5::PredType::NATIVE_DOUBLE, dataspace); - normalsDataset = meshGroup.createDataSet(DATASET_NAME_NORMALS, H5::PredType::NATIVE_DOUBLE, dataspace); - - positionsDataset.write(mesh.verts().data(), H5::PredType::NATIVE_DOUBLE); - normalsDataset.write(mesh.normals().data(), H5::PredType::NATIVE_DOUBLE); - indicesDataset.write(mesh.faces().data(), H5::PredType::NATIVE_INT); + brep_strings.emplace_back(); + write_shape(it->Shape(), brep_strings.back()); + parts[i].surface_style = { "", "", 0, {nan,nan,nan}, {nan,nan,nan}, nan, nan }; + if (it->hasStyle()) { + auto& s = it->Style(); + write_style(parts[i].surface_style, s); + } +#ifdef USE_BINARY + const auto& s = brep_strings.back(); + parts[i].shape_serialization.p = new char[s.size()]; + memcpy(parts[i].shape_serialization.p, s.c_str(), s.size()); + parts[i].shape_serialization.len = s.size(); +#else + parts[i].shape_serialization = brep_strings.back().c_str(); +#endif + } - + hsize_t dimsp[1]{ num_parts }; + H5::DataSpace dataspace_parts(1, dimsp); + + auto brepDataset = representation_group.createDataSet(DATASET_NAME_OCCT, compound, dataspace_parts); + brepDataset.write(parts, compound); +} + +namespace { + + template + void write_dataset(const H5::Group& group, const std::string& name, const std::vector& ts, size_t stride) { + hsize_t d[2]{ ts.size() / stride, stride }; + H5::DataSpace dataspace(stride == 1 ? 1 : 2, d); + auto dt = h5_datatype_for_cpp(); + + auto ds = group.createDataSet(name, dt, dataspace); + ds.write(ts.data(), dt); + } + +} + +void HdfSerializer::write(const IfcGeom::TriangulationElement* o) { + auto element_group = write((const IfcGeom::Element*)o); + + const auto& mesh = o->geometry(); + H5::Group representation_group = createRepresentationGroup(element_group, o->geometry().id()); + H5::Group meshGroup = representation_group.createGroup(GROUP_NAME_MESH); + + write_dataset(meshGroup, DATASET_NAME_POSITIONS, mesh.verts(), 3); + write_dataset(meshGroup, DATASET_NAME_INDICES, mesh.faces(), 3); + write_dataset(meshGroup, DATASET_NAME_EDGES, mesh.edges(), 2); + write_dataset(meshGroup, DATASET_NAME_NORMALS, mesh.normals(), 2); + write_dataset(meshGroup, DATASET_NAME_UVCOORDS, mesh.uvs(), 2); + write_dataset(meshGroup, DATASET_NAME_MATERIAL_IDS, mesh.material_ids(), 1); + + { + auto& ts = mesh.materials(); + hsize_t d[2] { ts.size() }; + H5::DataSpace dataspace(1, d); + const auto& dt = style_compound; + + std::vector data; + data.reserve(ts.size()); + for (auto& m : ts) { + data.emplace_back(); + write_style(data.back(), m.get_style()); + } + + auto ds = meshGroup.createDataSet(DATASET_NAME_MATERIALS, dt, dataspace); + ds.write(data.data(), dt); } } + +const H5std_string HdfSerializer::DATASET_NAME_POSITIONS = "positions"; +const H5std_string HdfSerializer::DATASET_NAME_UVCOORDS = "uvcoords"; +const H5std_string HdfSerializer::DATASET_NAME_NORMALS = "normals"; +const H5std_string HdfSerializer::DATASET_NAME_INDICES = "indices"; +const H5std_string HdfSerializer::DATASET_NAME_EDGES = "edges"; +const H5std_string HdfSerializer::DATASET_NAME_MATERIAL_IDS = "material_ids"; +const H5std_string HdfSerializer::DATASET_NAME_MATERIALS = "materials"; +const H5std_string HdfSerializer::DATASET_NAME_OCCT = "brep"; +const H5std_string HdfSerializer::DATASET_NAME_PLACEMENT = "placement"; + +const H5std_string HdfSerializer::GROUP_NAME_MESH = "mesh"; + + #endif \ No newline at end of file diff --git a/src/serializers/HdfSerializer.h b/src/serializers/HdfSerializer.h index 9cc971f602..0859f41728 100644 --- a/src/serializers/HdfSerializer.h +++ b/src/serializers/HdfSerializer.h @@ -17,8 +17,8 @@ * * ********************************************************************************/ -#ifndef HdfSERIALIZER_H -#define HdfSERIALIZER_H +#ifndef HDFSERIALIZER_H +#define HDFSERIALIZER_H #ifdef WITH_HDF5 @@ -30,32 +30,89 @@ #include "../serializers/GeometrySerializer.h" +#define USE_BINARY class HdfSerializer : public GeometrySerializer { +public: + enum read_type { READ_BREP, READ_TRIANGULATION }; + private: const std::string hdf_filename; unsigned int vcount_total; H5::H5File file; - std::set guids; - const H5std_string DATASET_NAME_POSITIONS; - const H5std_string DATASET_NAME_NORMALS; - const H5std_string DATASET_NAME_INDICES; - const H5std_string DATASET_NAME_OCCT; + SerializerSettings settings_; + + static const H5std_string DATASET_NAME_POSITIONS; + static const H5std_string DATASET_NAME_UVCOORDS; + static const H5std_string DATASET_NAME_NORMALS; + static const H5std_string DATASET_NAME_INDICES; + static const H5std_string DATASET_NAME_EDGES; + static const H5std_string DATASET_NAME_MATERIAL_IDS; + static const H5std_string DATASET_NAME_MATERIALS; + static const H5std_string DATASET_NAME_OCCT; + static const H5std_string DATASET_NAME_PLACEMENT; + + static const H5std_string GROUP_NAME_MESH; + + struct surface_style_serialization { + const char* name; + const char* original_name; + // 0 if unset + unsigned int id; + // nan if unset + double diffuse[3]; + double specular[3]; + double transparency; + double specularity; + }; + + struct brep_element { + int id; + double matrix[4][4]; +#ifdef USE_BINARY + hvl_t shape_serialization; +#else + const char* shape_serialization; +#endif + surface_style_serialization surface_style; + }; + + H5::CompType compound; + H5::CompType style_compound; + H5::StrType str_type; + H5::ArrayType double4x4, double3; + H5::DataType shape_type; + +private: + + H5::Group createRepresentationGroup(const H5::Group& element_group, const std::string& gid); + void read_surface_style(surface_style_serialization& sss, std::shared_ptr& style_ptr); + void write_style(surface_style_serialization& data, const IfcGeom::SurfaceStyle& s); - public: HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings); virtual ~HdfSerializer() {} bool ready(); void writeHeader(); + + H5::Group write(const IfcGeom::Element* o); void write(const IfcGeom::BRepElement* o); - void write(const IfcGeom::TriangulationElement* /*o*/) {} + void write(const IfcGeom::TriangulationElement* o); + + const IfcGeom::Element* read(IfcParse::IfcFile& f, const std::string& guid, unsigned int representation_id, read_type rt = READ_BREP); + void finalize() {} bool isTesselated() const { return false; } void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {} void setFile(IfcParse::IfcFile*) {} }; +#else + +// We just define something here so that the symbol exists and the Iterator class +// methods don't need to look so different +class HdfSerializer {}; + #endif #endif diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 2fa3ebbca6..391ff9126e 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -19,6 +19,8 @@ * * ********************************************************************************/ +#include "../ifcgeom_schema_agnostic/Kernel.h" + #include #include #include