See #2516. WARNING: breaking changes! Restructure drawing system to use relative paths based on IfcDocumentReference Locations.

This commit is contained in:
Dion Moult
2023-04-08 21:59:11 +10:00
parent 5864873945
commit 2e466ead37
18 changed files with 285 additions and 173 deletions

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

+2 -1
View File
@@ -24,6 +24,7 @@ import zipfile
import tempfile import tempfile
import ifcopenshell import ifcopenshell
import blenderbim.bim.handler import blenderbim.bim.handler
import blenderbim.tool as tool
from pathlib import Path from pathlib import Path
@@ -371,7 +372,7 @@ class IfcStore:
def unlink_element(element=None, obj=None): def unlink_element(element=None, obj=None):
if element is None: if element is None:
try: try:
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id) element = tool.Ifc.get_entity(obj)
except: except:
pass pass
@@ -179,6 +179,7 @@ class CreateDrawing(bpy.types.Operator):
self.camera = context.scene.camera self.camera = context.scene.camera
self.camera_element = tool.Ifc.get_entity(self.camera) self.camera_element = tool.Ifc.get_entity(self.camera)
self.camera_document = tool.Drawing.get_drawing_document(self.camera_element)
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
with profile("Drawing generation process"): with profile("Drawing generation process"):
@@ -198,14 +199,8 @@ class CreateDrawing(bpy.types.Operator):
self.svg_writer.camera_projection = tuple( self.svg_writer.camera_projection = tuple(
self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
) )
pset = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]
related_paths = { self.svg_writer.setup_drawing_resource_paths(self.camera_element)
"Stylesheet": pset.get("Stylesheet"),
"Markers": pset.get("Markers"),
"Symbols": pset.get("Symbols"),
"Patterns": pset.get("Patterns"),
}
self.svg_writer.define_related_paths(**related_paths)
with profile("Generate underlay"): with profile("Generate underlay"):
underlay_svg = self.generate_underlay(context) underlay_svg = self.generate_underlay(context)
@@ -237,7 +232,7 @@ class CreateDrawing(bpy.types.Operator):
def combine_svgs(self, context, underlay, linework, annotation): def combine_svgs(self, context, underlay, linework, annotation):
# Hacky :) # Hacky :)
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "diagrams", self.drawing_name + ".svg") svg_path = self.get_svg_path()
with open(svg_path, "w") as outfile: with open(svg_path, "w") as outfile:
self.svg_writer.create_blank_svg(svg_path).define_boilerplate() self.svg_writer.create_blank_svg(svg_path).define_boilerplate()
boilerplate = self.svg_writer.svg.tostring() boilerplate = self.svg_writer.svg.tostring()
@@ -282,7 +277,7 @@ class CreateDrawing(bpy.types.Operator):
def generate_underlay(self, context): def generate_underlay(self, context):
if not self.cprops.has_underlay: if not self.cprops.has_underlay:
return return
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-underlay.svg") svg_path = self.get_svg_path(cache_type="underlay")
context.scene.render.filepath = svg_path[0:-4] + ".png" context.scene.render.filepath = svg_path[0:-4] + ".png"
drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index] drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index]
@@ -322,7 +317,7 @@ class CreateDrawing(bpy.types.Operator):
def generate_linework(self, context): def generate_linework(self, context):
if not self.cprops.has_linework: if not self.cprops.has_linework:
return return
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-linework.svg") svg_path = self.get_svg_path(cache_type="linework")
if os.path.isfile(svg_path) and self.props.should_use_linework_cache: if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
return svg_path return svg_path
@@ -605,7 +600,7 @@ class CreateDrawing(bpy.types.Operator):
def generate_annotation(self, context): def generate_annotation(self, context):
if not self.cprops.has_annotation: if not self.cprops.has_annotation:
return return
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-annotation.svg") svg_path = self.get_svg_path(cache_type="annotation")
if os.path.isfile(svg_path) and self.props.should_use_annotation_cache: if os.path.isfile(svg_path) and self.props.should_use_annotation_cache:
return svg_path return svg_path
@@ -662,6 +657,17 @@ class CreateDrawing(bpy.types.Operator):
return element.LayerSetName return element.LayerSetName
return "mat-" + str(element.id()) return "mat-" + str(element.id())
def get_svg_path(self, cache_type=None):
drawing_path = tool.Drawing.get_document_uri(self.camera_document)
drawings_dir = os.path.dirname(drawing_path)
if cache_type:
drawings_dir = os.path.join(drawings_dir, "cache")
os.makedirs(drawings_dir, exist_ok=True)
return os.path.join(drawings_dir, f"{self.drawing_name}-{cache_type}.svg")
os.makedirs(drawings_dir, exist_ok=True)
return drawing_path
class AddAnnotation(bpy.types.Operator, Operator): class AddAnnotation(bpy.types.Operator, Operator):
bl_idname = "bim.add_annotation" bl_idname = "bim.add_annotation"
@@ -711,7 +717,7 @@ class OpenSheet(bpy.types.Operator, Operator):
core.open_sheet(tool.Drawing, sheet=sheet) core.open_sheet(tool.Drawing, sheet=sheet)
class AddDrawingToSheet(bpy.types.Operator): class AddDrawingToSheet(bpy.types.Operator, Operator):
bl_idname = "bim.add_drawing_to_sheet" bl_idname = "bim.add_drawing_to_sheet"
bl_label = "Add Drawing To Sheet" bl_label = "Add Drawing To Sheet"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -721,11 +727,13 @@ class AddDrawingToSheet(bpy.types.Operator):
props = context.scene.DocProperties props = context.scene.DocProperties
return props.drawings and props.sheets and context.scene.BIMProperties.data_dir return props.drawings and props.sheets and context.scene.BIMProperties.data_dir
def execute(self, context): def _execute(self, context):
props = context.scene.DocProperties props = context.scene.DocProperties
active_drawing = props.drawings[props.active_drawing_index] active_drawing = props.drawings[props.active_drawing_index]
active_sheet = props.sheets[props.active_sheet_index] active_sheet = props.sheets[props.active_sheet_index]
drawing = tool.Ifc.get().by_id(active_drawing.ifc_definition_id) drawing = tool.Ifc.get().by_id(active_drawing.ifc_definition_id)
drawing_uri = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_document(drawing))
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id) sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
if not sheet.is_a("IfcDocumentInformation"): if not sheet.is_a("IfcDocumentInformation"):
return {"FINISHED"} return {"FINISHED"}
@@ -737,13 +745,7 @@ class AddDrawingToSheet(bpy.types.Operator):
has_drawing = False has_drawing = False
for reference in references: for reference in references:
if tool.Ifc.get_schema() == "IFC2X3": if reference.Location == drawing_uri:
element = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
0
].RelatedObjects[0]
else:
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
if element == drawing:
has_drawing = True has_drawing = True
break break
@@ -756,7 +758,6 @@ class AddDrawingToSheet(bpy.types.Operator):
else: else:
attributes = {"Identification": str(len(sheet.HasDocumentReferences or []))} attributes = {"Identification": str(len(sheet.HasDocumentReferences or []))}
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes) tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
tool.Ifc.run("document.assign_document", product=drawing, document=reference)
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_drawing(reference, drawing, sheet) sheet_builder.add_drawing(reference, drawing, sheet)
@@ -810,15 +811,19 @@ class CreateSheets(bpy.types.Operator):
name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0] name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir sheet_builder.data_dir = scene.BIMProperties.data_dir
sheet_builder.build(sheet)
# These variables will be made available to the evaluated commands
svg = sheet_builder.build(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"
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
if svg2pdf_command: if svg2pdf_command:
path = os.path.join(scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
pdf = os.path.join(path, name + ".pdf")
# With great power comes great responsibility. Example: # With great power comes great responsibility. Example:
# [['inkscape', svg, '-o', pdf]] # [['inkscape', svg, '-o', pdf]]
commands = eval(svg2pdf_command) commands = eval(svg2pdf_command)
@@ -826,11 +831,6 @@ class CreateSheets(bpy.types.Operator):
subprocess.run(command) subprocess.run(command)
if svg2dxf_command: if svg2dxf_command:
path = os.path.join(scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
eps = os.path.join(path, name + ".eps")
dxf = os.path.join(path, name + ".dxf")
base = os.path.join(path, name)
# With great power comes great responsibility. Example: # With great power comes great responsibility. Example:
# [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']] # [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]
commands = eval(svg2dxf_command) commands = eval(svg2dxf_command)
@@ -840,10 +840,7 @@ class CreateSheets(bpy.types.Operator):
if svg2pdf_command: if svg2pdf_command:
open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf) open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
else: else:
open_with_user_command( open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(scene.BIMProperties.data_dir, "build", name, name + ".svg"),
)
return {"FINISHED"} return {"FINISHED"}
@@ -894,19 +891,19 @@ class OpenView(bpy.types.Operator):
return self.execute(context) return self.execute(context)
def execute(self, context): def execute(self, context):
if not self.open_all: if self.open_all:
view_list = [self.view] drawings = [tool.Ifc.get().by_id(d.ifc_definition_id) for d in context.scene.DocProperties.drawings]
else: else:
view_list = [d.name for d in context.scene.DocProperties.drawings] drawings = [tool.Ifc.get().by_id(context.scene.DocProperties.drawings.get(self.view).ifc_definition_id)]
data_dir = Path(context.scene.BIMProperties.data_dir) drawing_uris = []
diagrams = []
drawings_not_found = [] drawings_not_found = []
for view_name in view_list.copy():
path = data_dir / "diagrams" / (view_name + ".svg") for drawing in drawings:
if not path.is_file(): drawing_uri = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_document(drawing))
drawings_not_found.append(view_name) drawing_uris.append(drawing_uri)
diagrams.append(path) if not os.path.exists(drawing_uri):
drawings_not_found.append(drawing.Name)
if drawings_not_found: if drawings_not_found:
msg = "Some drawings .svg files were not found, need to print them first: \n{}.".format( msg = "Some drawings .svg files were not found, need to print them first: \n{}.".format(
@@ -915,11 +912,8 @@ class OpenView(bpy.types.Operator):
self.report({"ERROR"}, msg) self.report({"ERROR"}, msg)
return {"CANCELLED"} return {"CANCELLED"}
for diagram_path in diagrams: for drawing_uri in drawing_uris:
open_with_user_command( open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri)
context.preferences.addons["blenderbim"].preferences.svg_command,
str(diagram_path),
)
return {"FINISHED"} return {"FINISHED"}
@@ -1143,12 +1137,15 @@ class AddSchedule(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH") filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ods;*.xls;*.xlsx", options={"HIDDEN"}) filter_glob: bpy.props.StringProperty(default="*.ods;*.xls;*.xlsx", options={"HIDDEN"})
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False) use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
def _execute(self, context): def _execute(self, context):
filepath = self.filepath filepath = self.filepath
if self.use_relative_path: if self.use_relative_path:
filepath = os.path.relpath(filepath, bpy.path.abspath("//")) ifc_path = tool.Ifc.get_path()
if os.path.isfile(ifc_path):
ifc_path = os.path.dirname(ifc_path)
filepath = os.path.relpath(filepath, ifc_path)
core.add_schedule( core.add_schedule(
tool.Ifc, tool.Ifc,
tool.Drawing, tool.Drawing,
@@ -1216,7 +1213,7 @@ class AddScheduleToSheet(bpy.types.Operator):
has_schedule = False has_schedule = False
for reference in references: for reference in references:
if reference.Location == tool.Drawing.get_schedule_location(schedule): if reference.Location == tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(schedule), "svg"):
has_schedule = True has_schedule = True
break break
@@ -354,6 +354,13 @@ class DocProperties(PropertyGroup):
decorations_colour: FloatVectorProperty( decorations_colour: FloatVectorProperty(
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4 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")
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")
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")
class BIMCameraProperties(PropertyGroup): class BIMCameraProperties(PropertyGroup):
@@ -17,14 +17,15 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os import os
import bpy
import uuid import uuid
import shutil
import ntpath import ntpath
import pystache import pystache
import urllib.parse import urllib.parse
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import blenderbim.tool as tool import blenderbim.tool as tool
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
from shutil import copy
from xml.dom import minidom from xml.dom import minidom
@@ -40,15 +41,14 @@ class SheetBuilder:
root.attrib["id"] = "root" root.attrib["id"] = "root"
root.attrib["version"] = "1.1" root.attrib["version"] = "1.1"
view_root = ET.parse( titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg")
os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg") view_root = ET.parse(titleblock_path).getroot()
).getroot()
view_width = self.convert_to_mm(view_root.attrib.get("width")) view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height")) view_height = self.convert_to_mm(view_root.attrib.get("height"))
view = ET.SubElement(root, "g") view = ET.SubElement(root, "g")
view.attrib["data-type"] = "titleblock" view.attrib["data-type"] = "titleblock"
titleblock = ET.SubElement(view, "image") titleblock = ET.SubElement(view, "image")
titleblock.attrib["xlink:href"] = f"../templates/titleblocks/{titleblock_name}.svg" titleblock.attrib["xlink:href"] = f"./titleblocks/{titleblock_name}.svg"
titleblock.attrib["x"] = "0" titleblock.attrib["x"] = "0"
titleblock.attrib["y"] = "0" titleblock.attrib["y"] = "0"
titleblock.attrib["width"] = str(view_width) titleblock.attrib["width"] = str(view_width)
@@ -58,17 +58,24 @@ class SheetBuilder:
root.attrib["height"] = "{}mm".format(view_height) root.attrib["height"] = "{}mm".format(view_height)
root.attrib["viewBox"] = "0 0 {} {}".format(view_width, view_height) root.attrib["viewBox"] = "0 0 {} {}".format(view_width, view_height)
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)
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: with open(sheet_path, "w") as f:
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" ")) f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
def add_drawing(self, reference, drawing, sheet): def add_drawing(self, reference, drawing, sheet):
filename = drawing.Name filename = drawing.Name
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0] sheet_path = tool.Drawing.get_document_uri(sheet)
sheet_dir = os.path.join(self.data_dir, "sheets") sheet_name = os.path.splitext(os.path.basename(sheet_path))[0]
drawing_dir = os.path.join(self.data_dir, "diagrams") sheet_dir = os.path.dirname(sheet_path)
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
drawing_path = os.path.join(drawing_dir, filename + ".svg") drawing_path = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_reference(drawing))
underlay_path = os.path.join(drawing_dir, filename + "-underlay.png") 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.isfile(sheet_path):
raise FileNotFoundError raise FileNotFoundError
@@ -110,7 +117,7 @@ class SheetBuilder:
foreground.attrib["width"] = str(view_width) foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height) foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view) self.add_view_title(30, view_height + 35, view, sheet_dir)
sheet_tree.write(sheet_path) sheet_tree.write(sheet_path)
def remove_drawing(self, reference, sheet): def remove_drawing(self, reference, sheet):
@@ -128,11 +135,12 @@ class SheetBuilder:
sheet_tree.write(sheet_path) sheet_tree.write(sheet_path)
def add_schedule(self, reference, schedule, sheet): def add_schedule(self, reference, schedule, sheet):
view_path = tool.Drawing.get_document_uri(schedule) view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(schedule), "svg")
if not os.path.exists(view_path): if not os.path.exists(view_path):
tool.Drawing.create_svg_schedule(schedule) tool.Drawing.create_svg_schedule(schedule)
schedule_name = os.path.splitext(os.path.basename(view_path))[0] schedule_name = os.path.splitext(os.path.basename(view_path))[0]
sheet_path = tool.Drawing.get_document_uri(sheet) sheet_path = tool.Drawing.get_document_uri(sheet)
sheet_dir = os.path.dirname(sheet_path)
ET.register_namespace("", "http://www.w3.org/2000/svg") ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink") ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
@@ -152,31 +160,40 @@ class SheetBuilder:
foreground = ET.SubElement(view, "image") foreground = ET.SubElement(view, "image")
foreground.attrib["data-type"] = "table" foreground.attrib["data-type"] = "table"
foreground.attrib["xlink:href"] = "../schedules/{}.svg".format(schedule_name) foreground.attrib["xlink:href"] = os.path.relpath(view_path, sheet_dir)
foreground.attrib["x"] = "30" foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30" foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width) foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height) foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view) self.add_view_title(30, view_height + 35, view, sheet_dir)
sheet_tree.write(sheet_path) sheet_tree.write(sheet_path)
def add_view_title(self, x, y, parent): def add_view_title(self, x, y, parent, sheet_dir):
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg")) title_path = os.path.join(sheet_dir, "assets", "view-title.svg")
os.makedirs(os.path.dirname(title_path), exist_ok=True)
if not os.path.exists(title_path):
ootb_title = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "view-title.svg")
shutil.copy(ootb_title, title_path)
title_tree = ET.parse(title_path)
title_root = title_tree.getroot() title_root = title_tree.getroot()
title = ET.SubElement(parent, "image") title = ET.SubElement(parent, "image")
title.attrib["data-type"] = "view-title" title.attrib["data-type"] = "view-title"
title.attrib["xlink:href"] = "../templates/view-title.svg" title.attrib["xlink:href"] = os.path.relpath(title_path, sheet_dir)
title.attrib["x"] = str(x) title.attrib["x"] = str(x)
title.attrib["y"] = str(y) title.attrib["y"] = str(y)
title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width"))) title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width")))
title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height"))) title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height")))
def build(self, sheet): def build(self, sheet):
sheet_name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0] sheet_path = tool.Drawing.get_document_uri(sheet)
os.makedirs(os.path.join(self.data_dir, "build", sheet_name), exist_ok=True) sheet_name = os.path.splitext(os.path.basename(sheet_path))[0]
self.sheet_dir = os.path.dirname(sheet_path)
sheet_path = os.path.join(self.data_dir, "sheets", f"{sheet_name}.svg") docs_dir = tool.Ifc.resolve_uri(os.path.join(bpy.context.scene.DocProperties.docs_dir, sheet_name))
os.makedirs(docs_dir, exist_ok=True)
ET.register_namespace("", "http://www.w3.org/2000/svg") ET.register_namespace("", "http://www.w3.org/2000/svg")
ET.register_namespace("xlink", "http://www.w3.org/1999/xlink") ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
@@ -191,9 +208,13 @@ class SheetBuilder:
self.build_drawings(root, sheet) self.build_drawings(root, sheet)
self.build_schedules(root) self.build_schedules(root)
with open(os.path.join(self.data_dir, "build", sheet_name, f"{sheet_name}.svg"), "wb") as output: output_filename = os.path.join(docs_dir, f"{sheet_name}.svg")
with open(output_filename, "wb") as output:
tree.write(output) tree.write(output)
return output_filename
def build_titleblock(self, root, sheet): def build_titleblock(self, root, sheet):
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0] titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0] image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
@@ -233,7 +254,7 @@ class SheetBuilder:
if background is not None: if background is not None:
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background)) background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
copy(background_path, os.path.join(self.data_dir, "build", sheet_name)) shutil.copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
if view_title is not None: if view_title is not None:
foreground_path = self.get_href(foreground) foreground_path = self.get_href(foreground)
@@ -298,7 +319,7 @@ class SheetBuilder:
self.defs.append(clip_path) self.defs.append(clip_path)
svg_path = self.get_href(image) svg_path = self.get_href(image)
with open(os.path.join(self.data_dir, "sheets", svg_path), "r") as template: with open(os.path.join(self.sheet_dir, svg_path), "r") as template:
embedded = ET.fromstring(pystache.render(template.read(), data)) embedded = ET.fromstring(pystache.render(template.read(), data))
# viewBox should not be nested # viewBox should not be nested
embedded.attrib["viewBox"] = "" embedded.attrib["viewBox"] = ""
@@ -21,6 +21,7 @@ import re
import bpy import bpy
import math import math
import bmesh import bmesh
import shutil
import pystache import pystache
import mathutils import mathutils
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@@ -65,10 +66,9 @@ class SvgWriter:
self.scale = 1 / 100 # 1:100 self.scale = 1 / 100 # 1:100
self.camera_width = None self.camera_width = None
self.camera_height = None self.camera_height = None
self.related_paths = None self.resource_paths = {}
def create_blank_svg(self, output_path): def create_blank_svg(self, output_path):
self.define_related_paths() # making sure all paths are defined
self.calculate_scale() self.calculate_scale()
self.svg = svgwrite.Drawing( self.svg = svgwrite.Drawing(
output_path, output_path,
@@ -94,26 +94,19 @@ class SvgWriter:
) )
return self return self
def define_related_paths(self, **related_paths): def setup_drawing_resource_paths(self, element):
if not self.related_paths: pset = ifcopenshell.util.element.get_pset(element, "EPset_Drawing")
self.related_paths = {} for resource in ("Stylesheet", "Markers", "Symbols", "Patterns"):
resource_path = pset.get(resource)
if not related_paths: if not resource_path:
related_paths = { continue
"Stylesheet": os.path.join(self.data_dir, "styles", f"default.css"), os.makedirs(os.path.dirname(resource_path), exist_ok=True)
"Markers": os.path.join(self.data_dir, "templates", "markers.svg"), if not os.path.exists(resource_path):
"Symbols": os.path.join(self.data_dir, "templates", "symbols.svg"), resource_basename = os.path.basename(resource_path)
"Patterns": os.path.join(self.data_dir, "templates", "patterns.svg"), ootb_resource = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", resource_basename)
} if os.path.exists(ootb_resource):
for path_name in list(related_paths.keys()): shutil.copy(ootb_resource, resource_path)
if path_name in self.related_paths: self.resource_paths[resource] = tool.Ifc.resolve_uri(resource_path)
del related_paths[path_name]
for path_name in related_paths:
uri = related_paths[path_name]
custom_path = tool.Ifc.resolve_uri(uri)
if custom_path:
self.related_paths[path_name] = custom_path
def define_boilerplate(self): def define_boilerplate(self):
self.add_stylesheet() self.add_stylesheet()
@@ -132,28 +125,36 @@ class SvgWriter:
self.height = self.raw_height * self.svg_scale self.height = self.raw_height * self.svg_scale
def add_stylesheet(self): def add_stylesheet(self):
with open(self.related_paths["Stylesheet"], "r") as stylesheet: if not self.resource_paths["Stylesheet"] or not os.path.exists(self.resource_paths["Stylesheet"]):
return
with open(self.resource_paths["Stylesheet"], "r") as stylesheet:
self.svg.defs.add(self.svg.style(stylesheet.read())) self.svg.defs.add(self.svg.style(stylesheet.read()))
def add_markers(self): def add_markers(self):
tree = ET.parse(self.related_paths["Markers"]) if not self.resource_paths["Markers"] or not os.path.exists(self.resource_paths["Markers"]):
return
tree = ET.parse(self.resource_paths["Markers"])
root = tree.getroot() root = tree.getroot()
for child in root: for child in root:
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
def add_symbols(self): def add_symbols(self):
tree = ET.parse(self.related_paths["Symbols"]) if not self.resource_paths["Symbols"] or not os.path.exists(self.resource_paths["Symbols"]):
return
tree = ET.parse(self.resource_paths["Symbols"])
root = tree.getroot() root = tree.getroot()
for child in root: for child in root:
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
def find_xml_symbol_by_id(self, id): def find_xml_symbol_by_id(self, id):
tree = ET.parse(self.related_paths["Symbols"]) tree = ET.parse(self.resource_paths["Symbols"])
xml_symbol = tree.find(f'.//*[@id="{id}"]') xml_symbol = tree.find(f'.//*[@id="{id}"]')
return External(xml_symbol) if xml_symbol else None return External(xml_symbol) if xml_symbol else None
def add_patterns(self): def add_patterns(self):
tree = ET.parse(self.related_paths["Patterns"]) if not self.resource_paths["Patterns"] or not os.path.exists(self.resource_paths["Patterns"]):
return
tree = ET.parse(self.resource_paths["Patterns"])
root = tree.getroot() root = tree.getroot()
for child in root: for child in root:
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
+15
View File
@@ -182,6 +182,21 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
row.prop(context.scene.BIMProperties, "data_dir") row.prop(context.scene.BIMProperties, "data_dir")
row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="") 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, "drawings_dir")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "stylesheet_path")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "markers_path")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "symbols_path")
row = self.layout.row(align=True)
row.prop(context.scene.DocProperties, "patterns_path")
row = layout.row() row = layout.row()
row.operator("bim.configure_visibility") row.operator("bim.configure_visibility")
+25 -7
View File
@@ -72,6 +72,9 @@ def add_sheet(ifc, drawing, titleblock=None):
if ifc.get_schema() == "IFC2X3": if ifc.get_schema() == "IFC2X3":
attributes["DocumentId"] = attributes["Identification"] attributes["DocumentId"] = attributes["Identification"]
del attributes["Identification"] del attributes["Identification"]
# TODO: How does IFC2X3 store the location?
else:
attributes["Location"] = drawing.get_default_sheet_path(identification, "UNTITLED")
ifc.run("document.edit_information", information=sheet, attributes=attributes) ifc.run("document.edit_information", information=sheet, attributes=attributes)
drawing.create_svg_sheet(sheet, titleblock) drawing.create_svg_sheet(sheet, titleblock)
drawing.import_sheets() drawing.import_sheets()
@@ -103,13 +106,13 @@ def disable_editing_schedules(drawing):
def add_schedule(ifc, drawing, uri=None): def add_schedule(ifc, drawing, uri=None):
schedule = ifc.run("document.add_information") schedule = ifc.run("document.add_information")
reference = ifc.run("document.add_reference", information=schedule) reference = ifc.run("document.add_reference", information=schedule)
name = drawing.get_path_filename(uri)
if ifc.get_schema() == "IFC2X3": if ifc.get_schema() == "IFC2X3":
attributes = {"DocumentId": "X", "Name": "UNTITLED", "Scope": "SCHEDULE"} attributes = {"DocumentId": "X", "Name": name, "Scope": "SCHEDULE"}
ifc.run("document.edit_information", information=schedule, attributes=attributes)
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
else: else:
attributes = {"Identification": "X", "Name": "UNTITLED", "Scope": "SCHEDULE", "Location": uri} attributes = {"Identification": "X", "Name": name, "Scope": "SCHEDULE"}
ifc.run("document.edit_information", information=schedule, attributes=attributes) ifc.run("document.edit_information", information=schedule, attributes=attributes)
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
drawing.import_schedules() drawing.import_schedules()
@@ -119,7 +122,7 @@ def remove_schedule(ifc, drawing, schedule=None):
def open_schedule(drawing, schedule=None): def open_schedule(drawing, schedule=None):
drawing.open_spreadsheet(drawing.get_schedule_location(schedule)) drawing.open_spreadsheet(drawing.get_document_uri(schedule))
def update_schedule_name(ifc, drawing, schedule=None, name=None): def update_schedule_name(ifc, drawing, schedule=None, name=None):
@@ -164,8 +167,22 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
"HasLinework": True, "HasLinework": True,
"HasAnnotation": True, "HasAnnotation": True,
"GlobalReferencing": True, "GlobalReferencing": True,
"Stylesheet": drawing.get_default_drawing_resource_path("Stylesheet"),
"Markers": drawing.get_default_drawing_resource_path("Markers"),
"Symbols": drawing.get_default_drawing_resource_path("Symbols"),
"Patterns": drawing.get_default_drawing_resource_path("Patterns"),
}, },
) )
information = ifc.run("document.add_information")
uri = drawing.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=element, document=reference)
drawing.import_drawings() drawing.import_drawings()
@@ -204,6 +221,7 @@ def remove_drawing(ifc, drawing_tool, drawing=None):
if reference_obj: if reference_obj:
drawing_tool.delete_object(reference_obj) drawing_tool.delete_object(reference_obj)
ifc.run("root.remove_product", product=reference) ifc.run("root.remove_product", product=reference)
ifc.run("document.remove_information", information=drawing_tool.get_drawing_document(drawing))
ifc.run("root.remove_product", product=drawing) ifc.run("root.remove_product", product=drawing)
drawing_tool.import_drawings() drawing_tool.import_drawings()
@@ -243,7 +261,7 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
def build_schedule(drawing, schedule=None): def build_schedule(drawing, schedule=None):
drawing.create_svg_schedule(schedule) drawing.create_svg_schedule(schedule)
drawing.open_svg(drawing.get_document_uri(schedule)) drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg"))
def sync_references(ifc, collector, drawing_tool, drawing=None): def sync_references(ifc, collector, drawing_tool, drawing=None):
+5 -1
View File
@@ -255,15 +255,19 @@ class Drawing:
def get_annotation_context(cls, target_view): pass def get_annotation_context(cls, target_view): pass
def get_assigned_product(cls, element): pass def get_assigned_product(cls, element): pass
def get_body_context(cls): pass 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_sheet_path(cls, identification, name): pass
def get_document_uri(cls, document): pass def get_document_uri(cls, document): pass
def get_drawing_collection(cls, drawing): pass def get_drawing_collection(cls, drawing): pass
def get_drawing_document(cls, drawing): pass
def get_drawing_group(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_references(cls, drawing): pass
def get_drawing_target_view(cls, drawing): pass def get_drawing_target_view(cls, drawing): pass
def get_group_elements(cls, group): pass def get_group_elements(cls, group): pass
def get_ifc_representation_class(cls, object_type): pass def get_ifc_representation_class(cls, object_type): pass
def get_name(cls, element): pass def get_name(cls, element): pass
def get_schedule_location(cls, schedule): pass
def get_text_literal(cls, obj): pass def get_text_literal(cls, obj): pass
def remove_literal_from_annotation(cls, obj, literal): pass def remove_literal_from_annotation(cls, obj, literal): pass
def synchronise_ifc_and_text_attributes(cls, obj): pass def synchronise_ifc_and_text_attributes(cls, obj): pass
+46 -19
View File
@@ -128,13 +128,17 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod @classmethod
def create_svg_schedule(cls, schedule): def create_svg_schedule(cls, schedule):
schedule_creator = scheduler.Scheduler() schedule_creator = scheduler.Scheduler()
schedule_creator.schedule(cls.get_schedule_location(schedule), cls.get_document_uri(schedule)) schedule_creator.schedule(
cls.get_document_uri(schedule), cls.get_path_with_ext(cls.get_document_uri(schedule), "svg")
)
@classmethod @classmethod
def create_svg_sheet(cls, document, titleblock): def create_svg_sheet(cls, document, titleblock):
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.create(cls.get_document_uri(document), titleblock) uri = cls.get_document_uri(document)
sheet_builder.create(uri, titleblock)
return uri
@classmethod @classmethod
def delete_collection(cls, collection): def delete_collection(cls, collection):
@@ -252,17 +256,28 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod @classmethod
def get_document_uri(cls, document): def get_document_uri(cls, document):
if hasattr(document, "Identification"): if getattr(document, "Location", None):
name = document.Identification or "X" ifc_path = tool.Ifc.get_path()
else: if os.path.isfile(ifc_path):
name = document.DocumentId or "X" ifc_path = os.path.dirname(ifc_path)
name += " - " + (document.Name or "Unnamed") return os.path.abspath(os.path.join(ifc_path, document.Location))
if not hasattr(document, "Scope"): if document.is_a("IfcDocumentInformation"):
return if tool.Ifc.get_schema() == "IFC2X3":
if document.Scope == "DOCUMENTATION": references = document.DocumentReferences
return os.path.join(bpy.context.scene.BIMProperties.data_dir, "sheets", name + ".svg") else:
elif document.Scope == "SCHEDULE": references = document.HasDocumentReferences
return os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", name + ".svg") for reference in references:
location = cls.get_document_uri(reference)
if location:
return location
@classmethod
def get_path_filename(cls, path):
return os.path.splitext(os.path.basename(path))[0]
@classmethod
def get_path_with_ext(cls, path, ext):
return os.path.splitext(path)[0] + f".{ext}"
@classmethod @classmethod
def get_drawing_collection(cls, drawing): def get_drawing_collection(cls, drawing):
@@ -276,6 +291,12 @@ class Drawing(blenderbim.core.tool.Drawing):
if rel.is_a("IfcRelAssignsToGroup"): if rel.is_a("IfcRelAssignsToGroup"):
return rel.RelatingGroup return rel.RelatingGroup
@classmethod
def get_drawing_document(cls, drawing):
for rel in drawing.HasAssociations:
if rel.is_a("IfcRelAssociatesDocument"):
return rel.RelatingDocument
@classmethod @classmethod
def get_drawing_references(cls, drawing): def get_drawing_references(cls, drawing):
results = set() results = set()
@@ -305,12 +326,6 @@ class Drawing(blenderbim.core.tool.Drawing):
def get_name(cls, element): def get_name(cls, element):
return element.Name return element.Name
@classmethod
def get_schedule_location(cls, schedule):
if tool.Ifc.get_schema() == "IFC2X3":
return schedule.DocumentReferences[0].Location
return schedule.Location
@classmethod @classmethod
def generate_drawing_matrix(cls, target_view, location_hint): def generate_drawing_matrix(cls, target_view, location_hint):
x = 0 if location_hint == 0 else bpy.context.scene.cursor.matrix[0][3] x = 0 if location_hint == 0 else bpy.context.scene.cursor.matrix[0][3]
@@ -721,6 +736,18 @@ class Drawing(blenderbim.core.tool.Drawing):
return location_hint + " " + target_view.split("_")[0] return location_hint + " " + target_view.split("_")[0]
return target_view return target_view
@classmethod
def get_default_sheet_path(cls, identification, name):
return os.path.join(bpy.context.scene.DocProperties.sheets_dir, f"{identification} - {name}.svg")
@classmethod
def get_default_drawing_path(cls, name):
return os.path.join(bpy.context.scene.DocProperties.drawings_dir, f"{name}.svg")
@classmethod
def get_default_drawing_resource_path(cls, resource):
return getattr(bpy.context.scene.DocProperties, f"{resource.lower()}_path") or None
@classmethod @classmethod
def get_potential_reference_elements(cls, drawing): def get_potential_reference_elements(cls, drawing):
elements = [] elements = []
+4 -1
View File
@@ -106,7 +106,10 @@ class Ifc(blenderbim.core.tool.Ifc):
@classmethod @classmethod
def resolve_uri(cls, uri): def resolve_uri(cls, uri):
return uri if not uri or os.path.isabs(uri) else os.path.join(os.path.dirname(cls.get_path()), uri) ifc_path = cls.get_path()
if os.path.isfile(ifc_path):
ifc_path = os.path.dirname(ifc_path)
return uri if not uri or os.path.isabs(uri) else os.path.join(ifc_path, uri)
@classmethod @classmethod
def unlink(cls, element=None, obj=None): def unlink(cls, element=None, obj=None):
+24 -3
View File
@@ -87,10 +87,11 @@ class TestAddSheet:
drawing.generate_sheet_identification().should_be_called().will_return("identification") drawing.generate_sheet_identification().should_be_called().will_return("identification")
drawing.ensure_unique_identification("identification").should_be_called().will_return("u_identification") drawing.ensure_unique_identification("identification").should_be_called().will_return("u_identification")
ifc.get_schema().should_be_called().will_return("IFC4") ifc.get_schema().should_be_called().will_return("IFC4")
drawing.get_default_sheet_path("u_identification", "UNTITLED").should_be_called().will_return("uri")
ifc.run( ifc.run(
"document.edit_information", "document.edit_information",
information="sheet", information="sheet",
attributes={"Identification": "u_identification", "Name": "UNTITLED", "Scope": "DOCUMENTATION"}, attributes={"Identification": "u_identification", "Name": "UNTITLED", "Scope": "DOCUMENTATION", "Location": "uri"},
).should_be_called() ).should_be_called()
drawing.create_svg_sheet("sheet", "titleblock").should_be_called() drawing.create_svg_sheet("sheet", "titleblock").should_be_called()
drawing.import_sheets().should_be_called() drawing.import_sheets().should_be_called()
@@ -141,18 +142,21 @@ class TestDisableEditingSchedules:
class TestAddSchedule: class TestAddSchedule:
def test_run(self, ifc, drawing): def test_run(self, ifc, drawing):
ifc.run("document.add_information").should_be_called().will_return("schedule") ifc.run("document.add_information").should_be_called().will_return("schedule")
drawing.get_path_filename("uri").should_be_called().will_return("UNTITLED")
ifc.run("document.add_reference", information="schedule").should_be_called().will_return("reference") ifc.run("document.add_reference", information="schedule").should_be_called().will_return("reference")
ifc.get_schema().should_be_called().will_return("IFC4") ifc.get_schema().should_be_called().will_return("IFC4")
ifc.run( ifc.run(
"document.edit_information", "document.edit_information",
information="schedule", information="schedule",
attributes={"Identification": "X", "Name": "UNTITLED", "Scope": "SCHEDULE", "Location": "uri"}, attributes={"Identification": "X", "Name": "UNTITLED", "Scope": "SCHEDULE"},
).should_be_called() ).should_be_called()
ifc.run("document.edit_reference", reference="reference", attributes={"Location": "uri"}).should_be_called()
drawing.import_schedules().should_be_called() drawing.import_schedules().should_be_called()
subject.add_schedule(ifc, drawing, uri="uri") subject.add_schedule(ifc, drawing, uri="uri")
def test_using_a_document_id_in_ifc2x3(self, ifc, drawing): def test_using_a_document_id_in_ifc2x3(self, ifc, drawing):
ifc.run("document.add_information").should_be_called().will_return("schedule") ifc.run("document.add_information").should_be_called().will_return("schedule")
drawing.get_path_filename("uri").should_be_called().will_return("UNTITLED")
ifc.run("document.add_reference", information="schedule").should_be_called().will_return("reference") ifc.run("document.add_reference", information="schedule").should_be_called().will_return("reference")
ifc.get_schema().should_be_called().will_return("IFC2X3") ifc.get_schema().should_be_called().will_return("IFC2X3")
ifc.run( ifc.run(
@@ -174,7 +178,7 @@ class TestRemoveSchedule:
class TestOpenSchedule: class TestOpenSchedule:
def test_run(self, drawing): def test_run(self, drawing):
drawing.get_schedule_location("schedule").should_be_called().will_return("uri") drawing.get_document_uri("schedule").should_be_called().will_return("uri")
drawing.open_spreadsheet("uri").should_be_called() drawing.open_spreadsheet("uri").should_be_called()
subject.open_schedule(drawing, schedule="schedule") subject.open_schedule(drawing, schedule="schedule")
@@ -225,6 +229,10 @@ class TestAddDrawing:
ifc.run("group.assign_group", group="group", products=["element"]).should_be_called() ifc.run("group.assign_group", group="group", products=["element"]).should_be_called()
collector.assign("obj").should_be_called() collector.assign("obj").should_be_called()
ifc.run("pset.add_pset", product="element", name="EPset_Drawing").should_be_called().will_return("pset") ifc.run("pset.add_pset", product="element", name="EPset_Drawing").should_be_called().will_return("pset")
drawing.get_default_drawing_resource_path("Stylesheet").should_be_called().will_return("stylesheet.css")
drawing.get_default_drawing_resource_path("Markers").should_be_called().will_return("markers.svg")
drawing.get_default_drawing_resource_path("Symbols").should_be_called().will_return("symbols.svg")
drawing.get_default_drawing_resource_path("Patterns").should_be_called().will_return("patterns.svg")
ifc.run( ifc.run(
"pset.edit_pset", "pset.edit_pset",
pset="pset", pset="pset",
@@ -236,8 +244,19 @@ class TestAddDrawing:
"HasLinework": True, "HasLinework": True,
"HasAnnotation": True, "HasAnnotation": True,
"GlobalReferencing": True, "GlobalReferencing": True,
"Stylesheet": "stylesheet.css",
"Markers": "markers.svg",
"Symbols": "symbols.svg",
"Patterns": "patterns.svg",
}, },
).should_be_called() ).should_be_called()
drawing.get_default_drawing_path("name").should_be_called().will_return("uri")
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_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() drawing.import_drawings().should_be_called()
subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint") subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint")
@@ -277,6 +296,8 @@ class TestRemoveDrawing:
ifc.get_object("reference").should_be_called().will_return("reference_obj") ifc.get_object("reference").should_be_called().will_return("reference_obj")
drawing.delete_object("reference_obj").should_be_called() drawing.delete_object("reference_obj").should_be_called()
ifc.run("root.remove_product", product="reference").should_be_called() ifc.run("root.remove_product", product="reference").should_be_called()
drawing.get_drawing_document("drawing").should_be_called().will_return("information")
ifc.run("document.remove_information", information="information").should_be_called()
ifc.run("root.remove_product", product="drawing").should_be_called() ifc.run("root.remove_product", product="drawing").should_be_called()
drawing.import_drawings().should_be_called() drawing.import_drawings().should_be_called()
subject.remove_drawing(ifc, drawing, drawing="drawing") subject.remove_drawing(ifc, drawing, drawing="drawing")
+34 -37
View File
@@ -61,9 +61,15 @@ class TestCreateCamera(NewFile):
class TestCreateSvgSheet(NewFile): class TestCreateSvgSheet(NewFile):
def test_run(self): def test_run(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="DOCUMENTATION") document = ifc.createIfcDocumentInformation(
subject.create_svg_sheet(document, "A1") Identification="X",
assert os.path.isfile(os.path.join(bpy.context.scene.BIMProperties.data_dir, "sheets", "X - FOOBAR.svg")) Name="FOOBAR",
Scope="DOCUMENTATION",
Location=os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", "X - FOOBAR.svg"),
)
uri = subject.create_svg_sheet(document, "A1")
assert uri.endswith(".svg")
assert os.path.isfile(uri)
class TestDeleteCollection(NewFile): class TestDeleteCollection(NewFile):
@@ -204,11 +210,13 @@ class TestEnsureUniqueIdentification(NewFile):
class TestExportTextLiteralAttributes(NewFile): class TestExportTextLiteralAttributes(NewFile):
def test_run(self): def test_run(self):
TestImportTextAttributes().test_run() TestImportTextAttributes().test_run()
assert subject.export_text_literal_attributes(bpy.data.objects.get("Object")) == [{ assert subject.export_text_literal_attributes(bpy.data.objects.get("Object")) == [
"Literal": "Literal", {
"Path": "RIGHT", "Literal": "Literal",
"BoxAlignment": "bottom-left", "Path": "RIGHT",
}] "BoxAlignment": "bottom-left",
}
]
class TestGetAnnotationContext(NewFile): class TestGetAnnotationContext(NewFile):
@@ -236,23 +244,29 @@ class TestGetBodyContext(NewFile):
class TestGetDocumentUri(NewFile): class TestGetDocumentUri(NewFile):
def test_get_sheet_uri(self): def test_run(self):
ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(
Identification="X", Name="FOOBAR", Scope="DOCUMENTATION", Location="Location"
)
assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
def test_get_indirect_locations(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="DOCUMENTATION") document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="DOCUMENTATION")
result = subject.get_document_uri(document) reference = ifc.createIfcDocumentReference(Location="Location", ReferencedDocument=document)
assert result == os.path.join(bpy.context.scene.BIMProperties.data_dir, "sheets", "X - FOOBAR.svg") assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
assert subject.get_document_uri(reference) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
def test_get_schedule_uri(self):
ifc = ifcopenshell.file()
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SCHEDULE")
result = subject.get_document_uri(document)
assert result == os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", "X - FOOBAR.svg")
def test_run_ifc2x3(self): def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3") ifc = ifcopenshell.file(schema="IFC2X3")
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="DOCUMENTATION") tool.Ifc.set(ifc)
result = subject.get_document_uri(document) reference = ifc.createIfcDocumentReference(Location="Location")
assert result == os.path.join(bpy.context.scene.BIMProperties.data_dir, "sheets", "X - FOOBAR.svg") document = ifc.createIfcDocumentInformation(
DocumentId="X", Name="FOOBAR", Scope="DOCUMENTATION", DocumentReferences=[reference]
)
assert subject.get_document_uri(document) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
assert subject.get_document_uri(reference) == os.path.abspath(os.path.join(tool.Ifc.get_path(), "Location"))
class TestGetDrawingCollection(NewFile): class TestGetDrawingCollection(NewFile):
@@ -311,23 +325,6 @@ class TestGetName(NewFile):
assert subject.get_name(ifc.createIfcWall(Name="Foobar")) == "Foobar" assert subject.get_name(ifc.createIfcWall(Name="Foobar")) == "Foobar"
class TestGetScheduleLocation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
schedule = ifcopenshell.api.run("document.add_information", ifc)
schedule.Location = "uri"
reference = ifcopenshell.api.run("document.add_reference", ifc, information=schedule)
subject.get_schedule_location(schedule) == "uri"
def test_run_ifc2x3(self):
ifc = ifcopenshell.file(schema="IFC2X3")
tool.Ifc.set(ifc)
reference = ifc.createIfcDocumentReference(Location="uri")
schedule = ifc.createIfcDocumentInformation(DocumentReferences=[reference])
subject.get_schedule_location(schedule) == "uri"
class TestGenerateDrawingMatrix(NewFile): class TestGenerateDrawingMatrix(NewFile):
def test_returning_the_origin_as_a_fallback(self): def test_returning_the_origin_as_a_fallback(self):
assert subject.generate_drawing_matrix("PLAN_VIEW", None) == mathutils.Matrix() assert subject.generate_drawing_matrix("PLAN_VIEW", None) == mathutils.Matrix()