mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Drawings now load on-demand instead of at project load. See #2520.
This commit is contained in:
@@ -236,8 +236,6 @@ class IfcImporter:
|
||||
self.profile_code("Create materials")
|
||||
self.create_styles()
|
||||
self.profile_code("Create styles")
|
||||
self.create_annotations()
|
||||
self.profile_code("Create annotation")
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_native_elements()
|
||||
@@ -586,7 +584,7 @@ class IfcImporter:
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.lock_grids_on_import:
|
||||
grid_obj.lock_location = (True, True, True)
|
||||
grid_obj.lock_rotation = (True, True, True)
|
||||
collection = bpy.data.collections.new(self.get_name(grid))
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(grid))
|
||||
u_axes = bpy.data.collections.new("UAxes")
|
||||
collection.children.link(u_axes)
|
||||
v_axes = bpy.data.collections.new("VAxes")
|
||||
@@ -628,11 +626,11 @@ class IfcImporter:
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, representation_map.MappedRepresentation)
|
||||
mesh = self.create_mesh(element, shape)
|
||||
self.link_mesh(shape, mesh)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(element), mesh)
|
||||
self.link_element(element, obj)
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
self.type_products[element.GlobalId] = obj
|
||||
@@ -687,9 +685,6 @@ class IfcImporter:
|
||||
def create_spatial_elements(self):
|
||||
self.create_generic_elements(self.spatial_elements)
|
||||
|
||||
def create_annotations(self):
|
||||
self.create_generic_elements(self.annotations)
|
||||
|
||||
def create_elements(self):
|
||||
self.create_generic_elements(self.elements)
|
||||
|
||||
@@ -923,21 +918,21 @@ class IfcImporter:
|
||||
pass
|
||||
elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
|
||||
mesh = self.create_camera(element, shape)
|
||||
self.link_mesh(shape, mesh)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
elif element.is_a("IfcAnnotation") and self.is_curve_annotation(element) and shape:
|
||||
mesh = self.create_curve(element, shape)
|
||||
self.link_mesh(shape, mesh)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
elif shape:
|
||||
mesh_name = self.get_mesh_name(shape.geometry)
|
||||
mesh_name = tool.Loader.get_mesh_name(shape.geometry)
|
||||
mesh = self.meshes.get(mesh_name)
|
||||
if mesh is None:
|
||||
mesh = self.create_mesh(element, shape)
|
||||
self.link_mesh(shape, mesh)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
else:
|
||||
mesh = None
|
||||
|
||||
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(element), mesh)
|
||||
self.link_element(element, obj)
|
||||
|
||||
if shape:
|
||||
@@ -1385,12 +1380,6 @@ class IfcImporter:
|
||||
if not view_collection:
|
||||
view_collection = bpy.data.collections.new("Views")
|
||||
self.project["blender"].children.link(view_collection)
|
||||
for element in self.file.by_type("IfcAnnotation"):
|
||||
if element.ObjectType == "DRAWING":
|
||||
group = [r for r in element.HasAssignments if r.is_a("IfcRelAssignsToGroup")][0].RelatingGroup
|
||||
collection = bpy.data.collections.new("IfcGroup/" + group.Name)
|
||||
self.collections[group.GlobalId] = collection
|
||||
view_collection.children.link(collection)
|
||||
|
||||
def create_spatial_decomposition_collection(self, parent, related_objects):
|
||||
for element in related_objects:
|
||||
@@ -1404,7 +1393,7 @@ class IfcImporter:
|
||||
collection = obj.users_collection[0]
|
||||
self.collections[element.GlobalId] = collection
|
||||
if not is_existing:
|
||||
collection = bpy.data.collections.new(self.get_name(element))
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(element))
|
||||
self.collections[element.GlobalId] = collection
|
||||
parent.children.link(collection)
|
||||
if element.IsDecomposedBy:
|
||||
@@ -1432,7 +1421,7 @@ class IfcImporter:
|
||||
aggregates = {}
|
||||
for rel_aggregate in rel_aggregates:
|
||||
element = rel_aggregate.RelatingObject
|
||||
collection = bpy.data.collections.new(self.get_name(element))
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(element))
|
||||
aggregates[element.GlobalId] = {"element": element, "collection": collection}
|
||||
self.collections[element.GlobalId] = collection
|
||||
|
||||
@@ -1663,9 +1652,6 @@ class IfcImporter:
|
||||
else:
|
||||
blender_material.node_tree.links.new(coord.outputs["UV"], node.inputs["Vector"])
|
||||
|
||||
def get_name(self, element):
|
||||
return "{}/{}".format(element.is_a(), element.Name)
|
||||
|
||||
def place_objects_in_collections(self):
|
||||
for ifc_definition_id, obj in self.added_data.items():
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
@@ -1813,16 +1799,6 @@ class IfcImporter:
|
||||
):
|
||||
return representation.Items[0].MappingTarget
|
||||
|
||||
def get_mesh_name(self, geometry):
|
||||
representation_id = geometry.id
|
||||
if "-" in representation_id:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||
else:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||
representation = self.file.by_id(representation_id)
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
return "{}/{}".format(context_id, representation_id)
|
||||
|
||||
def create_camera(self, element, shape):
|
||||
if hasattr(shape, "geometry"):
|
||||
geometry = shape.geometry
|
||||
@@ -1837,7 +1813,7 @@ class IfcImporter:
|
||||
height = max(y) - min(y)
|
||||
depth = max(z) - min(z)
|
||||
|
||||
camera = bpy.data.cameras.new(self.get_mesh_name(geometry))
|
||||
camera = bpy.data.cameras.new(tool.Loader.get_mesh_name(geometry))
|
||||
camera.type = "ORTHO"
|
||||
camera.ortho_scale = width if width > height else height
|
||||
camera.clip_end = depth
|
||||
@@ -1877,7 +1853,7 @@ class IfcImporter:
|
||||
else:
|
||||
geometry = shape
|
||||
|
||||
curve = bpy.data.curves.new(self.get_mesh_name(geometry), type="CURVE")
|
||||
curve = bpy.data.curves.new(tool.Loader.get_mesh_name(geometry), type="CURVE")
|
||||
curve.dimensions = "3D"
|
||||
curve.resolution_u = 2
|
||||
|
||||
@@ -1903,7 +1879,7 @@ class IfcImporter:
|
||||
else:
|
||||
geometry = shape
|
||||
|
||||
mesh = bpy.data.meshes.new(self.get_mesh_name(geometry))
|
||||
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry))
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if (
|
||||
@@ -2018,17 +1994,6 @@ class IfcImporter:
|
||||
self.added_data[element.id()] = obj
|
||||
IfcStore.link_element(element, obj)
|
||||
|
||||
def link_mesh(self, shape, mesh):
|
||||
if hasattr(shape, "geometry"):
|
||||
geometry = shape.geometry
|
||||
else:
|
||||
geometry = shape
|
||||
if "-" in geometry.id:
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0])
|
||||
else:
|
||||
# TODO: See #2002
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.replace(",", ""))
|
||||
|
||||
def set_matrix_world(self, obj, matrix_world):
|
||||
obj.matrix_world = matrix_world
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
@@ -154,16 +154,13 @@ class RemoveRepresentation(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class UpdateRepresentation(bpy.types.Operator):
|
||||
class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.update_representation"
|
||||
bl_label = "Update Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
ifc_representation_class: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
if context.active_object and context.active_object.mode != "OBJECT":
|
||||
# Ensure mode is object to prevent invalid mesh data causing CTD
|
||||
|
||||
@@ -197,7 +197,9 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
|
||||
group = drawing_tool.get_drawing_group(drawing)
|
||||
if drawing_tool.get_name(group) != name:
|
||||
ifc.run("attribute.edit_attributes", product=group, attributes={"Name": name})
|
||||
drawing_tool.set_drawing_collection_name(group, drawing_tool.get_drawing_collection(drawing))
|
||||
collection = drawing_tool.get_drawing_collection(drawing)
|
||||
if collection:
|
||||
drawing_tool.set_drawing_collection_name(group, collection)
|
||||
|
||||
|
||||
def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None):
|
||||
@@ -242,14 +244,13 @@ def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
should_delete_existing_annotation = False
|
||||
should_create_annotation = False
|
||||
|
||||
if annotation and (
|
||||
not reference_obj
|
||||
or ifc.is_moved(reference_obj)
|
||||
or ifc.is_edited(reference_obj)
|
||||
or ifc.is_deleted(reference_element)
|
||||
or ifc.is_deleted(annotation)
|
||||
):
|
||||
should_delete_existing_annotation = True
|
||||
if annotation:
|
||||
if ifc.is_deleted(annotation):
|
||||
should_delete_existing_annotation = True
|
||||
elif reference_obj and (ifc.is_moved(reference_obj) or ifc.is_edited(reference_obj)):
|
||||
should_delete_existing_annotation = True
|
||||
elif not reference_obj and ifc.is_deleted(reference_element):
|
||||
should_delete_existing_annotation = True
|
||||
|
||||
if reference_obj and (should_delete_existing_annotation or not annotation):
|
||||
should_create_annotation = True
|
||||
@@ -279,5 +280,6 @@ def select_assigned_product(drawing):
|
||||
def activate_drawing_view(ifc, drawing_tool, drawing):
|
||||
camera = ifc.get_object(drawing)
|
||||
if not camera:
|
||||
return
|
||||
camera = drawing_tool.import_drawing(drawing)
|
||||
drawing_tool.import_annotations_in_group(drawing_tool.get_drawing_group(drawing))
|
||||
drawing_tool.activate_view(camera)
|
||||
|
||||
@@ -308,6 +308,11 @@ class Library:
|
||||
def set_editing_mode(cls, mode): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Loader:
|
||||
pass
|
||||
|
||||
|
||||
@interface
|
||||
class Material:
|
||||
def add_default_material_object(cls): pass
|
||||
|
||||
@@ -31,6 +31,7 @@ from blenderbim.tool.geometry import Geometry
|
||||
from blenderbim.tool.georeference import Georeference
|
||||
from blenderbim.tool.ifc import Ifc
|
||||
from blenderbim.tool.library import Library
|
||||
from blenderbim.tool.loader import Loader
|
||||
from blenderbim.tool.material import Material
|
||||
from blenderbim.tool.misc import Misc
|
||||
from blenderbim.tool.model import Model
|
||||
|
||||
@@ -21,6 +21,7 @@ import re
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import logging
|
||||
import mathutils
|
||||
import webbrowser
|
||||
import subprocess
|
||||
@@ -33,6 +34,7 @@ import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
from blenderbim.bim.module.drawing.prop import get_diagram_scales
|
||||
|
||||
|
||||
class Drawing(blenderbim.core.tool.Drawing):
|
||||
@@ -204,7 +206,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
@classmethod
|
||||
def get_drawing_collection(cls, drawing):
|
||||
obj = tool.Ifc.get_object(drawing)
|
||||
return obj.users_collection[0]
|
||||
if obj:
|
||||
return obj.users_collection[0]
|
||||
|
||||
@classmethod
|
||||
def get_drawing_group(cls, drawing):
|
||||
@@ -296,6 +299,83 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
if rel.is_a("IfcRelAssignsToProduct"):
|
||||
return rel.RelatingProduct
|
||||
|
||||
@classmethod
|
||||
def import_annotations_in_group(cls, group):
|
||||
elements = set(
|
||||
[e for e in cls.get_group_elements(group) if e.is_a("IfcAnnotation") and e.ObjectType != "DRAWING"]
|
||||
)
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(ifc_import_settings)
|
||||
ifc_importer.file = tool.Ifc.get()
|
||||
ifc_importer.calculate_unit_scale()
|
||||
ifc_importer.create_generic_elements(elements)
|
||||
for obj in ifc_importer.added_data.values():
|
||||
tool.Collector.assign(obj)
|
||||
|
||||
@classmethod
|
||||
def import_drawing(cls, drawing):
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, drawing)
|
||||
geometry = shape.geometry
|
||||
|
||||
v = geometry.verts
|
||||
x = [v[i] for i in range(0, len(v), 3)]
|
||||
y = [v[i + 1] for i in range(0, len(v), 3)]
|
||||
z = [v[i + 2] for i in range(0, len(v), 3)]
|
||||
width = max(x) - min(x)
|
||||
height = max(y) - min(y)
|
||||
depth = max(z) - min(z)
|
||||
|
||||
camera = bpy.data.cameras.new(tool.Loader.get_mesh_name(geometry))
|
||||
camera.type = "ORTHO"
|
||||
camera.ortho_scale = width if width > height else height
|
||||
camera.clip_end = depth
|
||||
|
||||
if width > height:
|
||||
camera.BIMCameraProperties.raster_x = 1000
|
||||
camera.BIMCameraProperties.raster_y = round(1000 * (height / width))
|
||||
else:
|
||||
camera.BIMCameraProperties.raster_x = round(1000 * (width / height))
|
||||
camera.BIMCameraProperties.raster_y = 1000
|
||||
|
||||
psets = ifcopenshell.util.element.get_psets(drawing)
|
||||
pset = psets.get("EPset_Drawing")
|
||||
if pset:
|
||||
if "TargetView" in pset:
|
||||
camera.BIMCameraProperties.target_view = pset["TargetView"]
|
||||
if "Scale" in pset:
|
||||
valid_scales = [
|
||||
i[0] for i in get_diagram_scales(None, bpy.context) if pset["Scale"] == i[0].split("|")[-1]
|
||||
]
|
||||
if valid_scales:
|
||||
camera.BIMCameraProperties.diagram_scale = valid_scales[0]
|
||||
else:
|
||||
camera.BIMCameraProperties.diagram_scale = "CUSTOM"
|
||||
camera.BIMCameraProperties.custom_diagram_scale = pset["Scale"]
|
||||
if "HasUnderlay" in pset:
|
||||
camera.BIMCameraProperties.has_underlay = pset["HasUnderlay"]
|
||||
if "HasLinework" in pset:
|
||||
camera.BIMCameraProperties.has_linework = pset["HasLinework"]
|
||||
if "HasAnnotation" in pset:
|
||||
camera.BIMCameraProperties.has_annotation = pset["HasAnnotation"]
|
||||
|
||||
tool.Loader.link_mesh(shape, camera)
|
||||
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(drawing), camera)
|
||||
tool.Ifc.link(drawing, obj)
|
||||
|
||||
m = shape.transformation.matrix.data
|
||||
mat = mathutils.Matrix(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
obj.matrix_world = mat
|
||||
tool.Geometry.record_object_position(obj)
|
||||
tool.Collector.assign(obj)
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_drawings(cls):
|
||||
bpy.context.scene.DocProperties.drawings.clear()
|
||||
@@ -923,9 +1003,12 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
@classmethod
|
||||
def is_drawing_active(cls):
|
||||
camera = bpy.context.scene.camera
|
||||
return (camera and camera.type == "CAMERA" and
|
||||
camera.BIMObjectProperties.ifc_definition_id and
|
||||
any(a.type == 'VIEW_3D' for a in bpy.context.screen.areas))
|
||||
return (
|
||||
camera
|
||||
and camera.type == "CAMERA"
|
||||
and camera.BIMObjectProperties.ifc_definition_id
|
||||
and any(a.type == "VIEW_3D" for a in bpy.context.screen.areas)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_camera_orthographic(cls):
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
# Progressively we'll refactor loading elements into Blender objects into this
|
||||
# class. This will break down the monolithic import_ifc module and allow us to
|
||||
# partially load and unload objects for huge models, partial model editing, and
|
||||
# supplementary objects (e.g. drawings, structural analysis models, etc).
|
||||
|
||||
class Loader(blenderbim.core.tool.Loader):
|
||||
@classmethod
|
||||
def get_mesh_name(cls, geometry):
|
||||
representation_id = geometry.id
|
||||
if "-" in representation_id:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||
else:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||
representation = tool.Ifc.get().by_id(representation_id)
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
return "{}/{}".format(context_id, representation_id)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, element):
|
||||
return "{}/{}".format(element.is_a(), element.Name)
|
||||
|
||||
@classmethod
|
||||
def link_mesh(cls, shape, mesh):
|
||||
geometry = shape.geometry if hasattr(shape, "geometry") else shape
|
||||
if "-" in geometry.id:
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0])
|
||||
else:
|
||||
# TODO: See #2002
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.replace(",", ""))
|
||||
Reference in New Issue
Block a user