2020-12-26 23:25:39 +07:00
|
|
|
from itertools import chain
|
2019-11-30 22:42:04 +11:00
|
|
|
import os
|
2019-10-07 21:11:11 +11:00
|
|
|
import bpy
|
2020-08-11 14:50:21 +10:00
|
|
|
import uuid
|
2019-10-07 21:11:11 +11:00
|
|
|
import time
|
2019-11-13 18:48:12 +11:00
|
|
|
import json
|
2019-10-07 21:11:11 +11:00
|
|
|
import logging
|
2020-03-25 15:03:06 +11:00
|
|
|
import webbrowser
|
2020-09-11 19:02:33 +10:00
|
|
|
import subprocess
|
2019-12-11 18:44:46 +11:00
|
|
|
import ifcopenshell
|
2020-08-08 17:58:45 +10:00
|
|
|
import ifcopenshell.util.selector
|
2020-09-01 22:43:00 +10:00
|
|
|
import ifcopenshell.util.geolocation
|
2021-01-01 12:15:18 +11:00
|
|
|
import ifcopenshell.util.element
|
2020-12-02 09:51:00 +11:00
|
|
|
import ifcopenshell.util.schema
|
2020-04-28 14:52:04 +10:00
|
|
|
import tempfile
|
2021-01-02 19:33:32 +11:00
|
|
|
import numpy as np
|
2019-10-08 23:05:27 +11:00
|
|
|
from . import export_ifc
|
|
|
|
|
from . import import_ifc
|
2019-12-10 18:54:26 +11:00
|
|
|
from . import cut_ifc
|
2020-05-11 17:46:38 +10:00
|
|
|
from . import svgwriter
|
2019-12-11 18:50:01 +11:00
|
|
|
from . import sheeter
|
2020-08-17 22:10:06 +10:00
|
|
|
from . import scheduler
|
2020-01-15 17:17:03 +11:00
|
|
|
from . import schema
|
2020-03-30 12:47:14 +11:00
|
|
|
from . import ifc
|
2020-05-03 15:41:05 +10:00
|
|
|
from . import annotation
|
2020-08-08 16:31:27 +10:00
|
|
|
from . import helper
|
2019-10-08 23:05:27 +11:00
|
|
|
from bpy_extras.io_utils import ImportHelper
|
2020-08-29 18:56:23 +10:00
|
|
|
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
|
2020-01-28 18:10:33 +11:00
|
|
|
from pathlib import Path
|
2020-08-29 10:28:34 +10:00
|
|
|
from bpy.app.handlers import persistent
|
2019-10-07 21:11:11 +11:00
|
|
|
|
2020-08-29 10:28:34 +10:00
|
|
|
|
|
|
|
|
@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:
|
2020-08-29 10:28:34 +10:00
|
|
|
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
|
|
|
|
|
):
|
2020-08-29 10:28:34 +10:00
|
|
|
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)
|
2020-08-29 10:28:34 +10:00
|
|
|
|
|
|
|
|
|
2020-09-11 19:02:33 +10:00
|
|
|
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)
|
2020-09-11 19:02:33 +10:00
|
|
|
|
|
|
|
|
|
2019-10-07 21:11:11 +11:00
|
|
|
class ExportIFC(bpy.types.Operator):
|
2020-08-01 09:31:28 +10:00
|
|
|
bl_idname = "export_ifc.bim"
|
2020-04-12 09:51:10 +10:00
|
|
|
bl_label = "Export IFC"
|
2019-10-07 21:11:11 +11:00
|
|
|
filename_ext = ".ifc"
|
2020-11-01 20:08:48 +07:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
2019-10-07 21:11:11 +11:00
|
|
|
|
|
|
|
|
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"}
|
2019-10-07 21:11:11 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
start = time.time()
|
2020-11-01 20:08:48 +07:00
|
|
|
logger = logging.getLogger("ExportIFC")
|
2019-10-07 21:11:11 +11:00
|
|
|
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")
|
2020-04-12 09:51:10 +10:00
|
|
|
else:
|
2020-11-01 20:08:48 +07:00
|
|
|
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
|
2020-04-08 15:43:45 +02:00
|
|
|
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
|
2021-01-13 21:26:10 +11:00
|
|
|
qto_calculator = None # TODO: remove from export
|
2020-07-18 14:22:18 +10:00
|
|
|
ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator)
|
|
|
|
|
ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser)
|
2020-11-01 20:08:48 +07:00
|
|
|
ifc_export_settings.logger.info("Starting export")
|
2020-04-09 02:46:38 +02:00
|
|
|
ifc_exporter.export(context.selected_objects)
|
2020-11-01 20:08:48 +07:00
|
|
|
ifc_export_settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
|
2020-08-18 15:58:46 +10:00
|
|
|
if not bpy.context.scene.DocProperties.ifc_files:
|
|
|
|
|
new = bpy.context.scene.DocProperties.ifc_files.add()
|
|
|
|
|
new.name = output_file
|
2020-09-15 14:57:38 +10:00
|
|
|
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"}
|
|
|
|
|
|
2019-10-07 21:11:11 +11:00
|
|
|
|
2019-10-08 23:05:27 +11:00
|
|
|
class ImportIFC(bpy.types.Operator, ImportHelper):
|
2020-08-01 09:31:28 +10:00
|
|
|
bl_idname = "import_ifc.bim"
|
2020-04-12 09:51:10 +10:00
|
|
|
bl_label = "Import IFC"
|
2019-10-08 23:05:27 +11:00
|
|
|
filename_ext = ".ifc"
|
2020-11-01 20:08:48 +07:00
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
2019-10-08 23:05:27 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
start = time.time()
|
2020-11-01 20:08:48 +07:00
|
|
|
logger = logging.getLogger("ImportIFC")
|
2019-10-08 23:05:27 +11:00
|
|
|
logging.basicConfig(
|
2020-11-01 20:08:48 +07:00
|
|
|
filename=bpy.context.scene.BIMProperties.data_dir + "process.log", filemode="a", level=logging.DEBUG
|
|
|
|
|
)
|
2020-04-28 14:52:04 +10:00
|
|
|
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, self.filepath, logger)
|
2020-11-01 20:08:48 +07:00
|
|
|
ifc_import_settings.logger.info("Starting import")
|
2019-10-08 23:05:27 +11:00
|
|
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
|
|
|
|
ifc_importer.execute()
|
2020-11-01 20:08:48 +07:00
|
|
|
ifc_import_settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2019-10-08 23:05:27 +11:00
|
|
|
|
2020-12-18 16:28:17 +11:00
|
|
|
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):
|
2020-12-18 16:28:17 +11:00
|
|
|
webbrowser.open(self.uri)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-03-31 12:35:17 +11:00
|
|
|
|
|
|
|
|
|
2020-03-19 18:16:30 +11:00
|
|
|
class AddQto(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_qto"
|
|
|
|
|
bl_label = "Add Qto"
|
2020-03-19 18:16:30 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
name = bpy.context.active_object.BIMObjectProperties.qto_name
|
2021-01-04 02:13:04 +01:00
|
|
|
qto_template = schema.ifc.psetqto.get_by_name(name)
|
|
|
|
|
if not qto_template:
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-11-03 21:04:50 +11:00
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
|
if "/" not in obj.name or obj.BIMObjectProperties.qtos.find(name) != -1:
|
|
|
|
|
continue
|
2021-01-04 02:13:04 +01:00
|
|
|
applicable_qtos = schema.ifc.psetqto.get_applicable_names(obj.name.split("/")[0], qto_only=True)
|
2020-11-03 21:04:50 +11:00
|
|
|
if name not in applicable_qtos:
|
|
|
|
|
continue
|
|
|
|
|
qto = obj.BIMObjectProperties.qtos.add()
|
|
|
|
|
qto.name = name
|
2021-01-04 02:13:04 +01:00
|
|
|
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
|
2020-11-03 21:04:50 +11:00
|
|
|
prop = qto.properties.add()
|
|
|
|
|
prop.name = prop_name
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-03-19 18:16:30 +11:00
|
|
|
|
|
|
|
|
|
2020-03-19 16:48:50 +11:00
|
|
|
class RemoveQto(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_qto"
|
|
|
|
|
bl_label = "Remove Qto"
|
2020-03-19 16:48:50 +11:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-03 21:04:50 +11:00
|
|
|
name = bpy.context.active_object.BIMObjectProperties.qtos[self.index].name
|
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
|
if "/" not in obj.name:
|
|
|
|
|
continue
|
|
|
|
|
index = obj.BIMObjectProperties.qtos.find(name)
|
|
|
|
|
if index != -1:
|
|
|
|
|
obj.BIMObjectProperties.qtos.remove(index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-03-19 16:48:50 +11:00
|
|
|
|
|
|
|
|
|
2020-01-12 19:01:35 +11:00
|
|
|
class AddMaterialPset(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_material_pset"
|
|
|
|
|
bl_label = "Add Material Pset"
|
2020-01-12 19:01:35 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-11 23:02:07 +11:00
|
|
|
material = bpy.context.active_object.active_material
|
|
|
|
|
name = material.BIMMaterialProperties.pset_name
|
2021-01-04 02:13:04 +01:00
|
|
|
pset_template = schema.ifc.psetqto.get_by_name(name)
|
|
|
|
|
if not pset_template:
|
2020-11-11 23:02:07 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
if material.BIMMaterialProperties.psets.find(name) != -1:
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
pset = material.BIMMaterialProperties.psets.add()
|
|
|
|
|
pset.name = name
|
2021-01-04 02:13:04 +01:00
|
|
|
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
|
2020-11-11 23:02:07 +11:00
|
|
|
prop = pset.properties.add()
|
|
|
|
|
prop.name = prop_name
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-01-12 19:01:35 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveMaterialPset(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_material_pset"
|
|
|
|
|
bl_label = "Remove Pset"
|
2020-01-12 19:01:35 +11:00
|
|
|
pset_index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.active_object.active_material.BIMMaterialProperties.psets.remove(self.pset_index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-01-12 19:01:35 +11:00
|
|
|
|
|
|
|
|
|
2020-01-14 15:38:22 +11:00
|
|
|
class AddMaterialAttribute(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_material_attribute"
|
|
|
|
|
bl_label = "Add Material Attribute"
|
2020-01-14 15:38:22 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
if bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes:
|
|
|
|
|
attribute = bpy.context.active_object.active_material.BIMMaterialProperties.attributes.add()
|
|
|
|
|
attribute.name = bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-01-14 15:38:22 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveMaterialAttribute(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_material_attribute"
|
|
|
|
|
bl_label = "Remove Material Attribute"
|
2020-01-14 15:38:22 +11:00
|
|
|
attribute_index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.active_object.active_material.BIMMaterialProperties.attributes.remove(self.attribute_index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-01-14 15:38:22 +11:00
|
|
|
|
|
|
|
|
|
2019-11-13 18:48:12 +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"
|
2019-11-13 18:48:12 +11:00
|
|
|
|
|
|
|
|
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"}
|
|
|
|
|
|
2019-11-13 18:48:12 +11:00
|
|
|
|
|
|
|
|
class RemoveSweptSolid(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_swept_solid"
|
|
|
|
|
bl_label = "Remove Swept Solid"
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-11-13 18:48:12 +11:00
|
|
|
|
2019-11-08 12:39:34 +11:00
|
|
|
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"
|
2019-11-13 18:48:12 +11:00
|
|
|
index: bpy.props.IntProperty()
|
2019-10-13 15:36:49 +11:00
|
|
|
|
|
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-11-13 18:48:12 +11:00
|
|
|
|
|
|
|
|
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"
|
2019-11-13 18:48:12 +11:00
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-11-08 12:39:34 +11:00
|
|
|
|
|
|
|
|
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"
|
2019-11-13 18:48:12 +11:00
|
|
|
index: bpy.props.IntProperty()
|
2019-11-08 12:39:34 +11:00
|
|
|
|
|
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-11-13 18:48:12 +11:00
|
|
|
|
|
|
|
|
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"
|
2019-11-13 18:48:12 +11:00
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-10-13 15:36:49 +11:00
|
|
|
|
|
|
|
|
class AssignSweptSolidExtrusion(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.assign_swept_solid_extrusion"
|
|
|
|
|
bl_label = "Assign Extrusion"
|
2019-11-13 18:48:12 +11:00
|
|
|
index: bpy.props.IntProperty()
|
2019-10-13 15:36:49 +11:00
|
|
|
|
|
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-11-13 18:48:12 +11:00
|
|
|
|
|
|
|
|
class SelectSweptSolidExtrusion(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.select_swept_solid_extrusion"
|
|
|
|
|
bl_label = "Select Extrusion"
|
2019-11-13 18:48:12 +11:00
|
|
|
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")
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
2019-11-13 18:48:12 +11:00
|
|
|
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"}
|
|
|
|
|
|
2019-10-13 15:36:49 +11:00
|
|
|
|
|
|
|
|
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):
|
2019-10-21 14:56:40 +11:00
|
|
|
bpy.context.active_object.active_material.BIMMaterialProperties.location = self.filepath
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2019-10-29 18:13:10 +11:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2019-10-29 18:13:10 +11:00
|
|
|
|
2020-07-23 17:02:48 +10:00
|
|
|
|
2020-08-05 17:18:10 +10:00
|
|
|
class ExecuteIfcPatch(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.execute_ifc_patch"
|
|
|
|
|
bl_label = "Execute IFCPatch"
|
2020-08-05 17:18:10 +10:00
|
|
|
file_format: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
import ifcpatch
|
2020-11-01 20:08:48 +07:00
|
|
|
|
|
|
|
|
ifcpatch.execute(
|
|
|
|
|
{
|
|
|
|
|
"input": bpy.context.scene.BIMProperties.ifc_patch_input,
|
|
|
|
|
"output": bpy.context.scene.BIMProperties.ifc_patch_output,
|
|
|
|
|
"recipe": bpy.context.scene.BIMProperties.ifc_patch_recipes,
|
|
|
|
|
"arguments": json.loads("[" + bpy.context.scene.BIMProperties.ifc_patch_args + "]"),
|
|
|
|
|
"log": bpy.context.scene.BIMProperties.data_dir + "process.log",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
return {"FINISHED"}
|
2020-08-05 17:18:10 +10:00
|
|
|
|
|
|
|
|
|
2020-05-19 20:35:00 +10:00
|
|
|
class ExportClashSets(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.export_clash_sets"
|
|
|
|
|
bl_label = "Export Clash Sets"
|
|
|
|
|
filename_ext = ".json"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2020-11-01 20:08:48 +07:00
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
2020-05-19 20:35:00 +10:00
|
|
|
WindowManager = context.window_manager
|
|
|
|
|
WindowManager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-01 20:08:48 +07:00
|
|
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
2020-05-19 20:35:00 +10:00
|
|
|
clash_sets = []
|
|
|
|
|
for clash_set in bpy.context.scene.BIMProperties.clash_sets:
|
|
|
|
|
self.a = []
|
|
|
|
|
self.b = []
|
2020-11-01 20:08:48 +07:00
|
|
|
for ab in ["a", "b"]:
|
2020-05-19 20:35:00 +10:00
|
|
|
for data in getattr(clash_set, ab):
|
2020-11-01 20:08:48 +07:00
|
|
|
clash_source = {"file": data.name}
|
2020-05-19 20:35:00 +10:00
|
|
|
if data.selector:
|
2020-11-01 20:08:48 +07:00
|
|
|
clash_source["selector"] = data.selector
|
|
|
|
|
clash_source["mode"] = data.mode
|
2020-05-19 20:35:00 +10:00
|
|
|
getattr(self, ab).append(clash_source)
|
2020-11-01 20:08:48 +07:00
|
|
|
clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b})
|
|
|
|
|
with open(self.filepath, "w") as destination:
|
2020-05-19 20:35:00 +10:00
|
|
|
destination.write(json.dumps(clash_sets, indent=4))
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImportClashSets(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.import_clash_sets"
|
|
|
|
|
bl_label = "Import Clash Sets"
|
|
|
|
|
filename_ext = ".json"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2020-11-01 20:08:48 +07:00
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
2020-05-19 20:35:00 +10:00
|
|
|
WindowManager = context.window_manager
|
|
|
|
|
WindowManager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
with open(self.filepath) as f:
|
|
|
|
|
clash_sets = json.load(f)
|
|
|
|
|
for clash_set in clash_sets:
|
|
|
|
|
new = bpy.context.scene.BIMProperties.clash_sets.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
new.name = clash_set["name"]
|
|
|
|
|
new.tolerance = clash_set["tolerance"]
|
|
|
|
|
for clash_source in clash_set["a"]:
|
2020-05-19 20:35:00 +10:00
|
|
|
new_source = new.a.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
new_source.name = clash_source["file"]
|
|
|
|
|
if "selector" in clash_source:
|
|
|
|
|
new_source.selector = clash_source["selector"]
|
|
|
|
|
new_source.mode = clash_source["mode"]
|
|
|
|
|
if clash_set["b"]:
|
|
|
|
|
for clash_source in clash_set["b"]:
|
2020-05-19 20:35:00 +10:00
|
|
|
new_source = new.b.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
new_source.name = clash_source["file"]
|
|
|
|
|
if "selector" in clash_source:
|
|
|
|
|
new_source.selector = clash_source["selector"]
|
|
|
|
|
new_source.mode = clash_source["mode"]
|
|
|
|
|
return {"FINISHED"}
|
2020-05-19 20:35:00 +10:00
|
|
|
|
|
|
|
|
|
2020-05-19 15:08:54 +10:00
|
|
|
class AddClashSet(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_clash_set"
|
|
|
|
|
bl_label = "Add Clash Set"
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
new = bpy.context.scene.BIMProperties.clash_sets.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
new.name = "New Clash Set"
|
2020-05-19 15:08:54 +10:00
|
|
|
new.tolerance = 0.01
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveClashSet(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_clash_set"
|
|
|
|
|
bl_label = "Remove Clash Set"
|
2020-05-19 15:08:54 +10:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.clash_sets.remove(self.index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AddClashSource(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_clash_source"
|
|
|
|
|
bl_label = "Add Clash Source"
|
2020-05-19 15:08:54 +10:00
|
|
|
group: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
|
|
|
|
|
source = getattr(clash_set, self.group).add()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveClashSource(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_clash_source"
|
|
|
|
|
bl_label = "Remove Clash Source"
|
2020-05-19 15:08:54 +10:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
group: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
|
|
|
|
|
getattr(clash_set, self.group).remove(self.index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SelectClashSource(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_clash_source"
|
|
|
|
|
bl_label = "Select Clash Source"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
group: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
|
|
|
|
|
getattr(clash_set, self.group)[self.index].name = self.filepath
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-21 16:29:55 -05:00
|
|
|
class SelectClashResults(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_clash_results"
|
|
|
|
|
bl_label = "Select Clash Results"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.clash_results_path = self.filepath
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-21 16:29:55 -05:00
|
|
|
class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_smart_grouped_clashes_path"
|
|
|
|
|
bl_label = "Select Smart-Grouped Clashes Path"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.smart_grouped_clashes_path = self.filepath
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-19 15:08:54 +10:00
|
|
|
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-05-16 12:54:26 +10:00
|
|
|
class ExecuteIfcClash(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.execute_ifc_clash"
|
|
|
|
|
bl_label = "Execute IFC Clash"
|
2020-12-29 22:49:22 +11:00
|
|
|
filename_ext = ".bcf"
|
2020-11-01 20:08:48 +07:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
2020-05-16 12:54:26 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2020-12-29 22:49:22 +11:00
|
|
|
if ".json" not in bpy.data.filepath:
|
|
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
|
2020-05-16 12:54:26 +10:00
|
|
|
WindowManager = context.window_manager
|
|
|
|
|
WindowManager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-16 12:54:26 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
import ifcclash
|
2020-11-01 20:08:48 +07:00
|
|
|
|
2020-05-16 12:54:26 +10:00
|
|
|
settings = ifcclash.IfcClashSettings()
|
2020-12-29 22:49:22 +11:00
|
|
|
if ".json" not in self.filepath:
|
|
|
|
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
|
2020-05-16 12:54:26 +10:00
|
|
|
settings.output = self.filepath
|
2020-11-01 20:08:48 +07:00
|
|
|
settings.logger = logging.getLogger("Clash")
|
2020-05-16 12:54:26 +10:00
|
|
|
settings.logger.setLevel(logging.DEBUG)
|
2020-05-19 15:08:54 +10:00
|
|
|
ifc_clasher = ifcclash.IfcClasher(settings)
|
2020-12-30 12:18:41 +11:00
|
|
|
|
|
|
|
|
if bpy.context.scene.BIMProperties.should_create_clash_snapshots:
|
2021-01-08 13:49:31 +11:00
|
|
|
|
2020-12-30 12:18:41 +11:00
|
|
|
def get_viewpoint_snapshot(self, viewpoint, mat):
|
2021-01-08 13:49:31 +11:00
|
|
|
camera = bpy.data.objects.get("IFC Clash Camera")
|
2020-12-30 12:18:41 +11:00
|
|
|
if not camera:
|
|
|
|
|
camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera"))
|
|
|
|
|
bpy.context.scene.collection.objects.link(camera)
|
|
|
|
|
camera.matrix_world = Matrix(mat)
|
|
|
|
|
bpy.context.scene.camera = camera
|
|
|
|
|
camera.data.angle = radians(60)
|
|
|
|
|
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
|
|
|
|
area.spaces[0].region_3d.view_perspective = "CAMERA"
|
|
|
|
|
area.spaces[0].shading.show_xray = True
|
|
|
|
|
bpy.context.scene.render.resolution_x = 480
|
|
|
|
|
bpy.context.scene.render.resolution_y = 270
|
|
|
|
|
bpy.context.scene.render.image_settings.file_format = "PNG"
|
2021-01-08 13:49:31 +11:00
|
|
|
bpy.context.scene.render.filepath = os.path.join(
|
|
|
|
|
bpy.context.scene.BIMProperties.data_dir, "snapshot.png"
|
|
|
|
|
)
|
2020-12-30 12:18:41 +11:00
|
|
|
bpy.ops.render.opengl(write_still=True)
|
|
|
|
|
return bpy.context.scene.render.filepath
|
|
|
|
|
|
|
|
|
|
ifcclash.IfcClasher.get_viewpoint_snapshot = get_viewpoint_snapshot
|
|
|
|
|
|
2020-05-19 15:08:54 +10:00
|
|
|
ifc_clasher.clash_sets = []
|
|
|
|
|
for clash_set in bpy.context.scene.BIMProperties.clash_sets:
|
|
|
|
|
self.a = []
|
|
|
|
|
self.b = []
|
2020-11-01 20:08:48 +07:00
|
|
|
for ab in ["a", "b"]:
|
2020-05-19 15:08:54 +10:00
|
|
|
for data in getattr(clash_set, ab):
|
2020-11-01 20:08:48 +07:00
|
|
|
clash_source = {"file": data.name}
|
2020-05-19 15:08:54 +10:00
|
|
|
if data.selector:
|
2020-11-01 20:08:48 +07:00
|
|
|
clash_source["selector"] = data.selector
|
|
|
|
|
clash_source["mode"] = data.mode
|
2020-05-19 15:08:54 +10:00
|
|
|
getattr(self, ab).append(clash_source)
|
2020-11-01 20:08:48 +07:00
|
|
|
ifc_clasher.clash_sets.append(
|
|
|
|
|
{"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b}
|
|
|
|
|
)
|
2020-05-16 12:54:26 +10:00
|
|
|
ifc_clasher.clash()
|
|
|
|
|
ifc_clasher.export()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-16 12:54:26 +10:00
|
|
|
|
|
|
|
|
|
2020-05-25 19:07:09 +10:00
|
|
|
class SelectIfcClashResults(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.select_ifc_clash_results"
|
|
|
|
|
bl_label = "Select IFC Clash Results"
|
|
|
|
|
filename_ext = ".json"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
2020-05-25 19:07:09 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2020-11-01 20:08:48 +07:00
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
2020-05-25 19:07:09 +10:00
|
|
|
WindowManager = context.window_manager
|
|
|
|
|
WindowManager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-05-25 19:07:09 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-01 20:08:48 +07:00
|
|
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
2020-05-25 19:07:09 +10:00
|
|
|
with open(self.filepath) as f:
|
|
|
|
|
clash_sets = json.load(f)
|
|
|
|
|
clash_set_name = bpy.context.scene.BIMProperties.clash_sets[
|
2020-11-01 20:08:48 +07:00
|
|
|
bpy.context.scene.BIMProperties.active_clash_set_index
|
|
|
|
|
].name
|
2020-05-25 19:07:09 +10:00
|
|
|
global_ids = []
|
|
|
|
|
for clash_set in clash_sets:
|
2020-11-01 20:08:48 +07:00
|
|
|
if clash_set["name"] != clash_set_name:
|
2020-05-25 19:07:09 +10:00
|
|
|
continue
|
2020-11-06 21:41:11 -05:00
|
|
|
if not "clashes" in clash_set.keys():
|
|
|
|
|
self.report({"WARNING"}, "No clashes found for the selected Clash Set.")
|
|
|
|
|
return {"CANCELLED"}
|
2020-11-01 20:08:48 +07:00
|
|
|
for clash in clash_set["clashes"].values():
|
|
|
|
|
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
|
2020-05-25 19:07:09 +10:00
|
|
|
for obj in bpy.context.visible_objects:
|
2020-11-01 20:08:48 +07:00
|
|
|
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
2020-05-25 19:07:09 +10:00
|
|
|
if global_id and global_id.string_value in global_ids:
|
|
|
|
|
obj.select_set(True)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-25 19:07:09 +10:00
|
|
|
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-06 21:14:21 -05:00
|
|
|
class SmartClashGroup(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.smart_clash_group"
|
2020-11-21 18:43:07 -05:00
|
|
|
bl_label = "Smart Group Clashes"
|
2020-11-06 21:14:21 -05:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-10 21:01:51 -05:00
|
|
|
import ifcclash
|
|
|
|
|
|
|
|
|
|
settings = ifcclash.IfcClashSettings()
|
2020-11-21 16:29:55 -05:00
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.context.scene.BIMProperties.clash_results_path, ".json")
|
2020-11-10 21:01:51 -05:00
|
|
|
settings.output = self.filepath
|
|
|
|
|
settings.logger = logging.getLogger("Clash")
|
|
|
|
|
settings.logger.setLevel(logging.DEBUG)
|
|
|
|
|
ifc_clasher = ifcclash.IfcClasher(settings)
|
|
|
|
|
|
2020-11-06 21:14:21 -05:00
|
|
|
with open(self.filepath) as f:
|
|
|
|
|
clash_sets = json.load(f)
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-21 16:29:55 -05:00
|
|
|
# execute the smart grouping
|
|
|
|
|
save_path = bpy.path.ensure_ext(bpy.context.scene.BIMProperties.smart_grouped_clashes_path, ".json")
|
2020-12-02 09:51:00 +11:00
|
|
|
smart_grouped_clashes = ifc_clasher.smart_group_clashes(
|
|
|
|
|
clash_sets, bpy.context.scene.BIMProperties.smart_clash_grouping_max_distance
|
|
|
|
|
)
|
2020-11-10 21:01:51 -05:00
|
|
|
|
|
|
|
|
# save smart_groups to json
|
2020-12-02 09:51:00 +11:00
|
|
|
with open(save_path, "w") as f:
|
2020-11-10 21:01:51 -05:00
|
|
|
f.write(json.dumps(smart_grouped_clashes))
|
|
|
|
|
|
|
|
|
|
clash_set_name = bpy.context.scene.BIMProperties.clash_sets[
|
|
|
|
|
bpy.context.scene.BIMProperties.active_clash_set_index
|
|
|
|
|
].name
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-10 21:01:51 -05:00
|
|
|
# Reset the list of smart_clash_groups for the UI
|
|
|
|
|
bpy.context.scene.BIMProperties.smart_clash_groups.clear()
|
|
|
|
|
|
|
|
|
|
for clash_set, smart_groups in smart_grouped_clashes.items():
|
|
|
|
|
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
|
|
|
|
if clash_set != clash_set_name:
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
|
|
|
|
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
2020-12-11 20:46:07 -05:00
|
|
|
new_group.number = f"{smart_group}"
|
2020-11-10 21:01:51 -05:00
|
|
|
|
|
|
|
|
for pair in global_id_pairs:
|
|
|
|
|
for id in pair:
|
|
|
|
|
new_global_id = new_group.global_ids.add()
|
|
|
|
|
new_global_id.name = id
|
|
|
|
|
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2020-11-21 18:43:07 -05:00
|
|
|
|
|
|
|
|
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.load_smart_groups_for_active_clash_set"
|
|
|
|
|
bl_label = "Load Smart Groups for Active Clash Set"
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-21 18:43:07 -05:00
|
|
|
def execute(self, context):
|
|
|
|
|
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMProperties.smart_grouped_clashes_path, ".json")
|
|
|
|
|
|
|
|
|
|
clash_set_name = bpy.context.scene.BIMProperties.clash_sets[
|
|
|
|
|
bpy.context.scene.BIMProperties.active_clash_set_index
|
|
|
|
|
].name
|
|
|
|
|
|
|
|
|
|
with open(smart_groups_path) as f:
|
|
|
|
|
smart_grouped_clashes = json.load(f)
|
|
|
|
|
|
2020-11-21 19:17:29 -05:00
|
|
|
# Reset the list of smart_clash_groups for the UI
|
2020-11-21 18:43:07 -05:00
|
|
|
bpy.context.scene.BIMProperties.smart_clash_groups.clear()
|
2020-12-02 09:51:00 +11:00
|
|
|
|
2020-11-21 18:43:07 -05:00
|
|
|
for clash_set, smart_groups in smart_grouped_clashes.items():
|
|
|
|
|
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
|
|
|
|
if clash_set != clash_set_name:
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
|
|
|
|
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
2020-12-11 20:46:07 -05:00
|
|
|
new_group.number = f"{smart_group}"
|
2020-11-21 18:43:07 -05:00
|
|
|
for pair in global_id_pairs:
|
|
|
|
|
for id in pair:
|
|
|
|
|
new_global_id = new_group.global_ids.add()
|
|
|
|
|
new_global_id.name = id
|
|
|
|
|
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-11-21 16:37:53 -05:00
|
|
|
class SelectSmartGroup(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_smart_group"
|
|
|
|
|
bl_label = "Select Smart Group"
|
2020-11-10 21:01:51 -05:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
# Select smart group in view
|
2020-12-02 09:51:00 +11:00
|
|
|
selected_smart_group = bpy.context.scene.BIMProperties.smart_clash_groups[
|
|
|
|
|
bpy.context.scene.BIMProperties.active_smart_group_index
|
|
|
|
|
]
|
|
|
|
|
# print(selected_smart_group.number)
|
2020-11-10 21:01:51 -05:00
|
|
|
|
2020-11-06 21:14:21 -05:00
|
|
|
for obj in bpy.context.visible_objects:
|
|
|
|
|
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
|
|
|
|
if global_id:
|
2020-11-10 21:01:51 -05:00
|
|
|
for id in selected_smart_group.global_ids:
|
2020-12-02 09:51:00 +11:00
|
|
|
# print("Id: ", id)
|
|
|
|
|
# print("Global id: ", global_id.string_value)
|
2020-11-10 21:01:51 -05:00
|
|
|
if global_id.string_value in id.name:
|
2020-12-02 09:51:00 +11:00
|
|
|
# print("object match: ", global_id)
|
2020-11-10 21:01:51 -05:00
|
|
|
obj.select_set(True)
|
2020-11-06 21:14:21 -05:00
|
|
|
|
|
|
|
|
return {"FINISHED"}
|
2020-12-02 09:51:00 +11:00
|
|
|
|
|
|
|
|
|
2020-01-29 11:42:43 +11:00
|
|
|
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"}
|
2020-01-29 11:42:43 +11:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-01-29 11:42:43 +11:00
|
|
|
|
|
|
|
|
|
2019-10-07 21:11:11 +11:00
|
|
|
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"}
|
2019-10-07 21:11:11 +11:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2019-10-07 21:11:11 +11:00
|
|
|
|
2020-01-29 11:42:43 +11:00
|
|
|
|
2019-10-07 21:11:11 +11:00
|
|
|
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"}
|
2019-10-07 21:11:11 +11:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2019-11-30 22:42:04 +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"
|
2019-11-30 22:42:04 +11:00
|
|
|
|
|
|
|
|
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"}
|
2019-12-05 09:54:35 +11:00
|
|
|
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))
|
2019-11-30 22:42:04 +11:00
|
|
|
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"}
|
2019-11-30 22:42:04 +11:00
|
|
|
|
|
|
|
|
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"]
|
2019-12-05 09:54:35 +11:00
|
|
|
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"))
|
2019-11-30 22:42:04 +11:00
|
|
|
for material in bpy.data.materials:
|
2020-11-01 20:08:48 +07:00
|
|
|
if material.name == identification and material.library:
|
2019-11-30 22:42:04 +11:00
|
|
|
bpy.context.active_object.material_slots[0].material = material
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
2019-11-21 07:09:08 +11:00
|
|
|
class FetchLibraryInformation(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.fetch_library_information"
|
|
|
|
|
bl_label = "Fetch Library Information"
|
2019-11-21 07:09:08 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
# TODO
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2019-12-03 17:27:22 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class FetchObjectPassport(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.fetch_object_passport"
|
|
|
|
|
bl_label = "Fetch Object Passport"
|
2019-12-03 17:27:22 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-07-03 21:54:22 +10:00
|
|
|
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":
|
2020-07-03 21:54:22 +10:00
|
|
|
self.fetch_blender(reference)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2019-12-03 17:27:22 +11:00
|
|
|
|
2020-07-03 21:54:22 +10:00
|
|
|
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"))
|
2020-07-03 21:54:22 +10:00
|
|
|
bpy.context.active_object.data = bpy.data.meshes[reference.name]
|
2019-12-04 17:49:28 +11:00
|
|
|
|
|
|
|
|
|
2020-03-26 20:47:21 +11:00
|
|
|
class OpenView(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.open_view"
|
|
|
|
|
bl_label = "Open View"
|
2020-08-11 15:01:30 +10:00
|
|
|
view: bpy.props.StringProperty()
|
2020-03-26 20:47:21 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-09-11 19:02:33 +10:00
|
|
|
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"}
|
2020-03-26 20:47:21 +11:00
|
|
|
|
|
|
|
|
|
2020-12-29 06:00:49 +07:00
|
|
|
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"}
|
|
|
|
|
|
|
|
|
|
|
2019-12-10 18:54:26 +11:00
|
|
|
class CutSection(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.cut_section"
|
|
|
|
|
bl_label = "Cut Section"
|
2019-12-10 18:54:26 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-01-01 12:15:18 +11:00
|
|
|
self.file = ifc.IfcStore.get_file()
|
2019-12-19 13:00:19 +11:00
|
|
|
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"}
|
2020-12-03 21:51:14 +11:00
|
|
|
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]
|
2019-12-10 18:54:26 +11:00
|
|
|
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)
|
2019-12-10 18:54:26 +11:00
|
|
|
)
|
2020-09-11 18:29:09 +10:00
|
|
|
self.create_raster(camera, drawing_style)
|
2019-12-10 18:54:26 +11:00
|
|
|
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
|
|
|
|
2020-05-09 15:50:24 +10:00
|
|
|
ifc_cutter.ifc_filenames = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
|
2019-12-10 18:54:26 +11:00
|
|
|
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
|
2020-09-02 21:51:58 +10:00
|
|
|
ifc_cutter.vector_style = drawing_style.vector_style
|
2019-12-10 18:54:26 +11:00
|
|
|
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":
|
2020-08-11 13:52:16 +10:00
|
|
|
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects_custom
|
|
|
|
|
else:
|
|
|
|
|
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects
|
2019-12-13 17:25:52 +11:00
|
|
|
ifc_cutter.leader_obj = None
|
|
|
|
|
ifc_cutter.stair_obj = None
|
2020-10-30 10:58:02 +11:00
|
|
|
ifc_cutter.dimension_objs = []
|
2020-05-11 10:00:54 +10:00
|
|
|
ifc_cutter.break_obj = None
|
2020-10-30 10:58:02 +11:00
|
|
|
ifc_cutter.equal_objs = []
|
2020-05-09 16:38:05 +10:00
|
|
|
ifc_cutter.hidden_objs = []
|
|
|
|
|
ifc_cutter.solid_objs = []
|
2019-12-17 17:05:03 +11:00
|
|
|
ifc_cutter.plan_level_obj = None
|
|
|
|
|
ifc_cutter.section_level_obj = None
|
2019-12-13 17:25:52 +11:00
|
|
|
ifc_cutter.grid_objs = []
|
|
|
|
|
ifc_cutter.text_objs = []
|
2020-09-02 21:50:58 +10:00
|
|
|
ifc_cutter.misc_objs = []
|
2020-09-02 21:51:58 +10:00
|
|
|
ifc_cutter.attributes = [a.name for a in drawing_style.attributes]
|
2019-12-19 08:17:14 +11:00
|
|
|
for obj in camera.users_collection[0].objects:
|
2020-11-01 20:08:48 +07:00
|
|
|
if "IfcGrid" in obj.name:
|
2020-09-02 21:50:58 +10:00
|
|
|
ifc_cutter.grid_objs.append(obj)
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "IfcGroup" in obj.name and obj.type == "CAMERA":
|
2020-09-02 21:50:58 +10:00
|
|
|
ifc_cutter.camera_obj = obj
|
|
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
if "IfcAnnotation/" not in obj.name:
|
2020-09-02 21:50:58 +10:00
|
|
|
continue
|
|
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
if "Leader" in obj.name:
|
2020-05-09 22:26:39 +10:00
|
|
|
ifc_cutter.leader_obj = (obj, obj.data)
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Stair" in obj.name:
|
2019-12-13 17:25:52 +11:00
|
|
|
ifc_cutter.stair_obj = obj
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Equal" in obj.name:
|
2020-10-30 10:58:02 +11:00
|
|
|
ifc_cutter.equal_objs.append(obj)
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Dimension" in obj.name:
|
2020-10-30 10:58:02 +11:00
|
|
|
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:
|
2020-05-09 22:26:39 +10:00
|
|
|
ifc_cutter.hidden_objs.append((obj, obj.data))
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Solid" in obj.name:
|
2020-05-09 22:26:39 +10:00
|
|
|
ifc_cutter.solid_objs.append((obj, obj.data))
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Plan Level" in obj.name:
|
2019-12-17 17:05:03 +11:00
|
|
|
ifc_cutter.plan_level_obj = obj
|
2020-11-01 20:08:48 +07:00
|
|
|
elif "Section Level" in obj.name:
|
2019-12-17 17:05:03 +11:00
|
|
|
ifc_cutter.section_level_obj = obj
|
2020-11-01 20:08:48 +07:00
|
|
|
elif obj.type == "FONT":
|
2019-12-13 17:25:52 +11:00
|
|
|
ifc_cutter.text_objs.append(obj)
|
2020-09-02 21:50:58 +10:00
|
|
|
else:
|
|
|
|
|
ifc_cutter.misc_objs.append(obj)
|
2020-05-09 22:26:39 +10:00
|
|
|
|
2019-12-10 18:54:26 +11:00
|
|
|
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,
|
2019-12-10 18:54:26 +11:00
|
|
|
}
|
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
|
2020-07-10 21:22:02 +10:00
|
|
|
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:
|
2020-07-10 21:22:02 +10:00
|
|
|
continue
|
|
|
|
|
for attribute in obj.BIMObjectProperties.attributes:
|
2020-11-01 20:08:48 +07:00
|
|
|
if attribute.name == "GlobalId":
|
2020-07-10 21:22:02 +10:00
|
|
|
selected_global_ids.append(attribute.string_value)
|
|
|
|
|
break
|
|
|
|
|
ifc_cutter.selected_global_ids = selected_global_ids
|
2020-05-04 12:20:34 +10:00
|
|
|
ifc_cutter.should_extract = bpy.context.scene.DocProperties.should_extract
|
2020-05-11 17:46:38 +10:00
|
|
|
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("|")
|
2020-08-28 21:40:24 +10:00
|
|
|
else:
|
2020-11-01 20:08:48 +07:00
|
|
|
human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|")
|
|
|
|
|
numerator, denominator = fraction.split("/")
|
2020-03-26 21:44:54 +11:00
|
|
|
if camera.data.BIMCameraProperties.is_nts:
|
2020-11-01 20:08:48 +07:00
|
|
|
svg_writer.human_scale = "NTS"
|
2020-03-26 21:44:54 +11:00
|
|
|
else:
|
2020-08-28 21:40:24 +10:00
|
|
|
svg_writer.human_scale = human_scale
|
2019-12-12 12:51:47 +11:00
|
|
|
svg_writer.scale = float(numerator) / float(denominator)
|
2019-12-10 18:54:26 +11:00
|
|
|
ifc_cutter.cut()
|
|
|
|
|
svg_writer.write()
|
2020-08-11 15:01:30 +10:00
|
|
|
bpy.ops.bim.open_view(view=self.diagram_name)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2019-12-10 18:54:26 +11:00
|
|
|
|
2020-09-11 18:29:09 +10:00
|
|
|
def create_raster(self, camera, drawing_style):
|
2020-11-01 20:08:48 +07:00
|
|
|
if drawing_style.render_type == "NONE":
|
2020-09-11 18:29:09 +10:00
|
|
|
return
|
|
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
if drawing_style.render_type == "DEFAULT":
|
2020-09-11 18:29:09 +10:00
|
|
|
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 (
|
2020-12-10 13:51:51 +11:00
|
|
|
(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)
|
|
|
|
|
):
|
2020-09-11 18:29:09 +10:00
|
|
|
previous_visibility[obj.name] = obj.hide_get()
|
|
|
|
|
obj.hide_set(True)
|
|
|
|
|
|
|
|
|
|
space = self.get_view_3d()
|
|
|
|
|
previous_shading = space.shading.type
|
2020-10-30 09:17:57 +11:00
|
|
|
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"
|
2020-09-11 18:29:09 +10:00
|
|
|
bpy.ops.render.opengl(write_still=True)
|
|
|
|
|
space.shading.type = previous_shading
|
2020-10-30 09:17:57 +11:00
|
|
|
bpy.context.scene.render.image_settings.file_format = previous_format
|
2020-09-11 18:29:09 +10:00
|
|
|
|
|
|
|
|
for name, value in previous_visibility.items():
|
|
|
|
|
bpy.data.objects[name].hide_set(value)
|
|
|
|
|
|
|
|
|
|
def does_obj_have_target_view_representation(self, obj, camera):
|
2021-01-01 12:15:18 +11:00
|
|
|
for representation in obj.BIMObjectProperties.representations:
|
|
|
|
|
element = self.file.by_id(representation.ifc_definition_id)
|
2021-01-08 13:49:31 +11:00
|
|
|
if ifcopenshell.util.element.is_representation_of_context(
|
|
|
|
|
element, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view
|
|
|
|
|
):
|
2021-01-01 12:15:18 +11:00
|
|
|
return True
|
2020-09-11 18:29:09 +10:00
|
|
|
|
2019-12-10 18:54:26 +11:00
|
|
|
def is_landscape(self):
|
|
|
|
|
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
|
2019-12-11 18:50:01 +11:00
|
|
|
|
2020-09-11 18:29:09 +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":
|
2020-09-11 18:29:09 +10:00
|
|
|
continue
|
|
|
|
|
for space in area.spaces:
|
2020-11-01 20:08:48 +07:00
|
|
|
if space.type != "VIEW_3D":
|
2020-09-11 18:29:09 +10:00
|
|
|
continue
|
|
|
|
|
return space
|
|
|
|
|
|
|
|
|
|
|
2020-08-16 11:54:05 +10:00
|
|
|
class AddSheet(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_sheet"
|
|
|
|
|
bl_label = "Add Sheet"
|
2020-03-26 18:26:35 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-08-16 11:54:05 +10:00
|
|
|
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))
|
2020-03-26 18:26:35 +11:00
|
|
|
sheet_builder = sheeter.SheetBuilder()
|
|
|
|
|
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
|
2020-08-28 23:16:25 +10:00
|
|
|
sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-03-26 18:26:35 +11:00
|
|
|
|
|
|
|
|
|
2020-03-26 20:47:21 +11:00
|
|
|
class OpenSheet(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.open_sheet"
|
|
|
|
|
bl_label = "Open Sheet"
|
2020-03-26 20:47:21 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-08-16 11:54:05 +10:00
|
|
|
props = bpy.context.scene.DocProperties
|
2020-09-11 19:02:33 +10:00
|
|
|
open_with_user_command(
|
2020-11-01 20:08:48 +07:00
|
|
|
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
|
2020-09-11 19:02:33 +10:00
|
|
|
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"}
|
2020-03-26 20:47:21 +11:00
|
|
|
|
|
|
|
|
|
2020-08-16 11:54:05 +10:00
|
|
|
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"
|
2020-03-26 18:26:35 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
props = bpy.context.scene.DocProperties
|
|
|
|
|
sheet_builder = sheeter.SheetBuilder()
|
|
|
|
|
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
|
2020-09-05 08:08:50 +10:00
|
|
|
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
|
|
|
|
|
)
|
2020-09-05 08:08:50 +10:00
|
|
|
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"}
|
2020-03-26 18:26:35 +11:00
|
|
|
|
|
|
|
|
|
2019-12-11 18:50:01 +11:00
|
|
|
class CreateSheets(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.create_sheets"
|
|
|
|
|
bl_label = "Create Sheets"
|
2019-12-11 18:50:01 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-08-16 11:54:05 +10:00
|
|
|
props = bpy.context.scene.DocProperties
|
2020-08-17 21:12:31 +10:00
|
|
|
name = props.sheets[props.active_sheet_index].name
|
2019-12-11 18:50:01 +11:00
|
|
|
sheet_builder = sheeter.SheetBuilder()
|
|
|
|
|
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
|
2020-08-17 21:12:31 +10:00
|
|
|
sheet_builder.build(name)
|
2020-08-31 10:38:48 +10:00
|
|
|
|
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
|
2020-09-11 19:02:33 +10:00
|
|
|
|
2020-08-31 10:38:48 +10:00
|
|
|
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")
|
2020-08-31 10:38:48 +10:00
|
|
|
# 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")
|
2020-08-31 10:38:48 +10:00
|
|
|
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)
|
2020-08-31 10:38:48 +10:00
|
|
|
else:
|
2020-09-11 19:02:33 +10:00
|
|
|
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"}
|
2019-12-20 10:43:39 +11:00
|
|
|
|
2020-03-26 18:26:35 +11:00
|
|
|
|
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:
|
2020-10-30 09:11:43 +11:00
|
|
|
# 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
|
2020-10-30 09:11:43 +11:00
|
|
|
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
|
2020-09-02 21:49:18 +10:00
|
|
|
bpy.ops.bim.activate_drawing_style()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-01-08 16:58:59 +11:00
|
|
|
|
|
|
|
|
|
2020-03-25 16:32:14 +11:00
|
|
|
class OpenUpstream(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.open_upstream"
|
|
|
|
|
bl_label = "Open Upstream Reference"
|
2020-03-25 16:32:14 +11:00
|
|
|
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"}
|
2020-04-20 11:06:34 +10:00
|
|
|
|
|
|
|
|
|
2020-09-03 19:24:55 +10:00
|
|
|
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"
|
2020-09-03 19:24:55 +10:00
|
|
|
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:
|
2020-09-03 19:24:55 +10:00
|
|
|
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:
|
2020-09-03 19:24:55 +10:00
|
|
|
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):
|
2020-09-03 19:24:55 +10:00
|
|
|
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"}
|
2020-09-03 19:24:55 +10:00
|
|
|
|
|
|
|
|
|
2020-11-23 16:01:21 +11:00
|
|
|
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):
|
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
|
|
2020-04-24 17:14:07 +10:00
|
|
|
class AddPropertySetTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_property_set_template"
|
|
|
|
|
bl_label = "Add Property Set Template"
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-01 20:08:48 +07:00
|
|
|
context.scene.BIMProperties.active_property_set_template.global_id = ""
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.name = "New_Pset"
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.description = ""
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.template_type = "PSET_TYPEDRIVENONLY"
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.applicable_entity = "IfcTypeObject"
|
2020-04-24 18:05:59 +10:00
|
|
|
while len(bpy.context.scene.BIMProperties.property_templates) > 0:
|
|
|
|
|
bpy.context.scene.BIMProperties.property_templates.remove(0)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
|
2020-04-24 18:05:59 +10:00
|
|
|
class RemovePropertySetTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_property_set_template"
|
|
|
|
|
bl_label = "Remove Property Set Template"
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-04-24 18:05:59 +10:00
|
|
|
template = ifc.IfcStore.pset_template_file.by_guid(context.scene.BIMProperties.property_set_templates)
|
|
|
|
|
ifc.IfcStore.pset_template_file.remove(template)
|
|
|
|
|
ifc.IfcStore.pset_template_file.write(ifc.IfcStore.pset_template_path)
|
|
|
|
|
from . import prop
|
2020-11-01 20:08:48 +07:00
|
|
|
|
2020-04-24 18:05:59 +10:00
|
|
|
prop.refreshPropertySetTemplates(self, context)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
class EditPropertySetTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.edit_property_set_template"
|
|
|
|
|
bl_label = "Edit Property Set Template"
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-04-24 18:05:59 +10:00
|
|
|
template = ifc.IfcStore.pset_template_file.by_guid(context.scene.BIMProperties.property_set_templates)
|
2020-04-24 17:14:07 +10:00
|
|
|
context.scene.BIMProperties.active_property_set_template.global_id = template.GlobalId
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.name = template.Name
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.description = template.Description
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.template_type = template.TemplateType
|
|
|
|
|
context.scene.BIMProperties.active_property_set_template.applicable_entity = template.ApplicableEntity
|
|
|
|
|
|
|
|
|
|
while len(bpy.context.scene.BIMProperties.property_templates) > 0:
|
|
|
|
|
bpy.context.scene.BIMProperties.property_templates.remove(0)
|
|
|
|
|
|
2020-04-24 18:05:59 +10:00
|
|
|
if template.HasPropertyTemplates:
|
|
|
|
|
for property_template in template.HasPropertyTemplates:
|
2020-11-01 20:08:48 +07:00
|
|
|
if not property_template.is_a("IfcSimplePropertyTemplate"):
|
2020-04-24 18:05:59 +10:00
|
|
|
continue
|
|
|
|
|
new = context.scene.BIMProperties.property_templates.add()
|
|
|
|
|
new.global_id = property_template.GlobalId
|
|
|
|
|
new.name = property_template.Name
|
|
|
|
|
new.description = property_template.Description
|
|
|
|
|
new.primary_measure_type = property_template.PrimaryMeasureType
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
class SavePropertySetTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.save_property_set_template"
|
|
|
|
|
bl_label = "Save Property Set Template"
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-04-24 18:05:59 +10:00
|
|
|
blender_property_set_template = context.scene.BIMProperties.active_property_set_template
|
|
|
|
|
if blender_property_set_template.global_id:
|
|
|
|
|
template = ifc.IfcStore.pset_template_file.by_guid(blender_property_set_template.global_id)
|
|
|
|
|
else:
|
|
|
|
|
template = ifc.IfcStore.pset_template_file.createIfcPropertySetTemplate()
|
|
|
|
|
template.GlobalId = ifcopenshell.guid.new()
|
|
|
|
|
template.Name = blender_property_set_template.name
|
|
|
|
|
template.Description = blender_property_set_template.description
|
|
|
|
|
template.TemplateType = blender_property_set_template.template_type
|
|
|
|
|
template.ApplicableEntity = blender_property_set_template.applicable_entity
|
|
|
|
|
|
|
|
|
|
saved_global_ids = []
|
|
|
|
|
|
|
|
|
|
for blender_property_template in context.scene.BIMProperties.property_templates:
|
|
|
|
|
if blender_property_template.global_id:
|
|
|
|
|
property_template = ifc.IfcStore.pset_template_file.by_guid(blender_property_template.global_id)
|
|
|
|
|
else:
|
|
|
|
|
property_template = ifc.IfcStore.pset_template_file.createIfcSimplePropertyTemplate()
|
|
|
|
|
property_template.GlobalId = ifcopenshell.guid.new()
|
|
|
|
|
if template.HasPropertyTemplates:
|
|
|
|
|
has_property_templates = list(template.HasPropertyTemplates)
|
|
|
|
|
else:
|
|
|
|
|
has_property_templates = []
|
|
|
|
|
has_property_templates.append(property_template)
|
|
|
|
|
template.HasPropertyTemplates = has_property_templates
|
|
|
|
|
property_template.Name = blender_property_template.name
|
|
|
|
|
property_template.Description = blender_property_template.description
|
|
|
|
|
property_template.PrimaryMeasureType = blender_property_template.primary_measure_type
|
2020-11-01 20:08:48 +07:00
|
|
|
property_template.TemplateType = "P_SINGLEVALUE"
|
|
|
|
|
property_template.AccessState = "READWRITE"
|
2020-04-24 18:05:59 +10:00
|
|
|
saved_global_ids.append(property_template.GlobalId)
|
|
|
|
|
|
|
|
|
|
for element in template.HasPropertyTemplates:
|
|
|
|
|
if element.GlobalId not in saved_global_ids:
|
|
|
|
|
ifc.IfcStore.pset_template_file.remove(element)
|
|
|
|
|
|
|
|
|
|
ifc.IfcStore.pset_template_file.write(ifc.IfcStore.pset_template_path)
|
|
|
|
|
from . import prop
|
2020-11-01 20:08:48 +07:00
|
|
|
|
2020-04-24 18:05:59 +10:00
|
|
|
prop.refreshPropertySetTemplates(self, context)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AddPropertyTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_property_template"
|
|
|
|
|
bl_label = "Add Property Template"
|
2020-04-24 17:14:07 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-04-24 18:05:59 +10:00
|
|
|
context.scene.BIMProperties.property_templates.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-04-24 18:05:59 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemovePropertyTemplate(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_property_template"
|
|
|
|
|
bl_label = "Remove Property Template"
|
2020-04-24 18:05:59 +10:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.property_templates.remove(self.index)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
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)
|
2020-04-25 17:40:24 +10:00
|
|
|
else:
|
|
|
|
|
self.append_obj_to_section_override_node(obj)
|
2020-04-25 16:21:15 +10:00
|
|
|
self.add_default_material_if_none_exists()
|
|
|
|
|
self.override_materials()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
section.empty_display_size = 5
|
2020-04-25 17:40:24 +10:00
|
|
|
section.show_in_front = True
|
2020-11-27 22:07:32 +11:00
|
|
|
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()
|
|
|
|
|
)
|
2020-09-08 18:14:39 +10:00
|
|
|
else:
|
2020-11-01 20:08:48 +07:00
|
|
|
section.rotation_euler = Euler((radians(180.0), 0.0, 0.0), "XYZ")
|
2020-09-08 18:14:39 +10:00
|
|
|
section.location = bpy.context.scene.cursor.location
|
2020-11-01 20:08:48 +07:00
|
|
|
collection = bpy.data.collections.get("Sections")
|
2020-04-25 16:21:15 +10:00
|
|
|
if not collection:
|
2020-11-01 20:08:48 +07:00
|
|
|
collection = bpy.data.collections.new("Sections")
|
2020-04-25 16:21:15 +10:00
|
|
|
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")
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
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])
|
2020-06-12 16:59:27 +10:00
|
|
|
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[""])
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
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-04-25 16:21:15 +10:00
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
group_input = group.nodes.new(type="NodeGroupInput")
|
|
|
|
|
group_output = group.nodes.new(type="NodeGroupOutput")
|
2020-04-25 16:21:15 +10:00
|
|
|
|
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")
|
2020-06-12 16:59:27 +10:00
|
|
|
emission.inputs[0].default_value = list(bpy.context.scene.BIMProperties.section_plane_colour) + [1]
|
2020-04-25 16:21:15 +10:00
|
|
|
|
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-04-25 16:21:15 +10:00
|
|
|
|
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-04-25 16:21:15 +10:00
|
|
|
|
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-04-25 16:21:15 +10:00
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
group.links.new(section_mix.outputs["Shader"], group_output.inputs[""])
|
2020-04-25 16:21:15 +10:00
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
cut_obj = group.nodes.new(type="ShaderNodeTexCoord")
|
2020-04-25 16:21:15 +10:00
|
|
|
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])
|
2020-06-12 16:59:27 +10:00
|
|
|
group.links.new(value.outputs[0], section_compare.inputs[1])
|
|
|
|
|
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
|
2020-04-25 17:40:24 +10:00
|
|
|
|
|
|
|
|
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")
|
2020-04-25 17:40:24 +10:00
|
|
|
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-04-25 17:40:24 +10:00
|
|
|
|
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")
|
2020-04-25 17:40:24 +10:00
|
|
|
|
2020-06-12 16:59:27 +10:00
|
|
|
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])
|
2020-06-12 16:59:27 +10:00
|
|
|
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
|
2020-04-25 17:40:24 +10:00
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
section_compare.name = "Last Section Compare"
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
def add_default_material_if_none_exists(self):
|
2020-11-01 20:08:48 +07:00
|
|
|
material = bpy.data.materials.get("Section Override")
|
2020-04-25 16:21:15 +10:00
|
|
|
if not material:
|
2020-11-01 20:08:48 +07:00
|
|
|
material = bpy.data.materials.new("Section Override")
|
2020-04-25 16:21:15 +10:00
|
|
|
material.use_nodes = True
|
|
|
|
|
|
2020-04-25 19:17:47 +10:00
|
|
|
if bpy.context.scene.BIMProperties.should_section_selected_objects:
|
2020-04-30 08:28:00 +10:00
|
|
|
objects = list(bpy.context.selected_objects)
|
2020-04-25 19:17:47 +10:00
|
|
|
else:
|
2020-04-30 08:28:00 +10:00
|
|
|
objects = list(bpy.context.visible_objects)
|
2020-04-25 19:17:47 +10:00
|
|
|
|
|
|
|
|
for obj in objects:
|
2020-04-30 08:28:00 +10:00
|
|
|
aggregate = obj.instance_collection
|
2020-11-01 20:08:48 +07:00
|
|
|
if aggregate and "IfcRelAggregates/" in aggregate.name:
|
2020-04-30 08:28:00 +10:00
|
|
|
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)
|
2020-04-25 16:21:15 +10:00
|
|
|
|
|
|
|
|
def override_materials(self):
|
2020-11-01 20:08:48 +07:00
|
|
|
override = bpy.data.node_groups.get("Section Override")
|
2020-04-25 16:21:15 +10:00
|
|
|
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"):
|
2020-04-25 17:40:24 +10:00
|
|
|
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")
|
2020-06-12 16:59:27 +10:00
|
|
|
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"
|
2020-04-25 16:21:15 +10:00
|
|
|
section_override.node_tree = override
|
|
|
|
|
material.node_tree.links.new(from_socket, section_override.inputs[0])
|
2020-06-12 16:59:27 +10:00
|
|
|
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")
|
2020-06-12 16:59:27 +10:00
|
|
|
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")
|
2020-06-12 16:59:27 +10:00
|
|
|
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-26 14:41:38 +10:00
|
|
|
|
|
|
|
|
|
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):
|
2021-01-20 16:53:34 +11:00
|
|
|
# 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
|
|
|
|
2020-05-02 17:33:16 +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"
|
2020-05-02 17:33:16 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-08-15 11:52:58 +10:00
|
|
|
bpy.context.scene.DocProperties.ifc_files.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-02 17:33:16 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveIfcFile(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_ifc_file"
|
|
|
|
|
bl_label = "Remove IFC File"
|
2020-05-02 17:33:16 +10:00
|
|
|
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"}
|
2020-05-02 17:33:16 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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"}
|
2020-05-02 17:33:16 +10:00
|
|
|
|
|
|
|
|
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"
|
2020-11-23 16:01:21 +11:00
|
|
|
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":
|
2020-05-07 09:36:38 +10:00
|
|
|
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)
|
2021-01-08 13:49:31 +11:00
|
|
|
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"}
|
2020-05-03 19:54:27 +10:00
|
|
|
|
|
|
|
|
|
2020-08-29 18:56:23 +10:00
|
|
|
class GenerateReferences(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.generate_references"
|
|
|
|
|
bl_label = "Generate References"
|
2020-08-29 18:56:23 +10:00
|
|
|
|
|
|
|
|
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":
|
2020-08-29 18:56:23 +10:00
|
|
|
self.generate_grids()
|
2020-11-01 20:08:48 +07:00
|
|
|
if self.camera.data.BIMCameraProperties.target_view == "ELEVATION_VIEW":
|
2020-08-29 18:56:23 +10:00
|
|
|
self.generate_grids()
|
|
|
|
|
self.generate_levels()
|
2020-11-01 20:08:48 +07:00
|
|
|
if self.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW":
|
2020-08-29 18:56:23 +10:00
|
|
|
self.generate_grids()
|
|
|
|
|
self.generate_levels()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-08-29 18:56:23 +10:00
|
|
|
|
|
|
|
|
def filter_potential_references(self):
|
2020-11-01 20:08:48 +07:00
|
|
|
for name in ["grids", "levels"]:
|
2020-08-29 18:56:23 +10:00
|
|
|
setattr(self, name, [])
|
|
|
|
|
for obj in bpy.data.objects:
|
2020-11-01 20:08:48 +07:00
|
|
|
if "IfcGridAxis/" in obj.name:
|
2020-08-29 18:56:23 +10:00
|
|
|
self.grids.append(obj)
|
2020-11-01 20:08:48 +07:00
|
|
|
if "IfcBuildingStorey/" in obj.name:
|
2020-08-29 18:56:23 +10:00
|
|
|
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
|
|
|
|
|
)
|
2020-08-29 18:56:23 +10:00
|
|
|
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")
|
2020-08-29 18:56:23 +10:00
|
|
|
|
|
|
|
|
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("|")
|
2020-08-29 18:56:23 +10:00
|
|
|
else:
|
2020-11-01 20:08:48 +07:00
|
|
|
human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|")
|
|
|
|
|
numerator, denominator = fraction.split("/")
|
2020-08-29 18:56:23 +10:00
|
|
|
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))
|
2020-08-29 18:56:23 +10:00
|
|
|
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
|
|
|
|
|
)
|
2020-08-29 18:56:23 +10:00
|
|
|
|
|
|
|
|
|
2020-05-03 19:54:27 +10:00
|
|
|
class ResizeText(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.resize_text"
|
|
|
|
|
bl_label = "Resize Text"
|
2020-05-03 19:54:27 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-05-04 12:20:34 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AddVariable(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_variable"
|
|
|
|
|
bl_label = "Add Variable"
|
2020-05-04 12:20:34 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.active_object.data.BIMTextProperties.variables.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-05-04 12:20:34 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveVariable(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_variable"
|
|
|
|
|
bl_label = "Remove Variable"
|
2020-05-04 12:20:34 +10:00
|
|
|
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"}
|
2020-05-07 09:36:38 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class PropagateTextData(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.propagate_text_data"
|
|
|
|
|
bl_label = "Propagate Text Data"
|
2020-05-07 09:36:38 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-05-07 22:15:16 +10:00
|
|
|
|
|
|
|
|
|
2020-08-05 17:18:10 +10:00
|
|
|
class SelectIfcPatchInput(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_ifc_patch_input"
|
|
|
|
|
bl_label = "Select IFC Patch Input"
|
2020-11-01 20:08:48 +07:00
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
2020-08-05 17:18:10 +10:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.ifc_patch_input = self.filepath
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-08-05 17:18:10 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-08-05 17:18:10 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SelectIfcPatchOutput(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_ifc_patch_output"
|
|
|
|
|
bl_label = "Select IFC Patch Output"
|
2020-11-01 20:08:48 +07:00
|
|
|
filename_ext = ".ifc"
|
2020-08-05 17:18:10 +10:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.ifc_patch_output = self.filepath
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-08-05 17:18:10 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-08-08 14:22:48 +10:00
|
|
|
|
|
|
|
|
|
2020-08-12 19:59:30 +10:00
|
|
|
class SetOverrideColour(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.set_override_colour"
|
|
|
|
|
bl_label = "Set Override Colour"
|
2020-08-12 19:59:30 +10:00
|
|
|
|
|
|
|
|
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"
|
2020-08-13 09:22:27 +10:00
|
|
|
index: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-09-09 18:49:27 +10:00
|
|
|
space = self.get_view_3d()
|
2020-08-13 09:22:27 +10:00
|
|
|
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,
|
2020-08-13 09:22:27 +10:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2020-09-09 18:49:27 +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":
|
2020-09-09 18:49:27 +10:00
|
|
|
continue
|
|
|
|
|
for space in area.spaces:
|
2020-11-01 20:08:48 +07:00
|
|
|
if space.type != "VIEW_3D":
|
2020-09-09 18:49:27 +10:00
|
|
|
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
|
|
|
|
|
]
|
2020-10-29 16:27:48 +11:00
|
|
|
self.set_raster_style()
|
|
|
|
|
self.set_query()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-08-15 11:52:58 +10:00
|
|
|
|
|
|
|
|
def set_raster_style(self):
|
2020-09-09 18:49:27 +10:00
|
|
|
space = self.get_view_3d()
|
2020-08-15 11:52:58 +10:00
|
|
|
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"]
|
2020-08-15 11:52:58 +10:00
|
|
|
|
|
|
|
|
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
|
2020-08-15 11:52:58 +10:00
|
|
|
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")
|
2020-08-15 11:52:58 +10:00
|
|
|
if self.drawing_style.exclude_query:
|
2020-11-01 20:08:48 +07:00
|
|
|
self.parse_filter_query("EXCLUDE")
|
2020-08-15 11:52:58 +10:00
|
|
|
|
|
|
|
|
def parse_filter_query(self, mode):
|
2020-11-01 20:08:48 +07:00
|
|
|
if mode == "INCLUDE":
|
2020-08-15 11:52:58 +10:00
|
|
|
objects = bpy.context.scene.objects
|
2020-11-01 20:08:48 +07:00
|
|
|
elif mode == "EXCLUDE":
|
2020-08-15 11:52:58 +10:00
|
|
|
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")
|
2020-08-15 11:52:58 +10:00
|
|
|
if not global_id:
|
|
|
|
|
continue
|
|
|
|
|
global_id = global_id.string_value
|
2020-11-01 20:08:48 +07:00
|
|
|
if mode == "INCLUDE":
|
2020-08-15 11:52:58 +10:00
|
|
|
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":
|
2020-08-15 11:52:58 +10:00
|
|
|
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
|
2020-08-13 20:17:51 +10:00
|
|
|
|
2020-09-09 18:49:27 +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":
|
2020-09-09 18:49:27 +10:00
|
|
|
continue
|
|
|
|
|
for space in area.spaces:
|
2020-11-01 20:08:48 +07:00
|
|
|
if space.type != "VIEW_3D":
|
2020-09-09 18:49:27 +10:00
|
|
|
continue
|
|
|
|
|
return space
|
|
|
|
|
|
|
|
|
|
|
2020-08-13 20:17:51 +10:00
|
|
|
class AddDrawing(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_drawing"
|
|
|
|
|
bl_label = "Add Drawing"
|
2020-08-13 20:17:51 +10:00
|
|
|
|
|
|
|
|
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)
|
2020-08-13 20:17:51 +10:00
|
|
|
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":
|
2020-08-28 21:40:24 +10:00
|
|
|
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"
|
2020-08-13 20:17:51 +10:00
|
|
|
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"
|
2020-08-13 20:17:51 +10:00
|
|
|
new.camera = camera
|
2020-09-11 18:29:09 +10:00
|
|
|
bpy.ops.bim.activate_drawing_style()
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-08-13 20:17:51 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveDrawing(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_drawing"
|
|
|
|
|
bl_label = "Remove Drawing"
|
2020-08-13 20:17:51 +10:00
|
|
|
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"}
|
2020-08-15 09:45:20 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class EditVectorStyle(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.edit_vector_style"
|
|
|
|
|
bl_label = "Edit Vector Style"
|
2020-08-15 09:45:20 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-08-15 18:37:03 +10:00
|
|
|
|
|
|
|
|
|
2020-08-16 11:54:05 +10:00
|
|
|
class RemoveSheet(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_sheet"
|
|
|
|
|
bl_label = "Remove Sheet"
|
2020-08-16 11:54:05 +10:00
|
|
|
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"}
|
2020-08-17 21:43:33 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AddSchedule(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.add_schedule"
|
|
|
|
|
bl_label = "Add Schedule"
|
2020-08-17 21:43:33 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-08-17 21:43:33 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveSchedule(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.remove_schedule"
|
|
|
|
|
bl_label = "Remove Schedule"
|
2020-08-17 21:43:33 +10:00
|
|
|
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"}
|
2020-08-17 21:43:33 +10:00
|
|
|
|
|
|
|
|
|
2020-11-04 21:28:47 +11:00
|
|
|
class AddMaterialLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.add_material_layer"
|
|
|
|
|
bl_label = "Add Material Layer"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-05 22:41:43 +11:00
|
|
|
new = bpy.context.active_object.BIMObjectProperties.material_set.material_layers.add()
|
2020-11-04 21:28:47 +11:00
|
|
|
new.material = bpy.data.materials[0]
|
|
|
|
|
new.name = "Material Layer"
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveMaterialLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.remove_material_layer"
|
|
|
|
|
bl_label = "Remove Material Layer"
|
|
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-05 22:41:43 +11:00
|
|
|
bpy.context.active_object.BIMObjectProperties.material_set.material_layers.remove(self.index)
|
2020-11-04 21:28:47 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MoveMaterialLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.move_material_layer"
|
|
|
|
|
bl_label = "Move Material Layer"
|
|
|
|
|
direction: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-05 22:41:43 +11:00
|
|
|
props = bpy.context.active_object.BIMObjectProperties.material_set
|
2020-11-04 21:28:47 +11:00
|
|
|
index = props.active_material_layer_index
|
|
|
|
|
if self.direction == "UP" and index - 1 >= 0:
|
|
|
|
|
props.material_layers.move(index, index - 1)
|
|
|
|
|
props.active_material_layer_index = index - 1
|
|
|
|
|
elif self.direction == "DOWN" and index + 1 < len(props.material_layers):
|
|
|
|
|
props.material_layers.move(index, index + 1)
|
|
|
|
|
props.active_material_layer_index = index + 1
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-11-05 22:41:43 +11:00
|
|
|
class AddMaterialConstituent(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.add_material_constituent"
|
|
|
|
|
bl_label = "Add Material Constituent"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
new = bpy.context.active_object.BIMObjectProperties.material_set.material_constituents.add()
|
|
|
|
|
new.material = bpy.data.materials[0]
|
|
|
|
|
new.name = "Material Constituent"
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveMaterialConstituent(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.remove_material_constituent"
|
|
|
|
|
bl_label = "Remove Material Constituent"
|
|
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.active_object.BIMObjectProperties.material_set.material_constituents.remove(self.index)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MoveMaterialConstituent(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.move_material_constituent"
|
|
|
|
|
bl_label = "Move Material Constituent"
|
|
|
|
|
direction: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
props = bpy.context.active_object.BIMObjectProperties.material_set
|
|
|
|
|
index = props.active_material_constituent_index
|
|
|
|
|
if self.direction == "UP" and index - 1 >= 0:
|
|
|
|
|
props.material_constituents.move(index, index - 1)
|
|
|
|
|
props.active_material_constituent_index = index - 1
|
|
|
|
|
elif self.direction == "DOWN" and index + 1 < len(props.material_constituents):
|
|
|
|
|
props.material_constituents.move(index, index + 1)
|
|
|
|
|
props.active_material_constituent_index = index + 1
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-11-09 19:15:23 +11:00
|
|
|
class AddMaterialProfile(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.add_material_profile"
|
|
|
|
|
bl_label = "Add Material Profile"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
new = bpy.context.active_object.BIMObjectProperties.material_set.material_profiles.add()
|
|
|
|
|
new.material = bpy.data.materials[0]
|
|
|
|
|
new.name = "Material Profile"
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoveMaterialProfile(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.remove_material_profile"
|
|
|
|
|
bl_label = "Remove Material Profile"
|
|
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.active_object.BIMObjectProperties.material_set.material_profiles.remove(self.index)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MoveMaterialProfile(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.move_material_profile"
|
|
|
|
|
bl_label = "Move Material Profile"
|
|
|
|
|
direction: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
props = bpy.context.active_object.BIMObjectProperties.material_set
|
|
|
|
|
index = props.active_material_profile_index
|
|
|
|
|
if self.direction == "UP" and index - 1 >= 0:
|
|
|
|
|
props.material_profiles.move(index, index - 1)
|
|
|
|
|
props.active_material_profile_index = index - 1
|
|
|
|
|
elif self.direction == "DOWN" and index + 1 < len(props.material_profiles):
|
|
|
|
|
props.material_profiles.move(index, index + 1)
|
|
|
|
|
props.active_material_profile_index = index + 1
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-08-17 21:43:33 +10:00
|
|
|
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"})
|
2020-08-17 21:43:33 +10:00
|
|
|
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"}
|
2020-08-17 21:43:33 +10:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"RUNNING_MODAL"}
|
2020-08-17 22:10:06 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class BuildSchedule(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.build_schedule"
|
|
|
|
|
bl_label = "Build Schedule"
|
2020-08-17 22:10:06 +10:00
|
|
|
|
|
|
|
|
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")
|
2020-08-17 22:10:06 +10:00
|
|
|
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"}
|
2020-09-01 22:26:57 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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"
|
2020-09-01 22:26:57 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-09-01 22:43:00 +10:00
|
|
|
|
|
|
|
|
|
2020-11-14 22:17:56 +11:00
|
|
|
class AssignPresentationLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.assign_presentation_layer"
|
|
|
|
|
bl_label = "Assign Presentation Layer"
|
2020-11-05 13:54:20 +01:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-15 17:00:50 +11:00
|
|
|
layer = bpy.context.scene.BIMProperties.presentation_layers[self.index]
|
2020-11-13 14:45:08 +01:00
|
|
|
for obj in bpy.context.selected_objects:
|
2020-11-15 17:00:50 +11:00
|
|
|
if not obj.data or not hasattr(obj.data, "BIMMeshProperties"):
|
|
|
|
|
continue
|
|
|
|
|
obj.data.BIMMeshProperties.presentation_layer_index = self.index
|
|
|
|
|
obj.hide_set(not layer.layer_on)
|
2020-11-09 13:34:55 +01:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-11-14 22:17:56 +11:00
|
|
|
class UnassignPresentationLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.unassign_presentation_layer"
|
|
|
|
|
bl_label = "Unassign Presentation Layer"
|
2020-11-09 13:34:55 +01:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-15 17:00:50 +11:00
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
|
if not obj.data or not hasattr(obj.data, "BIMMeshProperties"):
|
|
|
|
|
continue
|
|
|
|
|
obj.data.BIMMeshProperties.presentation_layer_index = -1
|
2020-11-05 13:54:20 +01:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AddPresentationLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.add_presentation_layer"
|
|
|
|
|
bl_label = "Add Presentation Layer"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
new = bpy.context.scene.BIMProperties.presentation_layers.add()
|
2020-11-08 13:52:20 +01:00
|
|
|
new.name = "New Presentation Layer"
|
|
|
|
|
new.layer_on = True
|
|
|
|
|
new.layer_frozen = False
|
|
|
|
|
new.layer_blocked = False
|
2020-11-05 13:54:20 +01:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemovePresentationLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.remove_presentation_layer"
|
|
|
|
|
bl_label = "Remove Presentation Layer"
|
|
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMProperties.presentation_layers.remove(self.index)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-11-08 13:52:20 +01:00
|
|
|
class UpdatePresentationLayer(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.update_presentation_layer"
|
2020-11-13 14:45:08 +01:00
|
|
|
bl_label = "Hide/Show selected Presentation Layer"
|
2020-11-08 13:52:20 +01:00
|
|
|
index: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2020-11-13 14:45:08 +01:00
|
|
|
for obj in bpy.context.scene.objects:
|
2020-11-15 20:26:22 +11:00
|
|
|
if not obj.data or not hasattr(obj.data, "BIMMeshProperties"):
|
|
|
|
|
continue
|
|
|
|
|
if obj.data.BIMMeshProperties.presentation_layer_index == self.index:
|
2020-11-13 14:45:08 +01:00
|
|
|
set_status = obj.hide_get()
|
|
|
|
|
obj.hide_set(not obj.hide_get())
|
2020-11-08 13:52:20 +01:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2020-09-02 21:51:58 +10:00
|
|
|
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"
|
2020-09-02 21:51:58 +10:00
|
|
|
|
|
|
|
|
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"}
|
2020-09-02 21:51:58 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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"
|
2020-09-02 21:51:58 +10:00
|
|
|
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"}
|
2020-09-05 12:58:53 +10:00
|
|
|
|
|
|
|
|
|
2020-09-07 17:47:19 +10:00
|
|
|
class RefreshDrawingList(bpy.types.Operator):
|
2020-11-01 20:08:48 +07:00
|
|
|
bl_idname = "bim.refresh_drawing_list"
|
|
|
|
|
bl_label = "Refresh Drawing List"
|
2020-09-07 17:47:19 +10:00
|
|
|
|
|
|
|
|
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:
|
2020-09-07 17:47:19 +10:00
|
|
|
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:
|
2020-09-07 17:47:19 +10:00
|
|
|
new = bpy.context.scene.DocProperties.drawings.add()
|
2020-11-01 20:08:48 +07:00
|
|
|
new.name = obj.name.split("/")[1]
|
2020-09-07 17:47:19 +10:00
|
|
|
new.camera = obj
|
2020-11-01 20:08:48 +07:00
|
|
|
return {"FINISHED"}
|
2020-09-17 17:28:17 +10:00
|
|
|
|
|
|
|
|
|
2020-11-27 22:07:32 +11:00
|
|
|
class BlenderClasher:
|
2020-11-27 18:18:23 +11:00
|
|
|
def process_clash_set(self):
|
|
|
|
|
import collision
|
2020-11-27 22:07:32 +11:00
|
|
|
|
2020-11-27 18:18:23 +11:00
|
|
|
a_cm = collision.CollisionManager()
|
|
|
|
|
b_cm = collision.CollisionManager()
|
|
|
|
|
self.add_to_cm(a_cm, bpy.context.scene.BIMProperties.blender_clash_set_a)
|
|
|
|
|
self.add_to_cm(b_cm, bpy.context.scene.BIMProperties.blender_clash_set_b)
|
|
|
|
|
results = a_cm.in_collision_other(b_cm, return_data=True)
|
|
|
|
|
if not results[0]:
|
|
|
|
|
print("No clashes")
|
|
|
|
|
return
|
|
|
|
|
for contact in results[1]:
|
|
|
|
|
if contact.raw.penetration_depth < 0.01:
|
|
|
|
|
continue
|
|
|
|
|
print("-----")
|
|
|
|
|
print(contact.names)
|
|
|
|
|
print(contact.raw.normal)
|
|
|
|
|
print(contact.raw.pos)
|
|
|
|
|
|
|
|
|
|
def add_to_cm(self, cm, object_names):
|
|
|
|
|
import numpy as np
|
|
|
|
|
import ifcclash
|
2020-11-27 22:07:32 +11:00
|
|
|
|
2020-11-27 18:18:23 +11:00
|
|
|
for object_name in object_names:
|
|
|
|
|
name = object_name.name
|
|
|
|
|
obj = bpy.data.objects[name]
|
|
|
|
|
triangulated_mesh = self.triangulate_mesh(obj)
|
|
|
|
|
mesh = ifcclash.Mesh()
|
2020-12-04 08:47:00 +01:00
|
|
|
mesh.vertices = np.array([tuple(obj.matrix_world @ v.co) for v in triangulated_mesh.vertices])
|
2020-11-27 18:18:23 +11:00
|
|
|
mesh.faces = np.array([tuple(p.vertices) for p in triangulated_mesh.polygons])
|
2020-12-04 08:47:00 +01:00
|
|
|
cm.add_object(name, mesh)
|
2020-11-27 18:18:23 +11:00
|
|
|
|
|
|
|
|
def triangulate_mesh(self, obj):
|
|
|
|
|
import bmesh
|
2020-11-27 22:07:32 +11:00
|
|
|
|
2020-11-27 18:18:23 +11:00
|
|
|
mesh = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
|
|
|
|
|
bm = bmesh.new()
|
|
|
|
|
bm.from_mesh(mesh)
|
|
|
|
|
bmesh.ops.triangulate(bm, faces=bm.faces)
|
|
|
|
|
bm.to_mesh(mesh)
|
|
|
|
|
bm.free()
|
|
|
|
|
del bm
|
|
|
|
|
return mesh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SetBlenderClashSetA(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.set_blender_clash_set_a"
|
|
|
|
|
bl_label = "Set Blender Clash Set A"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
while len(bpy.context.scene.BIMProperties.blender_clash_set_a) > 0:
|
|
|
|
|
bpy.context.scene.BIMProperties.blender_clash_set_a.remove(0)
|
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
|
new = bpy.context.scene.BIMProperties.blender_clash_set_a.add()
|
|
|
|
|
new.name = obj.name
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SetBlenderClashSetB(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.set_blender_clash_set_b"
|
|
|
|
|
bl_label = "Set Blender Clash Set B"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
while len(bpy.context.scene.BIMProperties.blender_clash_set_b) > 0:
|
|
|
|
|
bpy.context.scene.BIMProperties.blender_clash_set_b.remove(0)
|
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
|
new = bpy.context.scene.BIMProperties.blender_clash_set_b.add()
|
|
|
|
|
new.name = obj.name
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExecuteBlenderClash(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.execute_blender_clash"
|
|
|
|
|
bl_label = "Execute Blender Clash"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
blender_clasher = BlenderClasher()
|
|
|
|
|
blender_clasher.process_clash_set()
|
|
|
|
|
return {"FINISHED"}
|
2020-12-10 13:50:43 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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"}
|
2020-12-10 13:52:29 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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):
|
2021-01-08 13:49:31 +11:00
|
|
|
# bpy.context.active_object.active_material.BIMMaterialProperties.location = self.filepath
|
|
|
|
|
# coll_name = "MyCollection"
|
2020-12-10 13:52:29 +11:00
|
|
|
|
|
|
|
|
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"}
|
2020-12-21 18:26:39 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2021-01-08 13:49:31 +11:00
|
|
|
result = obj2.ray_cast(obj2.matrix_world.inverted() @ center, polygon.normal, distance=threshold)
|
2020-12-21 18:26:39 +11:00
|
|
|
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"
|
2021-01-08 13:49:31 +11:00
|
|
|
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'}
|