#1153. You can now assign and remove drawings from sheets.

This commit is contained in:
Dion Moult
2022-03-30 18:14:51 +11:00
parent 8686462eed
commit a0c8d87ad0
7 changed files with 221 additions and 54 deletions
@@ -33,6 +33,7 @@ classes = (
operator.AddSheet,
operator.BuildSchedule,
operator.CleanWireframes,
operator.ContractSheet,
operator.CopyGrid,
operator.CreateDrawing,
operator.CreateSheets,
@@ -45,12 +46,14 @@ classes = (
operator.EditVectorStyle,
operator.EnableEditingText,
operator.EnableEditingTextProduct,
operator.ExpandSheet,
operator.GenerateReferences,
operator.LoadDrawings,
operator.LoadSheets,
operator.OpenSheet,
operator.OpenView,
operator.RemoveDrawing,
operator.RemoveDrawingFromSheet,
operator.RemoveDrawingStyle,
operator.RemoveDrawingStyleAttribute,
operator.RemoveSchedule,
@@ -571,7 +571,6 @@ class AddDrawingToSheet(bpy.types.Operator):
bl_idname = "bim.add_drawing_to_sheet"
bl_label = "Add Drawing To Sheet"
bl_options = {"REGISTER", "UNDO"}
# TODO: check undo redo
@classmethod
def poll(cls, context):
@@ -580,9 +579,78 @@ class AddDrawingToSheet(bpy.types.Operator):
def execute(self, context):
props = context.scene.DocProperties
active_drawing = props.drawings[props.active_drawing_index]
active_sheet = props.sheets[props.active_sheet_index]
drawing = tool.Ifc.get().by_id(active_drawing.ifc_definition_id)
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
if not sheet.is_a("IfcDocumentInformation"):
return {"FINISHED"}
if tool.Ifc.get_schema() == "IFC2X3":
references = sheet.DocumentReferences or []
else:
references = sheet.HasDocumentReferences or []
has_drawing = False
for reference in references:
new = props.sheets.add()
new.ifc_definition_id = reference.id()
new.is_sheet = False
if tool.Ifc.get_schema() == "IFC2X3":
new.identification = reference.ItemReference or "X"
element = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
0
].RelatedObjects[0]
else:
new.identification = reference.Identification or "X"
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
if element == drawing:
has_drawing = True
break
if has_drawing:
return {"FINISHED"}
reference = tool.Ifc.run("document.add_reference", information=sheet)
if tool.Ifc.get_schema() == "IFC2X3":
attributes = {"ItemReference": str(len(sheet.DocumentReferences or []))}
else:
attributes = {"Identification": str(len(sheet.HasDocumentReferences or []))}
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.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_drawing(props.active_drawing.name, props.active_sheet.name)
sheet_builder.add_drawing(drawing, sheet)
tool.Drawing.import_sheets()
return {"FINISHED"}
class RemoveDrawingFromSheet(bpy.types.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):
reference = tool.Ifc.get().by_id(self.reference)
if tool.Ifc.get_schema() == "IFC2X3":
sheet = reference.ReferenceToDocument[0]
drawing = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
0
].RelatedObjects[0]
else:
sheet = reference.ReferencedDocument
drawing = reference.DocumentRefForObjects[0].RelatedObjects[0]
tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
tool.Ifc.run("document.remove_reference", reference=reference)
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.remove_drawing(drawing, sheet)
tool.Drawing.import_sheets()
return {"FINISHED"}
@@ -598,7 +666,8 @@ class CreateSheets(bpy.types.Operator):
def execute(self, context):
scene = context.scene
props = scene.DocProperties
name = props.active_sheet.name
active_sheet = props.sheets[props.active_sheet_index]
name = active_sheet.name
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir
sheet_builder.build(name)
@@ -943,12 +1012,10 @@ class RemoveSheet(bpy.types.Operator, Operator):
bl_idname = "bim.remove_sheet"
bl_label = "Remove Sheet"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty()
sheet: bpy.props.IntProperty()
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.remove_sheet(tool.Ifc, tool.Drawing, sheet=sheet)
core.remove_sheet(tool.Ifc, tool.Drawing, sheet=tool.Ifc.get().by_id(self.sheet))
class AddSchedule(bpy.types.Operator):
@@ -1323,3 +1390,31 @@ class DisableEditingDrawings(bpy.types.Operator, Operator):
def _execute(self, context):
core.disable_editing_drawings(tool.Drawing)
class ExpandSheet(bpy.types.Operator):
bl_idname = "bim.expand_sheet"
bl_label = "Expand Sheet"
bl_options = {"REGISTER", "UNDO"}
sheet: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.DocProperties
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
sheet.is_expanded = True
core.load_sheets(tool.Drawing)
return {"FINISHED"}
class ContractSheet(bpy.types.Operator):
bl_idname = "bim.contract_sheet"
bl_label = "Contract Sheet"
bl_options = {"REGISTER", "UNDO"}
sheet: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.DocProperties
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
sheet.is_expanded = False
core.load_sheets(tool.Drawing)
return {"FINISHED"}
@@ -221,8 +221,9 @@ class Sheet(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
identification: StringProperty(name="Identification")
name: StringProperty(name="Name", get=get_name, set=set_name)
drawings: CollectionProperty(name="Drawings", type=Drawing)
active_drawing_index: IntProperty(name="Active Drawing Index")
is_sheet: BoolProperty(name="Is Sheet", default=False)
reference_type: StringProperty(name="Reference Type")
is_expanded: BoolProperty(name="Is Expanded", default=False)
class DrawingStyle(PropertyGroup):
@@ -313,10 +314,6 @@ class DocProperties(PropertyGroup):
def active_schedule(self):
return self.schedules[self.active_schedule_index]
@property
def active_sheet(self):
return self.sheets[self.active_sheet_index]
class BIMCameraProperties(PropertyGroup):
representation: StringProperty(name="Representation")
@@ -16,11 +16,12 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import xml.etree.ElementTree as ET
import urllib.parse
import pystache
import ntpath
import os
import ntpath
import pystache
import urllib.parse
import xml.etree.ElementTree as ET
import blenderbim.tool as tool
from shutil import copy
from xml.dom import minidom
@@ -59,11 +60,16 @@ class SheetBuilder:
with open(sheet_path, "w") as f:
f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" "))
def add_drawing(self, view_name, sheet_name):
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
view_path = os.path.join(self.data_dir, "diagrams", view_name + ".svg")
def add_drawing(self, drawing, sheet):
filename = drawing.Name
sheet_name = tool.Drawing.get_sheet_filename(sheet)
sheet_dir = os.path.join(self.data_dir, "sheets")
drawing_dir = os.path.join(self.data_dir, "diagrams")
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
drawing_path = os.path.join(drawing_dir, filename + ".svg")
underlay_path = os.path.join(drawing_dir, filename + ".png")
if not os.path.isfile(view_path):
if not os.path.isfile(sheet_path):
raise FileNotFoundError
ET.register_namespace("", "http://www.w3.org/2000/svg")
@@ -72,7 +78,7 @@ class SheetBuilder:
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
view_tree = ET.parse(view_path)
view_tree = ET.parse(drawing_path)
view_root = view_tree.getroot()
# The view is placed into a group with a background image element.
@@ -80,26 +86,48 @@ class SheetBuilder:
# here to accommodate browsers which do not nest images.
view = ET.SubElement(sheet_root, "g")
view.attrib["data-type"] = "drawing"
view.attrib["data-guid"] = drawing.GlobalId
view_width = self.convert_to_mm(view_root.attrib.get("width"))
view_height = self.convert_to_mm(view_root.attrib.get("height"))
background = ET.SubElement(view, "image")
background.attrib["xlink:href"] = "../diagrams/{}.png".format(view_name)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
background.attrib["width"] = str(view_width)
background.attrib["height"] = str(view_height)
if os.path.isfile(underlay_path):
background = ET.SubElement(view, "image")
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
background.attrib["width"] = str(view_width)
background.attrib["height"] = str(view_height)
foreground = ET.SubElement(view, "image")
foreground.attrib["xlink:href"] = "../diagrams/{}.svg".format(view_name)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
if os.path.isfile(drawing_path):
foreground = ET.SubElement(view, "image")
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
foreground.attrib["width"] = str(view_width)
foreground.attrib["height"] = str(view_height)
self.add_view_title(30, view_height + 35, view)
sheet_tree.write(sheet_path)
def remove_drawing(self, drawing, sheet):
sheet_name = tool.Drawing.get_sheet_filename(sheet)
sheet_dir = os.path.join(self.data_dir, "sheets")
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
ET.register_namespace("", "http://www.w3.org/2000/svg")
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
print('removing drawing', sheet_root.findall("{http://www.w3.org/2000/svg}g"))
for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
print('checking g', g)
if g.attrib["data-type"] == "drawing" and g.attrib["data-guid"] == drawing.GlobalId:
sheet_root.remove(g)
break
sheet_tree.write(sheet_path)
def add_schedule(self, schedule_name, sheet_name):
sheet_path = os.path.join(self.data_dir, "sheets", sheet_name + ".svg")
view_path = os.path.join(self.data_dir, "schedules", schedule_name + ".svg")
@@ -246,14 +246,19 @@ class BIM_PT_sheets(Panel):
row.operator("bim.add_sheet", text="", icon="ADD")
row.operator("bim.disable_editing_sheets", text="", icon="CANCEL")
if self.props.sheets:
if self.props.sheets and self.props.active_sheet_index < len(self.props.sheets):
active_sheet = self.props.sheets[self.props.active_sheet_index]
row = self.layout.row(align=True)
row.alignment = "RIGHT"
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="")
row.operator("bim.create_sheets", icon="FILE_REFRESH", text="")
row.operator("bim.remove_sheet", icon="X", text="").index = self.props.active_sheet_index
if active_sheet.is_sheet:
row.operator("bim.remove_sheet", icon="X", text="").sheet = active_sheet.ifc_definition_id
else:
op = row.operator("bim.remove_drawing_from_sheet", icon="X", text="")
op.reference = active_sheet.ifc_definition_id
self.layout.template_list("BIM_UL_sheets", "", self.props, "sheets", self.props, "active_sheet_index")
@@ -388,9 +393,22 @@ class BIM_UL_sheets(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
split1 = row.split(factor=0.2)
split1.label(text=item.identification or "X")
split2 = split1.split(factor=0.8)
split2.label(text=item.name or "Unnamed")
if item.is_sheet:
if item.is_expanded:
row.operator(
"bim.contract_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).sheet = item.ifc_definition_id
else:
row.operator(
"bim.expand_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).sheet = item.ifc_definition_id
else:
row.label(text="", icon="BLANK1")
if item.reference_type == "DRAWING":
row.label(text="", icon="IMAGE_DATA")
name = "{} - {}".format(item.identification or "X", item.name or "Unnamed")
row.label(text=name)
else:
layout.label(text="", translate=False)
+1 -1
View File
@@ -85,7 +85,7 @@ def open_sheet(drawing, sheet=None):
def remove_sheet(ifc, drawing, sheet=None):
ifc.run("document.remove_document", document=sheet)
ifc.run("document.remove_information", information=sheet)
drawing.import_sheets()
+39 -13
View File
@@ -273,16 +273,45 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def import_sheets(cls):
bpy.context.scene.DocProperties.sheets.clear()
props = bpy.context.scene.DocProperties
expanded_sheets = {s.ifc_definition_id for s in props.sheets if s.is_expanded}
props.sheets.clear()
sheets = [d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"]
for sheet in sheets:
new = bpy.context.scene.DocProperties.sheets.add()
new = props.sheets.add()
new.ifc_definition_id = sheet.id()
if tool.Ifc.get_schema() == "IFC2X3":
new.identification = sheet.DocumentId
else:
new.identification = sheet.Identification
new.name = sheet.Name
new.is_sheet = True
new.is_expanded = sheet.id() in expanded_sheets
if not new.is_expanded:
continue
if tool.Ifc.get_schema() == "IFC2X3":
references = sheet.DocumentReferences or []
else:
references = sheet.HasDocumentReferences or []
for reference in references:
new = props.sheets.add()
new.ifc_definition_id = reference.id()
new.is_sheet = False
if tool.Ifc.get_schema() == "IFC2X3":
new.identification = reference.ItemReference or "X"
element = [
r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference
][0].RelatedObjects[0]
else:
new.identification = reference.Identification or "X"
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
new.name = element.Name
new.reference_type = "DRAWING"
@classmethod
def import_text_attributes(cls, obj):
@@ -456,9 +485,6 @@ class Drawing(blenderbim.core.tool.Drawing):
# 1. Select the 4 +Z vertices local to the reference element. This is the cutting plane.
verts_local_to_reference = [reference_obj.matrix_world.inverted() @ v for v in reference_mesh["verts"]]
cutting_plane_verts = sorted(verts_local_to_reference, key=lambda x: x.z)[-4:]
# Filter verts with the same XY coords
#set([v.xy for v in cutting_plane_verts])
global_cutting_plane_verts = [reference_obj.matrix_world @ v for v in cutting_plane_verts]
# 2. Project the cutting plane onto our viewing camera.
verts_local_to_camera = [camera.matrix_world.inverted() @ v for v in global_cutting_plane_verts]
@@ -640,14 +666,14 @@ class Drawing(blenderbim.core.tool.Drawing):
depth = obj.data.clip_end
verts = (
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, -depth)),
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, 0)),
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, -depth)),
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, 0)),
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, -depth)),
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, 0)),
obj.matrix_world @ mathutils.Vector((width / 2, height /2, -depth)),
obj.matrix_world @ mathutils.Vector((width / 2, height /2, 0))
obj.matrix_world @ mathutils.Vector((-width / 2, -height / 2, -depth)),
obj.matrix_world @ mathutils.Vector((-width / 2, -height / 2, 0)),
obj.matrix_world @ mathutils.Vector((-width / 2, height / 2, -depth)),
obj.matrix_world @ mathutils.Vector((-width / 2, height / 2, 0)),
obj.matrix_world @ mathutils.Vector((width / 2, -height / 2, -depth)),
obj.matrix_world @ mathutils.Vector((width / 2, -height / 2, 0)),
obj.matrix_world @ mathutils.Vector((width / 2, height / 2, -depth)),
obj.matrix_world @ mathutils.Vector((width / 2, height / 2, 0)),
)
faces = [
[0, 1, 3, 2],