diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py
index 62c56e72d2..4e0fedbcc8 100644
--- a/src/blenderbim/blenderbim/bim/__init__.py
+++ b/src/blenderbim/blenderbim/bim/__init__.py
@@ -174,9 +174,8 @@ def unregister():
for cls in reversed(classes):
if getattr(cls, "is_registered", None) is None:
bpy.utils.unregister_class(cls)
- else:
- if cls.is_registered is not False:
- bpy.utils.unregister_class(cls)
+ elif cls.is_registered:
+ bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/__init__.py b/src/blenderbim/blenderbim/bim/module/drawing/__init__.py
index d3bd242d3f..099c09826d 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/__init__.py
@@ -30,7 +30,9 @@ classes = (
operator.AddSchedule,
operator.AddScheduleToSheet,
operator.AddSheet,
+ operator.AddTextLiteral,
operator.BuildSchedule,
+ operator.ChangeSheetTitleBlock,
operator.CleanWireframes,
operator.ContractSheet,
operator.CreateDrawing,
@@ -40,8 +42,6 @@ classes = (
operator.DisableEditingSchedules,
operator.DisableEditingSheets,
operator.DisableEditingText,
- operator.AddTextLiteral,
- operator.RemoveTextLiteral,
operator.DuplicateDrawing,
operator.EditAssignedProduct,
operator.EditText,
@@ -53,20 +53,21 @@ classes = (
operator.LoadDrawings,
operator.LoadSchedules,
operator.LoadSheets,
+ operator.OpenDrawing,
operator.OpenSchedule,
operator.OpenSheet,
- operator.ChangeSheetTitleBlock,
- operator.OpenView,
operator.RemoveDrawing,
operator.RemoveDrawingFromSheet,
operator.RemoveDrawingStyle,
operator.RemoveDrawingStyleAttribute,
operator.RemoveSchedule,
operator.RemoveSheet,
+ operator.RemoveTextLiteral,
+ operator.RenameSheet,
operator.ResizeText,
operator.SaveDrawingStyle,
- operator.SelectDocIfcFile,
operator.SelectAssignedProduct,
+ operator.SelectDocIfcFile,
prop.Variable,
prop.Drawing,
prop.Schedule,
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/data.py b/src/blenderbim/blenderbim/bim/module/drawing/data.py
index e861a57c2e..eb5ad290bc 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/data.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/data.py
@@ -61,7 +61,7 @@ class SheetsData:
@classmethod
def total_sheets(cls):
- return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"])
+ return len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SHEET"])
class DrawingsData:
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
index f1a6ce7bff..073ac5edaf 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
@@ -159,9 +159,13 @@ class BaseDecorator:
"BATTING",
)
for obj in collection.all_objects:
+ if obj.hide_get():
+ continue
+
element = tool.Ifc.get_entity(obj)
if not element:
continue
+
if element.is_a("IfcAnnotation"):
if element.ObjectType == self.objecttype:
results.append(obj)
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
index 09b027ba5d..80026ec1a2 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
@@ -245,7 +245,7 @@ class CreateDrawing(bpy.types.Operator):
elif "" in line:
continue
outfile.write(line)
- shutil.copyfile(underlay[0:-4] + ".png", svg_path[0:-4] + "-underlay.png")
+ shutil.copyfile(os.path.splitext(underlay)[0] + ".png", os.path.splitext(svg_path)[0] + "-underlay.png")
if linework:
with open(linework) as infile:
should_skip = False
@@ -605,6 +605,9 @@ class CreateDrawing(bpy.types.Operator):
return svg_path
elements = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(self.camera_element))
+ filtered_drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
+ elements = [e for e in elements if e in filtered_drawing_elements]
+
annotations = sorted(elements, key=lambda a: tool.Drawing.get_annotation_z_index(a))
self.svg_writer.metadata = tool.Drawing.get_drawing_metadata(self.camera_element)
@@ -736,29 +739,24 @@ class AddDrawingToSheet(bpy.types.Operator, Operator):
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
if not sheet.is_a("IfcDocumentInformation"):
- return {"FINISHED"}
+ return
- if tool.Ifc.get_schema() == "IFC2X3":
- references = sheet.DocumentReferences or []
- else:
- references = sheet.HasDocumentReferences or []
+ references = tool.Drawing.get_document_references(sheet)
has_drawing = False
for reference in references:
if reference.Location == drawing_reference.Location:
has_drawing = True
break
-
if has_drawing:
- return {"FINISHED"}
+ return
+
+ if not tool.Drawing.does_file_exist(tool.Drawing.get_document_uri(drawing_reference)):
+ self.report({"ERROR"}, "The drawing must be generated before adding to a sheet.")
+ return
reference = tool.Ifc.run("document.add_reference", information=sheet)
- if tool.Ifc.get_schema() == "IFC2X3":
- references = sheet.DocumentReferences
- id_attr = "ItemReference"
- else:
- references = sheet.HasDocumentReferences
- id_attr = "Identification"
+ id_attr = "ItemReference" if tool.Ifc.get_schema() == "IFC2X3" else "Identification"
attributes = {
id_attr: str(len([r for r in references if r.Description in ("DRAWING", "SCHEDULE")]) + 1),
"Location": drawing_reference.Location,
@@ -770,16 +768,15 @@ class AddDrawingToSheet(bpy.types.Operator, Operator):
sheet_builder.add_drawing(reference, drawing, sheet)
tool.Drawing.import_sheets()
- return {"FINISHED"}
-class RemoveDrawingFromSheet(bpy.types.Operator):
+class RemoveDrawingFromSheet(bpy.types.Operator, Operator):
bl_idname = "bim.remove_drawing_from_sheet"
bl_label = "Remove Drawing From Sheet"
bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty()
- def execute(self, context):
+ def _execute(self, context):
reference = tool.Ifc.get().by_id(self.reference)
sheet = tool.Drawing.get_reference_document(reference)
@@ -787,48 +784,55 @@ class RemoveDrawingFromSheet(bpy.types.Operator):
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.remove_drawing(reference, sheet)
- drawing = tool.Drawing.get_reference_element(reference)
- if drawing:
- tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
-
tool.Ifc.run("document.remove_reference", reference=reference)
tool.Drawing.import_sheets()
- return {"FINISHED"}
-class CreateSheets(bpy.types.Operator):
+class CreateSheets(bpy.types.Operator, Operator):
bl_idname = "bim.create_sheets"
bl_label = "Create Sheets"
- # TODO: check undo redo
+ bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.DocProperties.sheets and context.scene.BIMProperties.data_dir
- def execute(self, context):
+ def _execute(self, context):
scene = context.scene
props = scene.DocProperties
active_sheet = props.sheets[props.active_sheet_index]
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
if not sheet.is_a("IfcDocumentInformation"):
- return {"FINISHED"}
+ return
name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir
+ references = sheet_builder.build(sheet)
+ raster_references = [tool.Ifc.get_relative_uri(r) for r in references["RASTER"]]
+
# These variables will be made available to the evaluated commands
- svg = sheet_builder.build(sheet)
+ svg = references["SHEET"]
basename = os.path.basename(svg)
path = os.path.dirname(svg)
pdf = os.path.splitext(svg)[0] + ".pdf"
eps = os.path.splitext(svg)[0] + ".eps"
dxf = os.path.splitext(svg)[0] + ".dxf"
- references = getattr(sheet, "HasDocumentReferences", getattr(sheet, "DocumentReferences", []))
- if not [r for r in references if r.Description == "SHEET"]:
+ has_sheet_reference = False
+ for reference in tool.Drawing.get_document_references(sheet):
+ if reference.Description == "SHEET":
+ has_sheet_reference = True
+ elif reference.Description == "RASTER":
+ if reference.Location in raster_references:
+ del raster_references[reference.Location]
+ else:
+ tool.Ifc.run("document.remove_reference", reference=reference)
+
+ if not has_sheet_reference:
reference = tool.Ifc.run("document.add_reference", information=sheet)
tool.Ifc.run(
"document.edit_reference",
@@ -836,6 +840,14 @@ class CreateSheets(bpy.types.Operator):
attributes={"Location": tool.Ifc.get_relative_uri(svg), "Description": "SHEET"},
)
+ for raster_reference in raster_references:
+ reference = tool.Ifc.run("document.add_reference", information=sheet)
+ tool.Ifc.run(
+ "document.edit_reference",
+ reference=reference,
+ attributes={"Location": tool.Ifc.get_relative_uri(raster_reference), "Description": "RASTER"},
+ )
+
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
@@ -857,37 +869,43 @@ class CreateSheets(bpy.types.Operator):
open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
else:
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
- return {"FINISHED"}
-class ChangeSheetTitleBlock(bpy.types.Operator):
+class ChangeSheetTitleBlock(bpy.types.Operator, Operator):
bl_idname = "bim.change_sheet_title_block"
bl_label = "Change Sheet Title Block"
bl_description = "Change the title block of the active sheet"
bl_options = {"REGISTER"}
- def execute(self, context):
+ def _execute(self, context):
scene = context.scene
props = scene.DocProperties
if not len(props.sheets):
return {"CANCELLED"}
+ titleblock = scene.DocProperties.titleblock
+
active_sheet = props.sheets[props.active_sheet_index]
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
+ for reference in tool.Drawing.get_document_references(sheet):
+ description = tool.Drawing.get_reference_description(reference)
+ if description == "TITLEBLOCK":
+ tool.Ifc.run(
+ "document.edit_reference",
+ reference=reference,
+ attributes={"Location": tool.Drawing.get_default_titleblock_path(titleblock)},
+ )
+
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir
- titleblock = scene.DocProperties.titleblock
sheet_builder.change_titleblock(sheet, titleblock)
- self.report({"INFO"}, 'Title block changed for sheet "{}" to {}'.format(sheet.Name, titleblock))
- return {"FINISHED"}
-
-class OpenView(bpy.types.Operator):
- bl_idname = "bim.open_view"
- bl_label = "Open View"
+class OpenDrawing(bpy.types.Operator):
+ bl_idname = "bim.open_drawing"
+ bl_label = "Open Drawing"
view: bpy.props.StringProperty()
bl_description = (
"Opens a .svg drawing based on currently active camera with default system viewer\n"
@@ -1226,27 +1244,22 @@ class AddScheduleToSheet(bpy.types.Operator, Operator):
if not sheet.is_a("IfcDocumentInformation"):
return
- if tool.Ifc.get_schema() == "IFC2X3":
- references = sheet.DocumentReferences or []
- else:
- references = sheet.HasDocumentReferences or []
+ references = tool.Drawing.get_document_references(sheet)
has_schedule = False
for reference in references:
if reference.Location == schedule_location:
has_schedule = True
break
-
if has_schedule:
return
+ if not tool.Drawing.does_file_exist(tool.Ifc.resolve_uri(schedule_location)):
+ self.report({"ERROR"}, "The schedule must be generated before adding to a sheet.")
+ return
+
reference = tool.Ifc.run("document.add_reference", information=sheet)
- if tool.Ifc.get_schema() == "IFC2X3":
- references = sheet.DocumentReferences
- id_attr = "ItemReference"
- else:
- references = sheet.HasDocumentReferences
- id_attr = "Identification"
+ id_attr = "ItemReference" if tool.Ifc.get_schema() == "IFC2X3" else "Identification"
attributes = {
id_attr: str(len([r for r in references if r.Description in ("DRAWING", "SCHEDULE")]) + 1),
"Location": schedule_location,
@@ -1456,7 +1469,7 @@ class EditAssignedProduct(bpy.types.Operator, Operator):
class EnableEditingAssignedProduct(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_assigned_product"
- bl_label = "Enable Editing Text Product"
+ bl_label = "Enable Editing Assigned Product"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -1465,7 +1478,7 @@ class EnableEditingAssignedProduct(bpy.types.Operator, Operator):
class DisableEditingAssignedProduct(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_assigned_product"
- bl_label = "Disable Editing Text Product"
+ bl_label = "Disable Editing Assigned Product"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -1498,9 +1511,36 @@ class LoadSheets(bpy.types.Operator, Operator):
self.report({"ERROR"}, "Some sheets svg files are missing:\n" + "\n".join(sheets_not_found))
+class RenameSheet(bpy.types.Operator, Operator):
+ bl_idname = "bim.rename_sheet"
+ bl_label = "Rename Sheet"
+ bl_options = {"REGISTER", "UNDO"}
+ identification: bpy.props.StringProperty()
+ name: bpy.props.StringProperty()
+
+ def invoke(self, context, event):
+ self.props = context.scene.DocProperties
+ sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
+ self.identification = sheet.Identification
+ self.name = sheet.Name
+ return context.window_manager.invoke_props_dialog(self)
+
+ def draw(self, context):
+ row = self.layout.row()
+ row.prop(self, "identification", text="Identification")
+ row = self.layout.row()
+ row.prop(self, "name", text="Name")
+
+ def _execute(self, context):
+ self.props = context.scene.DocProperties
+ sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
+ core.rename_sheet(tool.Ifc, tool.Drawing, sheet=sheet, identification=self.identification, name=self.name)
+ tool.Drawing.import_sheets()
+
+
class DisableEditingSheets(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_sheets"
- bl_label = "Disable Editing Text Product"
+ bl_label = "Disable Editing Sheets"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -1518,7 +1558,7 @@ class LoadSchedules(bpy.types.Operator, Operator):
class DisableEditingSchedules(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_schedules"
- bl_label = "Disable Editing Text Product"
+ bl_label = "Disable Editing Schedules"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -1536,7 +1576,7 @@ class LoadDrawings(bpy.types.Operator, Operator):
class DisableEditingDrawings(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_drawings"
- bl_label = "Disable Editing Text Product"
+ bl_label = "Disable Editing Drawings"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
index b8e68e4b7d..84ffb3cb22 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
@@ -248,26 +248,9 @@ class Schedule(PropertyGroup):
class Sheet(PropertyGroup):
- def set_name(self, new):
- old = self.get("name")
-
- if new == old:
- return
- sheet = tool.Ifc.get().by_id(self.ifc_definition_id)
- old_path = Path(tool.Drawing.get_document_uri(sheet))
- core.update_sheet_name(tool.Ifc, tool.Drawing, sheet=sheet, name=new)
- self["name"] = new
-
- new_path = Path(tool.Drawing.get_document_uri(sheet))
- if old and old_path.is_file():
- old_path.rename(new_path)
-
- def get_name(self):
- return self.get("name")
-
ifc_definition_id: IntProperty(name="IFC Definition ID")
identification: StringProperty(name="Identification")
- name: StringProperty(name="Name", get=get_name, set=set_name)
+ name: StringProperty(name="Name")
is_sheet: BoolProperty(name="Is Sheet", default=False)
reference_type: StringProperty(name="Reference Type")
is_expanded: BoolProperty(name="Is Expanded", default=False)
@@ -354,11 +337,15 @@ class DocProperties(PropertyGroup):
decorations_colour: FloatVectorProperty(
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
)
- docs_dir: StringProperty(default=os.path.join("docs") + os.path.sep, name="Default Docs Directory")
sheets_dir: StringProperty(default=os.path.join("sheets") + os.path.sep, name="Default Sheets Directory")
- titleblocks_dir: StringProperty(default=os.path.join("sheets", "titleblocks") + os.path.sep, name="Default Titleblocks Directory")
+ layouts_dir: StringProperty(default=os.path.join("layouts") + os.path.sep, name="Default Layouts Directory")
+ titleblocks_dir: StringProperty(
+ default=os.path.join("layouts", "titleblocks") + os.path.sep, name="Default Titleblocks Directory"
+ )
drawings_dir: StringProperty(default=os.path.join("drawings") + os.path.sep, name="Default Drawings Directory")
- stylesheet_path: StringProperty(default=os.path.join("drawings", "assets", "default.css"), name="Default Stylesheet")
+ stylesheet_path: StringProperty(
+ default=os.path.join("drawings", "assets", "default.css"), name="Default Stylesheet"
+ )
markers_path: StringProperty(default=os.path.join("drawings", "assets", "markers.svg"), name="Default Markers")
symbols_path: StringProperty(default=os.path.join("drawings", "assets", "symbols.svg"), name="Default Symbols")
patterns_path: StringProperty(default=os.path.join("drawings", "assets", "patterns.svg"), name="Default Patterns")
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
index fdffe1f40f..2c99331b71 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
@@ -34,8 +34,8 @@ class SheetBuilder:
self.data_dir = None
self.scale = "NTS"
- def create(self, sheet_path, titleblock_name):
- sheet_dir = os.path.dirname(sheet_path)
+ def create(self, layout_path, titleblock_name):
+ sheet_dir = os.path.dirname(layout_path)
root = ET.Element("svg")
root.attrib["xmlns"] = "http://www.w3.org/2000/svg"
@@ -43,16 +43,16 @@ class SheetBuilder:
root.attrib["id"] = "root"
root.attrib["version"] = "1.1"
- titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
- view_root = ET.parse(titleblock_path).getroot()
+ ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
+ titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name))
+
+ view_root = ET.parse(ootb_titleblock_path).getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
view = ET.SubElement(root, "g")
view.attrib["data-type"] = "titleblock"
titleblock = ET.SubElement(view, "image")
- titleblock.attrib["xlink:href"] = os.path.relpath(
- tool.Drawing.get_default_titleblock_path(titleblock_name), sheet_dir
- )
+ titleblock.attrib["xlink:href"] = os.path.relpath(titleblock_path, sheet_dir)
titleblock.attrib["x"] = "0"
titleblock.attrib["y"] = "0"
titleblock.attrib["width"] = str(view_width)
@@ -63,24 +63,22 @@ class SheetBuilder:
root.attrib["viewBox"] = "0 0 {} {}".format(view_width, view_height)
os.makedirs(sheet_dir, exist_ok=True)
- os.makedirs(os.path.join(sheet_dir, "titleblocks"), exist_ok=True)
- sheet_titleblock_path = os.path.join(sheet_dir, "titleblocks", titleblock_name + ".svg")
- if not os.path.exists(sheet_titleblock_path):
- shutil.copy(titleblock_path, sheet_titleblock_path)
- with open(sheet_path, "w") as f:
+ os.makedirs(os.path.dirname(titleblock_path), exist_ok=True)
+ if not os.path.exists(titleblock_path):
+ shutil.copy(ootb_titleblock_path, titleblock_path)
+
+ with open(layout_path, "w") as f:
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
def add_drawing(self, reference, drawing, sheet):
filename = drawing.Name
sheet_path = tool.Drawing.get_document_uri(sheet)
- sheet_name = os.path.splitext(os.path.basename(sheet_path))[0]
sheet_dir = os.path.dirname(sheet_path)
drawing_path = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_reference(drawing))
- drawing_path = tool.Ifc.resolve_uri(drawing_path)
underlay_path = os.path.splitext(drawing_path)[0] + "-underlay.png"
- if not os.path.isfile(sheet_path):
+ if not os.path.exists(sheet_path) or not os.path.exists(drawing_path):
raise FileNotFoundError
ET.register_namespace("", "http://www.w3.org/2000/svg")
@@ -190,18 +188,20 @@ class SheetBuilder:
title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height")))
def build(self, sheet):
- sheet_path = tool.Drawing.get_document_uri(sheet)
- sheet_name = os.path.splitext(os.path.basename(sheet_path))[0]
- self.sheet_dir = os.path.dirname(sheet_path)
+ self.references = {"SHEET": None, "RASTER": []}
- docs_dir = tool.Ifc.resolve_uri(os.path.join(bpy.context.scene.DocProperties.docs_dir, sheet_name))
+ layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
+ self.layout_dir = os.path.dirname(layout_path)
- os.makedirs(docs_dir, exist_ok=True)
+ output_filename = tool.Ifc.resolve_uri(tool.Drawing.get_default_sheet_path(sheet[0], sheet.Name))
+ self.sheets_dir = os.path.dirname(output_filename)
+
+ os.makedirs(self.sheets_dir, exist_ok=True)
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
- tree = ET.parse(sheet_path)
+ tree = ET.parse(layout_path)
root = tree.getroot()
self.defs = ET.Element("defs")
@@ -211,12 +211,12 @@ class SheetBuilder:
self.build_drawings(root, sheet)
self.build_schedules(root)
- output_filename = os.path.join(docs_dir, f"{sheet_name}.svg")
-
with open(output_filename, "wb") as output:
tree.write(output)
- return output_filename
+ self.references["SHEET"] = output_filename
+
+ return self.references
def build_titleblock(self, root, sheet):
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
@@ -232,8 +232,6 @@ class SheetBuilder:
titleblock.remove(image)
def build_drawings(self, root, sheet):
- sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
-
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
@@ -256,8 +254,10 @@ class SheetBuilder:
view.append(self.parse_embedded_svg(foreground, {}))
if background is not None:
- background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
- shutil.copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
+ background_path = os.path.join(self.layout_dir, self.get_href(background))
+ raster_path = os.path.join(self.sheets_dir, os.path.basename(background_path))
+ shutil.copy(background_path, raster_path)
+ self.references["RASTER"].append(raster_path)
if view_title is not None:
foreground_path = self.get_href(foreground)
@@ -322,7 +322,7 @@ class SheetBuilder:
self.defs.append(clip_path)
svg_path = self.get_href(image)
- with open(os.path.join(self.sheet_dir, svg_path), "r") as template:
+ with open(os.path.join(self.layout_dir, svg_path), "r") as template:
embedded = ET.fromstring(pystache.render(template.read(), data))
# viewBox should not be nested
embedded.attrib["viewBox"] = ""
@@ -339,23 +339,29 @@ class SheetBuilder:
return group
def change_titleblock(self, sheet, titleblock_name):
+ ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
+ titleblock_path = tool.Drawing.get_default_titleblock_path(titleblock_name)
+ sheet_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
+ sheet_dir = os.path.dirname(sheet_path)
+
+ os.makedirs(sheet_dir, exist_ok=True)
+ os.makedirs(os.path.join(sheet_dir, "titleblocks"), exist_ok=True)
+ if not os.path.exists(titleblock_path):
+ shutil.copy(ootb_titleblock_path, titleblock_path)
+
ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
- titleblock_svg_path = os.path.join(self.data_dir, "templates", "titleblocks", f"{titleblock_name}.svg")
- view_root = ET.parse(titleblock_svg_path).getroot()
+ view_root = ET.parse(ootb_titleblock_path).getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
- sheet_dir = os.path.join(self.data_dir, "sheets")
- sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
- sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
sheet_tree = ET.parse(sheet_path)
root = sheet_tree.getroot()
titleblock = sheet_tree.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image[@{http://www.w3.org/1999/xlink}href]")[0]
- image.attrib["{http://www.w3.org/1999/xlink}href"] = f"../templates/titleblocks/{titleblock_name}.svg"
+ image.attrib["{http://www.w3.org/1999/xlink}href"] = os.path.relpath(titleblock_path, sheet_dir)
image.attrib["width"] = str(view_width)
image.attrib["height"] = str(view_height)
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
index 972aa415ab..8108a5f2a8 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
@@ -85,13 +85,7 @@ class SvgWriter:
self.svg.save(pretty=True)
def draw_underlay(self, image):
- self.svg.add(
- self.svg.image(
- os.path.join("..", "diagrams", os.path.basename(image)),
- width=self.width,
- height=self.height,
- )
- )
+ self.svg.add(self.svg.image(os.path.basename(image), width=self.width, height=self.height))
return self
def setup_drawing_resource_paths(self, element):
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/ui.py b/src/blenderbim/blenderbim/bim/module/drawing/ui.py
index 0991580711..cb99a3574c 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/ui.py
@@ -84,7 +84,7 @@ class BIM_PT_camera(Panel):
row = layout.row(align=True)
row.operator("bim.create_drawing", text="Create Drawing", icon="OUTPUT")
- op = row.operator("bim.open_view", icon="URL", text="")
+ op = row.operator("bim.open_drawing", icon="URL", text="")
op.view = context.active_object.name.split("/")[1]
@@ -185,7 +185,7 @@ class BIM_PT_drawings(Panel):
).drawing = active_drawing.ifc_definition_id
col = row.column()
col.alignment = "RIGHT"
- op = row.operator("bim.open_view", icon="URL", text="")
+ op = row.operator("bim.open_drawing", icon="URL", text="")
op.view = active_drawing.name
op = row.operator("bim.activate_view", icon="OUTLINER_OB_CAMERA", text="")
op.drawing = active_drawing.ifc_definition_id
@@ -281,6 +281,7 @@ class BIM_PT_sheets(Panel):
active_sheet = self.props.sheets[self.props.active_sheet_index]
row = self.layout.row(align=True)
row.alignment = "RIGHT"
+ row.operator("bim.rename_sheet", icon="GREASEPENCIL", text="")
row.operator("bim.open_sheet", icon="URL", text="")
row.operator("bim.add_drawing_to_sheet", icon="IMAGE_PLANE", text="")
row.operator("bim.add_schedule_to_sheet", icon="PRESET_NEW", text="")
@@ -510,7 +511,7 @@ class BIM_UL_sheets(bpy.types.UIList):
layout.label(text="", translate=False)
return
- row = layout.row()
+ row = layout.row(align=True)
if item.is_sheet:
if item.is_expanded:
row.operator(
@@ -520,8 +521,8 @@ class BIM_UL_sheets(bpy.types.UIList):
row.operator(
"bim.expand_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).sheet = item.ifc_definition_id
- name = "{} - {}".format(item.identification or "X", item.name or "Unnamed")
- row.prop(item, "name", text=item.identification or "X", emboss=False)
+
+ row.label(text=f"{item.identification} - {item.name}")
else:
row.label(text="", icon="BLANK1")
if item.reference_type == "DRAWING":
@@ -530,6 +531,9 @@ class BIM_UL_sheets(bpy.types.UIList):
row.label(text="", icon="LONGDISPLAY")
elif item.reference_type == "TITLEBLOCK":
row.label(text="", icon="MENU_PANEL")
+ elif item.reference_type == "REVISION":
+ row.label(text="", icon="RECOVER_LAST")
+
if item.identification:
name = f"{item.identification} - {item.name or 'Unnamed'}"
else:
diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py
index c391532a58..5992a33b5c 100644
--- a/src/blenderbim/blenderbim/bim/module/material/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/material/operator.py
@@ -516,7 +516,7 @@ class EditAssignedMaterial(bpy.types.Operator, tool.Ifc.Operator):
class EnableEditingMaterialSetItemProfile(bpy.types.Operator):
bl_idname = "bim.enable_editing_material_set_item_profile"
- bl_label = "Enable Editing Material Set Item"
+ bl_label = "Enable Editing Material Set Item Profile"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
material_set_item: bpy.props.IntProperty()
@@ -532,7 +532,7 @@ class EnableEditingMaterialSetItemProfile(bpy.types.Operator):
class DisableEditingMaterialSetItemProfile(bpy.types.Operator):
bl_idname = "bim.disable_editing_material_set_item_profile"
- bl_label = "Disable Editing Material Set Item"
+ bl_label = "Disable Editing Material Set Item Profile"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py
index 06a12d50b8..0f3f4e8fee 100644
--- a/src/blenderbim/blenderbim/bim/module/model/product.py
+++ b/src/blenderbim/blenderbim/bim/module/model/product.py
@@ -402,7 +402,7 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.mirror_elements"
bl_label = "Mirror Elements"
bl_options = {"REGISTER", "UNDO"}
- bl_description = "Faux-mirrors the selected object by an active empty along a mirror plane."
+ bl_description = "Faux-mirrors the selected objects by an active empty along a mirror plane"
@classmethod
def poll(cls, context):
diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py
index 75419a3f44..54f044fc08 100644
--- a/src/blenderbim/blenderbim/bim/module/model/workspace.py
+++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py
@@ -68,7 +68,17 @@ class BimTool(WorkSpaceTool):
def add_layout_hotkey_operator(layout, text, hotkey, description):
- op = layout.operator("bim.hotkey", text=text)
+ modifiers = {
+ "A": "EVENT_ALT",
+ "S": "EVENT_SHIFT",
+ }
+ modifier, key = hotkey.split("_")
+
+ row = layout.row(align=True)
+ row.label(text="", icon=modifiers[modifier])
+ row.label(text="", icon=f"EVENT_{key}")
+
+ op = row.operator("bim.hotkey", text=text)
op.hotkey = hotkey
op.description = description
return op
@@ -132,10 +142,7 @@ class BimToolUI:
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="rl2", text="RL")
elif cls.props.ifc_class in ("IfcSpaceType"):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_G")
- add_layout_hotkey_operator(row, "Generate", "S_G", bpy.ops.bim.generate_space.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Generate", "S_G", bpy.ops.bim.generate_space.__doc__)
@classmethod
def draw_edit_object_interface(cls, context):
@@ -155,45 +162,19 @@ class BimToolUI:
op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="")
op.x_angle = cls.props.x_angle
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Extend").hotkey = "S_E"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_T")
- row.operator("bim.hotkey", text="Butt").hotkey = "S_T"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_Y")
- row.operator("bim.hotkey", text="Mitre").hotkey = "S_Y"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_M")
- add_layout_hotkey_operator(row, "Merge", "S_M", bpy.ops.bim.merge_wall.__doc__)
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_F")
- add_layout_hotkey_operator(row, "Flip", "S_F", bpy.ops.bim.flip_wall.__doc__)
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_K")
- add_layout_hotkey_operator(row, "Split", "S_K", bpy.ops.bim.split_wall.__doc__)
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_R")
- row.operator("bim.hotkey", text="Rotate 90").hotkey = "S_R"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_G")
- add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.recalculate_wall.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
+ add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
+ add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
+
+ add_layout_hotkey_operator(cls.layout, "Merge", "S_M", bpy.ops.bim.merge_wall.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_wall.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Split", "S_K", bpy.ops.bim.split_wall.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_wall.__doc__)
row.operator("bim.join_wall", icon="X", text="").join_type = ""
elif AuthoringData.data["active_material_usage"] == "LAYER3":
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Profile").hotkey = "S_E"
+ add_layout_hotkey_operator(cls.layout, "Edit Profile", "S_E", "")
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="x_angle", text="X Angle")
@@ -213,36 +194,16 @@ class BimToolUI:
op = row.operator("bim.change_profile_depth", icon="FILE_REFRESH", text="")
op.depth = cls.props.extrusion_depth
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Extend").hotkey = "S_E"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_ALT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Axis").hotkey = "A_E"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_T")
- row.operator("bim.hotkey", text="Butt").hotkey = "S_T"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_Y")
- row.operator("bim.hotkey", text="Mitre").hotkey = "S_Y"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_R")
- row.operator("bim.hotkey", text="Rotate 90").hotkey = "S_R"
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_G")
- add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
+ add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
+ add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
+ add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
+ add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
row.operator("bim.extend_profile", icon="X", text="").join_type = ""
+
elif AuthoringData.data["active_representation_type"] == "SweptSolid":
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Profile").hotkey = "S_E"
+ add_layout_hotkey_operator(cls.layout, "Edit Profile", "S_E", "")
elif AuthoringData.data["active_class"] in (
"IfcWindow",
@@ -257,20 +218,11 @@ class BimToolUI:
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="rl1", text="RL")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_G")
- add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.recalculate_fill.__doc__)
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_F")
- row.operator("bim.hotkey", text="Flip").hotkey = "S_F"
+ add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_fill.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Flip", "S_F", "")
elif AuthoringData.data["active_class"] in ("IfcSpace",):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_G")
- add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
+ add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
elif AuthoringData.data["active_class"] in (
"IfcCableCarrierSegmentType",
@@ -278,39 +230,29 @@ class BimToolUI:
"IfcDuctSegmentType",
"IfcPipeSegmentType",
):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="Extend", icon="EVENT_E")
+ add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
elif (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and not context.active_object.BIMRailingProperties.is_editing_path
):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Railing Path").hotkey = "S_E"
+ add_layout_hotkey_operator(cls.layout, "Edit Railing Path", "S_E", "")
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and not context.active_object.BIMRoofProperties.is_editing_path
):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Roof Path").hotkey = "S_E"
+ add_layout_hotkey_operator(cls.layout, "Edit Roof Path", "S_E", "")
elif DecoratorData.get_ifc_text_data(bpy.context.object):
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="", icon="EVENT_E")
- row.operator("bim.hotkey", text="Edit Text").hotkey = "S_E"
+ add_layout_hotkey_operator(cls.layout, "Edit Text", "S_E", "")
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_O")
+
if len(context.selected_objects) == 2:
row.operator("bim.add_opening", text="Apply Void")
else:
@@ -323,27 +265,14 @@ class BimToolUI:
else:
row.operator("bim.show_openings", icon="HIDE_OFF", text="")
- row = cls.layout.row(align=True)
- row.label(text="Align")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="Align Exterior", icon="EVENT_X")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="Align Centerline", icon="EVENT_C")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="Align Interior", icon="EVENT_V")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_SHIFT")
- row.label(text="Mirror", icon="EVENT_M")
+ cls.layout.row(align=True).label(text="Align")
+ add_layout_hotkey_operator(cls.layout, "Align Exterior", "S_X", "")
+ add_layout_hotkey_operator(cls.layout, "Align Centerline", "S_C", "")
+ add_layout_hotkey_operator(cls.layout, "Align Interior", "S_V", "")
+ add_layout_hotkey_operator(cls.layout, "Mirror", "S_M", bpy.ops.bim.mirror_elements.__doc__)
- row = cls.layout.row(align=True)
- row.label(text="Mode")
- row = cls.layout.row(align=True)
- row.label(text="", icon="EVENT_ALT")
- row.label(text="", icon="EVENT_O")
- add_layout_hotkey_operator(row, "Void", "A_O", "Show / edit openings")
+ cls.layout.row(align=True).label(text="Mode")
+ add_layout_hotkey_operator(cls.layout, "Void", "A_O", "Show / edit openings")
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_ALT")
row.label(text="", icon="EVENT_D")
diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py
index 49e81ec4dc..1df5400c48 100644
--- a/src/blenderbim/blenderbim/bim/module/resource/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py
@@ -74,7 +74,7 @@ class EnableEditingResource(bpy.types.Operator):
class DisableEditingResource(bpy.types.Operator):
bl_idname = "bim.disable_editing_resource"
- bl_label = "Disable Editing Resources"
+ bl_label = "Disable Editing Resource"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -161,7 +161,7 @@ class UnassignResource(bpy.types.Operator, tool.Ifc.Operator):
class EnableEditingResourceTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_resource_time"
- bl_label = "Enable Editing Resource Usage"
+ bl_label = "Enable Editing Resource Time"
bl_options = {"REGISTER", "UNDO"}
resource: bpy.props.IntProperty()
@@ -267,7 +267,7 @@ class EditResourceCostValue(bpy.types.Operator, tool.Ifc.Operator):
class EnableEditingResourceBaseQuantity(bpy.types.Operator):
bl_idname = "bim.enable_editing_resource_base_quantity"
- bl_label = "Enable Editing Resource Quantity"
+ bl_label = "Enable Editing Resource Base Quantity"
bl_options = {"REGISTER", "UNDO"}
resource: bpy.props.IntProperty()
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
index 2067b4e3a0..f4386c64a8 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
@@ -170,7 +170,7 @@ class EnableEditingWorkSchedule(bpy.types.Operator):
class EnableEditingWorkScheduleTasks(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_schedule_tasks"
- bl_label = "Enable Editing Tasks"
+ bl_label = "Enable Editing Work Schedule Tasks"
bl_options = {"REGISTER", "UNDO"}
work_schedule: bpy.props.IntProperty()
@@ -961,7 +961,7 @@ class EditSequenceTimeLag(bpy.types.Operator, tool.Ifc.Operator):
class DisableEditingSequence(bpy.types.Operator):
bl_idname = "bim.disable_editing_sequence"
- bl_label = "Disable Editing Sequence Attributes"
+ bl_label = "Disable Editing Sequence"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -1276,7 +1276,7 @@ class LoadTaskAnimationColors(bpy.types.Operator):
class DisableEditingTaskAnimationColors(bpy.types.Operator):
bl_idname = "bim.disable_editing_task_animation_colors"
- bl_label = "Disable Editing Colors"
+ bl_label = "Disable Editing Task Animation Colors"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py
index ad1c9665cb..eb14bf4647 100644
--- a/src/blenderbim/blenderbim/bim/ui.py
+++ b/src/blenderbim/blenderbim/bim/ui.py
@@ -182,11 +182,11 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
row.prop(context.scene.BIMProperties, "data_dir")
row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="")
- row = self.layout.row(align=True)
- row.prop(context.scene.DocProperties, "docs_dir")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "sheets_dir")
row = self.layout.row(align=True)
+ row.prop(context.scene.DocProperties, "layouts_dir")
+ row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "titleblocks_dir")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "drawings_dir")
diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py
index d792d92e11..568a6e6eab 100644
--- a/src/blenderbim/blenderbim/core/drawing.py
+++ b/src/blenderbim/blenderbim/core/drawing.py
@@ -78,7 +78,7 @@ def add_sheet(ifc, drawing, titleblock=None):
ifc.run(
"document.edit_reference",
reference=layout,
- attributes={"Location": drawing.get_default_sheet_path(identification, "UNTITLED"), "Description": "LAYOUT"},
+ attributes={"Location": drawing.get_default_layout_path(identification, "UNTITLED"), "Description": "LAYOUT"},
)
ifc.run(
"document.edit_reference",
@@ -94,13 +94,37 @@ def open_sheet(drawing, sheet=None):
def remove_sheet(ifc, drawing, sheet=None):
+ for reference in drawing.get_document_references(sheet):
+ if drawing.get_reference_description(reference) in ("LAYOUT", "SHEET", "REVISION", "RASTER"):
+ uri = ifc.resolve_uri(drawing.get_document_uri(reference))
+ if drawing.does_file_exist(uri):
+ drawing.delete_file(uri)
ifc.run("document.remove_information", information=sheet)
drawing.import_sheets()
-def update_sheet_name(ifc, drawing, sheet=None, name=None):
- if drawing.get_name(sheet) != name:
- ifc.run("document.edit_information", information=sheet, attributes={"Name": name})
+def rename_sheet(ifc, drawing, sheet=None, identification=None, name=None):
+ ifc.run(
+ "document.edit_information", information=sheet, attributes={"Identification": identification, "Name": name}
+ )
+ for reference in drawing.get_document_references(sheet):
+ description = drawing.get_reference_description(reference)
+ if description == "SHEET":
+ old_location = drawing.get_reference_location(reference)
+ new_location = drawing.get_default_sheet_path(identification, name)
+ if old_location != new_location:
+ ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location})
+ old_location = ifc.resolve_uri(old_location)
+ if drawing.does_file_exist(old_location):
+ drawing.move_file(old_location, ifc.resolve_uri(new_location))
+ elif description == "LAYOUT":
+ old_location = drawing.get_reference_location(reference)
+ new_location = drawing.get_default_layout_path(identification, name)
+ if old_location != new_location:
+ ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location})
+ old_location = ifc.resolve_uri(old_location)
+ if drawing.does_file_exist(old_location):
+ drawing.move_file(old_location, ifc.resolve_uri(new_location))
def load_schedules(drawing):
@@ -213,6 +237,21 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati
drawing_tool.copy_representation(annotation, new_annotation)
ifc.run("group.unassign_group", group=group, product=new_annotation)
ifc.run("group.assign_group", group=new_group, products=[new_annotation])
+
+ old_reference = drawing_tool.get_drawing_document(new_drawing)
+ ifc.run("document.unassign_document", product=new_drawing, document=old_reference)
+
+ information = ifc.run("document.add_information")
+ uri = drawing_tool.get_default_drawing_path(drawing_name)
+ reference = ifc.run("document.add_reference", information=information)
+ if ifc.get_schema() == "IFC2X3":
+ attributes = {"DocumentId": "X", "Name": drawing_name, "Scope": "DRAWING"}
+ else:
+ attributes = {"Identification": "X", "Name": drawing_name, "Scope": "DRAWING"}
+ ifc.run("document.edit_information", information=information, attributes=attributes)
+ ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
+ ifc.run("document.assign_document", product=new_drawing, document=reference)
+
drawing_tool.import_drawings()
return new_drawing
@@ -230,7 +269,11 @@ def remove_drawing(ifc, drawing_tool, drawing=None):
if reference_obj:
drawing_tool.delete_object(reference_obj)
ifc.run("root.remove_product", product=reference)
- ifc.run("document.remove_information", information=drawing_tool.get_drawing_document(drawing))
+ information = drawing_tool.get_reference_document(drawing_tool.get_drawing_document(drawing))
+ uri = ifc.resolve_uri(drawing_tool.get_document_uri(information))
+ if drawing_tool.does_file_exist(uri):
+ drawing_tool.delete_file(uri)
+ ifc.run("document.remove_information", information=information)
ifc.run("root.remove_product", product=drawing)
drawing_tool.import_drawings()
@@ -245,6 +288,17 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
if collection:
drawing_tool.set_drawing_collection_name(group, collection)
+ reference = drawing_tool.get_drawing_document(drawing)
+ information = drawing_tool.get_reference_document(reference)
+ ifc.run("document.edit_information", information=information, attributes={"Name": name})
+ old_location = drawing_tool.get_reference_location(reference)
+ new_location = drawing_tool.get_default_drawing_path(name)
+ if old_location != new_location:
+ ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location})
+ old_location = ifc.resolve_uri(old_location)
+ if drawing_tool.does_file_exist(old_location):
+ drawing_tool.move_file(old_location, ifc.resolve_uri(new_location))
+
def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None):
context = drawing_tool.get_annotation_context(drawing_tool.get_drawing_target_view(drawing))
diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py
index d7b09664dd..042050899c 100644
--- a/src/blenderbim/blenderbim/core/tool.py
+++ b/src/blenderbim/blenderbim/core/tool.py
@@ -226,20 +226,22 @@ class Document:
@interface
class Drawing:
def activate_view(cls, camera): pass
+ def add_literal_to_annotation(cls, obj, Literal='Literal', Path='RIGHT', BoxAlignment='bottom-left'): pass
def copy_representation(cls, source, dest): pass
def create_annotation_object(cls, drawing, object_type): pass
- def setup_annotation_object(cls, obj, object_type): pass
def create_camera(cls, name, matrix): pass
def create_svg_schedule(cls, schedule): pass
def create_svg_sheet(cls, document, titleblock): pass
def delete_collection(cls, collection): pass
def delete_drawing_elements(cls, elements): pass
+ def delete_file(cls, uri): pass
def delete_object(cls, obj): pass
def disable_editing_assigned_product(cls, obj): pass
def disable_editing_drawings(cls): pass
def disable_editing_schedules(cls): pass
def disable_editing_sheets(cls): pass
def disable_editing_text(cls, obj): pass
+ def does_file_exist(cls, uri): pass
def enable_editing(cls, obj): pass
def enable_editing_assigned_product(cls, obj): pass
def enable_editing_drawings(cls): pass
@@ -257,21 +259,25 @@ class Drawing:
def get_body_context(cls): pass
def get_default_drawing_path(cls, name): pass
def get_default_drawing_resource_path(cls, resource): pass
+ def get_default_layout_path(cls, identification, name): pass
def get_default_sheet_path(cls, identification, name): pass
- def get_document_uri(cls, document): pass
+ def get_default_sheet_path(cls, identification, name): pass
+ def get_default_titleblock_path(cls, name): pass
+ def get_document_references(cls, document): pass
+ def get_document_uri(cls, document, description=None): pass
def get_drawing_collection(cls, drawing): pass
def get_drawing_document(cls, drawing): pass
def get_drawing_group(cls, drawing): pass
- def get_path_filename(cls, uri): pass
def get_drawing_references(cls, drawing): pass
def get_drawing_target_view(cls, drawing): pass
def get_group_elements(cls, group): pass
def get_ifc_representation_class(cls, object_type): pass
def get_name(cls, element): pass
+ def get_path_filename(cls, uri): pass
+ def get_reference_description(cls, reference): pass
+ def get_reference_document(cls, reference): pass
+ def get_reference_location(cls, reference): pass
def get_text_literal(cls, obj): pass
- def remove_literal_from_annotation(cls, obj, literal): pass
- def synchronise_ifc_and_text_attributes(cls, obj): pass
- def add_literal_to_annotation(cls, obj, Literal='Literal', Path='RIGHT', BoxAlignment='bottom-left'): pass
def import_assigned_product(cls, obj): pass
def import_drawings(cls): pass
def import_schedules(cls): pass
@@ -279,16 +285,20 @@ class Drawing:
def import_text_attributes(cls, obj): pass
def is_camera_orthographic(cls): pass
def is_drawing_active(cls): pass
+ def move_file(cls, src, dest): pass
def open_spreadsheet(cls, uri): pass
def open_svg(cls, filepath): pass
+ def remove_literal_from_annotation(cls, obj, literal): pass
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
def select_assigned_product(cls, drawing): pass
def set_drawing_collection_name(cls, group, collection): pass
def set_name(cls, element, name): pass
+ def setup_annotation_object(cls, obj, object_type): pass
def show_decorations(cls): pass
def sync_object_placement(cls, obj): pass
- def update_text_value(cls, obj): pass
+ def synchronise_ifc_and_text_attributes(cls, obj): pass
def update_text_size_pset(cls, obj): pass
+ def update_text_value(cls, obj): pass
@interface
diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py
index 5682904ab2..2aa7263683 100644
--- a/src/blenderbim/blenderbim/tool/drawing.py
+++ b/src/blenderbim/blenderbim/tool/drawing.py
@@ -21,6 +21,7 @@ import re
import bpy
import math
import bmesh
+import shutil
import logging
import mathutils
import webbrowser
@@ -136,7 +137,7 @@ class Drawing(blenderbim.core.tool.Drawing):
def create_svg_sheet(cls, document, titleblock):
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
- uri = cls.get_document_uri(document)
+ uri = cls.get_document_uri(document, "LAYOUT")
sheet_builder.create(uri, titleblock)
return uri
@@ -152,7 +153,7 @@ class Drawing(blenderbim.core.tool.Drawing):
if obj:
obj_data = obj.data
bpy.data.objects.remove(obj)
- if obj_data.users == 0: # in case we have drawing element types
+ if obj_data and obj_data.users == 0: # in case we have drawing element types
cls.remove_object_data(obj_data)
@classmethod
@@ -163,7 +164,7 @@ class Drawing(blenderbim.core.tool.Drawing):
elif isinstance(data, bpy.types.Mesh):
bpy.data.meshes.remove(data)
elif isinstance(data, bpy.types.Curve):
- bpy.data.curves.remove(C.object.data)
+ bpy.data.curves.remove(data)
@classmethod
def delete_object(cls, obj):
@@ -583,8 +584,9 @@ class Drawing(blenderbim.core.tool.Drawing):
continue
for reference in cls.get_document_references(sheet):
- if reference.Description == "LAYOUT":
- continue # The layout itself is an internal detail and should not be visible to users
+ if reference.Description in ("SHEET", "LAYOUT", "RASTER"):
+ # These references are an internal detail and should not be visible to users
+ continue
new = props.sheets.add()
new.ifc_definition_id = reference.id()
new.is_sheet = False
@@ -726,6 +728,18 @@ class Drawing(blenderbim.core.tool.Drawing):
# TODO below this point is highly experimental prototype code with no tests
+ @classmethod
+ def does_file_exist(cls, uri):
+ return os.path.exists(uri)
+
+ @classmethod
+ def delete_file(cls, uri):
+ os.remove(uri)
+
+ @classmethod
+ def move_file(cls, src, dest):
+ shutil.move(src, dest)
+
@classmethod
def generate_drawing_name(cls, target_view, location_hint):
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") and location_hint:
@@ -737,6 +751,12 @@ class Drawing(blenderbim.core.tool.Drawing):
return location_hint + " " + target_view.split("_")[0]
return target_view
+ @classmethod
+ def get_default_layout_path(cls, identification, name):
+ return os.path.join(
+ bpy.context.scene.DocProperties.layouts_dir, cls.sanitise_filename(f"{identification} - {name}.svg")
+ )
+
@classmethod
def get_default_sheet_path(cls, identification, name):
return os.path.join(
@@ -753,7 +773,7 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def sanitise_filename(cls, name):
- return "".join(x for x in name if (x.isalnum() or x in "_- "))
+ return "".join(x for x in name if (x.isalnum() or x in "._- "))
@classmethod
def get_default_drawing_resource_path(cls, resource):
@@ -1176,6 +1196,14 @@ class Drawing(blenderbim.core.tool.Drawing):
return document.DocumentReferences or []
return document.HasDocumentReferences or []
+ @classmethod
+ def get_reference_description(cls, reference):
+ return reference.Description
+
+ @classmethod
+ def get_reference_location(cls, reference):
+ return reference.Location
+
@classmethod
def get_reference_element(cls, reference):
if tool.Ifc.get_schema() == "IFC2X3":
@@ -1211,16 +1239,21 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def get_drawing_elements(cls, drawing):
+ """returns a set of elements that are included in the drawing"""
+ ifc_file = tool.Ifc.get()
pset = ifcopenshell.util.element.get_psets(drawing).get("EPset_Drawing", {})
include = pset.get("Include", None)
if include:
- elements = set(ifcopenshell.util.selector.Selector.parse(tool.Ifc.get(), include))
+ elements = set(ifcopenshell.util.selector.Selector.parse(ifc_file, include))
else:
- elements = set(tool.Ifc.get().by_type("IfcElement"))
+ elements = set(ifc_file.by_type("IfcElement"))
+ annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing))
+ elements.update(annotations)
+
exclude = pset.get("Exclude", None)
if exclude:
- elements -= set(ifcopenshell.util.selector.Selector.parse(tool.Ifc.get(), exclude, elements=elements))
- elements -= set(tool.Ifc.get().by_type("IfcOpeningElement"))
+ elements -= set(ifcopenshell.util.selector.Selector.parse(ifc_file, exclude, elements=elements))
+ elements -= set(ifc_file.by_type("IfcOpeningElement"))
return elements
@classmethod
@@ -1293,11 +1326,17 @@ class Drawing(blenderbim.core.tool.Drawing):
tool.Spatial.set_active_object(camera)
# sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
- drawing_elements = cls.get_drawing_elements(tool.Ifc.get_entity(camera))
- for element in tool.Ifc.get().by_type("IfcElement"):
+ ifc_file = tool.Ifc.get()
+ drawing = tool.Ifc.get_entity(camera)
+ all_drawing_elements = set(ifc_file.by_type("IfcElement"))
+ annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing))
+ all_drawing_elements.update(annotations)
+ filtered_drawing_elements = cls.get_drawing_elements(drawing)
+
+ for element in all_drawing_elements:
if element.is_a() in ("IfcOpeningElement",):
continue
obj = tool.Ifc.get_object(element)
- obj.hide_viewport = element not in drawing_elements
- obj.hide_render = element not in drawing_elements
+ obj.hide_set(element not in filtered_drawing_elements)
+ obj.hide_render = element not in filtered_drawing_elements
diff --git a/src/blenderbim/desktop/blenderbim.xml b/src/blenderbim/desktop/blenderbim.xml
index a38d2f085b..fa9b6c2782 100644
--- a/src/blenderbim/desktop/blenderbim.xml
+++ b/src/blenderbim/desktop/blenderbim.xml
@@ -8,5 +8,7 @@
+
+
diff --git a/src/blenderbim/test/core/test_drawing.py b/src/blenderbim/test/core/test_drawing.py
index 86b6e3c020..b744581d1a 100644
--- a/src/blenderbim/test/core/test_drawing.py
+++ b/src/blenderbim/test/core/test_drawing.py
@@ -84,14 +84,26 @@ class TestDisableEditingSheets:
class TestAddSheet:
def test_run(self, ifc, drawing):
ifc.run("document.add_information").should_be_called().will_return("sheet")
+ ifc.run("document.add_reference", information="sheet").should_be_called().will_return("reference")
drawing.generate_sheet_identification().should_be_called().will_return("identification")
drawing.ensure_unique_identification("identification").should_be_called().will_return("u_identification")
ifc.get_schema().should_be_called().will_return("IFC4")
- drawing.get_default_sheet_path("u_identification", "UNTITLED").should_be_called().will_return("uri")
+ drawing.get_default_layout_path("u_identification", "UNTITLED").should_be_called().will_return("layout_path")
+ drawing.get_default_titleblock_path("titleblock").should_be_called().will_return("titleblock_path")
ifc.run(
"document.edit_information",
information="sheet",
- attributes={"Identification": "u_identification", "Name": "UNTITLED", "Scope": "DOCUMENTATION", "Location": "uri"},
+ attributes={"Identification": "u_identification", "Name": "UNTITLED", "Scope": "SHEET"},
+ ).should_be_called()
+ ifc.run(
+ "document.edit_reference",
+ reference="reference",
+ attributes={"Location": "layout_path", "Description": "LAYOUT"},
+ ).should_be_called()
+ ifc.run(
+ "document.edit_reference",
+ reference="reference",
+ attributes={"Location": "titleblock_path", "Description": "TITLEBLOCK"},
).should_be_called()
drawing.create_svg_sheet("sheet", "titleblock").should_be_called()
drawing.import_sheets().should_be_called()
@@ -99,13 +111,26 @@ class TestAddSheet:
def test_using_a_document_id_in_ifc2x3(self, ifc, drawing):
ifc.run("document.add_information").should_be_called().will_return("sheet")
+ ifc.run("document.add_reference", information="sheet").should_be_called().will_return("reference")
drawing.generate_sheet_identification().should_be_called().will_return("identification")
drawing.ensure_unique_identification("identification").should_be_called().will_return("u_identification")
ifc.get_schema().should_be_called().will_return("IFC2X3")
+ drawing.get_default_layout_path("u_identification", "UNTITLED").should_be_called().will_return("layout_path")
+ drawing.get_default_titleblock_path("titleblock").should_be_called().will_return("titleblock_path")
ifc.run(
"document.edit_information",
information="sheet",
- attributes={"DocumentId": "u_identification", "Name": "UNTITLED", "Scope": "DOCUMENTATION"},
+ attributes={"DocumentId": "u_identification", "Name": "UNTITLED", "Scope": "SHEET"},
+ ).should_be_called()
+ ifc.run(
+ "document.edit_reference",
+ reference="reference",
+ attributes={"Location": "layout_path", "Description": "LAYOUT"},
+ ).should_be_called()
+ ifc.run(
+ "document.edit_reference",
+ reference="reference",
+ attributes={"Location": "titleblock_path", "Description": "TITLEBLOCK"},
).should_be_called()
drawing.create_svg_sheet("sheet", "titleblock").should_be_called()
drawing.import_sheets().should_be_called()
@@ -114,13 +139,19 @@ class TestAddSheet:
class TestOpenSheet:
def test_run(self, drawing):
- drawing.get_document_uri("sheet").should_be_called().will_return("uri")
+ drawing.get_document_uri("sheet", "LAYOUT").should_be_called().will_return("uri")
drawing.open_svg("uri").should_be_called()
subject.open_sheet(drawing, sheet="sheet")
class TestRemoveSheet:
def test_run(self, ifc, drawing):
+ drawing.get_document_references("sheet").should_be_called().will_return(["reference"])
+ drawing.get_reference_description("reference").should_be_called().will_return("LAYOUT")
+ drawing.get_document_uri("reference").should_be_called().will_return("relative_uri")
+ ifc.resolve_uri("relative_uri").should_be_called().will_return("absolute_uri")
+ drawing.does_file_exist("absolute_uri").should_be_called().will_return(True)
+ drawing.delete_file("absolute_uri").should_be_called()
ifc.run("document.remove_information", information="sheet").should_be_called()
drawing.import_sheets().should_be_called()
subject.remove_sheet(ifc, drawing, sheet="sheet")
@@ -254,7 +285,11 @@ class TestAddDrawing:
ifc.run("document.add_information").should_be_called().will_return("information")
ifc.run("document.add_reference", information="information").should_be_called().will_return("reference")
ifc.get_schema().should_be_called().will_return("IFC4")
- ifc.run("document.edit_information", information="information", attributes={"Identification": "X", "Name": "name", "Scope": "DRAWING"}).should_be_called()
+ ifc.run(
+ "document.edit_information",
+ information="information",
+ attributes={"Identification": "X", "Name": "name", "Scope": "DRAWING"},
+ ).should_be_called()
ifc.run("document.edit_reference", reference="reference", attributes={"Location": "uri"}).should_be_called()
ifc.run("document.assign_document", product="element", document="reference").should_be_called()
drawing.import_drawings().should_be_called()
@@ -280,6 +315,24 @@ class TestDuplicateDrawing:
drawing.copy_representation("annotation", "new_annotation").should_be_called()
ifc.run("group.unassign_group", group="group", product="new_annotation").should_be_called()
ifc.run("group.assign_group", group="new_group", products=["new_annotation"]).should_be_called()
+
+ drawing.get_drawing_document("new_drawing").should_be_called().will_return("old_reference")
+ ifc.run("document.unassign_document", product="new_drawing", document="old_reference").should_be_called()
+
+ ifc.run("document.add_information").should_be_called().will_return("information")
+ ifc.run("document.add_reference", information="information").should_be_called().will_return("reference")
+ ifc.get_schema().should_be_called().will_return("IFC4")
+ drawing.get_default_drawing_path("unique_name").should_be_called().will_return("drawing_path")
+ ifc.run(
+ "document.edit_information",
+ information="information",
+ attributes={"Identification": "X", "Name": "unique_name", "Scope": "DRAWING"},
+ ).should_be_called()
+ ifc.run(
+ "document.edit_reference", reference="reference", attributes={"Location": "drawing_path"}
+ ).should_be_called()
+ ifc.run("document.assign_document", product="new_drawing", document="reference").should_be_called()
+
drawing.import_drawings().should_be_called()
subject.duplicate_drawing(ifc, drawing, drawing="drawing", should_duplicate_annotations=True)
@@ -296,7 +349,12 @@ class TestRemoveDrawing:
ifc.get_object("reference").should_be_called().will_return("reference_obj")
drawing.delete_object("reference_obj").should_be_called()
ifc.run("root.remove_product", product="reference").should_be_called()
- drawing.get_drawing_document("drawing").should_be_called().will_return("information")
+ drawing.get_drawing_document("drawing").should_be_called().will_return("reference")
+ drawing.get_reference_document("reference").should_be_called().will_return("information")
+ drawing.get_document_uri("information").should_be_called().will_return("relative_uri")
+ ifc.resolve_uri("relative_uri").should_be_called().will_return("absolute_uri")
+ drawing.does_file_exist("absolute_uri").should_be_called().will_return(True)
+ drawing.delete_file("absolute_uri").should_be_called()
ifc.run("document.remove_information", information="information").should_be_called()
ifc.run("root.remove_product", product="drawing").should_be_called()
drawing.import_drawings().should_be_called()
@@ -310,6 +368,13 @@ class TestUpdateDrawingName:
drawing.get_name("group").should_be_called().will_return("name")
drawing.get_drawing_collection("drawing").should_be_called().will_return("collection")
drawing.set_drawing_collection_name("group", "collection").should_be_called()
+
+ drawing.get_drawing_document("drawing").should_be_called().will_return("reference")
+ drawing.get_reference_document("reference").should_be_called().will_return("information")
+ ifc.run("document.edit_information", information="information", attributes={"Name": "name"}).should_be_called()
+ drawing.get_reference_location("reference").should_be_called().will_return("location")
+ drawing.get_default_drawing_path("name").should_be_called().will_return("location")
+
subject.update_drawing_name(ifc, drawing, drawing="drawing", name="name")
def test_run(self, ifc, drawing):
@@ -320,6 +385,20 @@ class TestUpdateDrawingName:
ifc.run("attribute.edit_attributes", product="group", attributes={"Name": "name"}).should_be_called()
drawing.get_drawing_collection("drawing").should_be_called().will_return("collection")
drawing.set_drawing_collection_name("group", "collection").should_be_called()
+
+ drawing.get_drawing_document("drawing").should_be_called().will_return("reference")
+ drawing.get_reference_document("reference").should_be_called().will_return("information")
+ ifc.run("document.edit_information", information="information", attributes={"Name": "name"}).should_be_called()
+ drawing.get_reference_location("reference").should_be_called().will_return("old_location")
+ drawing.get_default_drawing_path("name").should_be_called().will_return("new_location")
+ ifc.run(
+ "document.edit_reference", reference="reference", attributes={"Location": "new_location"}
+ ).should_be_called()
+ ifc.resolve_uri("old_location").should_be_called().will_return("old_uri")
+ drawing.does_file_exist("old_uri").should_be_called().will_return(True)
+ ifc.resolve_uri("new_location").should_be_called().will_return("new_uri")
+ drawing.move_file("old_uri", "new_uri").should_be_called()
+
subject.update_drawing_name(ifc, drawing, drawing="drawing", name="name")
diff --git a/src/blenderbim/test/tool/test_drawing.py b/src/blenderbim/test/tool/test_drawing.py
index 4e664fb9b6..92d02fadda 100644
--- a/src/blenderbim/test/tool/test_drawing.py
+++ b/src/blenderbim/test/tool/test_drawing.py
@@ -83,7 +83,7 @@ class TestDeleteDrawingElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
- obj = bpy.data.objects.new("Object", None)
+ obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
collection = bpy.data.collections.new("Collection")
bpy.context.scene.collection.children.link(collection)
collection.objects.link(obj)
@@ -488,7 +488,7 @@ class TestImportSheets(NewFile):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
- document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="DOCUMENTATION")
+ document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SHEET")
subject.import_sheets()
props = bpy.context.scene.DocProperties
assert props.sheets[0].ifc_definition_id == document.id()
@@ -499,7 +499,7 @@ class TestImportSheets(NewFile):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
- document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="DOCUMENTATION")
+ document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="SHEET")
subject.import_sheets()
props = bpy.context.scene.DocProperties
assert props.sheets[0].ifc_definition_id == document.id()
diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py
index 962a75a483..4b08dc3907 100644
--- a/src/ifcopenshell-python/ifcopenshell/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/__init__.py
@@ -160,9 +160,6 @@ def create_entity(type, schema="IFC4", *args, **kwargs):
return e
-gcroot = []
-
-
def register_schema(schema):
"""Registers a custom IFC schema
@@ -177,7 +174,8 @@ def register_schema(schema):
ifcopenshell.register_schema(schema)
ifcopenshell.file(schema="IFC_CUSTOM")
"""
- gcroot.append(schema)
+ schema.schema.this.disown()
+ schema.disown()
ifcopenshell_wrapper.register_schema(schema.schema)
register_schema_attributes(schema.schema)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
index ebe513d193..7a1b72132f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
@@ -58,4 +58,7 @@ class Usecase:
def execute(self):
for rel in self.settings["product"].HasAssociations:
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]:
- self.file.remove(rel)
+ if len(rel.RelatedObjects) == 1:
+ self.file.remove(rel)
+ else:
+ rel.RelatedObjects = [o for o in rel.RelatedObjects if o != self.settings["product"]]
diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py
index 82b5dcbff2..cc8d0adbc4 100644
--- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py
+++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py
@@ -40,6 +40,10 @@ except ImportError as e:
def set_derived_attribute(*args):
raise TypeError("Unable to set derived attribute")
+
+def set_unsupported_attribute(*args):
+ raise TypeError("This is an unsupported attribute type")
+
# For every schema and its entities populate a list
# of functions for every entity attribute (including
@@ -79,6 +83,9 @@ def register_schema_attributes(schema):
functions = [
set_derived_attribute
if mname == "setArgumentAsDerived"
+ else
+ set_unsupported_attribute
+ if mname == "setArgumentAsUnknown"
else getattr(ifcopenshell_wrapper.entity_instance, mname)
for mname in fn_names
]
diff --git a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py
index e6e4b1aeef..583a8e5bd7 100644
--- a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py
+++ b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py
@@ -84,7 +84,7 @@ class LateBoundSchemaInstantiator:
for attr_name, decl_type, optional in attribute_definitions:
attributes.append(w.attribute(attr_name, decl_type, optional))
self.declarations[str(name)].set_attributes(attributes, is_derived)
- self.cache.append(attributes)
+ self.cache.extend(attributes)
def inverse_attributes(self, name, inv_attrs):
attributes = []
@@ -100,6 +100,7 @@ class LateBoundSchemaInstantiator:
en.attributes()[attribute_entity_index],
)
)
+ self.cache.extend(attributes)
self.declarations[str(name)].set_inverse_attributes(attributes)
def entity_subtypes(self, name, tys):
@@ -110,6 +111,10 @@ class LateBoundSchemaInstantiator:
override_schema_name or self.schema_name, list(self.declarations.values()), None
)
+ def disown(self):
+ for elem in self.cache + list(self.declarations.values()):
+ elem.this.disown()
+
class EarlyBoundCodeWriter:
def __init__(self, schema_name):
diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py
index 2121630cee..4a83f6191a 100644
--- a/src/ifcopenshell-python/ifcopenshell/validate.py
+++ b/src/ifcopenshell-python/ifcopenshell/validate.py
@@ -20,6 +20,7 @@
from __future__ import print_function
+import os
import sys
import json
import functools
@@ -302,7 +303,19 @@ def validate(f, logger, express_rules=False):
ifcopenshell.ifcopenshell_wrapper.set_log_format_json()
filename = f
- f = ifcopenshell.open(f)
+ try:
+ f = ifcopenshell.open(f)
+ except ifcopenshell.SchemaError as e:
+ current_dir_files = {fn.lower(): fn for fn in os.listdir('.')}
+ schema_name = str(e).split(' ')[-1].lower()
+ exists = current_dir_files.get(schema_name + '.exp')
+ if exists:
+ schema = ifcopenshell.express.parse(exists)
+ ifcopenshell.register_schema(schema)
+
+ f = ifcopenshell.open(f)
+ else:
+ raise e
log_internal_cpp_errors(filename, logger)
diff --git a/src/ifcopenshell-python/test/api/document/test_assign_document.py b/src/ifcopenshell-python/test/api/document/test_assign_document.py
new file mode 100644
index 0000000000..6c76f844b4
--- /dev/null
+++ b/src/ifcopenshell-python/test/api/document/test_assign_document.py
@@ -0,0 +1,39 @@
+# IfcOpenShell - IFC toolkit and geometry engine
+# Copyright (C) 2023 Dion Moult
+#
+# This file is part of IfcOpenShell.
+#
+# IfcOpenShell is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with IfcOpenShell. If not, see .
+
+import test.bootstrap
+import ifcopenshell.api
+
+
+class TestAssignDocument(test.bootstrap.IFC4):
+ def test_assigning_a_document(self):
+ element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
+ assert element.HasAssociations[0].RelatingDocument == reference
+
+ def test_assigning_multiple_documents(self):
+ element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
+ assert len(self.file.by_type("IfcRelAssociatesDocument")) == 1
+ assert element.HasAssociations[0].RelatingDocument == reference
+ assert element2.HasAssociations[0].RelatingDocument == reference
+ assert element.HasAssociations[0] == element.HasAssociations[0]
diff --git a/src/ifcopenshell-python/test/api/document/test_unassign_document.py b/src/ifcopenshell-python/test/api/document/test_unassign_document.py
new file mode 100644
index 0000000000..8f227dd2b2
--- /dev/null
+++ b/src/ifcopenshell-python/test/api/document/test_unassign_document.py
@@ -0,0 +1,40 @@
+# IfcOpenShell - IFC toolkit and geometry engine
+# Copyright (C) 2023 Dion Moult
+#
+# This file is part of IfcOpenShell.
+#
+# IfcOpenShell is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with IfcOpenShell. If not, see .
+
+import test.bootstrap
+import ifcopenshell.api
+
+
+class TestUnassignDocument(test.bootstrap.IFC4):
+ def test_unassigning_a_document(self):
+ element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
+ ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
+ assert not element.HasAssociations
+ assert not len(self.file.by_type("IfcRelAssociatesDocument"))
+
+ def test_unassigning_a_document_used_by_multiple_entities(self):
+ element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
+ reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
+ ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
+ ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
+ assert not element.HasAssociations
+ assert element2.HasAssociations[0].RelatingDocument == reference