From 9f7a7a2c5b17ad232e0770ffde57da3791019f7c Mon Sep 17 00:00:00 2001 From: Dawid Huczynski Date: Wed, 6 Feb 2019 00:42:49 +0000 Subject: [PATCH 1/9] basic update to blender2.8, use of existing mesh if id match, --- .../io_import_scene_ifc/__init__.py | 224 ++++++++++-------- 1 file changed, 130 insertions(+), 94 deletions(-) diff --git a/src/ifcblender/io_import_scene_ifc/__init__.py b/src/ifcblender/io_import_scene_ifc/__init__.py index dee3adb560..8c6ed16d23 100644 --- a/src/ifcblender/io_import_scene_ifc/__init__.py +++ b/src/ifcblender/io_import_scene_ifc/__init__.py @@ -27,42 +27,46 @@ bl_info = { "name": "IfcBlender", - "description": "Import files in the "\ + "description": "Import files in the " "Industry Foundation Classes (.ifc) file format", "author": "Thomas Krijnen, IfcOpenShell", - "blender": (2, 73, 0), + "blender": (2, 80, 0), "location": "File > Import", - "tracker_url": "https://sourceforge.net/p/ifcopenshell/"\ + "tracker_url": "https://sourceforge.net/p/ifcopenshell/" "_list/tickets?source=navbar", "category": "Import-Export"} if "bpy" in locals(): - import imp + import importlib if "ifcopenshell" in locals(): - imp.reload(ifcopenshell) + importlib.reload(ifcopenshell) import bpy import mathutils from bpy.props import StringProperty, IntProperty, BoolProperty from bpy_extras.io_utils import ImportHelper -major,minor = bpy.app.version[0:2] +major, minor = bpy.app.version[0:2] transpose_matrices = minor >= 62 -bpy.types.Object.ifc_id = IntProperty(name="IFC Entity ID", +bpy.types.Object.ifc_id = IntProperty( + name="IFC Entity ID", description="The STEP entity instance name") -bpy.types.Object.ifc_guid = StringProperty(name="IFC Entity GUID", +bpy.types.Object.ifc_guid = StringProperty( + name="IFC Entity GUID", description="The IFC Globally Unique Identifier") -bpy.types.Object.ifc_name = StringProperty(name="IFC Entity Name", +bpy.types.Object.ifc_name = StringProperty( + name="IFC Entity Name", description="The optional name attribute") -bpy.types.Object.ifc_type = StringProperty(name="IFC Entity Type", +bpy.types.Object.ifc_type = StringProperty( + name="IFC Entity Type", description="The STEP Datatype keyword") def import_ifc(filename, use_names, process_relations, blender_booleans): from . import ifcopenshell from .ifcopenshell import geom as ifcopenshell_geom - print("Reading %s..."%bpy.path.basename(filename)) + print(f"Reading {bpy.path.basename(filename)}...") settings = ifcopenshell_geom.settings() settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans) iterator = ifcopenshell_geom.iterator(settings, filename) @@ -76,9 +80,14 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): openings = [] old_progress = -1 print("Creating geometry...") + collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}") + bpy.context.scene.collection.children.link(collection) + if process_relations: + rel_collection = bpy.data.collections.new("Relations") + collection.children.link(rel_collection) while True: ob = iterator.get() - + f = ob.geometry.faces v = ob.geometry.verts mats = ob.geometry.materials @@ -86,54 +95,75 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): m = ob.transformation.matrix.data t = ob.type[0:21] nm = ob.name if len(ob.name) and use_names else ob.guid - - verts = [[v[i], v[i + 1], v[i + 2]] \ - for i in range(0, len(v), 3)] - faces = [[f[i], f[i + 1], f[i + 2]] \ - for i in range(0, len(f), 3)] - + # MESH CREATION # Depending on version, geometry.id will be either int or str - me = bpy.data.meshes.new('mesh-%r' % ob.geometry.id) - me.from_pydata(verts, [], faces) - me.validate() - - def add_material(mname, props): - if mname in bpy.data.materials: - mat = bpy.data.materials[mname] - mat.use_fake_user = True - else: - mat = bpy.data.materials.new(mname) - for k,v in props.items(): - setattr(mat, k, v) - me.materials.append(mat) - - needs_default = -1 in matids - if needs_default: add_material(t, {}) - - for mat in mats: - props = {} - if mat.has_diffuse: props['diffuse_color'] = mat.diffuse - if mat.has_specular: props['specular_color'] = mat.specular - if mat.has_transparency and mat.transparency > 0: - props['alpha'] = 1.0 - mat.transparency - props['use_transparency'] = True - if mat.has_specularity: props['specular_hardness'] = mat.specularity - add_material(mat.name, props) + mesh_name = 'mesh-%r' % ob.geometry.id + if mesh_name in bpy.data.meshes: + me = bpy.data.meshes[mesh_name] + else: + verts = [[v[i], v[i + 1], v[i + 2]] + for i in range(0, len(v), 3)] + faces = [[f[i], f[i + 1], f[i + 2]] + for i in range(0, len(f), 3)] + me = bpy.data.meshes.new(mesh_name) + me.from_pydata(verts, [], faces) + me.validate() + # MATERIAL CREATION + def add_material(mname, props): + if mname in bpy.data.materials: + mat = bpy.data.materials[mname] + mat.use_fake_user = True + else: + mat = bpy.data.materials.new(mname) + for k, v in props.items(): + if k == 'transparency': + mat.blend_method = 'HASHED' + mat.use_screen_refraction = True + mat.refraction_depth = 0.1 + mat.use_nodes = True + mat.node_tree.nodes["Principled BSDF"].inputs[15].default_value = v + else: + setattr(mat, k, v) + me.materials.append(mat) + + needs_default = -1 in matids + if needs_default: + add_material(t, {}) + + for mat in mats: + props = {} + if mat.has_diffuse: + props['diffuse_color'] = mat.diffuse + if mat.has_specular: + props['specular_color'] = mat.specular + if mat.has_transparency and mat.transparency > 0: + props['transparency'] = mat.transparency + if mat.has_specularity: + props['specular_intensity'] = mat.specularity + add_material(mat.name, props) + + faces = me.polygons if hasattr(me, 'polygons') else me.faces + if len(faces) == len(matids): + for face, matid in zip(faces, matids): + face.material_index = matid + (1 if needs_default else 0) + + # OBJECT CREATION bob = bpy.data.objects.new(nm, me) mat = mathutils.Matrix(([m[0], m[1], m[2], 0], - [m[3], m[4], m[5], 0], - [m[6], m[7], m[8], 0], - [m[9], m[10], m[11], 1])) - if transpose_matrices: mat.transpose() - + [m[3], m[4], m[5], 0], + [m[6], m[7], m[8], 0], + [m[9], m[10], m[11], 1])) + if transpose_matrices: + mat.transpose() + if process_relations: id_to_matrix[ob.id] = mat else: bob.matrix_world = mat - bpy.context.scene.objects.link(bob) + collection.objects.link(bob) - bpy.context.scene.objects.active = bob + bpy.context.view_layer.objects.active = bob bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.normals_make_consistent() bpy.ops.object.mode_set(mode='OBJECT') @@ -143,23 +173,19 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): if ob.type == 'IfcSpace' or ob.type == 'IfcOpeningElement': if not (ob.type == 'IfcOpeningElement' and blender_booleans): - bob.hide = bob.hide_render = True - bob.draw_type = 'WIRE' - - if ob.id not in id_to_object: id_to_object[ob.id] = [] + bob.hide_viewport = bob.hide_render = True + bob.display_type = 'WIRE' + + if ob.id not in id_to_object: + id_to_object[ob.id] = [] id_to_object[ob.id].append(bob) if ob.parent_id > 0: id_to_parent[ob.id] = ob.parent_id - + if blender_booleans and ob.type == 'IfcOpeningElement': openings.append(ob.id) - - faces = me.polygons if hasattr(me, 'polygons') else me.faces - if len(faces) == len(matids): - for face, matid in zip(faces, matids): - face.material_index = matid + (1 if needs_default else 0) - + progress = iterator.progress() // 2 if progress > old_progress: print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="") @@ -170,13 +196,13 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): print("\rDone creating geometry" + " " * 30) id_to_parent_temp = dict(id_to_parent) - + if process_relations: print("Processing relations...") while len(id_to_parent_temp) and process_relations: id, parent_id = id_to_parent_temp.popitem() - + if parent_id in id_to_object: bob = id_to_object[parent_id][0] else: @@ -188,16 +214,17 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): nm = parent_ob.name if len(parent_ob.name) and use_names \ else parent_ob.guid bob = bpy.data.objects.new(nm, None) - + mat = mathutils.Matrix(( [m[0], m[1], m[2], 0], [m[3], m[4], m[5], 0], [m[6], m[7], m[8], 0], [m[9], m[10], m[11], 1])) - if transpose_matrices: mat.transpose() + if transpose_matrices: + mat.transpose() id_to_matrix[parent_ob.id] = mat - - bpy.context.scene.objects.link(bob) + + rel_collection.objects.link(bob) bob.ifc_id = parent_ob.id bob.ifc_name, bob.ifc_type, bob.ifc_guid = \ @@ -220,13 +247,13 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): parent_matrix = id_to_matrix.get(parent_id, None) for ob in id_to_object[id]: if parent_matrix: - ob.matrix_local = parent_matrix.inverted() * matrix + ob.matrix_local = parent_matrix.inverted() @ matrix else: ob.matrix_world = matrix - + if process_relations: print("Done processing relations") - + for opening_id in openings: parent_id = id_to_parent[opening_id] if parent_id in id_to_object: @@ -235,8 +262,8 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): mod = parent_ob.modifiers.new("opening", "BOOLEAN") mod.operation = "DIFFERENCE" mod.object = opening_ob - - txt = bpy.data.texts.new("%s.log"%bpy.path.basename(filename)) + + txt = bpy.data.texts.new(f"{bpy.path.basename(filename)}.log") txt.from_string(iterator.getLog()) return True @@ -247,42 +274,51 @@ class ImportIFC(bpy.types.Operator, ImportHelper): bl_label = "Import .ifc file" filename_ext = ".ifc" - filter_glob = StringProperty(default="*.ifc", options={'HIDDEN'}) + filter_glob: StringProperty(default="*.ifc", options={'HIDDEN'}) - use_names = BoolProperty(name="Use entity names", - description="Use entity names rather than GlobalIds for objects", - default=True) - process_relations = BoolProperty(name="Process relations", - description="Convert containment and aggregation" \ - " relations to parenting" \ - " (warning: may be slow on large files)", - default=False) - blender_booleans = BoolProperty(name="Use Blender booleans", - description="Use Blender boolean modifiers for opening" \ - " elements", - default=False) + use_names: BoolProperty(name="Use entity names", + description="Use entity names rather than " + "GlobalIds for objects", + default=True) + process_relations: BoolProperty(name="Process relations", + description="Convert containment and " + "aggregation relations to parenting" + " (warning: may be slow on large files)", + default=False) + blender_booleans: BoolProperty(name="Use Blender booleans", + description="Use Blender boolean modifiers " + "for opening elements", + default=False) def execute(self, context): - if not import_ifc(self.filepath, self.use_names, self.process_relations, self.blender_booleans): + if not import_ifc(self.filepath, self.use_names, + self.process_relations, self.blender_booleans): self.report({'ERROR'}, - 'Unable to parse .ifc file or no geometrical entities found' - ) + 'Unable to parse .ifc file or no geometrical entities found' + ) return {'FINISHED'} def menu_func_import(self, context): self.layout.operator(ImportIFC.bl_idname, - text="Industry Foundation Classes (.ifc)") + text="Industry Foundation Classes (.ifc)") + + +classes = ( + ImportIFC, +) def register(): - bpy.utils.register_module(__name__) - bpy.types.INFO_MT_file_import.append(menu_func_import) + for cls in classes: + bpy.utils.register_class(cls) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) def unregister(): - bpy.utils.unregister_module(__name__) - bpy.types.INFO_MT_file_import.remove(menu_func_import) + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) if __name__ == "__main__": From 3fdd92dc841d71e89d74a8a45d0fcca95972f28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Huczy=C5=84ski?= Date: Wed, 6 Feb 2019 11:45:22 +0000 Subject: [PATCH 2/9] Create intersection.py --- .../io_import_scene_ifc/intersection.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/ifcblender/io_import_scene_ifc/intersection.py diff --git a/src/ifcblender/io_import_scene_ifc/intersection.py b/src/ifcblender/io_import_scene_ifc/intersection.py new file mode 100644 index 0000000000..31195be334 --- /dev/null +++ b/src/ifcblender/io_import_scene_ifc/intersection.py @@ -0,0 +1,103 @@ +import bpy +import bmesh + +def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): + """ + Returns a transformed, triangulated copy of the mesh + """ + + assert(obj.type == 'MESH') + + if apply_modifiers and obj.modifiers: + me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) + bm = bmesh.new() + bm.from_mesh(me) + bpy.data.meshes.remove(me) + else: + me = obj.data + if obj.mode == 'EDIT': + bm_orig = bmesh.from_edit_mesh(me) + bm = bm_orig.copy() + else: + bm = bmesh.new() + bm.from_mesh(me) + + # Remove custom data layers to save memory + for elem in (bm.faces, bm.edges, bm.verts, bm.loops): + for layers_name in dir(elem.layers): + if not layers_name.startswith("_"): + layers = getattr(elem.layers, layers_name) + for layer_name, layer in layers.items(): + layers.remove(layer) + + if transform: + bm.transform(obj.matrix_world) + + if triangulate: + bmesh.ops.triangulate(bm, faces=bm.faces) + + return bm + +def bmesh_check_intersect_objects(obj, obj2): + """ + Check if any faces intersect with the other object + + returns a boolean + """ + assert(obj != obj2) + + # Triangulate + bm = bmesh_copy_from_object(obj, transform=True, triangulate=True) + bm2 = bmesh_copy_from_object(obj2, transform=True, triangulate=True) + + # If bm has more edges, use bm2 instead for looping over its edges + # (so we cast less rays from the simpler object to the more complex object) + if len(bm.edges) > len(bm2.edges): + bm2, bm = bm, bm2 + + # Create a real mesh (lame!) + scene = bpy.context.scene + me_tmp = bpy.data.meshes.new(name="~temp~") + bm2.to_mesh(me_tmp) + bm2.free() + obj_tmp = bpy.data.objects.new(name=me_tmp.name, object_data=me_tmp) + scene.objects.link(obj_tmp) + scene.update() + ray_cast = obj_tmp.ray_cast + + intersect = False + + EPS_NORMAL = 0.000001 + EPS_CENTER = 0.01 # should always be bigger + + #for ed in me_tmp.edges: + for ed in bm.edges: + v1, v2 = ed.verts + + # setup the edge with an offset + co_1 = v1.co.copy() + co_2 = v2.co.copy() + co_mid = (co_1 + co_2) * 0.5 + no_mid = (v1.normal + v2.normal).normalized() * EPS_NORMAL + co_1 = co_1.lerp(co_mid, EPS_CENTER) + no_mid + co_2 = co_2.lerp(co_mid, EPS_CENTER) + no_mid + + co, no, index = ray_cast(co_1, co_2) + if index != -1: + intersect = True + break + + scene.objects.unlink(obj_tmp) + bpy.data.objects.remove(obj_tmp) + bpy.data.meshes.remove(me_tmp) + + scene.update() + + return intersect + + +obj = bpy.context.object +obj2 = (ob for ob in bpy.context.selected_objects if ob != obj).__next__() +intersect = bmesh_check_intersect_objects(obj, obj2) + +print("There are%s intersections." % ("" if intersect else " NO")) From 9b0be0fb4ef895969d0c158526a17f62d1e46e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Huczy=C5=84ski?= Date: Wed, 6 Feb 2019 14:08:13 +0000 Subject: [PATCH 3/9] Delete intersection.py --- .../io_import_scene_ifc/intersection.py | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 src/ifcblender/io_import_scene_ifc/intersection.py diff --git a/src/ifcblender/io_import_scene_ifc/intersection.py b/src/ifcblender/io_import_scene_ifc/intersection.py deleted file mode 100644 index 31195be334..0000000000 --- a/src/ifcblender/io_import_scene_ifc/intersection.py +++ /dev/null @@ -1,103 +0,0 @@ -import bpy -import bmesh - -def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): - """ - Returns a transformed, triangulated copy of the mesh - """ - - assert(obj.type == 'MESH') - - if apply_modifiers and obj.modifiers: - me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) - bm = bmesh.new() - bm.from_mesh(me) - bpy.data.meshes.remove(me) - else: - me = obj.data - if obj.mode == 'EDIT': - bm_orig = bmesh.from_edit_mesh(me) - bm = bm_orig.copy() - else: - bm = bmesh.new() - bm.from_mesh(me) - - # Remove custom data layers to save memory - for elem in (bm.faces, bm.edges, bm.verts, bm.loops): - for layers_name in dir(elem.layers): - if not layers_name.startswith("_"): - layers = getattr(elem.layers, layers_name) - for layer_name, layer in layers.items(): - layers.remove(layer) - - if transform: - bm.transform(obj.matrix_world) - - if triangulate: - bmesh.ops.triangulate(bm, faces=bm.faces) - - return bm - -def bmesh_check_intersect_objects(obj, obj2): - """ - Check if any faces intersect with the other object - - returns a boolean - """ - assert(obj != obj2) - - # Triangulate - bm = bmesh_copy_from_object(obj, transform=True, triangulate=True) - bm2 = bmesh_copy_from_object(obj2, transform=True, triangulate=True) - - # If bm has more edges, use bm2 instead for looping over its edges - # (so we cast less rays from the simpler object to the more complex object) - if len(bm.edges) > len(bm2.edges): - bm2, bm = bm, bm2 - - # Create a real mesh (lame!) - scene = bpy.context.scene - me_tmp = bpy.data.meshes.new(name="~temp~") - bm2.to_mesh(me_tmp) - bm2.free() - obj_tmp = bpy.data.objects.new(name=me_tmp.name, object_data=me_tmp) - scene.objects.link(obj_tmp) - scene.update() - ray_cast = obj_tmp.ray_cast - - intersect = False - - EPS_NORMAL = 0.000001 - EPS_CENTER = 0.01 # should always be bigger - - #for ed in me_tmp.edges: - for ed in bm.edges: - v1, v2 = ed.verts - - # setup the edge with an offset - co_1 = v1.co.copy() - co_2 = v2.co.copy() - co_mid = (co_1 + co_2) * 0.5 - no_mid = (v1.normal + v2.normal).normalized() * EPS_NORMAL - co_1 = co_1.lerp(co_mid, EPS_CENTER) + no_mid - co_2 = co_2.lerp(co_mid, EPS_CENTER) + no_mid - - co, no, index = ray_cast(co_1, co_2) - if index != -1: - intersect = True - break - - scene.objects.unlink(obj_tmp) - bpy.data.objects.remove(obj_tmp) - bpy.data.meshes.remove(me_tmp) - - scene.update() - - return intersect - - -obj = bpy.context.object -obj2 = (ob for ob in bpy.context.selected_objects if ob != obj).__next__() -intersect = bmesh_check_intersect_objects(obj, obj2) - -print("There are%s intersections." % ("" if intersect else " NO")) From a4c2aa8f4222e1072f66fdf71bb4ac3510c30899 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 8 Feb 2019 15:18:55 +0100 Subject: [PATCH 4/9] IfcConvert Windows unicode support (#258) --- src/ifcconvert/ColladaSerializer.cpp | 2 + src/ifcconvert/ColladaSerializer.h | 2 +- src/ifcconvert/IfcConvert.cpp | 399 ++++++++++-------- src/ifcconvert/OpenCascadeBasedSerializer.cpp | 10 +- src/ifcconvert/SvgSerializer.h | 4 +- src/ifcconvert/WavefrontObjSerializer.cpp | 13 + src/ifcconvert/WavefrontObjSerializer.h | 12 +- src/ifcconvert/XmlSerializer.cpp | 12 +- src/ifcgeom/IfcGeomRenderStyles.cpp | 1 + src/ifcparse/Argument.h | 4 - src/ifcparse/IfcLogger.cpp | 113 +++-- src/ifcparse/IfcLogger.h | 15 + src/ifcparse/IfcParse.cpp | 11 +- src/ifcparse/IfcUtil.cpp | 84 ++++ src/ifcparse/utils.h | 59 +++ 15 files changed, 506 insertions(+), 235 deletions(-) create mode 100644 src/ifcparse/utils.h diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp index e0c3eb169c..8d5ed42ab1 100644 --- a/src/ifcconvert/ColladaSerializer.cpp +++ b/src/ifcconvert/ColladaSerializer.cpp @@ -32,6 +32,8 @@ #include #include +#include "../ifcparse/utils.h" + using namespace IfcSchema; static std::string& collada_id(std::string& s) diff --git a/src/ifcconvert/ColladaSerializer.h b/src/ifcconvert/ColladaSerializer.h index 8c435f81a0..043ea3584a 100644 --- a/src/ifcconvert/ColladaSerializer.h +++ b/src/ifcconvert/ColladaSerializer.h @@ -198,7 +198,7 @@ private: ColladaExporter(const std::string& scene_name, const std::string& fn, ColladaSerializer *_serializer, bool double_precision) : filename(fn) - , stream(filename, double_precision) + , stream(COLLADASW::NativeString(filename.c_str(), COLLADASW::NativeString::ENCODING_UTF8), double_precision) , scene(scene_name, stream, _serializer) , materials(stream, _serializer) , geometries(stream, _serializer) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 7847f139b8..db984857eb 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -36,6 +36,8 @@ #include "../ifcgeom/IfcGeomIterator.h" #include "../ifcgeom/IfcGeomRenderStyles.h" +#include "../ifcparse/utils.h" + #include #include @@ -51,6 +53,23 @@ #include #endif +#ifdef _MSC_VER +#include +#include +// C++11 header: +#include +#endif + +#if defined(_MSC_VER) && defined(_UNICODE) +typedef std::wstring path_t; +static std::wostream& cout_ = std::wcout; +static std::wostream& cerr_ = std::wcerr; +#else +typedef std::string path_t; +static std::ostream& cout_ = std::cout; +static std::ostream& cerr_ = std::cerr; +#endif + const std::string DEFAULT_EXTENSION = "obj"; const std::string TEMP_FILE_EXTENSION = ".tmp"; @@ -58,12 +77,12 @@ namespace po = boost::program_options; void print_version() { - std::cout << "IfcOpenShell " << IfcSchema::Identifier << " IfcConvert " << IFCOPENSHELL_VERSION << " (OCC " << OCC_VERSION_STRING_EXT << ")\n"; + cout_ << "IfcOpenShell " << IfcSchema::Identifier << " IfcConvert " << IFCOPENSHELL_VERSION << " (OCC " << OCC_VERSION_STRING_EXT << ")\n"; } void print_usage(bool suggest_help = true) { - std::cout << "Usage: IfcConvert [options] []\n" + cout_ << "Usage: IfcConvert [options] []\n" << "\n" << "Converts the geometry in an IFC file into one of the following formats:\n" << " .obj WaveFront OBJ (a .mtl file is also created)\n" @@ -75,46 +94,43 @@ void print_usage(bool suggest_help = true) << " .xml XML Property definitions and decomposition tree\n" << " .svg SVG Scalable Vector Graphics (2D floor plan)\n" << "\n" - << "If no output filename given, ." + DEFAULT_EXTENSION + " will be used as the output file.\n"; + << "If no output filename given, ." << IfcUtil::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n"; if (suggest_help) { - std::cout << "\nRun 'IfcConvert --help' for more information."; + cout_ << "\nRun 'IfcConvert --help' for more information."; } - std::cout << std::endl; + cout_ << std::endl; } /// @todo Add help for single option void print_options(const po::options_description& options) { - std::cout << "\n" << options; - std::cout << std::endl; +#if defined(_MSC_VER) && defined(_UNICODE) + // See issue https://svn.boost.org/trac10/ticket/10952 + std::ostringstream temp; + temp << options; + cout_ << "\n" << temp.str().c_str(); +#else + cout_ << "\n" << options; +#endif + cout_ << std::endl; } -std::string change_extension(const std::string& fn, const std::string& ext) { - std::string::size_type dot = fn.find_last_of('.'); - if (dot != std::string::npos) { - return fn.substr(0,dot+1) + ext; +template +T change_extension(const T& fn, const T& ext) { + typename T::size_type dot = fn.find_last_of('.'); + if (dot != T::npos) { + return fn.substr(0, dot) + ext; } else { - return fn + "." + ext; + return fn + ext; } } -bool file_exists(const std::string& filename) -{ - /// @todo Windows Unicode support - std::ifstream file(filename.c_str()); +bool file_exists(const std::string& filename) { + std::ifstream file(IfcUtil::path::from_utf8(filename).c_str()); return file.good(); } -bool rename_file(const std::string& old_filename, const std::string& new_filename) -{ - // Whether or not rename() replaces an existing file is implementation-specific, - // so remove() possible existing file always. - /// @todo Windows Unicode support - std::remove(new_filename.c_str()); - return std::rename(old_filename.c_str(), new_filename.c_str()) == 0; -} - -static std::stringstream log_stream; +static std::basic_stringstream log_stream; void write_log(bool); std::string format_duration(time_t start, time_t end); @@ -153,9 +169,28 @@ std::vector setup_filters(const std::vector&, co bool init_input_file(const std::string& filename, IfcParse::IfcFile& ifc_file, bool no_progress, bool mmap); -int main(int argc, char** argv) -{ +#if defined(_MSC_VER) && defined(_UNICODE) +int wmain(int argc, wchar_t** argv) { + typedef po::wcommand_line_parser command_line_parser; + typedef wchar_t char_t; + + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stderr), _O_U16TEXT); +#else +int main(int argc, char** argv) { + typedef po::command_line_parser command_line_parser; + typedef char char_t; +#endif + + double deflection_tolerance; + inclusion_filter include_filter; + inclusion_traverse_filter include_traverse_filter; + exclusion_filter exclude_filter; + exclusion_traverse_filter exclude_traverse_filter; + path_t filter_filename; + path_t default_material_filename; std::string log_format; + po::options_description generic_options("Command line options"); generic_options.add_options() ("help,h", "display usage information") @@ -172,17 +207,8 @@ int main(int argc, char** argv) #ifdef USE_MMAP ("mmap", "use memory-mapped file for input") #endif - ("input-file", po::value(), "input IFC file") - ("output-file", po::value(), "output geometry file"); - - - double deflection_tolerance; - inclusion_filter include_filter; - inclusion_traverse_filter include_traverse_filter; - exclusion_filter exclude_filter; - exclusion_traverse_filter exclude_traverse_filter; - std::string filter_filename; - std::string default_material_filename; + ("input-file", new po::typed_value(0), "input IFC file") + ("output-file", new po::typed_value(0), "output geometry file"); po::options_description geom_options("Geometry options"); geom_options.add_options() @@ -259,12 +285,12 @@ int main(int argc, char** argv) ("generate-uvs", "Generates UVs (texture coordinates) by using simple box projection. Requires normals. " "Not guaranteed to work properly if used with --weld-vertices.") - ("filter-file", po::value(&filter_filename), + ("filter-file", new po::typed_value(&filter_filename), "Specifies a filter file that describes the used filtering criteria. Supported formats " "are '--include=arg GlobalId ...' and 'include arg GlobalId ...'. Spaces and tabs can be used as delimiters." "Multiple filters of same type with different values can be inserted on their own lines. " "See --include, --include+, --exclude, and --exclude+ for more details.") - ("default-material-file", po::value(&default_material_filename), + ("default-material-file", new po::typed_value(&default_material_filename), "Specifies a material file that describes the material object types will have" "if an object does not have any specified material in the IFC file."); @@ -327,21 +353,21 @@ int main(int argc, char** argv) po::variables_map vmap; try { - po::store(po::command_line_parser(argc, argv). + po::store(command_line_parser(argc, argv). options(cmdline_options).positional(positional_options).run(), vmap); } catch (const po::unknown_option& e) { - std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'\n\n"; + cerr_ << "[Error] Unknown option '" << e.get_option_name().c_str() << "'\n\n"; print_usage(); return EXIT_FAILURE; } catch (const po::error_with_option_name& e) { - std::cerr << "[Error] Invalid usage of '" << e.get_option_name() << "': " << e.what() << "\n\n"; + cerr_ << "[Error] Invalid usage of '" << e.get_option_name().c_str() << "': " << e.what() << "\n\n"; return EXIT_FAILURE; } catch (const std::exception& e) { - std::cerr << "[Error] " << e.what() << "\n\n"; + cerr_ << "[Error] " << e.what() << "\n\n"; print_usage(); return EXIT_FAILURE; } catch (...) { - std::cerr << "[Error] Unknown error parsing command line options\n\n"; + cerr_ << "[Error] Unknown error parsing command line options\n\n"; print_usage(); return EXIT_FAILURE; } @@ -376,11 +402,11 @@ int main(int argc, char** argv) const bool building_local_placement = vmap.count("building-local-placement") != 0; const bool generate_uvs = vmap.count("generate-uvs") != 0; - if (!quiet || vmap.count("version")) { + if (!quiet || vmap.count("version")) { print_version(); } - if (vmap.count("version")) { + if (vmap.count("version")) { return EXIT_SUCCESS; } else if (vmap.count("help")) { print_usage(false); @@ -391,70 +417,7 @@ int main(int argc, char** argv) print_usage(); return EXIT_FAILURE; } - -#ifdef HAVE_ICU - if (!unicode_mode.empty()) { - if (unicode_mode == "utf8") { - IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::UTF8; - } else if (unicode_mode == "escape") { - IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::JSON; - } else { - std::cerr << "[Error] Invalid value for --unicode" << std::endl; - print_options(serializer_options); - return 1; - } - } -#endif - - boost::optional bounding_width; - boost::optional bounding_height; - if (vmap.count("bounds") == 1) { - int w, h; - if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2 && w > 0 && h > 0) { - bounding_width = w; - bounding_height = h; - } else { - std::cerr << "[Error] Invalid use of --bounds" << std::endl; - print_options(serializer_options); - return EXIT_FAILURE; - } - } - - const std::string input_filename = vmap["input-file"].as(); - if (!file_exists(input_filename)) { - std::cerr << "[Error] Input file '" << input_filename << "' does not exist" << std::endl; - return EXIT_FAILURE; - } - - // If no output filename is specified a Wavefront OBJ file will be output - // to maintain backwards compatibility with the obsolete IfcObj executable. - const std::string output_filename = vmap.count("output-file") == 1 - ? vmap["output-file"].as() - : change_extension(input_filename, DEFAULT_EXTENSION); - - if (output_filename.size() < 5) { - std::cerr << "[Error] Invalid or unsupported output file '" << output_filename << "' given" << std::endl; - print_usage(); - return EXIT_FAILURE; - } - - if (file_exists(output_filename) && !vmap.count("yes")) { - std::string answer; - std::cout << "A file '" << output_filename << "' already exists. Overwrite the existing file?" << std::endl; - std::cin >> answer; - if (!boost::iequals(answer, "yes") && !boost::iequals(answer, "y")) { - return EXIT_SUCCESS; - } - } - - std::string output_temp_filename = output_filename + TEMP_FILE_EXTENSION; - - std::string output_extension = output_filename.substr(output_filename.size()-4); - boost::to_lower(output_extension); - - Logger::SetOutput(&std::cout, &log_stream); - Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); - + if (vmap.count("log-format") == 1) { boost::to_lower(log_format); if (log_format == "plain") { @@ -467,23 +430,114 @@ int main(int argc, char** argv) return EXIT_FAILURE; } } + + if (!filter_filename.empty()) { + size_t num_filters = read_filters_from_file(IfcUtil::path::to_utf8(filter_filename), include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter); + if (num_filters) { + Logger::Notice(boost::lexical_cast(num_filters) + " filters read from specifified file."); + } else { + std::cerr << "[Error] No filters read from specifified file.\n"; + return EXIT_FAILURE; + } + } + +#ifdef HAVE_ICU + if (!unicode_mode.empty()) { + if (unicode_mode == "utf8") { + IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::UTF8; + } else if (unicode_mode == "escape") { + IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::JSON; + } else { + cerr_ << "[Error] Invalid value for --unicode" << std::endl; + print_options(serializer_options); + return 1; + } + } +#endif + + if (!default_material_filename.empty()) { + try { + IfcGeom::set_default_style_file(IfcUtil::path::to_utf8(default_material_filename)); + } catch (const std::exception& e) { + std::cerr << "[Error] Could not read default material file:" << std::endl; + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + } + + boost::optional bounding_width; + boost::optional bounding_height; + if (vmap.count("bounds") == 1) { + int w, h; + if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2 && w > 0 && h > 0) { + bounding_width = w; + bounding_height = h; + } else { + cerr_ << "[Error] Invalid use of --bounds" << std::endl; + print_options(serializer_options); + return EXIT_FAILURE; + } + } + + const path_t input_filename = vmap["input-file"].as(); + if (!file_exists(IfcUtil::path::to_utf8(input_filename))) { + cerr_ << "[Error] Input file '" << input_filename << "' does not exist" << std::endl; + return EXIT_FAILURE; + } + + // If no output filename is specified a Wavefront OBJ file will be output + // to maintain backwards compatibility with the obsolete IfcObj executable. + const path_t output_filename = vmap.count("output-file") == 1 + ? vmap["output-file"].as() + : change_extension(input_filename, IfcUtil::path::from_utf8(DEFAULT_EXTENSION)); + + if (output_filename.size() < 5) { + cerr_ << "[Error] Invalid or unsupported output file '" << output_filename << "' given" << std::endl; + print_usage(); + return EXIT_FAILURE; + } + + if (file_exists(IfcUtil::path::to_utf8(output_filename)) && !vmap.count("yes")) { + std::string answer; + cout_ << "A file '" << output_filename << "' already exists. Overwrite the existing file?" << std::endl; + std::cin >> answer; + if (!boost::iequals(answer, "yes") && !boost::iequals(answer, "y")) { + return EXIT_SUCCESS; + } + } + + Logger::SetOutput(&cout_, &log_stream); + Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); + + path_t output_temp_filename = output_filename + IfcUtil::path::from_utf8(TEMP_FILE_EXTENSION); + + path_t output_extension = output_filename.substr(output_filename.size()-4); + boost::to_lower(output_extension); IfcParse::IfcFile ifc_file; - if (output_extension == ".xml") { + const path_t OBJ = IfcUtil::path::from_utf8(".obj"), + MTL = IfcUtil::path::from_utf8(".mtl"), + DAE = IfcUtil::path::from_utf8(".dae"), + STP = IfcUtil::path::from_utf8(".stp"), + IGS = IfcUtil::path::from_utf8(".igs"), + SVG = IfcUtil::path::from_utf8(".svg"), + XML = IfcUtil::path::from_utf8(".xml"); + + if (output_extension == XML) { int exit_code = EXIT_FAILURE; try { - if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { time_t start, end; time(&start); - XmlSerializer s(output_temp_filename); + XmlSerializer s(IfcUtil::path::to_utf8(output_temp_filename)); s.setFile(&ifc_file); Logger::Status("Writing XML output..."); s.finalize(); time(&end); Logger::Status("Done! Conversion took " + format_duration(start, end)); - rename_file(output_temp_filename, output_filename); + IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(output_filename)); exit_code = EXIT_SUCCESS; } } catch (const std::exception& e) { @@ -493,26 +547,6 @@ int main(int argc, char** argv) return exit_code; } - if (!filter_filename.empty()) { - size_t num_filters = read_filters_from_file(filter_filename, include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter); - if (num_filters) { - Logger::Notice(boost::lexical_cast(num_filters) + " filters read from '" + filter_filename + "'."); - } else { - std::cerr << "[Error] No filters read from '" + filter_filename + "'.\n"; - return EXIT_FAILURE; - } - } - - if (!default_material_filename.empty()) { - try { - IfcGeom::set_default_style_file(default_material_filename); - } catch (const std::exception& e) { - std::cerr << "[Error] Could not read default material file " << default_material_filename << ":" << std::endl; - std::cerr << e.what() << std::endl; - return EXIT_FAILURE; - } - } - /// @todo Clean up this filter code further. std::vector used_filters; if (include_filter.type != geom_filter::UNUSED) { used_filters.push_back(include_filter); } @@ -520,9 +554,9 @@ int main(int argc, char** argv) if (exclude_filter.type != geom_filter::UNUSED) { used_filters.push_back(exclude_filter); } if (exclude_traverse_filter.type != geom_filter::UNUSED) { used_filters.push_back(exclude_traverse_filter); } - std::vector filter_funcs = setup_filters(used_filters, output_extension); + std::vector filter_funcs = setup_filters(used_filters, IfcUtil::path::to_utf8(output_extension)); if (filter_funcs.empty()) { - std::cerr << "[Error] Failed to set up geometry filters\n"; + cerr_ << "[Error] Failed to set up geometry filters\n"; return EXIT_FAILURE; } @@ -533,6 +567,20 @@ int main(int argc, char** argv) if (!desc_filter.values.empty()) { desc_filter.update_description(); Logger::Notice(desc_filter.description); } if (!tag_filter.values.empty()) { tag_filter.update_description(); Logger::Notice(tag_filter.description); } +#ifdef _MSC_VER + if (output_extension == DAE || output_extension == STP || output_extension == IGS) { + // These serializers do not support opening unicode paths on Windows. Therefore + // a random temp file is generated using only ASCII characters instead. + std::random_device rng; + std::uniform_int_distribution index_dist(L'A', L'Z'); + output_temp_filename = L".ifcopenshell."; + for (int i = 0; i < 8; ++i) { + output_temp_filename.push_back(static_cast(index_dist(rng))); + } + output_temp_filename += L".tmp"; + } +#endif + SerializerSettings settings; /// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn. settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true); @@ -563,26 +611,26 @@ int main(int argc, char** argv) settings.precision = precision; boost::shared_ptr serializer; /**< @todo use std::unique_ptr when possible */ - if (output_extension == ".obj") { + if (output_extension == OBJ) { // Do not use temp file for MTL as it's such a small file. - const std::string mtl_filename = change_extension(output_filename, "mtl"); + const path_t mtl_filename = change_extension(output_filename, MTL); if (!use_world_coords) { Logger::Notice("Using world coords when writing WaveFront OBJ files"); settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); } - serializer = boost::make_shared(output_temp_filename, mtl_filename, settings); + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), settings); #ifdef WITH_OPENCOLLADA - } else if (output_extension == ".dae") { - serializer = boost::make_shared(output_temp_filename, settings); + } else if (output_extension == DAE) { + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); #endif - } else if (output_extension == ".stp") { - serializer = boost::make_shared(output_temp_filename, settings); - } else if (output_extension == ".igs") { + } else if (output_extension == STP) { + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + } else if (output_extension == IGS) { IGESControl_Controller::Init(); // work around Open Cascade bug - serializer = boost::make_shared(output_temp_filename, settings); - } else if (output_extension == ".svg") { + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + } else if (output_extension == SVG) { settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); - serializer = boost::make_shared(output_temp_filename, settings); + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); if (vmap.count("section-height") != 0) { Logger::Notice("Overriding section height"); static_cast(serializer.get())->setSectionHeight(section_height); @@ -591,18 +639,18 @@ int main(int argc, char** argv) static_cast(serializer.get())->setBoundingRectangle(bounding_width.get(), bounding_height.get()); } } else { - std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n"; + cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n"; write_log(!quiet); print_usage(); return EXIT_FAILURE; } - if (use_element_hierarchy && output_extension != ".dae") { - std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; + if (use_element_hierarchy && output_extension != DAE) { + cerr_ << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; /// @todo Lots of duplicate error-and-exit code. write_log(!quiet); print_usage(); - std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); return EXIT_FAILURE; } @@ -622,7 +670,7 @@ int main(int argc, char** argv) } if (!serializer->ready()) { - std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); write_log(!quiet); return EXIT_FAILURE; } @@ -630,9 +678,9 @@ int main(int argc, char** argv) time_t start,end; time(&start); - if (!init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) { + if (!init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { write_log(!quiet); - std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); /**< @todo Windows Unicode support */ return EXIT_FAILURE; } @@ -641,7 +689,7 @@ int main(int argc, char** argv) /// @todo It would be nice to know and print separate error prints for a case where we found no entities /// and for a case we found no entities that satisfy our filtering criteria. Logger::Error("No geometrical entities found"); - std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); write_log(!quiet); return EXIT_FAILURE; } @@ -676,8 +724,8 @@ int main(int argc, char** argv) offset[2] = -center.Z(); } else { if (sscanf(offset_str.c_str(), "%lf;%lf;%lf", &offset[0], &offset[1], &offset[2]) != 3) { - std::cerr << "[Error] Invalid use of --model-offset\n"; - std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ + cerr_ << "[Error] Invalid use of --model-offset\n"; + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); print_options(serializer_options); return EXIT_FAILURE; } @@ -755,10 +803,10 @@ int main(int argc, char** argv) // Renaming might fail (e.g. maybe the existing file was open in a viewer application) // Do not remove the temp file as user can salvage the conversion result from it. - bool successful = rename_file(output_temp_filename, output_filename); + bool successful = IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(output_filename)); if (!successful) { - Logger::Error("Unable to write output file '" + output_filename + "', see '" + - output_temp_filename + "' for the conversion result."); + cerr_ << "Unable to write output file '" << output_filename << "', see '" << + output_temp_filename << "' for the conversion result."; } write_log(!quiet); @@ -793,12 +841,12 @@ std::string format_duration(time_t start, time_t end) } void write_log(bool header) { - std::string log = log_stream.str(); + path_t log = log_stream.str(); if (!log.empty()) { - if (header) { - std::cout << "\nLog:\n"; - } - std::cout << log << std::endl; + if (header) { + cout_ << "\nLog:\n"; + } + cout_ << log << std::endl; } } @@ -821,7 +869,7 @@ bool init_input_file(const std::string &filename, IfcParse::IfcFile &ifc_file, b } time(&end); - if (no_progress) { Logger::SetOutput(&std::cout, &log_stream); } + if (no_progress) { Logger::SetOutput(&cout_, &log_stream); } else { Logger::Status("Parsing input file took " + format_duration(start, end)); } return true; @@ -833,7 +881,7 @@ bool append_filter(const std::string& type, const std::vector& valu parse_filter(temp, values); // Merge values only if type and arg match. if ((filter.type != geom_filter::UNUSED && filter.type != temp.type) || (!filter.arg.empty() && filter.arg != temp.arg)) { - std::cerr << "[Error] Multiple '" << type << "' filters specified with different criteria\n"; + cerr_ << "[Error] Multiple '" << type.c_str() << "' filters specified with different criteria\n"; return false; } filter.type = temp.type; @@ -849,9 +897,10 @@ size_t read_filters_from_file( exclusion_filter& exclude_filter, exclusion_traverse_filter& exclude_traverse_filter) { - std::ifstream filter_file(filename.c_str()); + std::ifstream filter_file(IfcUtil::path::from_utf8(filename).c_str()); + if (!filter_file.is_open()) { - std::cerr << "[Error] Unable to open filter file '" + filename + "' or the file does not exist.\n"; + cerr_ << "[Error] Unable to open filter file '" << IfcUtil::path::from_utf8(filename) << "' or the file does not exist.\n"; return 0; } @@ -886,11 +935,11 @@ size_t read_filters_from_file( else if (type == "exclude") { if (append_filter("exclude", values, exclude_filter)) { ++num_filters; } } else if (type == "exclude+") { if (append_filter("exclude+", values, exclude_traverse_filter)) { ++num_filters; } } else { - std::cerr << "[Error] Invalid filtering type at line " + boost::lexical_cast(line_number) + "\n"; + cerr_ << "[Error] Invalid filtering type at line " << boost::lexical_cast(line_number) << "\n"; return 0; } } catch(...) { - std::cerr << "[Error] Unable to parse filter at line " + boost::lexical_cast(line_number) + ".\n"; + cerr_ << "[Error] Unable to parse filter at line " << boost::lexical_cast(line_number) << ".\n"; return 0; } } @@ -965,7 +1014,7 @@ std::vector setup_filters(const std::vector& fil try { entity_filter.populate(f.values); } catch (const IfcParse::IfcException& e) { - std::cerr << "[Error] " << e.what() << std::endl; + cerr_ << "[Error] " << e.what() << std::endl; return std::vector(); } } else if (f.type == geom_filter::LAYER_NAME) { @@ -1005,7 +1054,7 @@ std::vector setup_filters(const std::vector& fil } entity_filter.populate(entities); } catch (const IfcParse::IfcException& e) { - std::cerr << "[Error] " << e.what() << std::endl; + cerr_ << "[Error] " << e.what() << std::endl; return std::vector(); } } diff --git a/src/ifcconvert/OpenCascadeBasedSerializer.cpp b/src/ifcconvert/OpenCascadeBasedSerializer.cpp index cf5ae2df62..181d870467 100644 --- a/src/ifcconvert/OpenCascadeBasedSerializer.cpp +++ b/src/ifcconvert/OpenCascadeBasedSerializer.cpp @@ -17,19 +17,21 @@ * * ********************************************************************************/ +#include "OpenCascadeBasedSerializer.h" + +#include "../ifcparse/utils.h" + #include #include #include #include -#include "OpenCascadeBasedSerializer.h" - bool OpenCascadeBasedSerializer::ready() { - std::ofstream test_file(out_filename.c_str(), std::ios_base::binary); + std::ofstream test_file(IfcUtil::path::from_utf8(out_filename).c_str(), std::ios_base::binary); bool succeeded = test_file.is_open(); test_file.close(); - remove(out_filename.c_str()); + IfcUtil::path::delete_file(out_filename); return succeeded; } diff --git a/src/ifcconvert/SvgSerializer.h b/src/ifcconvert/SvgSerializer.h index 8b242aaca3..4f61f96446 100644 --- a/src/ifcconvert/SvgSerializer.h +++ b/src/ifcconvert/SvgSerializer.h @@ -25,6 +25,8 @@ #include "../ifcconvert/GeometrySerializer.h" #include "../ifcconvert/util.h" +#include "../ifcparse/utils.h" + #include #include #include @@ -46,7 +48,7 @@ protected: public: SvgSerializer(const std::string& out_filename, const SerializerSettings& settings) : GeometrySerializer(settings) - , svg_file(out_filename.c_str()) + , svg_file(IfcUtil::path::from_utf8(out_filename).c_str()) , xmin(+std::numeric_limits::infinity()) , ymin(+std::numeric_limits::infinity()) , xmax(-std::numeric_limits::infinity()) diff --git a/src/ifcconvert/WavefrontObjSerializer.cpp b/src/ifcconvert/WavefrontObjSerializer.cpp index 80f92c1574..216fd8a792 100644 --- a/src/ifcconvert/WavefrontObjSerializer.cpp +++ b/src/ifcconvert/WavefrontObjSerializer.cpp @@ -22,9 +22,22 @@ #include "../ifcgeom/IfcGeomRenderStyles.h" +#include "../ifcparse/utils.h" + #include #include +WaveFrontOBJSerializer::WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings) + : GeometrySerializer(settings) + , mtl_filename(mtl_filename) + , obj_stream(IfcUtil::path::from_utf8(obj_filename).c_str()) + , mtl_stream(IfcUtil::path::from_utf8(mtl_filename).c_str()) + , vcount_total(1) +{ + obj_stream << std::setprecision(settings.precision); + mtl_stream << std::setprecision(settings.precision); +} + bool WaveFrontOBJSerializer::ready() { return obj_stream.is_open() && mtl_stream.is_open(); } diff --git a/src/ifcconvert/WavefrontObjSerializer.h b/src/ifcconvert/WavefrontObjSerializer.h index 2aa9708bdc..2acfa890d6 100644 --- a/src/ifcconvert/WavefrontObjSerializer.h +++ b/src/ifcconvert/WavefrontObjSerializer.h @@ -35,17 +35,7 @@ private: unsigned int vcount_total; std::set materials; public: - WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings) - : GeometrySerializer(settings) - , mtl_filename(mtl_filename) - , obj_stream(obj_filename.c_str()) - , mtl_stream(mtl_filename.c_str()) - , vcount_total(1) - { - obj_stream << std::setprecision(settings.precision); - mtl_stream << std::setprecision(settings.precision); - } - + WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings); virtual ~WaveFrontOBJSerializer() {} bool ready(); void writeHeader(); diff --git a/src/ifcconvert/XmlSerializer.cpp b/src/ifcconvert/XmlSerializer.cpp index 6bb3f54a54..6ed1780d19 100644 --- a/src/ifcconvert/XmlSerializer.cpp +++ b/src/ifcconvert/XmlSerializer.cpp @@ -17,8 +17,6 @@ * * ********************************************************************************/ -#include - #include #include #include @@ -26,10 +24,12 @@ #include "XmlSerializer.h" -#include - #include "../ifcparse/IfcSIPrefix.h" #include "../ifcgeom/IfcGeom.h" +#include "../ifcparse/utils.h" + +#include +#include using boost::property_tree::ptree; using namespace IfcSchema; @@ -528,5 +528,7 @@ void XmlSerializer::finalize() { #else boost::property_tree::xml_writer_settings settings('\t', 1); #endif - boost::property_tree::write_xml(xml_filename, root, std::locale(), settings); + + std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str()); + boost::property_tree::write_xml(f, root, settings); } diff --git a/src/ifcgeom/IfcGeomRenderStyles.cpp b/src/ifcgeom/IfcGeomRenderStyles.cpp index 09c211a58b..27b5e2ce8a 100644 --- a/src/ifcgeom/IfcGeomRenderStyles.cpp +++ b/src/ifcgeom/IfcGeomRenderStyles.cpp @@ -200,6 +200,7 @@ void IfcGeom::set_default_style_file(const std::string& json_file) { if (!default_materials_initialized) InitDefaultMaterials(); default_materials.clear(); + // @todo this will probably need to be updated for UTF-8 paths on Windows pt::ptree root; pt::read_json(json_file, root); diff --git a/src/ifcparse/Argument.h b/src/ifcparse/Argument.h index ef7787ac52..bcb1227dd0 100644 --- a/src/ifcparse/Argument.h +++ b/src/ifcparse/Argument.h @@ -52,10 +52,6 @@ namespace IfcUtil { IFC_PARSE_API const char* ArgumentTypeToString(ArgumentType argument_type); IFC_PARSE_API bool valid_binary_string(const std::string& s); - /// Replaces spaces and potentially other problem causing characters with underscores. - IFC_PARSE_API void sanitate_material_name(std::string &str); - IFC_PARSE_API void escape_xml(std::string &str); - IFC_PARSE_API void unescape_xml(std::string &str); } class IFC_PARSE_API Argument { diff --git a/src/ifcparse/IfcLogger.cpp b/src/ifcparse/IfcLogger.cpp index a9b43907dd..d966edec53 100644 --- a/src/ifcparse/IfcLogger.cpp +++ b/src/ifcparse/IfcLogger.cpp @@ -30,35 +30,56 @@ #include #include -using boost::property_tree::ptree; - namespace { - static const char* severity_strings[] = {"Notice", "Warning", "Error"}; + + template + struct severity_strings { + static const std::array, 3> value; + }; - void plain_text_message(std::ostream& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - os << "[" << severity_strings[type] << "] "; + const std::array, 3> severity_strings::value = { "Notice", "Warning", "Error" }; + const std::array, 3> severity_strings::value = { L"Notice", L"Warning", L"Error" }; + + template + void plain_text_message(T& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { + os << "[" << severity_strings::value[type] << "] "; if (current_product) { - os << "{" << (*current_product)->GlobalId() << "} "; + os << "{" << (*current_product)->GlobalId().c_str() << "} "; } - os << message << std::endl; + os << message.c_str() << std::endl; if (entity) { std::string instance_string = entity->toString(); if (instance_string.size() > 259) { instance_string = instance_string.substr(0, 256) + "..."; } - os << instance_string << std::endl; + os << instance_string.c_str() << std::endl; } } - void json_message(std::ostream& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - ptree pt; - pt.put("level", severity_strings[type]); + template + std::basic_string string_as(const std::string& s) { + std::basic_string v; + v.assign(s.begin(), s.end()); + return v; + } + + template + void json_message(T& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { + boost::property_tree::basic_ptree, std::basic_string > pt; + + // @todo this is crazy + static const T::char_type level_string[] = { 'l', 'e', 'v', 'e', 'l', 0 }; + static const T::char_type product_string[] = { 'p', 'r', 'o', 'd', 'u', 'c', 't', 0 }; + static const T::char_type message_string[] = { 'm', 'e', 's', 's', 'a', 'g', 'e', 0 }; + static const T::char_type instance_string[] = { 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 0 }; + + pt.put(level_string, severity_strings::value[type]); if (current_product) { - pt.put("product", (**current_product).entity->toString()); + pt.put(product_string, string_as((**current_product).entity->toString())); } - pt.put("message", message); + pt.put(message_string, string_as(message)); if (entity) { - pt.put("instance", entity); + pt.put(instance_string, string_as(entity->toString())); } boost::property_tree::write_json(os, pt, false); } @@ -68,20 +89,50 @@ void Logger::SetProduct(boost::optional product) { current_product = product; } -void Logger::SetOutput(std::ostream* l1, std::ostream* l2) { +void Logger::SetOutput(std::ostream* l1, std::ostream* l2) { + wlog1 = wlog2 = 0; log1 = l1; log2 = l2; - if ( ! log2 ) { + if (!log2) { log2 = &log_stream; } } +void Logger::SetOutput(std::wostream* l1, std::wostream* l2) { + log1 = log2 = 0; + wlog1 = l1; + wlog2 = l2; + if (!wlog2) { + log2 = &log_stream; + } +} + +template +void Logger::log(T& log2, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { + log2 << "[" << severity_strings[type] << "] "; + if (current_product) { + log2 << "{" << (*current_product)->GlobalId().c_str() << "} "; + } + log2 << message.c_str() << std::endl; + if (entity) { + log2 << entity->toString().c_str() << std::endl; + } +} + void Logger::Message(Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - if (log2 && type >= verbosity) { + if ((log2 || wlog2) && type >= verbosity) { if (format == FMT_PLAIN) { - plain_text_message(*log2, current_product, type, message, entity); + if (log2) { + plain_text_message(*log2, current_product, type, message, entity); + } else if (wlog2) { + plain_text_message(*wlog2, current_product, type, message, entity); + } } else if (format == FMT_JSON) { - json_message(*log2, current_product, type, message, entity); + if (log2) { + json_message(*log2, current_product, type, message, entity); + } else if (wlog2) { + json_message(*wlog2, current_product, type, message, entity); + } } } } @@ -90,18 +141,26 @@ void Logger::Message(Logger::Severity type, const std::exception& exception, Ifc Message(type, exception.what(), entity); } +template +void status(T& log1, const std::string& message, bool new_line) { + log1 << message.c_str(); + if (new_line) { + log1 << std::endl; + } else { + log1 << std::flush; + } +} + void Logger::Status(const std::string& message, bool new_line) { if (log1) { - (*log1) << message; - if ( new_line ) (*log1) << std::endl; - else (*log1) << std::flush; + status(*log1, message, new_line); + } else if (wlog1) { + status(*wlog1, message, new_line); } } void Logger::ProgressBar(int progress) { - if (log1) { - Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false); - } + Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false); } std::string Logger::GetLog() { @@ -116,7 +175,9 @@ Logger::Format Logger::OutputFormat() { return format; } std::ostream* Logger::log1 = 0; std::ostream* Logger::log2 = 0; +std::wostream* Logger::wlog1 = 0; +std::wostream* Logger::wlog2 = 0; std::stringstream Logger::log_stream; Logger::Severity Logger::verbosity = Logger::LOG_NOTICE; Logger::Format Logger::format = Logger::FMT_PLAIN; -boost::optional Logger::current_product; \ No newline at end of file +boost::optional Logger::current_product; diff --git a/src/ifcparse/IfcLogger.h b/src/ifcparse/IfcLogger.h index ce13ee7558..4db2618782 100644 --- a/src/ifcparse/IfcLogger.h +++ b/src/ifcparse/IfcLogger.h @@ -42,14 +42,29 @@ public: typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity; typedef enum { FMT_PLAIN, FMT_JSON } Format; private: + + // To both stream variants need to exist at runtime or should this be a + // template argument of Logger or controlled using preprocessor directives? static std::ostream* log1; static std::ostream* log2; + + static std::wostream* wlog1; + static std::wostream* wlog2; + static std::stringstream log_stream; + static Severity verbosity; static Format format; static boost::optional current_product; + + template + static void log(T& log2, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity); public: static void SetProduct(boost::optional product); + + /// Determines to what stream respectively progress and errors are logged + static void SetOutput(std::wostream* l1, std::wostream* l2); + /// Determines to what stream respectively progress and errors are logged static void SetOutput(std::ostream* l1, std::ostream* l2); diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 0e63c88360..ec54f1ffad 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -25,10 +25,6 @@ #include #include -#ifdef _MSC_VER -#include -#endif - #include #include @@ -39,6 +35,7 @@ #include "../ifcparse/IfcSpfStream.h" #include "../ifcparse/IfcFile.h" #include "../ifcparse/IfcSIPrefix.h" +#include "../ifcparse/utils.h" #ifdef USE_IFC4 #include "../ifcparse/Ifc4-latebound.h" @@ -122,9 +119,8 @@ IfcSpfStream::IfcSpfStream(const std::string& fn) , eof(false) { #ifdef _MSC_VER - int fn_buffer_size = MultiByteToWideChar(CP_UTF8, 0, fn.c_str(), -1, 0, 0); - wchar_t* fn_wide = new wchar_t[fn_buffer_size]; - MultiByteToWideChar(CP_UTF8, 0, fn.c_str(), -1, fn_wide, fn_buffer_size); + std::wstring fn_ws = IfcUtil::path::from_utf8(fn); + const wchar_t* fn_wide = fn_ws.c_str(); #ifdef USE_MMAP if (mmap) { @@ -136,7 +132,6 @@ IfcSpfStream::IfcSpfStream(const std::string& fn) } #endif - delete[] fn_wide; #else #ifdef USE_MMAP diff --git a/src/ifcparse/IfcUtil.cpp b/src/ifcparse/IfcUtil.cpp index fb4c48a815..c4dc183f1a 100644 --- a/src/ifcparse/IfcUtil.cpp +++ b/src/ifcparse/IfcUtil.cpp @@ -17,8 +17,43 @@ * * ********************************************************************************/ +#ifdef _MSC_VER +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef NOMSG +#define NOMSG NOMSG +#endif +#ifndef NODRAWTEXT +#define NODRAWTEXT NODRAWTEXT +#endif +#ifndef NOGDI +#define NOGDI NOGDI +#endif +#ifndef NOSERVICE +#define NOSERVICE NOSERVICE +#endif +#ifndef NOKERNEL +#define NOKERNEL NOKERNEL +#endif +#ifndef NOUSER +#define NOUSER NOUSER +#endif +#ifndef NOMCX +#define NOMCX NOMCX +#endif +#ifndef NOIME +#define NOIME NOIME +#endif +#include +#endif + #include "../ifcparse/IfcBaseClass.h" #include "../ifcparse/Argument.h" +#include "../ifcparse/utils.h" #include "../ifcparse/IfcException.h" #include "../ifcparse/IfcEntityList.h" @@ -198,3 +233,52 @@ Argument* IfcUtil::IfcBaseEntity::getArgumentByName(const std::string& name) con unsigned int i = IfcSchema::Type::GetAttributeIndex(type(), name); return getArgument(i); } + +#ifdef _MSC_VER +std::string IfcUtil::path::to_utf8(const std::wstring& str) { + int buffer_size = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, 0, 0, 0, 0); + char* buffer = new char[buffer_size]; + WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, buffer, buffer_size, 0, 0); + std::string str_utf8(buffer); + delete[] buffer; + return str_utf8; +} + +std::wstring IfcUtil::path::from_utf8(const std::string& str) { + int buffer_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); + wchar_t* buffer = new wchar_t[buffer_size]; + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, buffer_size); + std::wstring str_wide(buffer); + delete[] buffer; + return str_wide; +} + +IFC_PARSE_API bool IfcUtil::path::rename_file(const std::string& old_filename, const std::string& new_filename) { + std::wstring old_filename_w = from_utf8(old_filename); + std::wstring new_filename_w = from_utf8(new_filename); + delete_file(new_filename); + const bool success = !!MoveFileW(old_filename_w.c_str(), new_filename_w.c_str()); + return success; +} + +IFC_PARSE_API bool IfcUtil::path::delete_file(const std::string& filename) { + std::wstring filename_w = from_utf8(filename); + const bool success = !!DeleteFileW(filename_w.c_str()); + return success; +} + +#else + +IFC_PARSE_API bool IfcUtil::path::rename_file(const std::string& old_filename, const std::string& new_filename) { + // Whether or not rename() replaces an existing file is implementation-specific, + // so remove() possible existing file always. + delete_file(new_filename); + return std::rename(old_filename.c_str(), new_filename.c_str()) == 0; +} + +IFC_PARSE_API bool IfcUtil::path::delete_file(const std::string& filename) { + return std::remove(filename.c_str()); +} + +#endif + diff --git a/src/ifcparse/utils.h b/src/ifcparse/utils.h new file mode 100644 index 0000000000..ea60de4689 --- /dev/null +++ b/src/ifcparse/utils.h @@ -0,0 +1,59 @@ +/******************************************************************************** +* * +* 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 . * +* * +********************************************************************************/ + +#include "../ifcparse/ifc_parse_api.h" + +#include + +#ifndef IFCPARSE_UTILS_H +#define IFCPARSE_UTILS_H + +namespace IfcUtil { + + /// Replaces spaces and potentially other problem causing characters with underscores. + IFC_PARSE_API void sanitate_material_name(std::string &str); + + IFC_PARSE_API void escape_xml(std::string &str); + IFC_PARSE_API void unescape_xml(std::string &str); + + namespace path { + + IFC_PARSE_API bool delete_file(const std::string& filename); + IFC_PARSE_API bool rename_file(const std::string& old_filename, const std::string& new_filename); + +#ifdef _MSC_VER + + /// Uses windows.h string conversion functions + IFC_PARSE_API std::string to_utf8(const std::wstring& str); + + /// Uses windows.h string conversion functions + IFC_PARSE_API std::wstring from_utf8(const std::string& str); +#else + /// Identity operation + IFC_PARSE_API inline std::string to_utf8(const std::string& str) { return str; } + + /// Identity operation + IFC_PARSE_API inline std::string from_utf8(const std::string& str) { return str; } +#endif + + } + +} + +#endif From 5db91e52daeb8b7667ded744134554111834a422 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 12 Feb 2019 14:55:03 +0100 Subject: [PATCH 5/9] Fixes to logger templates --- src/ifcparse/IfcLogger.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ifcparse/IfcLogger.cpp b/src/ifcparse/IfcLogger.cpp index d966edec53..a105e5bcf4 100644 --- a/src/ifcparse/IfcLogger.cpp +++ b/src/ifcparse/IfcLogger.cpp @@ -37,12 +37,15 @@ namespace { static const std::array, 3> value; }; + template <> const std::array, 3> severity_strings::value = { "Notice", "Warning", "Error" }; + + template <> const std::array, 3> severity_strings::value = { L"Notice", L"Warning", L"Error" }; template void plain_text_message(T& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - os << "[" << severity_strings::value[type] << "] "; + os << "[" << severity_strings::value[type] << "] "; if (current_product) { os << "{" << (*current_product)->GlobalId().c_str() << "} "; } @@ -65,21 +68,21 @@ namespace { template void json_message(T& os, const boost::optional& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - boost::property_tree::basic_ptree, std::basic_string > pt; + boost::property_tree::basic_ptree, std::basic_string > pt; // @todo this is crazy - static const T::char_type level_string[] = { 'l', 'e', 'v', 'e', 'l', 0 }; - static const T::char_type product_string[] = { 'p', 'r', 'o', 'd', 'u', 'c', 't', 0 }; - static const T::char_type message_string[] = { 'm', 'e', 's', 's', 'a', 'g', 'e', 0 }; - static const T::char_type instance_string[] = { 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 0 }; + static const typename T::char_type level_string[] = { 'l', 'e', 'v', 'e', 'l', 0 }; + static const typename T::char_type product_string[] = { 'p', 'r', 'o', 'd', 'u', 'c', 't', 0 }; + static const typename T::char_type message_string[] = { 'm', 'e', 's', 's', 'a', 'g', 'e', 0 }; + static const typename T::char_type instance_string[] = { 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 0 }; - pt.put(level_string, severity_strings::value[type]); + pt.put(level_string, severity_strings::value[type]); if (current_product) { - pt.put(product_string, string_as((**current_product).entity->toString())); + pt.put(product_string, string_as((**current_product).entity->toString())); } - pt.put(message_string, string_as(message)); + pt.put(message_string, string_as(message)); if (entity) { - pt.put(instance_string, string_as(entity->toString())); + pt.put(instance_string, string_as(entity->toString())); } boost::property_tree::write_json(os, pt, false); } @@ -109,7 +112,7 @@ void Logger::SetOutput(std::wostream* l1, std::wostream* l2) { template void Logger::log(T& log2, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { - log2 << "[" << severity_strings[type] << "] "; + log2 << "[" << severity_strings::value[type] << "] "; if (current_product) { log2 << "{" << (*current_product)->GlobalId().c_str() << "} "; } From d12cacbd2ed9143cd79339caec417438f3ee8d43 Mon Sep 17 00:00:00 2001 From: David Leverton Date: Wed, 13 Feb 2019 15:34:50 +0000 Subject: [PATCH 6/9] Fix Position handling for IfcSurfaceCurveSweptAreaSolid --- src/ifcgeom/IfcGeomShapes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 1beb4cbcc6..c4ba25ce9b 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -885,7 +885,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularTrimmedSurface* l, } bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, TopoDS_Shape& shape) { - gp_Trsf directrix, position; + gp_Trsf directrix; TopoDS_Shape face; TopoDS_Wire wire, section; @@ -964,7 +964,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, if (has_position) { // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D // and therefore has a unit scale factor - shape.Move(position); + shape.Move(trsf); } return true; From acde878946ff789efe94d2e948ed37e2337b5476 Mon Sep 17 00:00:00 2001 From: hlg Date: Thu, 14 Feb 2019 03:05:38 +0800 Subject: [PATCH 7/9] process IfcIndexedPolyCurve without segments #549 --- src/ifcgeom/IfcGeomWires.cpp | 76 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp index 0c66cbcac5..7c1a93a7f0 100644 --- a/src/ifcgeom/IfcGeomWires.cpp +++ b/src/ifcgeom/IfcGeomWires.cpp @@ -873,45 +873,53 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wi BRepBuilderAPI_MakeWire w; - IfcEntityList::ptr segments = l->Segments(); - for (IfcEntityList::it it = segments->begin(); it != segments->end(); ++it) { - IfcUtil::IfcBaseClass* segment = *it; - if (segment->is(IfcSchema::Type::IfcLineIndex)) { - IfcSchema::IfcLineIndex* line = (IfcSchema::IfcLineIndex*) segment; - std::vector indices = *line; - gp_Pnt previous; - for (std::vector::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) { - if (*jt < 1 || *jt > max_index) { - throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast(*jt)); + if(l->hasSegments()) { + IfcEntityList::ptr segments = l->Segments(); + for (IfcEntityList::it it = segments->begin(); it != segments->end(); ++it) { + IfcUtil::IfcBaseClass* segment = *it; + if (segment->is(IfcSchema::Type::IfcLineIndex)) { + IfcSchema::IfcLineIndex* line = (IfcSchema::IfcLineIndex*) segment; + std::vector indices = *line; + gp_Pnt previous; + for (std::vector::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) { + if (*jt < 1 || *jt > max_index) { + throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast(*jt)); + } + const gp_Pnt& current = points[*jt - 1]; + if (jt != indices.begin()) { + w.Add(BRepBuilderAPI_MakeEdge(previous, current)); + } + previous = current; } - const gp_Pnt& current = points[*jt - 1]; - if (jt != indices.begin()) { - w.Add(BRepBuilderAPI_MakeEdge(previous, current)); + } else if (segment->is(IfcSchema::Type::IfcArcIndex)) { + IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment; + std::vector indices = *arc; + if (indices.size() != 3) { + throw IfcParse::IfcException("Invalid IfcArcIndex encountered"); } - previous = current; - } - } else if (segment->is(IfcSchema::Type::IfcArcIndex)) { - IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment; - std::vector indices = *arc; - if (indices.size() != 3) { - throw IfcParse::IfcException("Invalid IfcArcIndex encountered"); - } - for (int i = 0; i < 3; ++i) { - const int& idx = indices[i]; - if (idx < 1 || idx > max_index) { - throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast(idx)); + for (int i = 0; i < 3; ++i) { + const int& idx = indices[i]; + if (idx < 1 || idx > max_index) { + throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast(idx)); + } } + const gp_Pnt& a = points[indices[0] - 1]; + const gp_Pnt& b = points[indices[1] - 1]; + const gp_Pnt& c = points[indices[2] - 1]; + Handle(Geom_Circle) circ = GC_MakeCircle(a, b, c).Value(); + w.Add(BRepBuilderAPI_MakeEdge(circ, a, c)); + } else { + throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + IfcSchema::Type::ToString(segment->type())); } - const gp_Pnt& a = points[indices[0] - 1]; - const gp_Pnt& b = points[indices[1] - 1]; - const gp_Pnt& c = points[indices[2] - 1]; - Handle(Geom_Circle) circ = GC_MakeCircle(a, b, c).Value(); - w.Add(BRepBuilderAPI_MakeEdge(circ, a, c)); - } else { - throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + IfcSchema::Type::ToString(segment->type())); } - } - + } else { + std::vector::const_iterator previous = points.begin(); + for (std::vector::const_iterator current = previous+1; current < points.end(); ++current){ + w.Add(BRepBuilderAPI_MakeEdge(*previous, *current)); + previous = current; + } + } + result = w.Wire(); return true; } From df49f1bc1bfa4495ddb5a94968ce036693d259a6 Mon Sep 17 00:00:00 2001 From: hlg Date: Thu, 14 Feb 2019 03:16:03 +0800 Subject: [PATCH 8/9] prevent index out of bound in case of a single point --- src/ifcgeom/IfcGeomWires.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp index 7c1a93a7f0..46e9e72117 100644 --- a/src/ifcgeom/IfcGeomWires.cpp +++ b/src/ifcgeom/IfcGeomWires.cpp @@ -912,7 +912,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wi throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + IfcSchema::Type::ToString(segment->type())); } } - } else { + } else if (points.begin() < points.end()) { std::vector::const_iterator previous = points.begin(); for (std::vector::const_iterator current = previous+1; current < points.end(); ++current){ w.Add(BRepBuilderAPI_MakeEdge(*previous, *current)); From e4fdfd2eb38048523ba5be99430fe7772f2dc2ed Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 4 Mar 2019 17:23:15 +0100 Subject: [PATCH 9/9] Apply length unit to IfcCylindricalSurface Radius. Fixes #553 --- src/ifcgeom/IfcGeomShapes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index c4ba25ce9b..a8b777bed5 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -1113,9 +1113,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_ // IfcElementarySurface.Position has unit scale factor #if OCC_VERSION_HEX < 0x60502 - face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius())).Face().Moved(trsf); + face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT))).Face().Moved(trsf); #else - face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius()), getValue(GV_PRECISION)).Face().Moved(trsf); + face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); #endif return true; }