Files
IfcOpenShell/src/ifcblenderexport/blenderbim/bim/operator.py
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1835 lines
76 KiB
Python
Raw Normal View History

2020-12-26 23:25:39 +07:00
from itertools import chain
import os
import bpy
import uuid
import time
import json
import logging
import webbrowser
import subprocess
2019-12-11 18:44:46 +11:00
import ifcopenshell
2020-08-08 17:58:45 +10:00
import ifcopenshell.util.selector
import ifcopenshell.util.geolocation
import ifcopenshell.util.element
import ifcopenshell.util.schema
import numpy as np
from . import export_ifc
from . import import_ifc
from . import cut_ifc
from . import svgwriter
from . import sheeter
from . import scheduler
from . import schema
from . import ifc
2020-05-03 15:41:05 +10:00
from . import annotation
from . import helper
from bpy_extras.io_utils import ImportHelper
from mathutils import Vector, Matrix, Euler, geometry
2021-01-04 11:31:30 +07:00
import bmesh
2020-12-23 15:53:38 +11:00
from math import radians, degrees, atan, tan, cos, sin
from bpy.app.handlers import persistent
@persistent
def depsgraph_update_pre_handler(scene):
set_active_camera_resolution(scene)
def set_active_camera_resolution(scene):
2020-11-01 20:08:48 +07:00
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
return
2020-11-01 20:08:48 +07:00
if (
scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x
or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y
):
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y
2020-09-03 23:02:41 +10:00
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
if scene.camera != current_drawing.camera:
2020-11-01 20:08:48 +07:00
scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split("/")[1])
2020-09-03 23:02:41 +10:00
bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index)
def open_with_user_command(user_command, path):
if user_command:
commands = eval(user_command)
for command in commands:
subprocess.run(command)
else:
2020-11-01 20:08:48 +07:00
webbrowser.open("file://" + path)
class ExportIFC(bpy.types.Operator):
bl_idname = "export_ifc.bim"
bl_label = "Export IFC"
filename_ext = ".ifc"
2020-11-01 20:08:48 +07:00
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
def invoke(self, context, event):
if not self.filepath:
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".ifc")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
start = time.time()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ExportIFC")
logging.basicConfig(
2020-11-01 20:08:48 +07:00
filename=context.scene.BIMProperties.data_dir + "process.log", filemode="a", level=logging.DEBUG
)
extension = self.filepath.split(".")[-1]
if extension == "ifczip":
output_file = bpy.path.ensure_ext(self.filepath, ".ifczip")
elif extension == "ifcjson":
output_file = bpy.path.ensure_ext(self.filepath, ".ifcjson")
else:
2020-11-01 20:08:48 +07:00
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
settings.json_version = self.json_version
settings.json_compact = self.json_compact
ifc_exporter = export_ifc.IfcExporter(settings)
settings.logger.info("Starting export")
ifc_exporter.export()
settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
print("Export finished in {:.2f} seconds".format(time.time() - start))
if not bpy.context.scene.DocProperties.ifc_files:
new = bpy.context.scene.DocProperties.ifc_files.add()
new.name = output_file
if not bpy.context.scene.BIMProperties.ifc_file:
bpy.context.scene.BIMProperties.ifc_file = output_file
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class ImportIFC(bpy.types.Operator, ImportHelper):
bl_idname = "import_ifc.bim"
bl_label = "Import IFC"
filename_ext = ".ifc"
2020-11-01 20:08:48 +07:00
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
should_import_type_representations: bpy.props.BoolProperty(name="Import Type Representations", default=False)
should_import_spaces: bpy.props.BoolProperty(name="Import Spaces", default=False)
should_auto_set_workarounds: bpy.props.BoolProperty(name="Automatically Set Vendor Workarounds", default=True)
should_use_cpu_multiprocessing: bpy.props.BoolProperty(name="Import with CPU Multiprocessing", default=True)
should_merge_by_class: bpy.props.BoolProperty(name="Import and Merge by Class", default=False)
should_merge_by_material: bpy.props.BoolProperty(name="Import and Merge by Material", default=False)
should_merge_materials_by_colour: bpy.props.BoolProperty(name="Import and Merge Materials by Colour", default=False)
should_clean_mesh: bpy.props.BoolProperty(name="Import and Clean Mesh", default=True)
deflection_tolerance: bpy.props.FloatProperty(name="Import Deflection Tolerance", default=0.001)
angular_tolerance: bpy.props.FloatProperty(name="Import Angular Tolerance", default=0.5)
should_allow_non_element_aggregates: bpy.props.BoolProperty(name="Import Non-Element Aggregates", default=False)
should_offset_model: bpy.props.BoolProperty(name="Import and Offset Model", default=False)
model_offset_coordinates: bpy.props.StringProperty(name="Model Offset Coordinates", default="0,0,0")
ifc_import_filter: bpy.props.EnumProperty(
items=[("NONE", "None", ""), ("WHITELIST", "Whitelist", ""), ("BLACKLIST", "Blacklist", ""),],
name="Import Filter",
)
ifc_selector: bpy.props.StringProperty(default="", name="IFC Selector")
def execute(self, context):
start = time.time()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
logging.basicConfig(
2020-11-01 20:08:48 +07:00
filename=bpy.context.scene.BIMProperties.data_dir + "process.log", filemode="a", level=logging.DEBUG
)
settings = import_ifc.IfcImportSettings.factory(context, self.filepath, logger)
settings.should_import_type_representations = self.should_import_type_representations
settings.should_import_spaces = self.should_import_spaces
settings.should_auto_set_workarounds = self.should_auto_set_workarounds
settings.should_use_cpu_multiprocessing = self.should_use_cpu_multiprocessing
settings.should_merge_by_class = self.should_merge_by_class
settings.should_merge_by_material = self.should_merge_by_material
settings.should_merge_materials_by_colour = self.should_merge_materials_by_colour
settings.should_clean_mesh = self.should_clean_mesh
settings.deflection_tolerance = self.deflection_tolerance
settings.angular_tolerance = self.angular_tolerance
settings.should_allow_non_element_aggregates = self.should_allow_non_element_aggregates
settings.should_offset_model = self.should_offset_model
settings.model_offset_coordinates = (
[float(o) for o in self.model_offset_coordinates.split(",")]
if self.model_offset_coordinates
else (0, 0, 0)
)
settings.ifc_import_filter = self.ifc_import_filter
settings.ifc_selector = self.ifc_selector
settings.logger.info("Starting import")
ifc_importer = import_ifc.IfcImporter(settings)
ifc_importer.execute()
settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
2020-11-01 20:08:48 +07:00
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class OpenUri(bpy.types.Operator):
bl_idname = "bim.open_uri"
bl_label = "Open URI"
uri: bpy.props.StringProperty()
2020-03-31 12:35:17 +11:00
def execute(self, context):
webbrowser.open(self.uri)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-31 12:35:17 +11:00
class AddSweptSolid(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_swept_solid"
bl_label = "Add Swept Solid"
def execute(self, context):
swept_solids = bpy.context.active_object.data.BIMMeshProperties.swept_solids
swept_solid = swept_solids.add()
2020-11-01 20:08:48 +07:00
swept_solid.name = "Swept Solid {}".format(len(swept_solids))
return {"FINISHED"}
class RemoveSweptSolid(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_swept_solid"
bl_label = "Remove Swept Solid"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMMeshProperties.swept_solids.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignSweptSolidOuterCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_swept_solid_outer_curve"
bl_label = "Assign Outer Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].outer_curve = json.dumps(vertices)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidOuterCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_outer_curve"
bl_label = "Select Outer Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
outer_curve = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].outer_curve
if not outer_curve:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
indices = json.loads(outer_curve)
for index in indices:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class AddSweptSolidInnerCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_swept_solid_inner_curve"
bl_label = "Add Inner Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
swept_solid = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index]
if swept_solid.inner_curves:
curves = json.loads(swept_solid.inner_curves)
else:
curves = []
curves.append(vertices)
swept_solid.inner_curves = json.dumps(curves)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidInnerCurves(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_inner_curves"
bl_label = "Select Inner Curves"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
inner_curves = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].inner_curves
if not inner_curves:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
curves = json.loads(inner_curves)
for curve in curves:
for index in curve:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class AssignSweptSolidExtrusion(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_swept_solid_extrusion"
bl_label = "Assign Extrusion"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].extrusion = json.dumps(vertices)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidExtrusion(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_extrusion"
bl_label = "Select Extrusion"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
extrusion = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].extrusion
if not extrusion:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
indices = json.loads(extrusion)
for index in indices:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class SelectExternalMaterialDir(bpy.types.Operator):
bl_idname = "bim.select_external_material_dir"
bl_label = "Select Material File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.active_object.active_material.BIMMaterialProperties.location = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectIfcFile(bpy.types.Operator):
bl_idname = "bim.select_ifc_file"
bl_label = "Select IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.ifc_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectDataDir(bpy.types.Operator):
bl_idname = "bim.select_data_dir"
bl_label = "Select Data Directory"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.data_dir = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectSchemaDir(bpy.types.Operator):
bl_idname = "bim.select_schema_dir"
bl_label = "Select Schema Directory"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.schema_dir = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2019-11-01 16:32:11 +11:00
class FetchExternalMaterial(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.fetch_external_material"
bl_label = "Fetch External Material"
def execute(self, context):
location = bpy.context.active_object.active_material.BIMMaterialProperties.location
2020-11-01 20:08:48 +07:00
if location[-6:] != ".mpass":
return {"FINISHED"}
if not os.path.isabs(location):
2020-11-01 20:08:48 +07:00
location = os.path.join(os.path.join(bpy.context.scene.BIMProperties.data_dir, location))
with open(location) as f:
self.material_pass = json.load(f)
2020-11-01 20:08:48 +07:00
if bpy.context.scene.render.engine == "BLENDER_EEVEE" and "eevee" in self.material_pass:
self.fetch_eevee_or_cycles("eevee")
elif bpy.context.scene.render.engine == "CYCLES" and "cycles" in self.material_pass:
self.fetch_eevee_or_cycles("cycles")
return {"FINISHED"}
def fetch_eevee_or_cycles(self, name):
identification = bpy.context.active_object.active_material.BIMMaterialProperties.identification
2020-11-01 20:08:48 +07:00
uri = self.material_pass[name]["uri"]
if not os.path.isabs(uri):
2020-11-01 20:08:48 +07:00
uri = os.path.join(os.path.join(bpy.context.scene.BIMProperties.data_dir, uri))
bpy.ops.wm.link(filename=identification, directory=os.path.join(uri, "Material"))
for material in bpy.data.materials:
2020-11-01 20:08:48 +07:00
if material.name == identification and material.library:
bpy.context.active_object.material_slots[0].material = material
return
class FetchObjectPassport(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.fetch_object_passport"
bl_label = "Fetch Object Passport"
def execute(self, context):
for reference in bpy.context.active_object.BIMObjectProperties.document_references:
reference = bpy.context.scene.BIMProperties.document_references[reference.name]
2020-11-01 20:08:48 +07:00
if reference.location[-6:] == ".blend":
self.fetch_blender(reference)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def fetch_blender(self, reference):
2020-11-01 20:08:48 +07:00
bpy.ops.wm.link(filename=reference.name, directory=os.path.join(reference.location, "Mesh"))
bpy.context.active_object.data = bpy.data.meshes[reference.name]
class OpenView(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_view"
bl_label = "Open View"
view: bpy.props.StringProperty()
def execute(self, context):
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"),
)
return {"FINISHED"}
class OpenViewCamera(bpy.types.Operator):
bl_idname = "bim.open_view_camera"
bl_label = "Open View Camera"
view_name: bpy.props.StringProperty()
def execute(self, context):
new_drawing_index = bpy.context.scene.DocProperties.drawings.find(self.view_name)
bpy.context.scene.DocProperties.active_drawing_index = new_drawing_index
drawing = bpy.context.scene.DocProperties.drawings[new_drawing_index]
bpy.context.view_layer.objects.active = drawing.camera
bpy.ops.object.select_all(action="DESELECT")
drawing.camera.select_set(True)
for area in bpy.context.screen.areas:
if area.ui_type == "PROPERTIES":
for space in area.spaces:
space.context = "DATA"
return {"FINISHED"}
class CutSection(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.cut_section"
bl_label = "Cut Section"
def execute(self, context):
self.file = ifc.IfcStore.get_file()
camera = bpy.context.scene.camera
2020-11-01 20:08:48 +07:00
if not (camera.type == "CAMERA" and camera.data.type == "ORTHO"):
return {"FINISHED"}
if not bpy.context.scene.DocProperties.ifc_files and bpy.context.scene.BIMProperties.ifc_file:
new = bpy.context.scene.DocProperties.ifc_files.add()
new.name = bpy.context.scene.BIMProperties.ifc_file
2020-11-01 20:08:48 +07:00
bpy.ops.bim.activate_view(
drawing_index=bpy.context.scene.DocProperties.drawings.find(camera.name.split("/")[1])
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
]
self.diagram_name = camera.name.split("/")[1]
bpy.context.scene.render.filepath = os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.data_dir, "diagrams", "{}.png".format(self.diagram_name)
)
self.create_raster(camera, drawing_style)
location = camera.location
render = bpy.context.scene.render
if self.is_landscape():
width = camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
height = camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
depth = camera.data.clip_end
projection = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
x_axis = camera.matrix_world.to_quaternion() @ Vector((1, 0, 0))
y_axis = camera.matrix_world.to_quaternion() @ Vector((0, -1, 0))
top_left_corner = location - (width / 2 * x_axis) - (height / 2 * y_axis)
ifc_cutter = cut_ifc.IfcCutter()
2020-11-01 20:08:48 +07:00
ifc_cutter.ifc_filenames = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
ifc_cutter.vector_style = drawing_style.vector_style
ifc_cutter.diagram_name = self.diagram_name
ifc_cutter.background_image = bpy.context.scene.render.filepath
2020-11-01 20:08:48 +07:00
if camera.data.BIMCameraProperties.cut_objects == "CUSTOM":
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects_custom
else:
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects
ifc_cutter.leader_obj = None
ifc_cutter.stair_obj = None
ifc_cutter.dimension_objs = []
2020-05-11 10:00:54 +10:00
ifc_cutter.break_obj = None
ifc_cutter.equal_objs = []
ifc_cutter.hidden_objs = []
ifc_cutter.solid_objs = []
ifc_cutter.plan_level_obj = None
ifc_cutter.section_level_obj = None
ifc_cutter.grid_objs = []
ifc_cutter.text_objs = []
ifc_cutter.misc_objs = []
ifc_cutter.attributes = [a.name for a in drawing_style.attributes]
for obj in camera.users_collection[0].objects:
2020-11-01 20:08:48 +07:00
if "IfcGrid" in obj.name:
ifc_cutter.grid_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "IfcGroup" in obj.name and obj.type == "CAMERA":
ifc_cutter.camera_obj = obj
2020-11-01 20:08:48 +07:00
if "IfcAnnotation/" not in obj.name:
continue
2020-11-01 20:08:48 +07:00
if "Leader" in obj.name:
ifc_cutter.leader_obj = (obj, obj.data)
2020-11-01 20:08:48 +07:00
elif "Stair" in obj.name:
ifc_cutter.stair_obj = obj
2020-11-01 20:08:48 +07:00
elif "Equal" in obj.name:
ifc_cutter.equal_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "Dimension" in obj.name:
ifc_cutter.dimension_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "Break" in obj.name:
2020-05-11 10:00:54 +10:00
ifc_cutter.break_obj = obj
2020-11-01 20:08:48 +07:00
elif "Hidden" in obj.name:
ifc_cutter.hidden_objs.append((obj, obj.data))
2020-11-01 20:08:48 +07:00
elif "Solid" in obj.name:
ifc_cutter.solid_objs.append((obj, obj.data))
2020-11-01 20:08:48 +07:00
elif "Plan Level" in obj.name:
ifc_cutter.plan_level_obj = obj
2020-11-01 20:08:48 +07:00
elif "Section Level" in obj.name:
ifc_cutter.section_level_obj = obj
2020-11-01 20:08:48 +07:00
elif obj.type == "FONT":
ifc_cutter.text_objs.append(obj)
else:
ifc_cutter.misc_objs.append(obj)
ifc_cutter.section_box = {
2020-11-01 20:08:48 +07:00
"projection": tuple(projection),
"x_axis": tuple(x_axis),
"y_axis": tuple(y_axis),
"top_left_corner": tuple(top_left_corner),
"x": width,
"y": height,
"z": depth,
"shape": None,
"face": None,
}
2020-11-01 20:08:48 +07:00
ifc_cutter.cut_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-cut.pickle".format(self.diagram_name))
ifc_cutter.text_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-text.pickle".format(self.diagram_name))
ifc_cutter.metadata_pickle_file = os.path.join(
ifc_cutter.data_dir, "{}-metadata.pickle".format(self.diagram_name)
)
2019-12-12 18:16:02 +11:00
ifc_cutter.should_recut = bpy.context.scene.DocProperties.should_recut
ifc_cutter.should_recut_selected = bpy.context.scene.DocProperties.should_recut_selected
selected_global_ids = []
for obj in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
if "Ifc" not in obj.name:
continue
for attribute in obj.BIMObjectProperties.attributes:
2020-11-01 20:08:48 +07:00
if attribute.name == "GlobalId":
selected_global_ids.append(attribute.string_value)
break
ifc_cutter.selected_global_ids = selected_global_ids
ifc_cutter.should_extract = bpy.context.scene.DocProperties.should_extract
svg_writer = svgwriter.SvgWriter(ifc_cutter)
2020-11-01 20:08:48 +07:00
if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
else:
2020-11-01 20:08:48 +07:00
human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
if camera.data.BIMCameraProperties.is_nts:
2020-11-01 20:08:48 +07:00
svg_writer.human_scale = "NTS"
else:
svg_writer.human_scale = human_scale
2019-12-12 12:51:47 +11:00
svg_writer.scale = float(numerator) / float(denominator)
ifc_cutter.cut()
svg_writer.write()
bpy.ops.bim.open_view(view=self.diagram_name)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def create_raster(self, camera, drawing_style):
2020-11-01 20:08:48 +07:00
if drawing_style.render_type == "NONE":
return
2020-11-01 20:08:48 +07:00
if drawing_style.render_type == "DEFAULT":
return bpy.ops.render.render(write_still=True)
previous_visibility = {}
for obj in camera.users_collection[0].objects:
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
if (
(not obj.data and not obj.instance_collection)
2020-11-01 20:08:48 +07:00
or isinstance(obj.data, bpy.types.Camera)
or "IfcGrid/" in obj.name
or "IfcGridAxis/" in obj.name
or "IfcOpeningElement/" in obj.name
or self.does_obj_have_target_view_representation(obj, camera)
):
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
space = self.get_view_3d()
previous_shading = space.shading.type
previous_format = bpy.context.scene.render.image_settings.file_format
2020-11-01 20:08:48 +07:00
space.shading.type = "RENDERED"
bpy.context.scene.render.image_settings.file_format = "PNG"
bpy.ops.render.opengl(write_still=True)
space.shading.type = previous_shading
bpy.context.scene.render.image_settings.file_format = previous_format
for name, value in previous_visibility.items():
bpy.data.objects[name].hide_set(value)
def does_obj_have_target_view_representation(self, obj, camera):
if not obj.BIMObjectProperties.ifc_definition_id:
return False
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if not element.Representation:
return False
for representation in element.Representation.Representations:
if ifcopenshell.util.element.is_representation_of_context(
representation, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view
):
return True
def is_landscape(self):
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
class AddSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_sheet"
bl_label = "Add Sheet"
def execute(self, context):
new = bpy.context.scene.DocProperties.sheets.add()
2020-11-01 20:08:48 +07:00
new.name = "{} - SHEET".format(len(bpy.context.scene.DocProperties.sheets))
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class OpenSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_sheet"
bl_label = "Open Sheet"
def execute(self, context):
props = bpy.context.scene.DocProperties
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg"
),
)
return {"FINISHED"}
class AddDrawingToSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_to_sheet"
bl_label = "Add Drawing To Sheet"
def execute(self, context):
props = bpy.context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
try:
sheet_builder.add_drawing(
2020-11-01 20:08:48 +07:00
props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name
)
except:
2020-11-01 20:08:48 +07:00
self.report({"ERROR"}, "Drawings need to be created before being added to a sheet")
return {"FINISHED"}
class CreateSheets(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.create_sheets"
bl_label = "Create Sheets"
def execute(self, context):
props = bpy.context.scene.DocProperties
name = props.sheets[props.active_sheet_index].name
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.build(name)
2020-11-01 20:08:48 +07:00
svg2pdf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2dxf_command
if svg2pdf_command:
2020-11-01 20:08:48 +07:00
path = os.path.join(bpy.context.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:
# [['inkscape', svg, '-o', pdf]]
commands = eval(svg2pdf_command)
for command in commands:
subprocess.run(command)
if svg2dxf_command:
2020-11-01 20:08:48 +07:00
path = os.path.join(bpy.context.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:
# [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]
commands = eval(svg2dxf_command)
for command in commands:
subprocess.run(command)
if svg2pdf_command:
2020-11-01 20:08:48 +07:00
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
else:
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name, name + ".svg"),
)
return {"FINISHED"}
2020-01-06 14:30:44 +11:00
class ActivateView(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.activate_view"
bl_label = "Activate View"
2020-09-03 23:02:41 +10:00
drawing_index: bpy.props.IntProperty()
2020-01-06 14:30:44 +11:00
def execute(self, context):
2020-09-03 23:02:41 +10:00
camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera
2020-01-06 14:30:44 +11:00
if not camera:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-06 14:30:44 +11:00
bpy.context.scene.camera = camera
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
views_collection = bpy.data.collections.get("Views")
2020-01-06 14:30:44 +11:00
for collection in views_collection.children:
# We assume the project collection is at the top level
for project_collection in bpy.context.view_layer.layer_collection.children:
# We assume a convention that the 'Views' collection is directly
# in the project collection
2020-11-01 20:08:48 +07:00
if (
"Views" in project_collection.children
and collection.name in project_collection.children["Views"].children
):
project_collection.children["Views"].children[collection.name].hide_viewport = True
bpy.data.collections.get(collection.name).hide_render = True
2020-11-01 20:08:48 +07:00
bpy.context.view_layer.layer_collection.children["Views"].children[
camera.users_collection[0].name
].hide_viewport = False
2020-01-06 14:30:44 +11:00
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
bpy.ops.bim.activate_drawing_style()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class OpenUpstream(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_upstream"
bl_label = "Open Upstream Reference"
page: bpy.props.StringProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if self.page == "home":
webbrowser.open("https://blenderbim.org/")
elif self.page == "docs":
webbrowser.open("https://blenderbim.org/docs/")
elif self.page == "wiki":
webbrowser.open("https://wiki.osarch.org/index.php?title=Category:BlenderBIM_Add-on")
elif self.page == "community":
webbrowser.open("https://community.osarch.org/")
return {"FINISHED"}
class CopyPropertyToSelection(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.copy_property_to_selection"
bl_label = "Copy Property To Selection"
pset_name: bpy.props.StringProperty()
prop_name: bpy.props.StringProperty()
prop_value: bpy.props.StringProperty()
def execute(self, context):
for obj in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
if "/" not in obj.name:
continue
pset = obj.BIMObjectProperties.psets.get(self.pset_name)
if not pset:
2021-01-04 02:13:04 +01:00
applicable_psets = schema.ifc.psetqto.get_applicable(obj.name.split("/")[0], pset_only=True)
for pset_template in applicable_psets:
if pset_template.Name == self.pset_name:
break
else:
continue
pset = obj.BIMObjectProperties.psets.add()
pset.name = self.pset_name
2021-01-04 02:13:04 +01:00
for template_prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
prop = pset.properties.add()
prop.name = template_prop_name
prop = pset.properties.get(self.prop_name)
if prop:
prop.string_value = self.prop_value
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class CopyAttributeToSelection(bpy.types.Operator):
bl_idname = "bim.copy_attribute_to_selection"
bl_label = "Copy Attribute To Selection"
attribute_name: bpy.props.StringProperty()
attribute_value: bpy.props.StringProperty()
def execute(self, context):
# TODO: reimplement
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema)
self.applicable_attributes_cache = {}
for obj in bpy.context.selected_objects:
if "/" not in obj.name:
continue
attribute = obj.BIMObjectProperties.attributes.get(self.attribute_name)
if not attribute:
applicable_attributes = self.get_applicable_attributes(obj.name.split("/")[0])
if self.attribute_name not in applicable_attributes:
continue
attribute = obj.BIMObjectProperties.attributes.add()
attribute.name = self.attribute_name
attribute.string_value = self.attribute_value
return {"FINISHED"}
def get_applicable_attributes(self, ifc_class):
if ifc_class not in self.applicable_attributes_cache:
self.applicable_attributes_cache[ifc_class] = [
a.name() for a in self.schema.declaration_by_name(ifc_class).all_attributes()
]
return self.applicable_attributes_cache[ifc_class]
class AddSectionPlane(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_section_plane"
bl_label = "Add Temporary Section Cutaway"
def execute(self, context):
obj = self.create_section_obj()
if not self.has_section_override_node():
self.create_section_compare_node()
self.create_section_override_node(obj)
else:
self.append_obj_to_section_override_node(obj)
self.add_default_material_if_none_exists()
self.override_materials()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def create_section_obj(self):
2020-11-01 20:08:48 +07:00
section = bpy.data.objects.new("Section", None)
section.empty_display_type = "SINGLE_ARROW"
section.empty_display_size = 5
section.show_in_front = True
if (
bpy.context.active_object
and bpy.context.active_object.select_get()
and isinstance(bpy.context.active_object.data, bpy.types.Camera)
):
2020-11-01 20:08:48 +07:00
section.matrix_world = (
bpy.context.active_object.matrix_world @ Euler((radians(180.0), 0.0, 0.0), "XYZ").to_matrix().to_4x4()
)
else:
2020-11-01 20:08:48 +07:00
section.rotation_euler = Euler((radians(180.0), 0.0, 0.0), "XYZ")
section.location = bpy.context.scene.cursor.location
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.get("Sections")
if not collection:
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.new("Sections")
bpy.context.scene.collection.children.link(collection)
collection.objects.link(section)
return section
def has_section_override_node(self):
2020-11-01 20:08:48 +07:00
return bpy.data.node_groups.get("Section Override")
def create_section_compare_node(self):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.new("Section Compare", type="ShaderNodeTree")
group_input = group.nodes.new(type="NodeGroupInput")
group_output = group.nodes.new(type="NodeGroupOutput")
separate_xyz_a = group.nodes.new(type="ShaderNodeSeparateXYZ")
separate_xyz_b = group.nodes.new(type="ShaderNodeSeparateXYZ")
gt_a = group.nodes.new(type="ShaderNodeMath")
gt_a.operation = "GREATER_THAN"
gt_a.inputs[1].default_value = 0
2020-11-01 20:08:48 +07:00
gt_b = group.nodes.new(type="ShaderNodeMath")
gt_b.operation = "GREATER_THAN"
gt_b.inputs[1].default_value = 0
2020-11-01 20:08:48 +07:00
add = group.nodes.new(type="ShaderNodeMath")
compare = group.nodes.new(type="ShaderNodeMath")
compare.operation = "COMPARE"
compare.inputs[1].default_value = 2
2020-11-01 20:08:48 +07:00
group.links.new(group_input.outputs[""], separate_xyz_a.inputs[0])
group.links.new(group_input.outputs[""], separate_xyz_b.inputs[0])
group.links.new(separate_xyz_a.outputs[2], gt_a.inputs[0])
group.links.new(separate_xyz_b.outputs[2], gt_b.inputs[0])
group.links.new(gt_a.outputs[0], add.inputs[0])
group.links.new(gt_b.outputs[0], add.inputs[1])
group.links.new(add.outputs[0], compare.inputs[0])
2020-11-01 20:08:48 +07:00
group.links.new(compare.outputs[0], group_output.inputs[""])
def create_section_override_node(self, obj):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.new("Section Override", type="ShaderNodeTree")
2020-11-01 20:08:48 +07:00
group_input = group.nodes.new(type="NodeGroupInput")
group_output = group.nodes.new(type="NodeGroupOutput")
2020-11-01 20:08:48 +07:00
backfacing = group.nodes.new(type="ShaderNodeNewGeometry")
backfacing_mix = group.nodes.new(type="ShaderNodeMixShader")
emission = group.nodes.new(type="ShaderNodeEmission")
emission.inputs[0].default_value = list(bpy.context.scene.BIMProperties.section_plane_colour) + [1]
2020-11-01 20:08:48 +07:00
group.links.new(backfacing.outputs["Backfacing"], backfacing_mix.inputs[0])
group.links.new(group_input.outputs[""], backfacing_mix.inputs[1])
group.links.new(emission.outputs["Emission"], backfacing_mix.inputs[2])
2020-11-01 20:08:48 +07:00
transparent = group.nodes.new(type="ShaderNodeBsdfTransparent")
section_mix = group.nodes.new(type="ShaderNodeMixShader")
section_mix.name = "Section Mix"
2020-11-01 20:08:48 +07:00
group.links.new(transparent.outputs["BSDF"], section_mix.inputs[1])
group.links.new(backfacing_mix.outputs["Shader"], section_mix.inputs[2])
2020-11-01 20:08:48 +07:00
group.links.new(section_mix.outputs["Shader"], group_output.inputs[""])
2020-11-01 20:08:48 +07:00
cut_obj = group.nodes.new(type="ShaderNodeTexCoord")
cut_obj.object = obj
2020-11-01 20:08:48 +07:00
section_compare = group.nodes.new(type="ShaderNodeGroup")
section_compare.node_tree = bpy.data.node_groups.get("Section Compare")
section_compare.name = "Last Section Compare"
value = group.nodes.new(type="ShaderNodeValue")
value.name = "Mock Section"
group.links.new(cut_obj.outputs["Object"], section_compare.inputs[0])
group.links.new(value.outputs[0], section_compare.inputs[1])
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
def append_obj_to_section_override_node(self, obj):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.get("Section Override")
cut_obj = group.nodes.new(type="ShaderNodeTexCoord")
cut_obj.object = obj
2020-11-01 20:08:48 +07:00
section_compare = group.nodes.new(type="ShaderNodeGroup")
section_compare.node_tree = bpy.data.node_groups.get("Section Compare")
2020-11-01 20:08:48 +07:00
last_compare = group.nodes.get("Last Section Compare")
last_compare.name = "Section Compare"
mock_section = group.nodes.get("Mock Section")
section_mix = group.nodes.get("Section Mix")
group.links.new(last_compare.outputs[0], section_compare.inputs[0])
group.links.new(mock_section.outputs[0], section_compare.inputs[1])
2020-11-01 20:08:48 +07:00
group.links.new(cut_obj.outputs["Object"], last_compare.inputs[1])
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
2020-11-01 20:08:48 +07:00
section_compare.name = "Last Section Compare"
def add_default_material_if_none_exists(self):
2020-11-01 20:08:48 +07:00
material = bpy.data.materials.get("Section Override")
if not material:
2020-11-01 20:08:48 +07:00
material = bpy.data.materials.new("Section Override")
material.use_nodes = True
if bpy.context.scene.BIMProperties.should_section_selected_objects:
objects = list(bpy.context.selected_objects)
else:
objects = list(bpy.context.visible_objects)
for obj in objects:
aggregate = obj.instance_collection
2020-11-01 20:08:48 +07:00
if aggregate and "IfcRelAggregates/" in aggregate.name:
for part in aggregate.objects:
objects.append(part)
2020-11-01 20:08:48 +07:00
if not (obj.data and hasattr(obj.data, "materials") and obj.data.materials and obj.data.materials[0]):
if obj.data and hasattr(obj.data, "materials"):
2020-04-25 19:09:34 +10:00
if len(obj.material_slots):
obj.material_slots[0].material = material
else:
obj.data.materials.append(material)
def override_materials(self):
2020-11-01 20:08:48 +07:00
override = bpy.data.node_groups.get("Section Override")
for material in bpy.data.materials:
material.use_nodes = True
2020-11-01 20:08:48 +07:00
if material.node_tree.nodes.get("Section Override"):
continue
2020-11-01 20:08:48 +07:00
material.blend_method = "HASHED"
material.shadow_method = "HASHED"
material_output = self.get_node(material.node_tree.nodes, "OUTPUT_MATERIAL")
if not material_output:
continue
from_socket = material_output.inputs[0].links[0].from_socket
2020-11-01 20:08:48 +07:00
section_override = material.node_tree.nodes.new(type="ShaderNodeGroup")
section_override.name = "Section Override"
section_override.node_tree = override
material.node_tree.links.new(from_socket, section_override.inputs[0])
material.node_tree.links.new(section_override.outputs[0], material_output.inputs[0])
def get_node(self, nodes, node_type):
for node in nodes:
if node.type == node_type:
return node
2020-04-25 19:09:34 +10:00
class RemoveSectionPlane(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_section_plane"
bl_label = "Remove Temporary Section Cutaway"
2020-04-25 19:09:34 +10:00
def execute(self, context):
name = bpy.context.active_object.name
2020-11-01 20:08:48 +07:00
section_override = bpy.data.node_groups.get("Section Override")
2020-04-25 19:09:34 +10:00
if not section_override:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
for node in section_override.nodes:
2020-11-01 20:08:48 +07:00
if node.type != "TEX_COORD" or node.object.name != name:
2020-04-25 19:09:34 +10:00
continue
2020-11-01 20:08:48 +07:00
section_compare = node.outputs["Object"].links[0].to_node
2020-04-25 19:09:34 +10:00
# If the tex coord links to section_compare.inputs[1], it is called 'Input_3'
2020-11-01 20:08:48 +07:00
if node.outputs["Object"].links[0].to_socket.identifier == "Input_3":
2020-04-25 19:09:34 +10:00
section_override.links.new(
2020-11-01 20:08:48 +07:00
section_compare.inputs[0].links[0].from_socket, section_compare.outputs[0].links[0].to_socket
)
else: # If it links to section_compare.inputs[0]
if section_compare.inputs[1].links[0].from_node.name == "Mock Section":
2020-04-25 19:09:34 +10:00
# Then it is the very last section. Purge everything.
self.purge_all_section_data()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
section_override.links.new(
2020-11-01 20:08:48 +07:00
section_compare.inputs[1].links[0].from_socket, section_compare.outputs[0].links[0].to_socket
)
2020-04-25 19:09:34 +10:00
section_override.nodes.remove(section_compare)
section_override.nodes.remove(node)
2020-11-01 20:08:48 +07:00
old_last_compare = section_override.nodes.get("Last Section Compare")
old_last_compare.name = "Section Compare"
section_mix = section_override.nodes.get("Section Mix")
new_last_compare = section_mix.inputs[0].links[0].from_node
2020-11-01 20:08:48 +07:00
new_last_compare.name = "Last Section Compare"
2020-04-25 19:09:34 +10:00
bpy.ops.object.delete({"selected_objects": [bpy.context.active_object]})
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
def purge_all_section_data(self):
2020-11-01 20:08:48 +07:00
bpy.data.materials.remove(bpy.data.materials.get("Section Override"))
2020-04-25 19:09:34 +10:00
for material in bpy.data.materials:
if not material.node_tree:
continue
2020-11-01 20:08:48 +07:00
override = material.node_tree.nodes.get("Section Override")
if not override:
continue
2020-04-25 19:09:34 +10:00
material.node_tree.links.new(
2020-11-01 20:08:48 +07:00
override.inputs[0].links[0].from_socket, override.outputs[0].links[0].to_socket
)
2020-04-25 19:09:34 +10:00
material.node_tree.nodes.remove(override)
2020-11-01 20:08:48 +07:00
bpy.data.node_groups.remove(bpy.data.node_groups.get("Section Override"))
bpy.data.node_groups.remove(bpy.data.node_groups.get("Section Compare"))
2020-04-25 19:09:34 +10:00
bpy.ops.object.delete({"selected_objects": [bpy.context.active_object]})
2020-04-28 14:52:04 +10:00
class ReloadIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.reload_ifc_file"
bl_label = "Reload IFC File"
2020-04-28 14:52:04 +10:00
def execute(self, context):
# TODO: reimplement. See #1222.
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-28 14:52:04 +10:00
2020-04-29 20:26:36 +10:00
class AddIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_ifc_file"
bl_label = "Add IFC File"
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_ifc_file"
bl_label = "Remove IFC File"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectDocIfcFile(bpy.types.Operator):
bl_idname = "bim.select_doc_ifc_file"
bl_label = "Select Documentation IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files[self.index].name = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-05-03 15:41:05 +10:00
class AddAnnotation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_annotation"
bl_label = "Add Annotation"
obj_name: bpy.props.StringProperty()
data_type: bpy.props.StringProperty()
2020-05-03 15:41:05 +10:00
def execute(self, context):
if not bpy.context.scene.camera:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
if self.data_type == "text":
if bpy.context.selected_objects:
for selected_object in bpy.context.selected_objects:
obj = annotation.Annotator.add_text(related_element=selected_object)
else:
obj = annotation.Annotator.add_text()
2020-05-03 15:41:05 +10:00
else:
obj = annotation.Annotator.get_annotation_obj(self.obj_name, self.data_type)
if self.obj_name == "Break":
2020-12-26 21:59:57 +07:00
obj = annotation.Annotator.add_plane_to_annotation(obj)
else:
obj = annotation.Annotator.add_line_to_annotation(obj)
2020-11-01 20:08:48 +07:00
bpy.ops.object.select_all(action="DESELECT")
2020-05-03 15:41:05 +10:00
bpy.context.view_layer.objects.active = obj
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class GenerateReferences(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.generate_references"
bl_label = "Generate References"
def execute(self, context):
self.camera = bpy.context.scene.camera
self.filter_potential_references()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "PLAN_VIEW":
self.generate_grids()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "ELEVATION_VIEW":
self.generate_grids()
self.generate_levels()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW":
self.generate_grids()
self.generate_levels()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def filter_potential_references(self):
2020-11-01 20:08:48 +07:00
for name in ["grids", "levels"]:
setattr(self, name, [])
for obj in bpy.data.objects:
2020-11-01 20:08:48 +07:00
if "IfcGridAxis/" in obj.name:
self.grids.append(obj)
2020-11-01 20:08:48 +07:00
if "IfcBuildingStorey/" in obj.name:
self.levels.append(obj)
def generate_grids(self):
# TODO
pass
def generate_levels(self):
if self.camera.data.BIMCameraProperties.raster_x > self.camera.data.BIMCameraProperties.raster_y:
width = self.camera.data.ortho_scale
2020-11-01 20:08:48 +07:00
height = (
width / self.camera.data.BIMCameraProperties.raster_x * self.camera.data.BIMCameraProperties.raster_y
)
else:
height = self.camera.data.ortho_scale
2020-11-01 20:08:48 +07:00
width = (
height / self.camera.data.BIMCameraProperties.raster_y * self.camera.data.BIMCameraProperties.raster_x
)
level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve")
width_in_mm = width * 1000
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
else:
2020-11-01 20:08:48 +07:00
human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
scale = float(numerator) / float(denominator)
real_world_width_in_mm = width_in_mm * scale
offset_in_mm = 20
offset_percentage = offset_in_mm / real_world_width_in_mm
for obj in self.levels:
projection = self.project_point_onto_camera(obj.location)
2020-11-01 20:08:48 +07:00
co1 = self.camera.matrix_world @ Vector((width / 2 - (offset_percentage * width), projection[1], -1))
co2 = self.camera.matrix_world @ Vector((-(width / 2), projection[1], -1))
annotation.Annotator.add_line_to_annotation(level_obj, co1, co2)
def project_point_onto_camera(self, point):
projection = self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane(
2020-11-01 20:08:48 +07:00
point.xyz, point.xyz - projection, self.camera.location, projection
)
class ResizeText(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.resize_text"
bl_label = "Resize Text"
def execute(self, context):
for obj in bpy.context.scene.camera.users_collection[0].objects:
if isinstance(obj.data, bpy.types.TextCurve):
annotation.Annotator.resize_text(obj)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddVariable(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_variable"
bl_label = "Add Variable"
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveVariable(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_variable"
bl_label = "Remove Variable"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class PropagateTextData(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.propagate_text_data"
bl_label = "Propagate Text Data"
def execute(self, context):
source = bpy.context.active_object
for obj in bpy.context.selected_objects:
if obj == source:
continue
obj.data.body = source.data.body
obj.data.align_x = source.data.align_x
obj.data.align_y = source.data.align_y
obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size
obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol
while len(obj.data.BIMTextProperties.variables) > 0:
obj.data.BIMTextProperties.variables.remove(0)
for variable in source.data.BIMTextProperties.variables:
new_variable = obj.data.BIMTextProperties.variables.add()
new_variable.name = variable.name
new_variable.prop_key = variable.prop_key
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SetOverrideColour(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.set_override_colour"
bl_label = "Set Override Colour"
def execute(self, context):
result = 0
for obj in bpy.context.selected_objects:
obj.color = bpy.context.scene.BIMProperties.override_colour
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class AddDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_style"
bl_label = "Add Drawing Style"
2020-08-12 21:50:57 +10:00
def execute(self, context):
new = bpy.context.scene.DocProperties.drawing_styles.add()
2020-11-01 20:08:48 +07:00
new.name = "New Drawing Style"
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class RemoveDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing_style"
bl_label = "Remove Drawing Style"
2020-08-12 21:50:57 +10:00
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.drawing_styles.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class SaveDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.save_drawing_style"
bl_label = "Save Drawing Style"
index: bpy.props.StringProperty()
def execute(self, context):
space = self.get_view_3d()
style = {
2020-11-01 20:08:48 +07:00
"bpy.data.worlds[0].color": tuple(bpy.data.worlds[0].color),
"bpy.context.scene.render.engine": bpy.context.scene.render.engine,
"bpy.context.scene.render.film_transparent": bpy.context.scene.render.film_transparent,
"bpy.context.scene.display.shading.show_object_outline": bpy.context.scene.display.shading.show_object_outline,
"bpy.context.scene.display.shading.show_cavity": bpy.context.scene.display.shading.show_cavity,
"bpy.context.scene.display.shading.cavity_type": bpy.context.scene.display.shading.cavity_type,
"bpy.context.scene.display.shading.curvature_ridge_factor": bpy.context.scene.display.shading.curvature_ridge_factor,
"bpy.context.scene.display.shading.curvature_valley_factor": bpy.context.scene.display.shading.curvature_valley_factor,
"bpy.context.scene.view_settings.view_transform": bpy.context.scene.view_settings.view_transform,
"bpy.context.scene.display.shading.light": bpy.context.scene.display.shading.light,
"bpy.context.scene.display.shading.color_type": bpy.context.scene.display.shading.color_type,
"bpy.context.scene.display.shading.single_color": tuple(bpy.context.scene.display.shading.single_color),
"bpy.context.scene.display.shading.show_shadows": bpy.context.scene.display.shading.show_shadows,
"bpy.context.scene.display.shading.shadow_intensity": bpy.context.scene.display.shading.shadow_intensity,
"bpy.context.scene.display.light_direction": tuple(bpy.context.scene.display.light_direction),
"bpy.context.scene.view_settings.use_curve_mapping": bpy.context.scene.view_settings.use_curve_mapping,
"space.overlay.show_wireframes": space.overlay.show_wireframes,
"space.overlay.wireframe_threshold": space.overlay.wireframe_threshold,
"space.overlay.show_floor": space.overlay.show_floor,
"space.overlay.show_axis_x": space.overlay.show_axis_x,
"space.overlay.show_axis_y": space.overlay.show_axis_y,
"space.overlay.show_axis_z": space.overlay.show_axis_z,
"space.overlay.show_object_origins": space.overlay.show_object_origins,
"space.overlay.show_relationship_lines": space.overlay.show_relationship_lines,
}
if self.index:
index = int(self.index)
else:
index = bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index
bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
2020-08-12 21:50:57 +10:00
class ActivateDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.activate_drawing_style"
bl_label = "Activate Drawing Style"
2020-08-12 21:50:57 +10:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
if context.scene.camera.data.BIMCameraProperties.active_drawing_style_index < len(
bpy.context.scene.DocProperties.drawing_styles
):
self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
]
self.set_raster_style()
self.set_query()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def set_raster_style(self):
space = self.get_view_3d()
style = json.loads(self.drawing_style.raster_style)
2020-11-01 20:08:48 +07:00
bpy.data.worlds[0].color = style["bpy.data.worlds[0].color"]
bpy.context.scene.render.engine = style["bpy.context.scene.render.engine"]
bpy.context.scene.render.film_transparent = style["bpy.context.scene.render.film_transparent"]
bpy.context.scene.display.shading.show_object_outline = style[
"bpy.context.scene.display.shading.show_object_outline"
]
bpy.context.scene.display.shading.show_cavity = style["bpy.context.scene.display.shading.show_cavity"]
bpy.context.scene.display.shading.cavity_type = style["bpy.context.scene.display.shading.cavity_type"]
bpy.context.scene.display.shading.curvature_ridge_factor = style[
"bpy.context.scene.display.shading.curvature_ridge_factor"
]
bpy.context.scene.display.shading.curvature_valley_factor = style[
"bpy.context.scene.display.shading.curvature_valley_factor"
]
bpy.context.scene.view_settings.view_transform = style["bpy.context.scene.view_settings.view_transform"]
bpy.context.scene.display.shading.light = style["bpy.context.scene.display.shading.light"]
bpy.context.scene.display.shading.color_type = style["bpy.context.scene.display.shading.color_type"]
bpy.context.scene.display.shading.single_color = style["bpy.context.scene.display.shading.single_color"]
bpy.context.scene.display.shading.show_shadows = style["bpy.context.scene.display.shading.show_shadows"]
bpy.context.scene.display.shading.shadow_intensity = style["bpy.context.scene.display.shading.shadow_intensity"]
bpy.context.scene.display.light_direction = style["bpy.context.scene.display.light_direction"]
bpy.context.scene.view_settings.use_curve_mapping = style["bpy.context.scene.view_settings.use_curve_mapping"]
space.overlay.show_wireframes = style["space.overlay.show_wireframes"]
space.overlay.wireframe_threshold = style["space.overlay.wireframe_threshold"]
space.overlay.show_floor = style["space.overlay.show_floor"]
space.overlay.show_axis_x = style["space.overlay.show_axis_x"]
space.overlay.show_axis_y = style["space.overlay.show_axis_y"]
space.overlay.show_axis_z = style["space.overlay.show_axis_z"]
space.overlay.show_object_origins = style["space.overlay.show_object_origins"]
space.overlay.show_relationship_lines = style["space.overlay.show_relationship_lines"]
def set_query(self):
self.selector = ifcopenshell.util.selector.Selector()
self.include_global_ids = []
self.exclude_global_ids = []
for ifc_file in bpy.context.scene.DocProperties.ifc_files:
2020-09-05 14:15:37 +10:00
try:
ifc = ifcopenshell.open(ifc_file.name)
except:
continue
if self.drawing_style.include_query:
results = self.selector.parse(ifc, self.drawing_style.include_query)
self.include_global_ids.extend([e.GlobalId for e in results])
if self.drawing_style.exclude_query:
results = self.selector.parse(ifc, self.drawing_style.exclude_query)
self.exclude_global_ids.extend([e.GlobalId for e in results])
if self.drawing_style.include_query:
2020-11-01 20:08:48 +07:00
self.parse_filter_query("INCLUDE")
if self.drawing_style.exclude_query:
2020-11-01 20:08:48 +07:00
self.parse_filter_query("EXCLUDE")
def parse_filter_query(self, mode):
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
objects = bpy.context.scene.objects
2020-11-01 20:08:48 +07:00
elif mode == "EXCLUDE":
objects = bpy.context.visible_objects
for obj in objects:
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
obj.hide_viewport = False # Note: this breaks alt-H
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
if not global_id:
continue
global_id = global_id.string_value
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
if global_id not in self.include_global_ids:
2020-11-01 20:08:48 +07:00
obj.hide_viewport = True # Note: this breaks alt-H
elif mode == "EXCLUDE":
if global_id in self.exclude_global_ids:
2020-11-01 20:08:48 +07:00
obj.hide_viewport = True # Note: this breaks alt-H
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
class AddDrawing(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing"
bl_label = "Add Drawing"
def execute(self, context):
new = bpy.context.scene.DocProperties.drawings.add()
2020-11-01 20:08:48 +07:00
new.name = "DRAWING {}".format(len(bpy.context.scene.DocProperties.drawings))
if not bpy.data.collections.get("Views"):
bpy.context.scene.collection.children.link(bpy.data.collections.new("Views"))
views_collection = bpy.data.collections.get("Views")
view_collection = bpy.data.collections.new("IfcGroup/" + new.name)
views_collection.children.link(view_collection)
2020-11-01 20:08:48 +07:00
camera = bpy.data.objects.new("IfcGroup/" + new.name, bpy.data.cameras.new("IfcGroup/" + new.name))
camera.location = (0, 0, 1.7) # The view shall be 1.7m above the origin
camera.data.type = "ORTHO"
camera.data.ortho_scale = 50 # The default of 6m is too small
if bpy.context.scene.unit_settings.system == "IMPERIAL":
camera.data.BIMCameraProperties.diagram_scale = '1/8"=1\'-0"|1/96'
else:
2020-11-01 20:08:48 +07:00
camera.data.BIMCameraProperties.diagram_scale = "1:100|1/100"
bpy.context.scene.camera = camera
view_collection.objects.link(camera)
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
new.camera = camera
bpy.ops.bim.activate_drawing_style()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveDrawing(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing"
bl_label = "Remove Drawing"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
camera = props.drawings[self.index].camera
collection = camera.users_collection[0]
for obj in collection.objects:
bpy.data.objects.remove(obj)
bpy.data.collections.remove(collection, do_unlink=True)
props.drawings.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class EditVectorStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.edit_vector_style"
bl_label = "Edit Vector Style"
def execute(self, context):
camera = context.scene.camera
2020-11-01 20:08:48 +07:00
vector_style = context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
].vector_style
bpy.data.texts.load(os.path.join(context.scene.BIMProperties.data_dir, "styles", vector_style + ".css"))
return {"FINISHED"}
class RemoveSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_sheet"
bl_label = "Remove Sheet"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.sheets.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_schedule"
bl_label = "Add Schedule"
def execute(self, context):
new = bpy.context.scene.DocProperties.schedules.add()
2020-11-01 20:08:48 +07:00
new.name = "SCHEDULE {}".format(len(bpy.context.scene.DocProperties.schedules))
return {"FINISHED"}
class RemoveSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_schedule"
bl_label = "Remove Schedule"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.schedules.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectScheduleFile(bpy.types.Operator):
bl_idname = "bim.select_schedule_file"
bl_label = "Select Documentation IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
2020-11-01 20:08:48 +07:00
filter_glob: bpy.props.StringProperty(default="*.ods", options={"HIDDEN"})
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.schedules[props.active_schedule_index].file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class BuildSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.build_schedule"
bl_label = "Build Schedule"
def execute(self, context):
props = bpy.context.scene.DocProperties
schedule = props.schedules[props.active_schedule_index]
schedule_creator = scheduler.Scheduler()
2020-11-01 20:08:48 +07:00
outfile = os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg")
schedule_creator.schedule(schedule.file, outfile)
2020-11-01 20:08:48 +07:00
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, outfile)
return {"FINISHED"}
2020-08-18 10:04:19 +10:00
class AddScheduleToSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_schedule_to_sheet"
bl_label = "Add Schedule To Sheet"
2020-08-18 10:04:19 +10:00
def execute(self, context):
props = bpy.context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.add_schedule(
2020-11-01 20:08:48 +07:00
props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name
)
return {"FINISHED"}
class SetViewportShadowFromSun(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.set_viewport_shadow_from_sun"
bl_label = "Set Viewport Shadow from Sun"
def execute(self, context):
# The vector used for the light direction is a bit funny
2020-11-01 20:08:48 +07:00
mat = Matrix(((-1.0, 0.0, 0.0, 0.0), (0.0, 0, 1.0, 0.0), (-0.0, -1.0, 0, 0.0), (0.0, 0.0, 0.0, 1.0)))
context.scene.display.light_direction = mat.inverted() @ (
context.active_object.matrix_world.to_quaternion() @ Vector((0, 0, -1))
)
return {"FINISHED"}
class AddDrawingStyleAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_style_attribute"
bl_label = "Add Drawing Style Attribute"
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveDrawingStyleAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing_style_attribute"
bl_label = "Remove Drawing Style Attribute"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RefreshDrawingList(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.refresh_drawing_list"
bl_label = "Refresh Drawing List"
def execute(self, context):
while len(bpy.context.scene.DocProperties.drawings) > 0:
bpy.context.scene.DocProperties.drawings.remove(0)
2020-09-08 17:17:45 +10:00
for obj in bpy.context.scene.objects:
if not isinstance(obj.data, bpy.types.Camera):
continue
2020-11-01 20:08:48 +07:00
if "IfcGroup/" in obj.name and obj.users_collection[0].name == obj.name:
new = bpy.context.scene.DocProperties.drawings.add()
2020-11-01 20:08:48 +07:00
new.name = obj.name.split("/")[1]
new.camera = obj
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class CleanWireframes(bpy.types.Operator):
bl_idname = "bim.clean_wireframes"
bl_label = "Clean Wireframes"
def execute(self, context):
objects = bpy.data.objects
if bpy.context.selected_objects:
objects = bpy.context.selected_objects
for obj in objects:
if not isinstance(obj.data, bpy.types.Mesh):
continue
# TODO: probably breaks i18n
if not obj.modifiers.get("EdgeSplit"):
obj.modifiers.new("EdgeSplit", "EDGE_SPLIT")
return {"FINISHED"}
class LinkIfc(bpy.types.Operator):
bl_idname = "bim.link_ifc"
bl_label = "Link IFC"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
# bpy.context.active_object.active_material.BIMMaterialProperties.location = self.filepath
# coll_name = "MyCollection"
with bpy.data.libraries.load(self.filepath, link=True) as (data_from, data_to):
data_to.scenes = data_from.scenes
for scene in bpy.data.scenes:
if not scene.library or scene.library.filepath != self.filepath:
continue
for child in scene.collection.children:
if "IfcProject" not in child.name:
continue
bpy.data.scenes[0].collection.children.link(child)
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SnapSpacesTogether(bpy.types.Operator):
bl_idname = "bim.snap_spaces_together"
bl_label = "Snap Spaces Together"
def execute(self, context):
threshold = 0.5
processed_polygons = set()
for obj in bpy.context.selected_objects:
if obj.type != "MESH":
continue
for polygon in obj.data.polygons:
center = obj.matrix_world @ polygon.center
distance = None
for obj2 in bpy.context.selected_objects:
if obj2 == obj or obj.type != "MESH":
continue
result = obj2.ray_cast(obj2.matrix_world.inverted() @ center, polygon.normal, distance=threshold)
if not result[0]:
continue
hit = obj2.matrix_world @ result[1]
distance = (hit - center).length / 2
if distance < 0.01:
distance = None
break
if (obj2.name, result[3]) in processed_polygons:
distance *= 2
continue
offset = polygon.normal * distance * -1
processed_polygons.add((obj2.name, result[3]))
for v in obj2.data.polygons[result[3]].vertices:
obj2.data.vertices[v].co += offset
break
if distance:
offset = polygon.normal * distance
processed_polygons.add((obj.name, polygon.index))
for v in polygon.vertices:
obj.data.vertices[v].co += offset
return {"FINISHED"}
2020-12-26 23:25:39 +07:00
class CopyGrid(bpy.types.Operator):
bl_idname = "bim.add_grid"
bl_label = "Add Grid"
bl_options = {"UNDO"}
2020-12-26 23:25:39 +07:00
def execute(self, context):
2021-01-12 04:02:39 +07:00
proj_coll = helper.get_project_collection(context.scene)
view_coll, camera = helper.get_active_drawing(context.scene)
2021-01-12 07:48:18 +07:00
if view_coll is None:
return {'CANCELLED'}
is_ortho = camera.data.type == "ORTHO"
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ('PLAN_VIEW', 'REFLECTED_PLAN_VIEW')
elevating = is_ortho and camera.data.BIMCameraProperties.target_view in ('ELEVATION_VIEW', 'SECTION_VIEW')
2020-12-26 23:25:39 +07:00
2021-01-12 07:48:18 +07:00
def grep(coll):
return [obj for obj in coll.all_objects if obj.name.startswith("IfcGridAxis")]
2021-01-04 11:31:30 +07:00
2021-01-12 07:48:18 +07:00
def clone(src):
dst = src.copy()
dst.data = dst.data.copy()
return dst
def disassemble(obj):
mesh = bmesh.new()
mesh.verts.ensure_lookup_table()
mesh.from_mesh(obj.data)
return obj, mesh
def assemble(obj, mesh):
mesh.to_mesh(obj.data)
return obj
def localize(obj, mesh):
# convert to camera coords
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
obj.matrix_world = camera.matrix_world
return obj, mesh
def clip(mesh):
# clip segment against camera view bounds
mesh.verts.ensure_lookup_table()
points = [v.co for v in mesh.verts[0:2]]
points = helper.clip_segment(bounds, points)
if points is None:
return None
mesh.verts[0].co = points[0]
mesh.verts[1].co = points[1]
return mesh
def elev(mesh):
# put camera-perpendicular segments vertically
mesh.verts.ensure_lookup_table()
points = [v.co for v in mesh.verts[0:2]]
points = helper.elevate_segment(bounds, points)
if points is None:
return None
points = helper.clip_segment(bounds, points)
if points is None:
return None
mesh.verts[0].co = points[0]
mesh.verts[1].co = points[1]
return mesh
for obj in grep(view_coll):
view_coll.objects.unlink(obj)
2021-01-04 11:31:30 +07:00
2021-01-12 07:48:18 +07:00
grid = [localize(*disassemble(clone(obj))) for obj in grep(proj_coll)]
2021-01-04 11:31:30 +07:00
2021-01-12 07:48:18 +07:00
if clipping:
grid = [(obj, clip(mesh)) for obj, mesh in grid]
elif elevating:
grid = [(obj, elev(mesh)) for obj, mesh in grid]
2021-01-04 11:31:30 +07:00
2021-01-12 07:48:18 +07:00
for obj, mesh in grid:
if mesh is not None:
view_coll.objects.link(assemble(obj, mesh))
2020-12-29 02:04:08 +07:00
2020-12-26 23:25:39 +07:00
return {"FINISHED"}
2021-01-12 09:32:04 +07:00
class AddSectionsAnnotations(bpy.types.Operator):
bl_idname = "bim.add_sections_annotations"
bl_label = "Add Sections"
bl_options = {"UNDO"}
def execute(self, context):
scene = context.scene
view_coll, camera = helper.get_active_drawing(scene)
is_ortho = camera.data.type == "ORTHO"
if not is_ortho:
return {'CANCELLED'}
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
drawings = [d
for d in scene.DocProperties.drawings
if (d.camera is not camera and
d.camera.data.type == "ORTHO" and
d.camera.data.BIMCameraProperties.target_view == 'SECTION_VIEW')]
def sideview(cam):
# leftmost and righmost points of camera view area, local coords
xmin, xmax, _, _, _, _ = helper.ortho_view_frame(cam.data, margin=0)
proj = camera.matrix_world.inverted() @ cam.matrix_world
p_l = proj @ Vector((xmin, 0, 0))
p_r = proj @ Vector((xmax, 0, 0))
return helper.clip_segment(bounds, (p_l, p_r))
def annotation(drawing, points):
# object with path geometry
name = drawing.name
curve = bpy.data.curves.new(f"Section/{name}", 'CURVE')
spline = curve.splines.new('POLY') # has 1 initial point
spline.points.add(1)
p1, p2 = points
z = bounds[4] - 1e-5 # zmin
spline.points[0].co = (p1.x, p1.y, z, 1)
spline.points[1].co = (p2.x, p2.y, z, 1)
obj = bpy.data.objects.new(f"IfcAnnotation/Section/{name}", curve)
obj.matrix_world = camera.matrix_world
return obj
for d in drawings:
points = sideview(d.camera)
if points is None:
continue
obj = annotation(d, points)
view_coll.objects.link(obj)
return {'FINISHED'}