Backspace everything regarding HDF5

This commit is contained in:
Thomas Krijnen
2026-05-08 16:20:26 +02:00
parent bd436765bf
commit 554c7174e3
66 changed files with 50 additions and 1928 deletions
-1
View File
@@ -1 +0,0 @@
This cache folder contains .h5 files. These files cache IFC geometry for performance only. You may safely clear the contents of this cache folder without losing data.
-1
View File
@@ -43,7 +43,6 @@ class IfcExporter:
def export(self):
self.file = tool.Ifc.get()
self.set_header()
IfcStore.update_cache()
self.sync_all_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
if extension == "ifczip":
-75
View File
@@ -18,9 +18,7 @@
from __future__ import annotations
import hashlib
import os
import shutil
import tempfile
import traceback
import uuid
@@ -31,7 +29,6 @@ from typing import Literal, NotRequired, Optional, TypedDict, Union
import bpy
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper
from ifcopenshell.file import UndoSystemError
@@ -72,8 +69,6 @@ class IfcStore:
"""Should be set only using ``tool.Ifc.set``."""
schema: Optional[ifcopenshell.ifcopenshell_wrapper.schema_definition] = None
cache: Optional[ifcopenshell.ifcopenshell_wrapper.HdfSerializer] = None
cache_path: Optional[str] = None
id_map: dict[int, IFC_CONNECTED_TYPE] = {}
guid_map: dict[str, IFC_CONNECTED_TYPE] = {}
edited_objs: set[bpy.types.Object] = set()
@@ -95,8 +90,6 @@ class IfcStore:
IfcStore.path = ""
IfcStore.file = None
IfcStore.schema = None
IfcStore.cache = None
IfcStore.cache_path = None
IfcStore.id_map = {}
IfcStore.guid_map = {}
IfcStore.edited_objs = set()
@@ -130,74 +123,6 @@ class IfcStore:
if IfcStore.path and not os.path.isabs(IfcStore.path):
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
@staticmethod
def generate_cache_path() -> str:
"""Generate cache path based on the active file and it's path."""
assert IfcStore.file
ifc_key = IfcStore.path + IfcStore.file.header.file_name.time_stamp
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
prefs = tool.Blender.get_addon_preferences()
cache_path = os.path.join(prefs.cache_dir, f"{ifc_hash}.h5")
return cache_path
@staticmethod
def get_cache() -> ifcopenshell.geom.serializers.hdf5 | None:
"""Get existing cache for the current file or create a new one.
.h5 cache name reflects IFC filepath and it's current header's timestamp.
"""
if IfcStore.cache is None and IfcStore.path:
cache_path = IfcStore.generate_cache_path()
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
IfcStore.cache_path = cache_path
cache_path = Path(IfcStore.cache_path)
cache_settings = ifcopenshell.geom.settings()
serializer_settings = ifcopenshell.geom.serializer_settings()
cache_preexists = cache_path.exists()
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(
IfcStore.cache_path, cache_settings, serializer_settings
)
if cache_preexists:
print(f"Successfully loaded existing cache: {cache_path.name}.")
else:
print("New cache was created.")
except Exception as e:
if cache_preexists:
print(f"Failed to create a cache from existing file '{cache_path.name}': {str(e)}.")
else:
print(f"Failed to create a cache: {str(e)}.")
# No point to trying again the same operation.
return
os.remove(IfcStore.cache_path)
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(
IfcStore.cache_path, cache_settings, serializer_settings
)
print("New cache was created.")
except Exception as e:
print(f"Failed to create a cache: {str(e)}.")
return
return IfcStore.cache
@staticmethod
def update_cache() -> None:
"""Update cache filename after timestamp was updated."""
if not IfcStore.cache:
return
assert IfcStore.cache_path
new_cache_path = IfcStore.generate_cache_path()
IfcStore.cache = None
try:
shutil.move(IfcStore.cache_path, new_cache_path)
except PermissionError:
try:
shutil.copy2(IfcStore.cache_path, new_cache_path)
except PermissionError:
pass # Well we tried. No cache for you!
IfcStore.get_cache()
@staticmethod
def load_file(path: str) -> None:
if not os.path.isfile(path):
-7
View File
@@ -721,10 +721,6 @@ class IfcImporter:
iterator = ifcopenshell.geom.iterator(
settings, self.file, include=products, geometry_library=self.ifc_import_settings.geometry_library
)
if self.ifc_import_settings.should_cache:
cache = IfcStore.get_cache()
if cache:
iterator.set_cache(cache)
valid_file = iterator.initialize()
if not valid_file:
return results
@@ -1267,7 +1263,6 @@ class IfcImportSettings:
self.should_merge_materials_by_colour = False
self.should_load_geometry = True
self.should_clean_mesh = False
self.should_cache = True
self.deflection_tolerance = 0.05 # Default is 0.001, but I find this to be more practical
self.angular_tolerance = 0.5
self.void_limit = 30
@@ -1295,7 +1290,6 @@ class IfcImportSettings:
context=None, input_file: Optional[str] = None, logger: Optional[logging.Logger] = None
) -> IfcImportSettings:
scene_diff = tool.Blender.get_diff_props()
prefs = tool.Blender.get_addon_preferences()
props = tool.Project.get_project_props()
settings = IfcImportSettings()
settings.input_file = input_file
@@ -1308,7 +1302,6 @@ class IfcImportSettings:
settings.should_merge_materials_by_colour = props.should_merge_materials_by_colour
settings.should_load_geometry = props.should_load_geometry
settings.should_clean_mesh = props.should_clean_mesh
settings.should_cache = prefs.should_always_cache or props.should_cache
settings.deflection_tolerance = props.deflection_tolerance
settings.angular_tolerance = props.angular_tolerance
settings.void_limit = props.void_limit
@@ -37,7 +37,6 @@ classes = (
operator.PrintObjectPlacement,
operator.PrintUnusedElementStats,
operator.ProfileImportIFC,
operator.PurgeHdf5Cache,
operator.PurgeUnusedElementsByClass,
operator.PurgeUnusedObjects,
operator.RestartBlender,
@@ -570,17 +570,6 @@ class SelectExpressFile(bpy.types.Operator, ImportHelper):
return {"FINISHED"}
class PurgeHdf5Cache(bpy.types.Operator):
bl_idname = "bim.purge_hdf5_cache"
bl_label = "Purge HDF5 Cache"
bl_description = "Clean up HDF5 cache files except the ones that currently loaded"
def execute(self, context):
core.purge_hdf5_cache(tool.Debug)
self.report({"INFO"}, "HDF5 cache purged.")
return {"FINISHED"}
class OverrideDisplayType(bpy.types.Operator):
bl_idname = "bim.override_display_type"
bl_label = "Override Display Type"
-3
View File
@@ -64,9 +64,6 @@ class BIM_PT_debug(Panel):
row = layout.row()
row.operator("bim.copy_debug_information")
row = layout.row()
row.operator("bim.purge_hdf5_cache")
row = layout.row()
row.operator("bim.update_representation", text="Manually Save Representation")
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import hashlib
import json
import logging
import multiprocessing
@@ -901,10 +900,7 @@ class CreateDrawing(bpy.types.Operator):
# All very hackish whilst prototyping
exporter = bonsai.bim.export_ifc.IfcExporter(None)
exporter.file = tool.Ifc.get()
invalidated_elements = exporter.sync_all_objects()
invalidated_guids = [e.GlobalId for e in invalidated_elements if hasattr(e, "GlobalId")]
if cache := IfcStore.get_cache():
[cache.remove(guid) for guid in invalidated_guids]
exporter.sync_all_objects()
# If we have already calculated it in the SVG in the past, don't recalculate
edited_guids = set()
@@ -922,7 +918,6 @@ class CreateDrawing(bpy.types.Operator):
cached_linework -= edited_guids
bim_props = tool.Blender.get_bim_props()
prefs = tool.Blender.get_addon_preferences()
files = {bim_props.ifc_file: tool.Ifc.get()}
props = tool.Project.get_project_props()
@@ -935,12 +930,8 @@ class CreateDrawing(bpy.types.Operator):
tree = ifcopenshell.geom.tree()
tree.enable_face_styles(True)
for ifc_path, ifc in files.items():
for ifc in files.values():
# Don't use draw.main() just whilst we're prototyping and experimenting
# TODO: hash paths are never used
ifc_hash = hashlib.md5(ifc_path.encode("utf-8")).hexdigest()
ifc_cache_path = os.path.join(prefs.cache_dir, f"{ifc_hash}.h5")
self.serialiser.setFile(ifc)
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element, ifc_file=ifc)
@@ -576,9 +576,6 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
self.report({"ERROR"}, f"Object '{obj.name}' has openings - representation cannot be updated.")
return
if not product.is_a("IfcGridAxis"):
tool.Geometry.clear_cache(product)
if product.is_a("IfcGridAxis"):
# Grid geometry does not follow the "representation" paradigm and needs to be treated specially
tool.Model.create_axis_curve(obj, product)
@@ -1488,7 +1488,6 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
def link_ifc(self) -> Union[set[str], None]:
blend_filepath = self.filepath_.with_suffix(".ifc.cache.blend")
h5_filepath = self.filepath_.with_suffix(".ifc.cache.h5")
json_filepath = self.filepath_.with_suffix(".ifc.cache.json")
def should_clear_cache() -> bool:
@@ -345,14 +345,6 @@ class BIMProjectProperties(PropertyGroup):
),
default=False,
)
should_cache: BoolProperty(
name="Cache",
description=(
"Cache loaded geometry to .h5 file in your cache directory (see in preferences) "
"for faster imports and geometry reloads."
),
default=False,
)
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.05)
angular_tolerance: FloatProperty(name="Angular Tolerance", default=0.5)
void_limit: IntProperty(
@@ -521,7 +513,6 @@ class BIMProjectProperties(PropertyGroup):
should_merge_materials_by_colour: bool
should_load_geometry: bool
should_clean_mesh: bool
should_cache: bool
deflection_tolerance: float
angular_tolerance: float
void_limit: int
@@ -207,8 +207,6 @@ class BIM_PT_project(Panel):
row = self.layout.row()
row.prop(pprops, "should_clean_mesh")
row = self.layout.row()
row.prop(pprops, "should_cache")
row = self.layout.row()
row.prop(pprops, "should_load_geometry")
row = self.layout.row()
row.prop(pprops, "should_merge_materials_by_colour")
@@ -207,7 +207,6 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator):
representation=representation,
)
tool.Geometry.unlock_scale_object_with_openings(obj)
tool.Geometry.clear_cache(element)
return {"FINISHED"}
-6
View File
@@ -665,10 +665,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
name="Disable Undo When Saving (Faster saves, no undo for you!)", default=False
)
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
should_always_cache: BoolProperty(
name="Always Cache Geometry",
description="Whether to always cache geometry regardless of 'Cache' setting during Advanced Project Load.",
)
occurrence_name_style: bpy.props.EnumProperty(
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
name="Occurrence Name Style",
@@ -777,7 +773,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
bsdd_baseurl: str
should_disable_undo_on_save: bool
should_stream: bool
should_always_cache: bool
occurrence_name_style: Literal["CLASS", "TYPE", "CUSTOM"]
occurrence_name_function: str
gizmos: GizmoPreferences
@@ -976,7 +971,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
layout.prop(self, "opening_focus_opacity")
layout.prop(self, "should_disable_undo_on_save")
layout.prop(self, "should_stream")
layout.prop(self, "should_always_cache")
layout.label(text="bSDD:")
layout.prop(self, "bsdd_load_preview_dictionaries")
layout.prop(self, "bsdd_load_inactive_dictionaries")
-4
View File
@@ -30,10 +30,6 @@ def parse_express(debug: type[tool.Debug], filename: str) -> None:
debug.add_schema_identifier(debug.load_express(filename))
def purge_hdf5_cache(debug: type[tool.Debug]) -> None:
debug.purge_hdf5_cache()
def purge_unused_elements(ifc: type[tool.Ifc], debug: type[tool.Debug], ifc_class: str) -> int:
ifc_file = ifc.get()
unused_elements = [i for i in ifc_file.by_type(ifc_class) if ifc_file.get_total_inverses(i) == 0]
-2
View File
@@ -44,7 +44,6 @@ def edit_object_placement(
element = ifc.get_entity(obj)
if not element:
return
geometry.clear_cache(element)
if apply_scale:
geometry.clear_scale(obj)
geometry.get_blender_offset_type(obj)
@@ -125,7 +124,6 @@ def switch_representation(
element = ifc.get_entity(obj)
assert element
geometry.clear_cache(element)
geometry.reimport_element_representations(obj, representation, apply_openings=apply_openings)
-2
View File
@@ -283,7 +283,6 @@ class Cost:
class Debug:
def add_schema_identifier(cls, schema): pass
def load_express(cls, filename): pass
def purge_hdf5_cache(cls): pass
@interface
@@ -422,7 +421,6 @@ class Feature:
@interface
class Geometry:
def change_object_data(cls, obj, data, is_global=False): pass
def clear_cache(cls, element): pass
def clear_modifiers(cls, obj): pass
def clear_scale(cls, obj): pass
def copy_data_links(cls, data, copied_entities) -> None: pass
-11
View File
@@ -59,17 +59,6 @@ class Debug(bonsai.core.tool.Debug):
ifcopenshell.register_schema(schema)
return schema.schema
@classmethod
def purge_hdf5_cache(cls) -> None:
prefs = tool.Blender.get_addon_preferences()
cache_dir = prefs.cache_dir
filelist = [f for f in os.listdir(cache_dir) if f.endswith(".h5")]
for f in filelist:
try:
os.remove(os.path.join(cache_dir, f))
except PermissionError:
pass
@classmethod
def debug_bmesh(cls, bm: bmesh.types.BMesh, name: str = "Debug") -> bpy.types.Object:
mesh = bpy.data.meshes.new("Debug")
-12
View File
@@ -73,7 +73,6 @@ import bonsai.core.style
import bonsai.core.system
import bonsai.core.tool
import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
if TYPE_CHECKING:
from bonsai.bim.module.geometry.prop import (
@@ -109,16 +108,6 @@ class Geometry(bonsai.core.tool.Geometry):
raise Exception("user_remap is not supported for meshes in EDIT mode")
old_data.user_remap(new_data)
@classmethod
def get_cache(cls) -> Union[ifcopenshell.geom.serializers.hdf5, None]:
return IfcStore.get_cache()
@classmethod
def clear_cache(cls, element: ifcopenshell.entity_instance) -> None:
cache = IfcStore.get_cache()
if cache and hasattr(element, "GlobalId"):
cache.remove(element.GlobalId)
@classmethod
def clear_modifiers(cls, obj: bpy.types.Object) -> None:
for modifier in obj.modifiers:
@@ -818,7 +807,6 @@ class Geometry(bonsai.core.tool.Geometry):
if not cls.has_data_users(old_data):
cls.delete_data(old_data)
cls.clear_modifiers(obj)
cls.clear_cache(element)
# Import swept disk solids as Blender curves if possible.
elements_without_openings = {e for e in elements if not getattr(e, "HasOpenings", False)}
-3
View File
@@ -22,9 +22,6 @@ props = tool.Georeference.get_georeference_props()
props = tool.Project.get_project_props()
# Generally recommended to disable caching for stability right now
props.should_cache = False
# If you are not authoring, it is recommended to enable this.
# When enabled, types, openings, and non geometric elements are not loaded.
props.is_coordinating = True
@@ -9,7 +9,6 @@ class BlenderImporter:
def __init__(self):
self.file = ifcopenshell.open("/home/dion/untitled.ifc")
self.cache_path = "cache.h5"
self.should_use_cpu_multiprocessing = True
self.deflection_tolerance = 0.001
self.angular_tolerance = 0.5
@@ -82,9 +81,6 @@ class BlenderImporter:
iterator = ifcopenshell.geom.iterator(settings, self.file, multiprocessing.cpu_count(), include=products)
else:
iterator = ifcopenshell.geom.iterator(settings, self.file, include=products)
cache = self.get_cache()
if cache:
iterator.set_cache(cache)
valid_file = iterator.initialize()
if not valid_file:
return results
@@ -113,13 +109,4 @@ class BlenderImporter:
print("Done creating geometry")
return results
def get_cache(self):
cache_settings = ifcopenshell.geom.settings()
serializer_settings = ifcopenshell.geom.serializer_settings()
try:
return ifcopenshell.geom.serializers.hdf5(self.cache_path, cache_settings, serializer_settings)
except:
return
BlenderImporter().execute()
-6
View File
@@ -25,9 +25,3 @@ class TestParseExpress:
debug.load_express("filename").should_be_called().will_return("schema")
debug.add_schema_identifier("schema").should_be_called()
subject.parse_express(debug, "filename")
class TestPurgeHdf5Cache:
def test_run(self, debug):
debug.purge_hdf5_cache().should_be_called()
subject.purge_hdf5_cache(debug)
-2
View File
@@ -24,7 +24,6 @@ from test.core.bootstrap import geometry, ifc, style, surveyor
class TestEditObjectPlacement:
def predict(self, ifc, geometry, surveyor):
ifc.get_entity("obj").should_be_called().will_return("element")
geometry.clear_cache("element").should_be_called()
geometry.clear_scale("obj").should_be_called()
geometry.get_blender_offset_type("obj").should_be_called()
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
@@ -197,7 +196,6 @@ class TestSwitchRepresentation:
def test_switching_to_a_representation(self, ifc, geometry):
geometry.get_object_data("obj").should_be_called().will_return("current_obj_data")
ifc.get_entity("obj").should_be_called().will_return("element")
geometry.clear_cache("element").should_be_called()
geometry.reimport_element_representations("obj", "mapped_rep", apply_openings=True).should_be_called()
subject.switch_representation(
ifc,
-20
View File
@@ -17,8 +17,6 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
from pathlib import Path
import bpy
import ifcopenshell
import ifcopenshell.api.style
@@ -55,24 +53,6 @@ class TestLoadExpress(NewFile):
os.remove(schema_path + ".cache.dat")
class TestPurgeHdf5Cache(NewFile):
def test_run(self):
prefs = tool.Blender.get_addon_preferences()
cache_dir = Path(prefs.cache_dir)
test_file = cache_dir / "test.h5"
test_file.parent.mkdir(parents=True, exist_ok=True)
test_file.touch()
# Ensure it can skip currently loaded cache.
loaded_file_path = test_file.with_stem("test_loaded")
loaded_file = open(loaded_file_path, "w")
subject.purge_hdf5_cache()
# On Unix loaded files are not locked.
paths = [loaded_file_path] if os.name == "nt" else []
assert [f for f in cache_dir.iterdir() if f.suffix == ".h5"] == paths
class TestMergeIdenticalObject(NewFile):
def test_merge_identical_styles(self):
tool.Ifc.set(ifc := ifcopenshell.file())
-52
View File
@@ -279,16 +279,10 @@ 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;
std::string geometry_kernel;
auto& document_serializer_registry = ifcopenshell::serializers::document_serializer_registry_instance();
auto& geometry_serializer_registry = ifcopenshell::serializers::geometry_serializer_registry_instance();
#ifdef WITH_HDF5
const bool supports_geometry_cache = true;
#else
const bool supports_geometry_cache = false;
#endif
po::options_description generic_options("Command line options");
verbosity_counter vcounter;
@@ -302,10 +296,6 @@ int main(int argc, char** argv) {
("no-progress", "suppress possible progress bar type of prints that use carriage return")
("log-format", po::value<std::string>(&log_format), "log format: plain or json")
("log-file", new po::typed_value<path_t, char_t>(&log_file), "redirect log output to file");
if (supports_geometry_cache) {
generic_options.add_options()
("cache", "cache geometry creation. Use --cache-file to specify cache file path.");
}
po::options_description fileio_options;
fileio_options.add_options()
@@ -316,10 +306,6 @@ int main(int argc, char** argv) {
("output-file", new po::typed_value<path_t, char_t>(0), "output geometry file")
("stream", "Use streaming conversion when supported (RocksDB uses it automatically)")
;
if (supports_geometry_cache) {
fileio_options.add_options()
("cache-file", new po::typed_value<path_t, char_t>(&cache_file), "geometry cache file");
}
po::options_description ifc_options("IFC options");
ifc_options.add_options()
@@ -907,44 +893,6 @@ int main(int argc, char** argv) {
context_iterator.reset(new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(ifc_file, geometry_kernel, geometry_settings), geometry_settings, ifc_file, filter_funcs, num_threads));
}
#ifdef WITH_HDF5
boost::shared_ptr<GeometrySerializer> cache;
const bool use_cache = supports_geometry_cache && context_iterator && (vmap.count("cache-file") || vmap.count("cache"));
if (use_cache) {
const path_t CACHE = ifcopenshell::path::from_utf8(".cache");
const path_t HDF = ifcopenshell::path::from_utf8(".h5");
if (!vmap.count("cache-file")) {
cache_file = input_filename + CACHE + HDF;
}
try {
ifcopenshell::serializers::geometry_serializer_context cache_context{
ifcopenshell::path::to_utf8(cache_file),
ifcopenshell::path::to_utf8(cache_file),
geometry_settings,
serializer_settings
};
cache = geometry_serializer_registry.create(".h5", cache_context);
} catch (const std::exception& e) {
cerr_ << "[error] Unable to initialize geometry cache: " << e.what() << std::endl;
serializer.reset();
ifcopenshell::path::delete_file(ifcopenshell::path::to_utf8(output_temp_filename));
write_log(!quiet);
return EXIT_FAILURE;
}
if (!cache || !cache->ready()) {
cerr_ << "[error] Unable to initialize geometry cache serializer\n";
serializer.reset();
ifcopenshell::path::delete_file(ifcopenshell::path::to_utf8(output_temp_filename));
write_log(!quiet);
return EXIT_FAILURE;
}
context_iterator->set_cache(cache.get());
}
#endif
logger::message(logger::LOG_PERF, "file geometry conversion");
if (context_iterator && !context_iterator->initialize()) {
-3
View File
@@ -28,9 +28,6 @@ endif()
if(WITH_MANIFOLD)
target_compile_definitions(IfcGeom PUBLIC IFOPSH_WITH_MANIFOLD)
endif()
if(HDF5_SUPPORT)
target_compile_definitions(IfcGeom PUBLIC WITH_HDF5)
endif()
if(UNIX)
find_package(Threads)
+3 -10
View File
@@ -362,7 +362,7 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne
logger::set_product(product);
IfcGeom::BRepElement* brep = static_cast<IfcGeom::BRepElement*>(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product.as<express::Entity>().get("GlobalId"), std::to_string(rep->item->instance.id()), [kernel, settings, product, place, rep]() {
IfcGeom::BRepElement* brep = static_cast<IfcGeom::BRepElement*>(create_processed_element_([kernel, settings, product, place, rep]() {
return kernel->create_brep_for_representation_and_product(rep->item, product, place);
}));
@@ -385,7 +385,7 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne
const express::Base product2 = p.first;
const auto& place2 = p.second;
IfcGeom::BRepElement* brep2 = static_cast<IfcGeom::BRepElement*>(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product2.as<express::Entity>().get("GlobalId"), std::to_string(rep->item->instance.id()), [kernel, settings, product2, place2, brep]() {
IfcGeom::BRepElement* brep2 = static_cast<IfcGeom::BRepElement*>(create_processed_element_([kernel, settings, product2, place2, brep]() {
return kernel->create_brep_for_processed_representation(product2, place2, brep);
}));
if (brep2) {
@@ -410,14 +410,7 @@ IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geo
return nullptr;
}
} else if (settings.get<ifcopenshell::geometry::settings::IteratorOutput>().get() == ifcopenshell::geometry::settings::TRIANGULATED) {
// the part before the hyphen is the representation id
auto gid2 = elem->geometry().id();
auto hyphen = gid2.find("-");
if (hyphen != std::string::npos) {
gid2 = gid2.substr(0, hyphen);
}
return decorate_with_cache_(GeometrySerializer::READ_TRIANGULATION, elem->guid(), gid2, [elem, previous]() {
return create_processed_element_([elem, previous]() {
try {
if (!previous) {
return new TriangulationElement(*elem);
+2 -40
View File
@@ -66,7 +66,6 @@
#include "../ifcgeom/taxonomy.h"
#include "../ifcgeom/Converter.h"
#include "../ifcgeom/abstract_mapping.h"
#include "../ifcgeom/GeometrySerializer.h"
#include <boost/algorithm/string.hpp>
@@ -104,8 +103,6 @@ namespace IfcGeom {
class IFC_GEOM_API Iterator {
private:
GeometrySerializer* cache_ = nullptr;
std::atomic<bool> finished_{ false };
std::atomic<bool> terminating_{ false };
std::atomic<bool> had_error_processing_elements_ { false };
@@ -153,44 +150,11 @@ namespace IfcGeom {
// Should not be destructed because, destructor is blocking
std::future<void> init_future_;
std::mutex caching_mutex_;
std::array<std::chrono::high_resolution_clock::time_point, 4> time_points;
template <typename Fn>
Element* decorate_with_cache_(GeometrySerializer::read_type rt, const std::string& product_guid, const std::string& representation_id, Fn f) {
bool read_from_cache = false;
Element* element = nullptr;
#ifdef WITH_HDF5
if (cache_) {
std::lock_guard<std::mutex> lk(caching_mutex_);
auto from_cache = cache_->read(*ifc_file, product_guid, representation_id, rt);
if (from_cache) {
read_from_cache = true;
element = from_cache;
}
}
#endif
if (!read_from_cache) {
element = f();
}
#ifdef WITH_HDF5
if (cache_ && !read_from_cache && element) {
std::lock_guard<std::mutex> lk(caching_mutex_);
if (rt == GeometrySerializer::READ_TRIANGULATION) {
cache_->write((IfcGeom::TriangulationElement*)element);
} else {
cache_->write((IfcGeom::BRepElement*)element);
}
}
#endif
return element;
Element* create_processed_element_(Fn f) {
return f();
}
express::Base create_shape_model_for_next_entity();
@@ -244,8 +208,6 @@ namespace IfcGeom {
~Iterator();
void set_cache(GeometrySerializer* cache) { cache_ = cache; }
std::vector<ifcopenshell::geometry::taxonomy::item::ptr> get_task_items() const {
std::vector<ifcopenshell::geometry::taxonomy::item::ptr> items;
items.reserve(tasks_.size());
@@ -64,11 +64,6 @@
#include <IntTools_FaceFace.hxx>
#include "clash_utils.h"
#ifdef WITH_HDF5
#include "H5Cpp.h"
#endif
namespace IfcGeom {
namespace {
@@ -1498,225 +1493,6 @@ namespace IfcGeom {
}
}
#ifdef WITH_HDF5
void write_h5() {
H5::H5File file("filename.h5", H5F_ACC_TRUNC);
H5::Group shapes = file.createGroup("/shapes");
std::set<std::string> processed_geometry_ids;
std::vector<int> element_shape_ids;
std::unordered_map<std::string, int> geometry_id_to_shape_id;
int geometry_index = 0;
std::vector<std::vector<float>> matrices;
std::vector<std::array<float, 4>> colours;
std::vector<std::string> names;
std::vector<std::string> global_ids;
const float tolerance = 0.01f; // Tolerance value for comparison
for (const auto& elem : triangulation_elements_) {
const auto geometry_id = elem->geometry().id();
const auto& placement = placements_[elem->product()];
{
decltype(matrices)::value_type m;
for (size_t i = 0; i < 16; ++i) {
m.push_back(static_cast<float>(placement->ccomponents().data()[i]));
}
matrices.push_back(m);
}
names.push_back(names_[elem->product()]);
global_ids.push_back(global_ids_[elem->product()]);
if (processed_geometry_ids.find(geometry_id) != processed_geometry_ids.end()) {
element_shape_ids.push_back(geometry_id_to_shape_id[geometry_id]);
continue;
}
processed_geometry_ids.insert(geometry_id);
H5::Group group = shapes.createGroup(std::to_string(geometry_index));
geometry_id_to_shape_id[geometry_id] = geometry_index;
element_shape_ids.push_back(geometry_index);
geometry_index++;
const auto& faces = local_faces_[geometry_id];
const auto& verts = local_verts_[geometry_id];
const auto& materials = local_materials_[geometry_id];
const auto& material_ids = local_material_ids_[geometry_id];
std::vector<float> verts_float(verts.size());
std::transform(verts.begin(), verts.end(), verts_float.begin(),
[](double val) { return static_cast<float>(val); });
// Write faces
size_t total_verts = verts.size() / 3;
hsize_t faces_dims[1] = {faces.size()};
H5::DataSpace faces_dataspace(1, faces_dims);
H5::DSetCreatPropList faces_propList;
faces_propList.setChunk(1, faces_dims);
faces_propList.setDeflate(9);
if (total_verts < (1 << 8)) {
H5::DataType dtype = H5::PredType::NATIVE_UINT8;
std::vector<uint8_t> faces_dtype(faces.begin(), faces.end());
H5::DataSet faces_dataset = group.createDataSet("faces", dtype, faces_dataspace, faces_propList);
faces_dataset.write(faces_dtype.data(), dtype);
} else if (total_verts < (1 << 16)) {
H5::DataType dtype = H5::PredType::NATIVE_UINT16;
std::vector<uint16_t> faces_dtype(faces.begin(), faces.end());
H5::DataSet faces_dataset = group.createDataSet("faces", dtype, faces_dataspace, faces_propList);
faces_dataset.write(faces_dtype.data(), dtype);
} else {
H5::DataType dtype = H5::PredType::NATIVE_UINT32;
H5::DataSet faces_dataset = group.createDataSet("faces", dtype, faces_dataspace, faces_propList);
faces_dataset.write(faces.data(), dtype);
}
// Write verts
H5::DataType dtype = H5::PredType::NATIVE_FLOAT;
hsize_t dims[1] = {verts.size()};
H5::DataSpace dataspace(1, dims);
H5::DSetCreatPropList propList;
propList.setChunk(1, dims);
propList.setDeflate(9);
H5::DataSet dataset = group.createDataSet("verts", dtype, dataspace, propList);
dataset.write(verts_float.data(), H5::PredType::NATIVE_FLOAT);
// Write materials
std::vector<uint8_t> material_keys;
for (const auto& material : materials) {
float alpha = 1.0;
if (material->has_transparency() && material->transparency > 0) {
alpha = 1.0 - material->transparency;
}
int i = 0;
bool is_existing_colour = false;
for (const auto& colour : colours) {
if (std::abs(colour[0] - static_cast<float>(material->diffuse.ccomponents()[0])) < tolerance
&& std::abs(colour[1] - static_cast<float>(material->diffuse.ccomponents()[1])) < tolerance
&& std::abs(colour[2] - static_cast<float>(material->diffuse.ccomponents()[2])) < tolerance
&& std::abs(colour[3] - alpha) < tolerance) {
is_existing_colour = true;
break;
}
i++;
}
if ( ! is_existing_colour) {
colours.push_back({
static_cast<float>(material->diffuse.ccomponents()[0]),
static_cast<float>(material->diffuse.ccomponents()[1]),
static_cast<float>(material->diffuse.ccomponents()[2]),
alpha});
}
material_keys.push_back(static_cast<decltype(material_keys)::value_type>(i));
}
size_t total_material_keys = material_keys.size();
if (total_material_keys) {
hsize_t dims[1] = {material_keys.size()};
H5::DataSpace dataspace(1, dims);
H5::DSetCreatPropList propList;
propList.setChunk(1, dims);
propList.setDeflate(9);
H5::DataType dtype = H5::PredType::NATIVE_UINT8;
H5::DataSet dataset = group.createDataSet("materials", dtype, dataspace, propList);
dataset.write(material_keys.data(), dtype);
}
if (total_material_keys > 1) {
hsize_t dims[1] = {material_ids.size()};
H5::DataSpace dataspace(1, dims);
H5::DSetCreatPropList propList;
propList.setChunk(1, dims);
propList.setDeflate(9);
H5::DataType dtype = H5::PredType::NATIVE_UINT8;
H5::DataSet dataset = group.createDataSet("material_ids", dtype, dataspace, propList);
std::vector<uint8_t> data_dtype(material_ids.begin(), material_ids.end());
dataset.write(data_dtype.data(), dtype);
}
}
// Write GlobalIds
std::vector<uint8_t> uuids_array;
for (const auto& id_str : global_ids) {
for (size_t i = 0; i < id_str.length(); i += 2) {
uuids_array.push_back(std::stoi(id_str.substr(i, 2), 0, 16));
}
}
hsize_t global_ids_dims[2] = {global_ids.size(), 16}; // 16 bytes per UUID
H5::DataSpace global_ids_dataspace(2, global_ids_dims);
H5::DataSet global_ids_dataset = file.createDataSet("element_global_ids", H5::PredType::NATIVE_UINT8, global_ids_dataspace);
global_ids_dataset.write(uuids_array.data(), H5::PredType::NATIVE_UINT8);
// Write names
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
hsize_t names_dims[1] = {names.size()};
H5::DataSpace names_dataspace(1, names_dims);
H5::DataSet names_dataset = file.createDataSet("element_names", strType, names_dataspace);
std::vector<const char*> cstr_names;
for (const auto& name : names) {
cstr_names.push_back(name.c_str());
}
names_dataset.write(&cstr_names[0], strType);
// Write matrices
std::vector<float> flat_matrices;
for (const auto& matrix : matrices) {
flat_matrices.insert(flat_matrices.end(), matrix.begin(), matrix.end());
}
hsize_t dims[2] = {matrices.size(), matrices[0].size()};
H5::DataSpace dataspace(2, dims);
H5::DSetCreatPropList propList;
propList.setChunk(2, dims);
propList.setDeflate(9);
H5::DataSet dataset = file.createDataSet("element_matrices", H5::PredType::NATIVE_FLOAT, dataspace, propList);
dataset.write(flat_matrices.data(), H5::PredType::NATIVE_FLOAT);
// Write element_shape_ids
hsize_t element_shape_ids_dims[1] = {element_shape_ids.size()};
H5::DataSpace element_shape_ids_dataspace(1, element_shape_ids_dims);
H5::DSetCreatPropList element_shape_ids_propList;
element_shape_ids_propList.setChunk(1, element_shape_ids_dims);
element_shape_ids_propList.setDeflate(9);
if (geometry_index < (1 << 8)) {
H5::DataType dtype = H5::PredType::NATIVE_UINT8;
std::vector<uint8_t> element_shape_ids_dtype(element_shape_ids.begin(), element_shape_ids.end());
H5::DataSet element_shape_ids_dataset = file.createDataSet("element_shape_ids", dtype, element_shape_ids_dataspace, element_shape_ids_propList);
element_shape_ids_dataset.write(element_shape_ids_dtype.data(), dtype);
} else if (geometry_index < (1 << 16)) {
H5::DataType dtype = H5::PredType::NATIVE_UINT16;
std::vector<uint16_t> element_shape_ids_dtype(element_shape_ids.begin(), element_shape_ids.end());
H5::DataSet element_shape_ids_dataset = file.createDataSet("element_shape_ids", dtype, element_shape_ids_dataspace, element_shape_ids_propList);
element_shape_ids_dataset.write(element_shape_ids_dtype.data(), dtype);
} else if (geometry_index < (1UL << 32)) {
H5::DataType dtype = H5::PredType::NATIVE_UINT32;
std::vector<uint32_t> element_shape_ids_dtype(element_shape_ids.begin(), element_shape_ids.end());
H5::DataSet element_shape_ids_dataset = file.createDataSet("element_shape_ids", dtype, element_shape_ids_dataspace, element_shape_ids_propList);
element_shape_ids_dataset.write(element_shape_ids_dtype.data(), dtype);
}
// Write colours
if (colours.size()) {
std::vector<float> flat_colours;
for (const auto& colour : colours) {
flat_colours.insert(flat_colours.end(), colour.begin(), colour.end());
}
hsize_t colours_dims[2] = {colours.size(), colours[0].size()};
H5::DataSpace colours_dataspace(2, colours_dims);
H5::DSetCreatPropList colours_propList;
colours_propList.setChunk(2, colours_dims);
colours_propList.setDeflate(9);
H5::DataSet colours_dataset = file.createDataSet("materials", H5::PredType::NATIVE_FLOAT, colours_dataspace, colours_propList);
colours_dataset.write(flat_colours.data(), H5::PredType::NATIVE_FLOAT);
}
}
#endif
template <typename T>
void apply_matrix_to_flat_verts(const std::vector<T>& flat_list, const ifcopenshell::geometry::taxonomy::matrix4::ptr& matrix, std::vector<T>& result) {
Eigen::Vector3d vin;
@@ -164,12 +164,6 @@ namespace ifcopenshell {
return tree_.clash_clearance_many(set_a, set_b, clearance, check_all);
}
#ifdef WITH_HDF5
void write_h5() override {
tree_.write_h5();
}
#endif
private:
IfcGeom::opencascade_tree tree_;
};
-6
View File
@@ -227,12 +227,6 @@ const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& IfcGeom::tree::
return backend ? backend->styles() : empty_style_vector();
}
#ifdef WITH_HDF5
void IfcGeom::tree::write_h5() {
impl_->backend(default_clash_backend_id).write_h5();
}
#endif
std::string IfcGeom::tree::uint8_to_b64(const std::vector<uint8_t>& uuids_array) const {
std::string hex_str;
hex_str.reserve(uuids_array.size() * 2);
-4
View File
@@ -100,10 +100,6 @@ namespace IfcGeom {
void enable_face_styles(bool enable);
const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& styles() const;
#ifdef WITH_HDF5
void write_h5();
#endif
std::string uint8_to_b64(const std::vector<uint8_t>& uuids_array) const;
static bool is_manifold(const std::vector<int>& faces);
-6
View File
@@ -104,12 +104,6 @@ const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& ifcopenshell::g
unsupported_tree_operation(std::string(backend_id()), "styles()");
}
#ifdef WITH_HDF5
void ifcopenshell::geometry::trees::abstract_tree::write_h5() {
unsupported_tree_operation(std::string(backend_id()), "write_h5()");
}
#endif
void ifcopenshell::geometry::trees::tree_registry::bind(const tree_info& info, create_fn create, const plugin::module& module) {
entry entry;
entry.info_ = info;
-3
View File
@@ -70,9 +70,6 @@ namespace ifcopenshell {
virtual void enable_face_styles(bool enable);
virtual const std::vector<ifcopenshell::geometry::taxonomy::style::ptr>& styles() const;
#ifdef WITH_HDF5
virtual void write_h5();
#endif
};
class IFC_GEOM_API tree_registry {
+3 -3
View File
@@ -2,7 +2,7 @@ IfcConvert
==========
IfcConvert is a command-line application for converting IFC geometry into file
formats such as OBJ, DAE, GLB, STP, IGS, XML, SVG, H5, TTL/WKT, and IFC-SPF itself.
formats such as OBJ, DAE, GLB, STP, IGS, XML, SVG, TTL/WKT, and IFC-SPF itself.
For other formats, you may use other IfcOpenShell utilities as shown in the
table below.
@@ -11,8 +11,8 @@ table below.
| From Format | To Format | Tool |
+=========================+=========================+======================+
| .ifc | .obj, .dae, .glb, .stp, | IfcConvert |
| | .igs, .xml, .svg, .h5, | |
| | .ttl, .ifc, .rdb, | |
| | .igs, .xml, .svg, .ttl, | |
| | .ifc, .rdb, | |
| | .json (xeokit) | |
+-------------------------+-------------------------+----------------------+
| .ifc | .dae, .abc, .usd, .obj, | Bonsai_ |
@@ -83,7 +83,6 @@ CLI Manual
.json JSON Property definitions and decomposition tree in xeokit json format
.rdb RocksDB RocksDB Key-Value store serialization of IFC data
.svg SVG Scalable Vector Graphics (2D floor plan)
.h5 HDF Hierarchical Data Format storing positions, normals and indices
.ttl TTL/WKT RDF Turtle with Well-Known-Text geometry
.ifc IFC-SPF Industry Foundation Classes
@@ -29,8 +29,6 @@ IfcOpenShell depends on:
- (Optional) `SWIG <http://www.swig.org/>`__ and `Python
<https://www.python.org/>`__ - for building the IfcOpenShell Python interface
and use in Bonsai
- (Optional) `HDF5 <https://www.hdfgroup.org/solutions/hdf5>`__ - for caching
geometry using the HDF5 format
Compiling on Linux
------------------
@@ -125,13 +123,7 @@ operating systems. GCC (4.7 or newer) or Clang (any version) is required.
sudo apt-get install python-all-dev swig
6. For building support for HDF5 caching (ON by default), install dependencies:
.. code-block:: bash
sudo apt-get install libhdf5-dev libaec-dev zlibc
7. Compile IfcOpenShell itself.
6. Compile IfcOpenShell itself.
.. code-block:: bash
@@ -149,11 +141,6 @@ operating systems. GCC (4.7 or newer) or Clang (any version) is required.
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
\
# Optional HDF5 support
-DHDF5_SUPPORT=On \
-DHDF5_LIBRARIES="/usr/local/hdf5/lib/libhdf5_cpp.so;/usr/local/hdf5/lib/libhdf5.so;/usr/lib64/libz.so;/usr/lib64/libsz.so;/usr/lib64/libaec.so" \
-DHDF5_INCLUDE_DIR="/usr/local/hdf5/include" \
\
-DCGAL_INCLUDE_DIR=/usr/include \
-DGMP_INCLUDE_DIR=/usr/include \
-DMPFR_INCLUDE_DIR=/usr/include \
@@ -187,7 +174,7 @@ GCC (4.7 or newer) or Clang (any version) is required.
.. code-block:: bash
brew install boost cmake python3 cgal ftgl gmp libaec opencascade swig hdf5 zlib eigen
brew install boost cmake python3 cgal ftgl gmp opencascade swig zlib eigen
# homebrew automatically links most libraries, except some keg-only ones
brew link zlib --force
@@ -209,8 +196,6 @@ GCC (4.7 or newer) or Clang (any version) is required.
-DCGAL_INCLUDE_DIR=/opt/homebrew/include/ \
-DGMP_LIBRARY_DIR=/opt/homebrew/lib/ \
-DMPFR_LIBRARY_DIR=/opt/homebrew/lib/ \
-DHDF5_LIBRARY_DIR=/opt/homebrew/lib/ \
-DHDF5_INCLUDE_DIR=/opt/homebrew/include/ \
-DEIGEN_DIR=/opt/homebrew/Cellar/eigen/3.4.0_1/include/eigen3 \
-DCOLLADA_SUPPORT=0
# `sysctl -n hw.ncpu` returns the number of cpu cores on macOS
@@ -38,7 +38,7 @@ IfcOpenShell has a huge amount of unique features and capabilities not found in
- All tools can be used either as a developer library, through a command line interface, or using a rich graphical interface. Whether you're deploying headless server tools for your own pipeline, writing your own apps, or an end-user, there's something for you.
- Supports IFC2X3, IFC4, and IFC4.3. Custom schemas (such as experimental or draft schemas) may be loaded at run-time instead of having to recompile.
- Built-in IFC validation is possible from basic syntax validation to more detailed "Where Rule" checks. This is the same validation that powers the official buildingSMART validation engine.
- Read and write IFC-SPF, IFCJSON, IFCXML, IFCHDF5, MySQL, and SQLite.
- Read and write IFC-SPF, IFCJSON, IFCXML, MySQL, and SQLite.
- High level API for hundreds of tasks. Perform complex authoring like copying objects, cost calculation, or 4D simulation with one line of code. Imagine a complete native IFC authoring and editing platform where every function is available to you as a library.
- Convert parametric geometry into explicit geometry for any CAD system from booleans to complex sweeps. Geometry has been battle-tested over many years to accommodate complex geometric edge cases with an extensive test suite.
- Geometry may be converted into voxels and analysed through voxels to resolve complex non-manifold geometry and precision issues. This analysis may be used from things like head height calculations, formwork analysis, to egress distances.
@@ -56,7 +56,6 @@ class draw_settings:
space_areas: bool = False
door_arcs: bool = False
subtract_before_hlr: bool = False
cache: bool = False
css: str = ""
storey_heights: str = "none"
storey_filter: str = ""
@@ -157,12 +156,6 @@ def main(
)
)
if settings.cache:
serializer_settings = ifcopenshell.geom.serializer_settings()
cache = ifcopenshell.geom.serializers.hdf5("cache.h5", geom_settings, serializer_settings)
for it in iterators:
it.set_cache(cache)
# Initialize serializer
buffer = ifcopenshell.geom.serializers.buffer()
serialiser_settings = ifcopenshell.geom.serializer_settings()
@@ -555,8 +555,6 @@ def iterate(
exclude: Optional[Union[list[entity_instance], list[str]]] = None,
*,
with_progress: Literal[False] = False,
cache: Optional[str] = None,
serializer_settings: Optional[serializer_settings] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade",
) -> Generator[IteratorOutput, None, None]: ...
@overload
@@ -568,8 +566,6 @@ def iterate(
exclude: Optional[Union[list[entity_instance], list[str]]] = None,
*,
with_progress: Literal[True] = True,
cache: Optional[str] = None,
serializer_settings: Optional[serializer_settings] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade",
) -> Generator[tuple[int, IteratorOutput], None, None]: ...
@overload
@@ -581,8 +577,6 @@ def iterate(
exclude: Optional[Union[list[entity_instance], list[str]]] = None,
*,
with_progress: bool = False,
cache: Optional[str] = None,
serializer_settings: Optional[serializer_settings] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade",
) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]: ...
def iterate(
@@ -593,20 +587,10 @@ def iterate(
exclude: Optional[Union[list[entity_instance], list[str]]] = None,
*,
with_progress: bool = False,
cache: Optional[str] = None,
serializer_settings: Optional[serializer_settings] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade",
) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]:
"""Get a geometry iterator for the provided file.
:param cache: .h5 cache filepath (might not exist, will be created).
:param serializer_settings: Settings for cache serializer. Required if `cache` is provided.
"""
"""Get a geometry iterator for the provided file."""
it = iterator(settings, file_or_filename, num_threads, include, exclude, geometry_library)
if cache:
assert serializer_settings, "`serializer_settings` argument is not optional if `cache` is provided."
hdf5_cache = serializers.hdf5(cache, settings, serializer_settings)
it.set_cache(hdf5_cache)
yield from consume_iterator(it, with_progress=with_progress)
@@ -677,9 +661,6 @@ class _serializers_meta(type):
"ttl": "ttl",
"gltf": "glb",
"glb": "glb",
"hdf": "h5",
"hdf5": "h5",
"h5": "h5",
"collada": "dae",
"dae": "dae",
"stp": "stp",
@@ -711,9 +692,6 @@ class serializers(metaclass=_serializers_meta):
ext = filepath.rsplit(".", 1)[-1].lower()
mapping = {
"glb": "gltf",
"hdf": "hdf5",
"h5": "hdf5",
"hdf5": "hdf5",
"obj": "obj",
"svg": "svg",
"ttl": "ttl",
@@ -298,18 +298,6 @@ class GltfSerializer(WriteOnlyGeometrySerializer):
def write(self, *args): ...
def writeHeader(self): ...
class HdfSerializer(GeometrySerializer):
def __init__(self, hdf_filename, geometry_settings, settings, read_only=False): ...
def finalize(self): ...
def isTesselated(self): ...
def read(self, *args): ...
def ready(self): ...
def remove(self, guid: str) -> None: ...
def setFile(self, arg2): ...
def setUnitNameAndMagnitude(self, arg2, arg3): ...
def write(self, *args): ...
def writeHeader(self): ...
class IfcBaseEntity(entity_instance):
def __init__(self, *args, **kwargs): ...
def declaration(self): ...
@@ -383,7 +371,6 @@ class Iterator:
"""
...
def set_cache(self, cache: Union[GeometrySerializer, None]) -> None: ...
def unit_magnitude(self): ...
def unit_name(self): ...
+1 -2
View File
@@ -150,8 +150,7 @@ cmake ../cmake \
-DBUILD_DOCUMENTATION=OFF \
-DBUILD_EXAMPLES=OFF \
-DCOLLADA_SUPPORT=OFF \
-DGLTF_SUPPORT=OFF \
-DHDF5_SUPPORT=OFF
-DGLTF_SUPPORT=OFF
make -j$(nproc) IfcViewer
```
+1
View File
@@ -230,6 +230,7 @@
#include "../ifcgeom/Iterator.h"
#include "../ifcgeom/ConversionResult.h"
#include "../ifcgeom/hybrid_kernel.h"
#include "../ifcgeom/GeometrySerializer.h"
#include "../ifcparse/express.h"
#include "../ifcparse/file.h"
-4
View File
@@ -88,10 +88,6 @@ if(WITH_OPENCASCADE AND NOT WASM_BUILD)
add_geometry_serializer_plugin(geometry_serializer_stp "geometry.stp" SOURCES geometry_stp_plugin.cpp OpenCascadeBasedSerializer.cpp LIBRARIES ${opencascade_geometry_serializer_libraries})
add_geometry_serializer_plugin(geometry_serializer_igs "geometry.igs" SOURCES geometry_igs_plugin.cpp OpenCascadeBasedSerializer.cpp LIBRARIES ${opencascade_geometry_serializer_libraries})
add_geometry_serializer_plugin(geometry_serializer_svg "geometry.svg" SOURCES geometry_svg_plugin.cpp SvgSerializer.cpp util.cpp LIBRARIES ${opencascade_geometry_serializer_libraries})
if(HDF5_SUPPORT)
add_geometry_serializer_plugin(geometry_serializer_hdf "geometry.h5" SOURCES geometry_hdf_plugin.cpp HdfSerializer.cpp LIBRARIES ${opencascade_geometry_serializer_libraries} ${HDF5_LIBRARIES})
endif()
endif()
install(TARGETS ${document_serializer_libraries} ${geometry_serializer_libraries})
-728
View File
@@ -1,728 +0,0 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE)
#include "HdfSerializer.h"
#include "../ifcgeom/IfcGeomRenderStyles.h"
#include "../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h"
#include "../ifcparse/utils.h"
#include <BRepTools_ShapeSet.hxx>
#include <BinTools_ShapeSet.hxx>
#include <boost/lexical_cast.hpp>
#include <iomanip>
#include <numeric>
#include <functional>
#include <cmath>
#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 ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only)
: GeometrySerializer(geometry_settings, settings)
, hdf_filename(hdf_filename)
, settings_(settings)
{
H5E_auto2_t fn = &print_stack;
H5::Exception::setAutoPrint(fn, nullptr);
try {
file = H5::H5File(hdf_filename, read_only
? H5F_ACC_RDONLY
: (H5F_ACC_RDWR | H5F_ACC_CREAT));
} catch (H5::Exception& e) {
throw ifcopenshell::exception(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 <typename T>
H5::DataType h5_datatype_for_cpp();
template <>
H5::DataType h5_datatype_for_cpp<int>() {
return H5::PredType::NATIVE_INT;
}
template <>
H5::DataType h5_datatype_for_cpp<uint64_t>() {
return H5::PredType::NATIVE_UINT64;
}
template <>
H5::DataType h5_datatype_for_cpp<double>() {
return H5::PredType::NATIVE_DOUBLE;
}
template <>
H5::DataType h5_datatype_for_cpp<std::string>() {
return H5::StrType(H5::PredType::C_S1, H5T_VARIABLE);
}
template <typename T>
void do_read(H5::Attribute& attr, T& val) {
attr.read(h5_datatype_for_cpp<T>(), &val);
}
template <>
void do_read(H5::Attribute& attr, std::string& val) {
attr.read(h5_datatype_for_cpp<std::string>(), val);
}
template <typename T>
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<T>(attr, val);
return val;
}
}
#include <BinTools.hxx>
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<TopAbs_Orientation>(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<int>(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 <typename T>
std::vector<T> 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<hsize_t> dims(rank);
space.getSimpleExtentDims(dims.data(), NULL);
const hsize_t total = std::accumulate(dims.begin(), dims.end(), 1U, std::multiplies<hsize_t>());
std::vector<T> result(total);
ds.read(result.data(), h5_datatype_for_cpp<T>());
return result;
}
}
void HdfSerializer::read_surface_style(const surface_style_serialization& s,
ifcopenshell::geometry::taxonomy::style& gss,
ifcopenshell::file& f) {
if (strlen(s.name) || s.id) {
if (!std::isnan(s.diffuse[0])) {
gss.diffuse = ifcopenshell::geometry::taxonomy::colour(s.diffuse[0], s.diffuse[1], s.diffuse[2]);
}
if (!std::isnan(s.specular[0])) {
gss.specular = ifcopenshell::geometry::taxonomy::colour(s.specular[0], s.specular[1], s.specular[2]);
}
if (!std::isnan(s.transparency)) {
gss.transparency = s.transparency;
}
if (!std::isnan(s.specularity)) {
gss.specularity = s.specularity;
}
if (s.id != 0) {
gss.instance = f.instance_by_id(s.id).as<express::Entity>();
}
}
}
void HdfSerializer::remove(const std::string& guid) {
if (H5Lexists(file.getId(), guid.c_str(), H5P_DEFAULT)) {
file.unlink(guid);
}
}
IfcGeom::Element* HdfSerializer::read(ifcopenshell::file& 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<int>(element_group, "id");
int parent_id = read_scalar_attribute<int>(element_group, "parent_id");
std::string type = read_scalar_attribute<std::string>(element_group, "type");
std::string name = read_scalar_attribute<std::string>(element_group, "name");
std::string context = read_scalar_attribute<std::string>(element_group, "context");
std::string unique_id = read_scalar_attribute<std::string>(element_group, "unique_id");
auto trsf = ifcopenshell::geometry::taxonomy::make<ifcopenshell::geometry::taxonomy::matrix4>();
auto placeds = element_group.openDataSet(DATASET_NAME_PLACEMENT);
double m44[4][4];
placeds.read(m44, H5::PredType::NATIVE_DOUBLE);
// @todo check
trsf->components() << Eigen::Map<Eigen::Matrix4d>(&m44[0][0]);
auto representation_group = element_group.openGroup(representation_id_str);
std::string geom_id = read_scalar_attribute<std::string>(representation_group, "geom_id");
auto inst = f.instance_by_id(id).as<express::Entity>();
boost::shared_ptr<IfcGeom::Representation::BRep> brep_geometry;
boost::shared_ptr<IfcGeom::Representation::Triangulation> 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);
/*
// @todo
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::ELEMENT_HIERARCHY;
auto stored_settings = read_scalar_attribute<uint64_t>(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<brep_element> parts;
{
auto space = brepDataset.getSpace();
int rank = space.getSimpleExtentNdims();
if (rank != 1) {
return nullptr;
}
std::vector<hsize_t> dims(rank);
space.getSimpleExtentDims(dims.data(), NULL);
parts.resize(dims[0]);
brepDataset.read(parts.data(), compound);
}
IfcGeom::ConversionResults shapes;
for (auto& part : parts) {
TopoDS_Shape shp = read_shape(part.shape_serialization);
// @todo check
auto matrix = ifcopenshell::geometry::taxonomy::make<ifcopenshell::geometry::taxonomy::matrix4>();
matrix->components() << Eigen::Map<Eigen::Matrix4d>(&part.matrix[0][0]);
auto style_ptr = ifcopenshell::geometry::taxonomy::make<ifcopenshell::geometry::taxonomy::style>();
read_surface_style(part.surface_style, *style_ptr, f);
shapes.push_back(IfcGeom::ConversionResult(part.id, matrix, new ifcopenshell::geometry::OpenCascadeShape(shp), style_ptr));
}
/*
// @todo
// World coordinates can be applied post-hoc
if (settings_.get(IfcGeom::IteratorSettings::USE_WORLD_COORDS) && !(stored_settings & IfcGeom::IteratorSettings::USE_WORLD_COORDS)) {
for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) {
it->prepend(trsf);
}
trsf = ifcopenshell::geometry::taxonomy::make<ifcopenshell::geometry::taxonomy::matrix4>();
}
*/
brep_geometry = boost::shared_ptr<IfcGeom::Representation::BRep>(new IfcGeom::Representation::BRep(geometry_settings(), type, geom_id, shapes));
/*
// @todo
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<double>(meshGroup, DATASET_NAME_POSITIONS);
auto faces = read_dataset<int>(meshGroup, DATASET_NAME_INDICES);
auto edges = read_dataset<int>(meshGroup, DATASET_NAME_EDGES);
auto normals = read_dataset<double>(meshGroup, DATASET_NAME_NORMALS);
auto uvcoords = read_dataset<double>(meshGroup, DATASET_NAME_UVCOORDS);
auto material_ids = read_dataset<int>(meshGroup, DATASET_NAME_MATERIAL_IDS);
auto item_ids = read_dataset<int>(meshGroup, DATASET_NAME_ITEM_IDS);
auto edges_item_ids = read_dataset<int>(meshGroup, DATASET_NAME_EDGES_ITEM_IDS);
std::vector<surface_style_serialization> surface_styles;
{
auto ds = meshGroup.openDataSet(DATASET_NAME_MATERIALS);
auto space = ds.getSpace();
int rank = space.getSimpleExtentNdims();
if (rank != 1) {
return nullptr;
}
std::vector<hsize_t> dims(rank);
space.getSimpleExtentDims(dims.data(), NULL);
surface_styles.resize(dims[0]);
ds.read(surface_styles.data(), style_compound);
}
std::vector<ifcopenshell::geometry::taxonomy::style::ptr> surface_style_ptrs(surface_styles.size());
for (size_t i = 0; i < surface_styles.size(); ++i) {
surface_style_ptrs[i] = ifcopenshell::geometry::taxonomy::make<ifcopenshell::geometry::taxonomy::style>();
read_surface_style(surface_styles[i], *surface_style_ptrs[i], f);
}
triangulation_geometry = boost::shared_ptr<IfcGeom::Representation::Triangulation>(new IfcGeom::Representation::Triangulation(
geometry_settings_,
type,
geom_id,
verts,
faces,
edges,
normals,
uvcoords,
material_ids,
surface_style_ptrs,
item_ids
, edges_item_ids
));
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(
geometry_settings_,
id,
parent_id,
name,
type,
guid,
context,
trsf,
inst
),
triangulation_geometry
);
}
}
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<std::pair<const char* const, string_member_fun>> 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<std::pair<const char* const, int_member_fun>> 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 auto& m = o->transformation().data()->ccomponents();
// @todo check, is this needed, can we use the storage of Eigen?
double m44[4][4] = {
{ m(0,0), m(1,0), m(2,0), m(3,0) },
{ m(0,1), m(1,1), m(2,1), m(3,1) },
{ m(0,2), m(1,2), m(2,2), m(3,2) },
{ m(0,3), m(1,3), m(2,3), m(3,3) }
};
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 ifcopenshell::geometry::taxonomy::style::ptr& sptr) {
auto& s = *sptr;
data.name = s.name.c_str();
// @todo
data.original_name = s.name.c_str();
auto& instance = s.instance;
data.id = instance ? instance.id() : 0;
if (s.diffuse) {
data.diffuse[0] = s.diffuse.ccomponents()(0);
data.diffuse[1] = s.diffuse.ccomponents()(1);
data.diffuse[2] = s.diffuse.ccomponents()(2);
}
if (s.specular) {
data.specular[0] = s.specular.ccomponents()(0);
data.specular[1] = s.specular.ccomponents()(1);
data.specular[2] = s.specular.ccomponents()(2);
}
if (s.transparency == s.transparency) {
data.transparency = s.transparency;
}
if (s.specularity == s.specularity) {
data.specularity = s.specularity;
}
}
void HdfSerializer::write(const IfcGeom::BRepElement* o) {
// Currenly we only support OpenCascade shapes.
for (auto it = o->geometry().begin(); it != o->geometry().end(); ++it) {
auto shape_ptr = std::dynamic_pointer_cast<ifcopenshell::geometry::OpenCascadeShape>(it->Shape());
if (shape_ptr == nullptr) {
std::cerr << "WARNING. Only OpenCascade shapes support caching. "
<< "Skipping caching for item #" << it->ItemId() << "." << std::endl;
return;
}
}
static auto nan = std::numeric_limits<double>::quiet_NaN();
auto element_group = write((const IfcGeom::Element*)o);
/*
// For now we disable softlinks, as it can't be safely used with delete()
// @todo we can still cache the serialization or read it from the file probably for a comparible speedup.
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<std::string> 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();
const auto& m = o->transformation().data()->ccomponents();
// @todo check, is this needed, can we use the storage of Eigen?
std::array<std::array<double, 4>, 4> arr = { {
{ { m(0,0), m(1,0), m(2,0), m(3,0) } },
{ { m(0,1), m(1,1), m(2,1), m(3,1) } },
{ { m(0,2), m(1,2), m(2,2), m(3,2) } },
{ { m(0,3), m(1,3), m(2,3), m(3,3) } }
} };
for (int j = 0; j < 4; ++j) {
std::copy(arr[j].begin(), arr[j].end(), parts[i].matrix[j]);
}
brep_strings.emplace_back();
// OpenCascadeShape type ensured by the check at the method start.
write_shape(std::static_pointer_cast<ifcopenshell::geometry::OpenCascadeShape>(it->Shape())->shape(), brep_strings.back());
parts[i].surface_style = { "", "", 0, {nan,nan,nan}, {nan,nan,nan}, nan, nan };
if (it->hasStyle()) {
auto s = it->StylePtr();
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);
/*
// @todo
uint64_t value = o->geometry().settings().get_raw();
*/
uint64_t value = 0;
att.write(H5::PredType::NATIVE_UINT64, &value);
}
namespace {
template <typename T>
void write_dataset(const H5::Group& group, const std::string& name, const std::vector<T>& 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<T>();
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);
write_dataset(meshGroup, DATASET_NAME_ITEM_IDS, mesh.item_ids(), 1);
write_dataset(meshGroup, DATASET_NAME_EDGES_ITEM_IDS, mesh.edges_item_ids(), 1);
{
auto& ts = mesh.materials();
hsize_t d[2] { ts.size() };
H5::DataSpace dataspace(1, d);
const auto& dt = style_compound;
std::vector<surface_style_serialization> data;
data.reserve(ts.size());
for (auto& m : ts) {
data.emplace_back();
write_style(data.back(), m);
}
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_ITEM_IDS = "item_ids";
const H5std_string HdfSerializer::DATASET_NAME_EDGES_ITEM_IDS = "edges_item_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
-119
View File
@@ -1,119 +0,0 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef HDFSERIALIZER_H
#define HDFSERIALIZER_H
#if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE)
#include <set>
#include <string>
#include <fstream>
#include "H5Cpp.h"
#include "../serializers/serializers_api.h"
#include "../ifcgeom/GeometrySerializer.h"
#define USE_BINARY
class SERIALIZERS_API HdfSerializer : public GeometrySerializer {
private:
const std::string hdf_filename;
unsigned int vcount_total;
H5::H5File file;
ifcopenshell::geometry::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_ITEM_IDS;
static const H5std_string DATASET_NAME_EDGES_ITEM_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<std::string, boost::shared_ptr<IfcGeom::Representation::BRep>> brep_cache_;
std::map<std::string, boost::shared_ptr<IfcGeom::Representation::Triangulation>> triangulation_cache_;
std::map<std::string, std::string> group_cache_;
H5::Group createRepresentationGroup(const H5::Group& element_group, const std::string& gid);
void read_surface_style(const surface_style_serialization& s,
ifcopenshell::geometry::taxonomy::style& gss,
ifcopenshell::file& f);
void write_style(surface_style_serialization& data, const ifcopenshell::geometry::taxonomy::style::ptr& s);
public:
HdfSerializer(const std::string& hdf_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only=false);
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(ifcopenshell::file& 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(ifcopenshell::file*) {}
};
#endif
#endif
-68
View File
@@ -1,68 +0,0 @@
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE)
#include "geometry_serializer_plugin.h"
#include "HdfSerializer.h"
#include <boost/dll/alias.hpp>
#include <boost/make_shared.hpp>
namespace ifcopenshell {
namespace serializers {
namespace geometry_hdf_plugin {
plugin::abi_info plugin_abi() {
return plugin::host_abi();
}
plugin::metadata plugin_metadata() {
return geometry_serializer_plugin_metadata("h5");
}
boost::shared_ptr<GeometrySerializer> create_serializer(const geometry_serializer_context& context) {
return boost::make_shared<HdfSerializer>(context.output_temp_filename, context.geometry_settings, context.serializer_settings);
}
void configure_serializer(geometry_serializer_context& context) {
context.geometry_settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
}
void register_plugin(geometry_serializer_registry& registry, const plugin::module& module) {
geometry_serializer_info info;
info.format = "hdf";
info.name = "HDF";
info.description = "Hierarchical Data Format storing positions, normals and indices.";
info.extensions = { ".h5" };
info.kernel_ids = { "opencascade" };
info.supports_triangulation = true;
info.supports_brep = true;
registry.bind(info, create_serializer, configure_serializer, module);
}
}
}
}
BOOST_DLL_ALIAS(ifcopenshell::serializers::geometry_hdf_plugin::plugin_abi, ifcopenshell_plugin_abi_v1)
BOOST_DLL_ALIAS(ifcopenshell::serializers::geometry_hdf_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1)
BOOST_DLL_ALIAS(ifcopenshell::serializers::geometry_hdf_plugin::register_plugin, ifcopenshell_register_geometry_serializer_plugin_v1)
#endif