diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index e2e8859f7b..117e5451d4 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -138,7 +138,6 @@ if bpy is not None: ui.BIM_PT_drawings, ui.BIM_PT_schedules, ui.BIM_PT_sheets, - ui.BIM_PT_camera, ui.BIM_PT_text, ui.BIM_PT_annotation_utilities, ui.BIM_PT_misc_utilities, diff --git a/src/ifcblenderexport/blenderbim/bim/module/drawing/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/drawing/__init__.py index 998cb5f006..5fd4cf69cc 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/drawing/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/drawing/__init__.py @@ -1,8 +1,10 @@ import bpy -from . import operator +from . import ui, operator classes = ( operator.AddDrawing, + operator.CreateDrawing, + ui.BIM_PT_camera, ) diff --git a/src/ifcblenderexport/blenderbim/bim/module/drawing/operator.py b/src/ifcblenderexport/blenderbim/bim/module/drawing/operator.py index a6f7f3663a..72b3f7ccc8 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/drawing/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/drawing/operator.py @@ -1,6 +1,14 @@ +import os import bpy +import json +import subprocess +import webbrowser +from blenderbim.bim.operator import open_with_user_command from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.group.data import Data as GroupData +from blenderbim.bim.module.pset.data import Data as PsetData +cwd = os.path.dirname(os.path.realpath(__file__)) class AddDrawing(bpy.types.Operator): bl_idname = "bim.add_drawing" @@ -28,6 +36,54 @@ class AddDrawing(bpy.types.Operator): area = next(area for area in context.screen.areas if area.type == "VIEW_3D") area.spaces[0].region_3d.view_perspective = "CAMERA" new.camera = camera - bpy.ops.bim.assign_class(obj=camera.name, ifc_class="IfcAnnotation") + bpy.ops.bim.assign_class(obj=camera.name, ifc_class="IfcAnnotation", predefined_type="DRAWING") bpy.ops.bim.activate_drawing_style() + + bpy.ops.bim.add_group() + bpy.ops.bim.assign_group(product=camera.name, group=sorted(GroupData.groups.keys())[-1]) + bpy.ops.bim.add_pset(obj=camera.name, obj_type="Object", pset_name="EPset_Drawing") + pset_id = sorted(PsetData.products[camera.BIMObjectProperties.ifc_definition_id]["psets"])[-1] + bpy.ops.bim.edit_pset( + obj=camera.name, + obj_type="Object", + pset_id=pset_id, + properties=json.dumps({"TargetView": "PLAN_VIEW", "Scale": "1/100"}), + ) + return {"FINISHED"} + + +class CreateDrawing(bpy.types.Operator): + bl_idname = "bim.create_drawing" + bl_label = "Create Drawing" + + def execute(self, context): + ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert") + subprocess.run( + [ + ifcconvert_path, + context.scene.BIMProperties.ifc_file, + "-yv", + context.scene.BIMProperties.ifc_file[0:-4] + ".svg", + "--plan", + "--model", + "--section-height-from-storeys", + "--section-height=1.2", + "--door-arcs", + "--print-space-names", + "--print-space-areas", + "--bounds=1024x1024", + "--elevation-ref=DRAWING", + "--svg-xmlns", + "--svg-project", + "--svg-poly", + "--exclude", + "entities", + "IfcSpace", + "IfcOpeningElement", + ] + ) + open_with_user_command( + bpy.context.preferences.addons["blenderbim"].preferences.svg_command, + context.scene.BIMProperties.ifc_file[0:-4] + ".svg", + ) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/drawing/ui.py b/src/ifcblenderexport/blenderbim/bim/module/drawing/ui.py new file mode 100644 index 0000000000..fd7400b1f9 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/drawing/ui.py @@ -0,0 +1,108 @@ +import os +import bpy +from bpy.types import Panel +from bpy.props import StringProperty, BoolProperty + + +class BIM_PT_camera(Panel): + bl_label = "Drawing Generation" + bl_idname = "BIM_PT_camera" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "data" + + @classmethod + def poll(cls, context): + engine = context.engine + return context.camera and hasattr(context.active_object.data, "BIMCameraProperties") + + def draw(self, context): + layout = self.layout + + if "/" not in context.active_object.name: + layout.label(text="This is not a BIM camera.") + return + + layout.use_property_split = True + dprops = bpy.context.scene.DocProperties + props = context.active_object.data.BIMCameraProperties + + layout.label(text="Generation Options:") + + row = layout.row() + row.prop(dprops, "should_recut") + row = layout.row() + row.prop(dprops, "should_recut_selected") + row = layout.row() + row.prop(dprops, "should_extract") + + row = layout.row() + row.prop(props, "is_nts") + + row = layout.row() + row.operator("bim.generate_references") + row = layout.row() + row.operator("bim.resize_text") + + row = layout.row() + row.prop(props, "target_view") + + row = layout.row() + row.prop(props, "cut_objects") + if props.cut_objects == "CUSTOM": + row = layout.row() + row.prop(props, "cut_objects_custom") + + row = layout.row() + row.prop(props, "raster_x") + row = layout.row() + row.prop(props, "raster_y") + + row = layout.row() + row.prop(props, "diagram_scale") + if props.diagram_scale == "CUSTOM": + row = layout.row() + row.prop(props, "custom_diagram_scale") + + layout.label(text="Drawing Styles:") + + row = layout.row(align=True) + row.operator("bim.add_drawing_style") + + if dprops.drawing_styles: + layout.template_list("BIM_UL_generic", "", dprops, "drawing_styles", props, "active_drawing_style_index") + + if props.active_drawing_style_index < len(dprops.drawing_styles): + drawing_style = dprops.drawing_styles[props.active_drawing_style_index] + + row = layout.row(align=True) + row.prop(drawing_style, "name") + row.operator("bim.remove_drawing_style", icon="X", text="").index = props.active_drawing_style_index + + row = layout.row() + row.prop(drawing_style, "render_type") + row = layout.row(align=True) + row.prop(drawing_style, "vector_style") + row.operator("bim.edit_vector_style", text="", icon="GREASEPENCIL") + row = layout.row(align=True) + row.prop(drawing_style, "include_query") + row = layout.row(align=True) + row.prop(drawing_style, "exclude_query") + + row = layout.row() + row.operator("bim.add_drawing_style_attribute") + + for index, attribute in enumerate(drawing_style.attributes): + row = layout.row(align=True) + row.prop(attribute, "name", text="") + row.operator("bim.remove_drawing_style_attribute", icon="X", text="").index = index + + row = layout.row(align=True) + row.operator("bim.save_drawing_style") + row.operator("bim.activate_drawing_style") + + row = layout.row(align=True) + row.operator("bim.cut_section", text="Create Drawing") + row.operator("bim.create_drawing", text="Create Drawing 2.0") + op = row.operator("bim.open_view", icon="URL", text="") + op.view = context.active_object.name.split("/")[1] diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py b/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py index 28a93a66c1..7eaa907c3e 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py @@ -51,7 +51,7 @@ class EnablePsetEditing(bpy.types.Operator): elif prop["type"] == "integer": new.int_value = prop["value"] or 0 elif prop["type"] == "float": - new.float_value = prop["value"] or 0. + new.float_value = prop["value"] or 0.0 elif prop["type"] == "boolean": new.bool_value = prop["value"] or False elif prop["type"] == "enum": @@ -84,6 +84,8 @@ class EditPset(bpy.types.Operator): bl_label = "Edit Pset" obj: bpy.props.StringProperty() obj_type: bpy.props.StringProperty() + pset_id: bpy.props.IntProperty() + properties: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() @@ -94,35 +96,47 @@ class EditPset(bpy.types.Operator): oprops = obj.BIMObjectProperties props = obj.PsetProperties properties = {} - data = Data.psets if props.active_pset_id in Data.psets else Data.qtos - for prop in data[props.active_pset_id]["Properties"]: - blender_prop = props.properties.get(prop["Name"]) - if not blender_prop: - continue - if blender_prop.is_null: - properties[prop["Name"]] = None - elif prop["type"] == "string": - properties[prop["Name"]] = blender_prop.string_value - elif prop["type"] == "boolean": - properties[prop["Name"]] = blender_prop.bool_value - elif prop["type"] == "integer": - properties[prop["Name"]] = blender_prop.int_value - elif prop["type"] == "float": - properties[prop["Name"]] = blender_prop.float_value - elif prop["type"] == "enum": - properties[prop["Name"]] = blender_prop.enum_value - if props.active_pset_id in Data.psets: - edit_pset.Usecase(self.file, { - "pset": self.file.by_id(props.active_pset_id), - "Name": props.active_pset_name, - "Properties": properties - }).execute() + + pset_id = self.pset_id or props.active_pset_id + if self.properties: + properties = json.loads(self.properties) else: - edit_qto.Usecase(self.file, { - "qto": self.file.by_id(props.active_pset_id), - "Name": props.active_pset_name, - "Properties": properties - }).execute() + data = Data.psets if pset_id in Data.psets else Data.qtos + for prop in data[pset_id]["Properties"]: + blender_prop = props.properties.get(prop["Name"]) + if not blender_prop: + continue + if blender_prop.is_null: + properties[prop["Name"]] = None + elif prop["type"] == "string": + properties[prop["Name"]] = blender_prop.string_value + elif prop["type"] == "boolean": + properties[prop["Name"]] = blender_prop.bool_value + elif prop["type"] == "integer": + properties[prop["Name"]] = blender_prop.int_value + elif prop["type"] == "float": + properties[prop["Name"]] = blender_prop.float_value + elif prop["type"] == "enum": + properties[prop["Name"]] = blender_prop.enum_value + + if pset_id in Data.psets: + edit_pset.Usecase( + self.file, + { + "pset": self.file.by_id(pset_id), + "Name": props.active_pset_name, + "Properties": properties, + }, + ).execute() + else: + edit_qto.Usecase( + self.file, + { + "qto": self.file.by_id(pset_id), + "Name": props.active_pset_name, + "Properties": properties, + }, + ).execute() Data.load(oprops.ifc_definition_id) bpy.ops.bim.disable_pset_editing(obj=self.obj, obj_type=self.obj_type) return {"FINISHED"} @@ -142,10 +156,13 @@ class RemovePset(bpy.types.Operator): elif self.obj_type == "Material": obj = bpy.data.materials.get(self.obj) props = obj.BIMObjectProperties - remove_pset.Usecase(self.file, { - "product": self.file.by_id(props.ifc_definition_id), - "pset": self.file.by_id(self.pset_id), - }).execute() + remove_pset.Usecase( + self.file, + { + "product": self.file.by_id(props.ifc_definition_id), + "pset": self.file.by_id(self.pset_id), + }, + ).execute() Data.load(props.ifc_definition_id) return {"FINISHED"} @@ -155,6 +172,7 @@ class AddPset(bpy.types.Operator): bl_label = "Add Pset" obj: bpy.props.StringProperty() obj_type: bpy.props.StringProperty() + pset_name: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() @@ -164,10 +182,21 @@ class AddPset(bpy.types.Operator): obj = bpy.data.materials.get(self.obj) oprops = obj.BIMObjectProperties props = obj.PsetProperties - add_pset.Usecase(self.file, { - "product": self.file.by_id(oprops.ifc_definition_id), - "Name": props.pset_name if self.obj_type == "Object" else props.material_pset_name, - }).execute() + + if self.pset_name: + pset_name = self.pset_name + elif self.obj_type == "Object": + pset_name = props.pset_name + elif self.obj_type == "Material": + pset_name = props.material_pset_name + + add_pset.Usecase( + self.file, + { + "product": self.file.by_id(oprops.ifc_definition_id), + "Name": pset_name, + }, + ).execute() Data.load(oprops.ifc_definition_id) return {"FINISHED"} @@ -181,10 +210,13 @@ class AddQto(bpy.types.Operator): obj = bpy.context.active_object oprops = obj.BIMObjectProperties props = obj.PsetProperties - add_qto.Usecase(self.file, { - "product": self.file.by_id(oprops.ifc_definition_id), - "Name": props.qto_name, - }).execute() + add_qto.Usecase( + self.file, + { + "product": self.file.by_id(oprops.ifc_definition_id), + "Name": props.qto_name, + }, + ).execute() Data.load(oprops.ifc_definition_id) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/create_product.py b/src/ifcblenderexport/blenderbim/bim/module/root/create_product.py index 9c28729096..2a460df11b 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/root/create_product.py +++ b/src/ifcblenderexport/blenderbim/bim/module/root/create_product.py @@ -19,10 +19,13 @@ class Usecase: "OwnerHistory": create_owner_history() }) element.Name = self.settings["name"] or None - if self.settings["predefined_type"] and hasattr(element, "PredefinedType"): - try: - element.PredefinedType = self.settings["predefined_type"] - except: - element.PredefinedType = "USERDEFINED" + if self.settings["predefined_type"]: + if hasattr(element, "PredefinedType"): + try: + element.PredefinedType = self.settings["predefined_type"] + except: + element.PredefinedType = "USERDEFINED" + element.ObjectType = self.settings["predefined_type"] + elif hasattr(element, "ObjectType"): element.ObjectType = self.settings["predefined_type"] return element diff --git a/src/ifcblenderexport/blenderbim/bim/svgwriter.py b/src/ifcblenderexport/blenderbim/bim/svgwriter.py index 3006ac3d98..7c8b58873b 100644 --- a/src/ifcblenderexport/blenderbim/bim/svgwriter.py +++ b/src/ifcblenderexport/blenderbim/bim/svgwriter.py @@ -326,6 +326,8 @@ class SvgWriter: # We have to decide whether this should come from Blender or from IFC. # For the moment, for convenience of experimenting with ideas, it comes # from Blender. In the future, it should probably come from IFC. + if not isinstance(obj.data, bpy.types.Mesh): + return classes.extend(self.get_attribute_classes(obj)) if len(obj.data.polygons) == 0: self.draw_edge_annotation(obj, classes) @@ -350,12 +352,8 @@ class SvgWriter: for slot in obj.material_slots: if slot.material: classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", slot.material.name))) - result = obj.BIMObjectProperties.attributes.get("GlobalId") - if not result: - result = obj.BIMObjectProperties.attributes.add() - result.name = "GlobalId" - result.string_value = ifcopenshell.guid.new() - classes.append("globalid-{}".format(result.string_value)) + global_id = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId + classes.append("globalid-{}".format(global_id)) for attribute in self.ifc_cutter.attributes: result = self.get_obj_value(obj, attribute) if result: diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index 3a1d60610b..903f1788c4 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -115,109 +115,6 @@ class BIM_PT_section_plane(Panel): row.operator("bim.remove_section_plane") -class BIM_PT_camera(Panel): - bl_label = "Drawing Generation" - bl_idname = "BIM_PT_camera" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "data" - - @classmethod - def poll(cls, context): - engine = context.engine - return context.camera and hasattr(context.active_object.data, "BIMCameraProperties") - - def draw(self, context): - layout = self.layout - - if "/" not in context.active_object.name: - layout.label(text="This is not a BIM camera.") - return - - layout.use_property_split = True - dprops = bpy.context.scene.DocProperties - props = context.active_object.data.BIMCameraProperties - - layout.label(text="Generation Options:") - - row = layout.row() - row.prop(dprops, "should_recut") - row = layout.row() - row.prop(dprops, "should_recut_selected") - row = layout.row() - row.prop(dprops, "should_extract") - - row = layout.row() - row.prop(props, "is_nts") - - row = layout.row() - row.operator("bim.generate_references") - row = layout.row() - row.operator("bim.resize_text") - - row = layout.row() - row.prop(props, "target_view") - - row = layout.row() - row.prop(props, "cut_objects") - if props.cut_objects == "CUSTOM": - row = layout.row() - row.prop(props, "cut_objects_custom") - - row = layout.row() - row.prop(props, "raster_x") - row = layout.row() - row.prop(props, "raster_y") - - row = layout.row() - row.prop(props, "diagram_scale") - if props.diagram_scale == "CUSTOM": - row = layout.row() - row.prop(props, "custom_diagram_scale") - - layout.label(text="Drawing Styles:") - - row = layout.row(align=True) - row.operator("bim.add_drawing_style") - - if dprops.drawing_styles: - layout.template_list("BIM_UL_generic", "", dprops, "drawing_styles", props, "active_drawing_style_index") - - if props.active_drawing_style_index < len(dprops.drawing_styles): - drawing_style = dprops.drawing_styles[props.active_drawing_style_index] - - row = layout.row(align=True) - row.prop(drawing_style, "name") - row.operator("bim.remove_drawing_style", icon="X", text="").index = props.active_drawing_style_index - - row = layout.row() - row.prop(drawing_style, "render_type") - row = layout.row(align=True) - row.prop(drawing_style, "vector_style") - row.operator("bim.edit_vector_style", text="", icon="GREASEPENCIL") - row = layout.row(align=True) - row.prop(drawing_style, "include_query") - row = layout.row(align=True) - row.prop(drawing_style, "exclude_query") - - row = layout.row() - row.operator("bim.add_drawing_style_attribute") - - for index, attribute in enumerate(drawing_style.attributes): - row = layout.row(align=True) - row.prop(attribute, "name", text="") - row.operator("bim.remove_drawing_style_attribute", icon="X", text="").index = index - - row = layout.row(align=True) - row.operator("bim.save_drawing_style") - row.operator("bim.activate_drawing_style") - - row = layout.row(align=True) - row.operator("bim.cut_section", text="Create Drawing") - op = row.operator("bim.open_view", icon="URL", text="") - op.view = context.active_object.name.split("/")[1] - - class BIM_PT_text(Panel): bl_label = "Text Paper Space" bl_idname = "BIM_PT_text"