diff --git a/src/serializers/HdfSerializer.cpp b/src/serializers/HdfSerializer.cpp
index be11b4f72f..6dfdcae054 100644
--- a/src/serializers/HdfSerializer.cpp
+++ b/src/serializers/HdfSerializer.cpp
@@ -52,20 +52,22 @@ herr_t print_stack(hid_t /*estack*/, void*) {
return 0;
}
-HdfSerializer::HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings)
+HdfSerializer::HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings, bool read_only)
: GeometrySerializer(settings)
, hdf_filename(hdf_filename)
, settings_(settings)
{
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);
+ file = H5::H5File(hdf_filename, read_only
+ ? H5F_ACC_RDONLY
+ : (H5F_ACC_RDWR | H5F_ACC_CREAT));
+ } catch (H5::Exception& e) {
+ throw IfcParse::IfcException(e.getDetailMsg());
}
-
+
str_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE);
#ifdef USE_BINARY
diff --git a/src/serializers/HdfSerializer.cpp~ b/src/serializers/HdfSerializer.cpp~
new file mode 100644
index 0000000000..021ec397c8
--- /dev/null
+++ b/src/serializers/HdfSerializer.cpp~
@@ -0,0 +1,760 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+#ifdef WITH_HDF5
+
+#include "HdfSerializer.h"
+
+#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
+
+#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 /*estack*/, 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, bool read_only)
+ : GeometrySerializer(settings)
+ , hdf_filename(hdf_filename)
+ , settings_(settings)
+{
+ H5E_auto2_t fn = &print_stack;
+ // H5::Exception::setAutoPrint(fn, nullptr);
+
+ /*
+ static std::array, 3> modes = {{
+ { H5F_ACC_RDWR | H5F_ACC_CREAT, "Opened cache file read-write"},
+ { H5F_ACC_RDONLY, "Open cache file read-only"},
+ { H5F_ACC_TRUNC, "Truncated cache file"},
+ }};
+
+ for (size_t i = 0; i < modes.size(); ++i) {
+ try {
+ std::cout << "Trying " << modes[i].second << std::endl;
+ file = H5::H5File(hdf_filename, modes[i].first);
+ std::cout << modes[i].second << std::endl;
+ break;
+ } catch (H5::Exception& e) {
+ if (i == modes.size() - 1) {
+ throw IfcParse::IfcException(e.getDetailMsg());
+ }
+ }
+ }
+ */
+
+ open_file:
+
+ try {
+ file = H5::H5File(hdf_filename, read_only ? H5F_ACC_RDONLY : (H5F_ACC_RDWR | H5F_ACC_CREAT));
+ } catch (H5::Exception& e) {
+ if (!read_only) {
+ read_only = true;
+ goto open_file;
+ }
+ throw IfcParse::IfcException(e.getDetailMsg());
+ }
+
+ 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() {
+ return true;
+}
+
+void HdfSerializer::writeHeader() {
+}
+
+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_UINT64;
+ }
+
+ 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;
+ }
+ }
+
+}
+
+void HdfSerializer::remove(const std::string& guid) {
+ if (H5Lexists(file.getId(), guid.c_str(), H5P_DEFAULT)) {
+ file.unlink(guid);
+ }
+}
+
+IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& guid, const std::string& representation_id_str, read_type rt) {
+ if (!H5Lexists(file.getId(), guid.c_str(), H5P_DEFAULT)) {
+ return nullptr;
+ }
+
+ 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(representation_id_str);
+ 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();
+
+ boost::shared_ptr brep_geometry;
+ boost::shared_ptr triangulation_geometry;
+
+ if (rt == READ_BREP) {
+ auto it = brep_cache_.find(representation_id_str);
+ if (it != brep_cache_.end()) {
+ brep_geometry = it->second;
+ }
+ } else {
+ auto it = triangulation_cache_.find(representation_id_str);
+ if (it != triangulation_cache_.end()) {
+ triangulation_geometry = it->second;
+ }
+ }
+
+ if (rt == READ_BREP && !brep_geometry) {
+ auto brepDataset = representation_group.openDataSet(DATASET_NAME_OCCT);
+
+ static const auto ignored_settings =
+ // Settings that do not affect storage of brep data
+ IfcGeom::IteratorSettings::DISABLE_TRIANGULATION | IfcGeom::IteratorSettings::USE_BREP_DATA |
+ // Settings that affect which representation is considered, but cache does not need to be complete
+ IfcGeom::IteratorSettings::INCLUDE_CURVES | IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES |
+ // Only affects triangulation
+ IfcGeom::IteratorSettings::WELD_VERTICES | IfcGeom::IteratorSettings::NO_NORMALS |
+ IfcGeom::IteratorSettings::GENERATE_UVS | IfcGeom::IteratorSettings::EDGE_ARROWS |
+ // Is applied in the serializer
+ IfcGeom::IteratorSettings::SEARCH_FLOOR;
+
+ auto stored_settings = read_scalar_attribute(brepDataset, "settings");
+ auto requested_settings = settings_.get_raw();
+ stored_settings &= ~ignored_settings;
+ requested_settings &= ~ignored_settings;
+
+ // World coordinates can be applied post hoc
+ if (stored_settings != requested_settings && (stored_settings | IfcGeom::IteratorSettings::USE_WORLD_COORDS) != requested_settings) {
+ throw std::runtime_error("Settings mismatch");
+ }
+
+ 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);
+
+ // The gp_GTrsf(Mat, V) constructor isn't very smart in that
+ // it sets the Form to gp_Other. This, in turn, then means that
+ // in IfcOpenShell when the BRepElement is cast to a TopoDS_Compound
+ // (happens e.g in SVG and Python), and the trsf is multiplied into
+ // the shape, it is automatically converted to a Nurbs object.
+ // For this reason we do a quick identity check so that we in that
+ // case can keep the Form at gp_Identity. Better yet would be to
+ // do a full decomposition of the matrix in Translation Rotation and
+ // Scale components and use the OCCT APIs to reconstruct the Trsf
+ // from that.
+
+ gp_Mat M(
+ 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]
+ );
+
+ bool is_identity = true;
+
+ // quick identity test
+
+ for (int i = 1; i < 4; ++i) {
+ for (int j = 1; j < 4; ++j) {
+ if (std::fabs(M.Row(i).Coord(j) - ((i == j) ? 1.0 : 0.0)) > 1.e-9) {
+ is_identity = false;
+ }
+ }
+ }
+
+ gp_XYZ V(
+ part.matrix[3][0], part.matrix[3][1], part.matrix[3][2]
+ );
+
+ if (gp_Pnt(V).Distance(gp::Origin()) > 1.e-9) {
+ is_identity = false;
+ }
+
+ gp_GTrsf trsf;
+
+ if (!is_identity) {
+ trsf = gp_GTrsf(M, V);
+ trsf.SetForm();
+ }
+
+ std::shared_ptr style_ptr;
+ read_surface_style(part.surface_style, style_ptr);
+
+ shapes.push_back(IfcGeom::IfcRepresentationShapeItem(part.id, trsf, shp, style_ptr));
+ }
+
+ // World coordinates can be applied post-hoc
+ if (settings_.get(IfcGeom::IteratorSettings::USE_WORLD_COORDS) && !(stored_settings & IfcGeom::IteratorSettings::USE_WORLD_COORDS)) {
+ for (IfcGeom::IfcRepresentationShapeItems::iterator it = shapes.begin(); it != shapes.end(); ++it) {
+ it->prepend(trsf);
+ }
+ trsf = gp_Trsf();
+ }
+
+ brep_geometry = boost::shared_ptr(new IfcGeom::Representation::BRep(element_settings, geom_id, shapes));
+
+
+ if (!settings_.get(IfcGeom::IteratorSettings::USE_WORLD_COORDS)) {
+ brep_cache_.insert({ representation_id_str, brep_geometry });
+ }
+ }
+
+ if (rt == READ_TRIANGULATION && !triangulation_geometry) {
+ 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]);
+ }
+
+ triangulation_geometry = boost::shared_ptr(new IfcGeom::Representation::Triangulation(
+ element_settings,
+ geom_id,
+ verts,
+ faces,
+ edges,
+ normals,
+ uvcoords,
+ material_ids,
+ surface_style_ptrs
+ ));
+
+ triangulation_cache_.insert({ representation_id_str, triangulation_geometry });
+ }
+
+ if (rt == READ_BREP) {
+ return new IfcGeom::BRepElement(id, parent_id, name, type, guid, context, trsf, brep_geometry, inst);
+ } else {
+ return new IfcGeom::TriangulationElement(
+ IfcGeom::Element(
+ element_settings,
+ id,
+ parent_id,
+ name,
+ type,
+ guid,
+ context,
+ trsf,
+ inst
+ ),
+ triangulation_geometry
+ );
+ }
+}
+
+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();
+
+ auto element_group = write((const IfcGeom::Element*)o);
+
+ auto it = group_cache_.find(o->geometry().id());
+ if (it != group_cache_.end()) {
+ H5Lcreate_soft(it->second.c_str(), element_group.getLocId(), o->geometry().id().c_str(), H5P_DEFAULT, H5P_DEFAULT);
+ return;
+ }
+
+ H5::Group representation_group = createRepresentationGroup(element_group, o->geometry().id());
+
+ const size_t len = H5Iget_name(representation_group.getId(), NULL, 0);
+ char* name_buffer = new char[len];
+ H5Iget_name(representation_group.getId(), name_buffer, len + 1);
+ group_cache_.insert(it, { o->geometry().id(), name_buffer });
+ delete[] name_buffer;
+
+ 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]);
+ }
+
+ 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);
+
+ H5::DataSpace attrdspace(H5S_SCALAR);
+ H5::Attribute att = brepDataset.createAttribute("settings", H5::PredType::NATIVE_UINT64, attrdspace);
+ uint64_t value = o->geometry().settings().get_raw();
+ att.write(H5::PredType::NATIVE_UINT64, &value);
+}
+
+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 058cc284c8..568ab5be54 100644
--- a/src/serializers/HdfSerializer.h
+++ b/src/serializers/HdfSerializer.h
@@ -91,7 +91,7 @@ private:
void write_style(surface_style_serialization& data, const IfcGeom::SurfaceStyle& s);
public:
- HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings);
+ HdfSerializer(const std::string& hdf_filename, const SerializerSettings& settings, bool read_only=false);
virtual ~HdfSerializer() {}
bool ready();
void writeHeader();
diff --git a/src/serializers/HdfSerializer.h~ b/src/serializers/HdfSerializer.h~
new file mode 100644
index 0000000000..058cc284c8
--- /dev/null
+++ b/src/serializers/HdfSerializer.h~
@@ -0,0 +1,114 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+#ifndef HDFSERIALIZER_H
+#define HDFSERIALIZER_H
+
+#ifdef WITH_HDF5
+
+#include
+#include
+#include
+
+#include "H5Cpp.h"
+
+#include "../ifcgeom_schema_agnostic/GeometrySerializer.h"
+
+#define USE_BINARY
+
+class HdfSerializer : public GeometrySerializer {
+private:
+ const std::string hdf_filename;
+ unsigned int vcount_total;
+ H5::H5File file;
+ 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:
+
+ std::map> brep_cache_;
+ std::map> triangulation_cache_;
+ std::map group_cache_;
+
+ 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 remove(const std::string& guid);
+
+ IfcGeom::Element* read(IfcParse::IfcFile& f, const std::string& guid, const std::string&, read_type rt = READ_BREP);
+
+ void finalize() {}
+ bool isTesselated() const { return false; }
+ void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
+ void setFile(IfcParse::IfcFile*) {}
+};
+
+#endif
+
+#endif