Merge branch 'v0.7.0' into fix_bug

This commit is contained in:
Andrej
2023-04-11 01:23:27 +05:00
committed by GitHub
28 changed files with 554 additions and 301 deletions
+2 -3
View File
@@ -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)
@@ -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,
@@ -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:
@@ -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)
@@ -245,7 +245,7 @@ class CreateDrawing(bpy.types.Operator):
elif "</svg>" 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):
@@ -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")
@@ -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)
@@ -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):
@@ -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:
@@ -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()
@@ -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):
@@ -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")
@@ -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()
@@ -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):
+2 -2
View File
@@ -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")
+59 -5
View File
@@ -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))
+17 -7
View File
@@ -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
+53 -14
View File
@@ -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
+2
View File
@@ -8,5 +8,7 @@
</magic>
<generic-icon name="x-ifc"/>
<glob pattern="*.ifc"/>
<glob pattern="*.ifczip"/>
<glob pattern="*.ifcxml"/>
</mime-type>
</mime-info>
+85 -6
View File
@@ -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")
+3 -3
View File
@@ -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()
@@ -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)
@@ -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"]]
@@ -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
]
@@ -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):
@@ -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)
@@ -0,0 +1,39 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
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]
@@ -0,0 +1,40 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
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