You can now load a project from the project panel. See commit message.

This is in preparation for partial loading and unloading for scaling up to larger IFC scenes.

Similarly, there are bits in place for different collection modes instead of the hardcoded collection hierarchy which we currently have.
This commit is contained in:
Dion Moult
2021-09-11 20:29:14 +10:00
parent d350a17ce5
commit 48cb598975
8 changed files with 263 additions and 25 deletions
+2 -23
View File
@@ -200,10 +200,6 @@ class IfcImporter:
bpy.context.window_manager.progress_begin(0, 100)
self.progress = 0
self.profile_code("Starting import process")
self.load_diff()
self.profile_code("Load diff")
self.purge_diff()
self.profile_code("Purge diffs")
self.load_file()
self.profile_code("Loading file")
self.calculate_unit_scale()
@@ -953,15 +949,10 @@ class IfcImporter:
return
self.openings[element.GlobalId] = obj
def load_diff(self):
if not self.ifc_import_settings.diff_file:
return
with open(self.ifc_import_settings.diff_file, "r") as file:
self.diff = json.load(file)
def load_file(self):
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
if not bpy.context.scene.BIMProperties.ifc_file:
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
self.file = IfcStore.get_file()
def calculate_unit_scale(self):
@@ -1128,18 +1119,6 @@ class IfcImporter:
def get_name(self, element):
return "{}/{}".format(element.is_a(), element.Name)
def purge_diff(self):
if not self.diff:
return
objects_to_purge = []
for obj in bpy.data.objects:
if "GlobalId" not in obj.BIMObjectProperties.attributes:
continue
global_id = obj.BIMObjectProperties.attributes["GlobalId"].string_value
if global_id in self.diff["deleted"] or global_id in self.diff["changed"].keys():
objects_to_purge.append(obj)
bpy.ops.object.delete({"selected_objects": objects_to_purge})
def place_objects_in_spatial_tree(self):
for ifc_definition_id, obj in self.added_data.items():
if isinstance(obj, bpy.types.Object):
@@ -21,6 +21,8 @@ from . import ui, prop, operator
classes = (
operator.CreateProject,
operator.LoadProject,
operator.LoadProjectElements,
operator.SelectLibraryFile,
operator.ChangeLibraryElement,
operator.RefreshLibrary,
@@ -16,8 +16,11 @@
# 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 os
import bpy
import time
import logging
import tempfile
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.representation
@@ -501,3 +504,55 @@ class DisableEditingHeader(bpy.types.Operator):
def execute(self, context):
context.scene.BIMProjectProperties.is_editing = False
return {"FINISHED"}
class LoadProject(bpy.types.Operator):
bl_idname = "bim.load_project"
bl_label = "Load Project"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
def execute(self, context):
if os.path.exists(self.filepath) and "ifc" in os.path.splitext(self.filepath)[1]:
context.scene.BIMProperties.ifc_file = self.filepath
context.scene.BIMProjectProperties.is_loading = True
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class LoadProjectElements(bpy.types.Operator):
bl_idname = "bim.load_project_elements"
bl_label = "Load Project Elements"
bl_options = {"REGISTER", "UNDO"}
mode: bpy.props.EnumProperty(
items=[
("ALL", "All", ""),
("WHITELIST", "Whitelist", ""),
("BLACKLIST", "Blacklist", ""),
],
name="Mode",
)
def execute(self, context):
start = time.time()
logger = logging.getLogger("ImportIFC")
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log")
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK):
path_log = os.path.join(tempfile.mkdtemp(), "process.log")
logging.basicConfig(
filename=path_log,
filemode="a",
level=logging.DEBUG,
)
settings = import_ifc.IfcImportSettings.factory(context, context.scene.BIMProperties.ifc_file, logger)
settings.logger.info("Starting import")
ifc_importer = import_ifc.IfcImporter(settings)
ifc_importer.execute()
settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
print("Import finished in {:.2f} seconds".format(time.time() - start))
context.scene.BIMProjectProperties.is_loading = False
return {"FINISHED"}
@@ -41,6 +41,7 @@ class LibraryElement(PropertyGroup):
class BIMProjectProperties(PropertyGroup):
is_authoring: BoolProperty(name="Enable Authoring Mode", default=True)
is_editing: BoolProperty(name="Is Editing", default=False)
is_loading: BoolProperty(name="Is Loading", default=False)
mvd: StringProperty(name="MVD")
author_name: StringProperty(name="Author")
author_email: StringProperty(name="Author Email")
@@ -51,6 +52,15 @@ class BIMProjectProperties(PropertyGroup):
library_breadcrumb: CollectionProperty(name="Library Breadcrumb", type=StrProperty)
library_elements: CollectionProperty(name="Library Elements", type=LibraryElement)
active_library_element_index: IntProperty(name="Active Library Element Index")
collection_mode: bpy.props.EnumProperty(
items=[
("DECOMPOSITION", "Decomposition", "Collections represent aggregates and spatial containers"),
("SPATIAL_DECOMPOSITION", "Spatial Decomposition", "Collections represent spatial containers"),
("IFC_CLASS", "IFC Class", "Collections represention IFC class"),
],
name="Collection Mode",
)
def get_library_element_index(self, lib_element):
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
@@ -32,12 +32,22 @@ class BIM_PT_project(Panel):
self.layout.use_property_decorate = False
self.layout.use_property_split = True
props = context.scene.BIMProperties
pprops = context.scene.BIMProjectProperties
self.file = IfcStore.get_file()
if self.file or props.ifc_file:
if pprops.is_loading:
self.draw_load_ui(context)
elif self.file or props.ifc_file:
self.draw_project_ui(context)
else:
self.draw_create_project_ui(context)
def draw_load_ui(self, context):
pprops = context.scene.BIMProjectProperties
row = self.layout.row()
row.prop(pprops, "collection_mode")
row = self.layout.row()
row.operator("bim.load_project_elements").mode = "ALL"
def draw_project_ui(self, context):
props = context.scene.BIMProperties
pprops = context.scene.BIMProjectProperties
@@ -109,8 +119,9 @@ class BIM_PT_project(Panel):
row.prop(props, "area_unit", text="Area Unit")
row = self.layout.row()
row.prop(props, "volume_unit", text="Volume Unit")
row = self.layout.row()
row = self.layout.row(align=True)
row.operator("bim.create_project")
row.operator("bim.load_project")
class BIM_PT_project_library(Panel):
+21
View File
@@ -78,12 +78,32 @@ def additionally_the_object_name_is_selected(name):
def i_set_prop_to_value(prop, value):
try:
eval(f"bpy.context.{prop}")
except:
assert False, "Property does not exist"
try:
exec(f'bpy.context.{prop} = "{value}"')
except:
exec(f"bpy.context.{prop} = {value}")
def prop_is_value(prop, value):
is_value = False
try:
exec(f'assert bpy.context.{prop} == "{value}"')
is_value = True
except:
try:
exec(f"assert bpy.context.{prop} == {value}")
is_value = True
except:
pass
if not is_value:
actual_value = eval(f"bpy.context.{prop}")
assert False, f"Value is {actual_value}"
def i_enable_prop(prop):
exec(f"bpy.context.{prop} = True")
@@ -222,6 +242,7 @@ definitions = {
'the object "(.*)" is selected': the_object_name_is_selected,
'additionally the object "(.*)" is selected': additionally_the_object_name_is_selected,
'I set "(.*)" to "(.*)"': i_set_prop_to_value,
'"(.*)" is "(.*)"': prop_is_value,
'I enable "(.*)"': i_enable_prop,
'I press "(.*)"': i_press_operator,
'the object "(.*)" is an "(.*)"': the_object_name_is_an_ifc_class,
@@ -34,3 +34,37 @@ class TestCreateProject(test.bim.bootstrap.NewFile):
And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building"
And the object "IfcBuildingStorey/My Storey" is in the collection "IfcBuildingStorey/My Storey"
"""
class TestLoadProject(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_loading_a_project(self):
return """
When I press "bim.load_project(filepath='{cwd}/test/files/basic.ifc')"
Then an IFC file exists
And "scene.BIMProjectProperties.is_loading" is "True"
"""
class TestLoadProjectElements(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_loading_all_project_elements(self):
return """
Given I press "bim.load_project(filepath='{cwd}/test/files/basic.ifc')"
When I set "scene.BIMProjectProperties.collection_mode" to "DECOMPOSITION"
And I press "bim.load_project_elements(mode='ALL')"
Then the object "IfcProject/My Project" is an "IfcProject"
And the object "IfcSite/My Site" is an "IfcSite"
And the object "IfcBuilding/My Building" is an "IfcBuilding"
And the object "IfcBuildingStorey/Ground Floor" is an "IfcBuildingStorey"
And the object "IfcBuildingStorey/Level 1" is an "IfcBuildingStorey"
And the object "IfcSlab/Slab" is an "IfcSlab"
And the object "IfcWall/Wall" is an "IfcWall"
And the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building"
And the object "IfcBuildingStorey/Ground Floor" is in the collection "IfcBuildingStorey/Ground Floor"
And the object "IfcBuildingStorey/Level 1" is in the collection "IfcBuildingStorey/Level 1"
And the object "IfcSlab/Slab" is in the collection "IfcBuildingStorey/Ground Floor"
And the object "IfcWall/Wall" is in the collection "IfcBuildingStorey/Level 1"
And "scene.BIMProjectProperties.is_loading" is "False"
"""
+126
View File
@@ -0,0 +1,126 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('basic.ifc','2021-09-11T19:44:13+10:00',(),(),'IfcOpenShell 0.6.0b0','BlenderBIM 0.0.999999','Nobody');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPERSON('HSeldon','Seldon','Hari',$,$,$,$,$);
#2=IFCORGANIZATION('APTR','Aperture Science',$,$,$);
#3=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$);
#4=IFCTELECOMADDRESS(.USERDEFINED.,'The main webpage of the software collection.','WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$);
#5=IFCTELECOMADDRESS(.USERDEFINED.,'The BlenderBIM Add-on webpage of the software collection.','WEBPAGE',$,$,$,$,'https://blenderbim.org',$);
#6=IFCTELECOMADDRESS(.USERDEFINED.,'The source code repository of the software collection.','REPOSITORY',$,$,$,$,'https://github.com/IfcOpenShell/IfcOpenShell.git',$);
#7=IFCORGANIZATION($,'IfcOpenShell','IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.',(#3),(#4,#5,#6));
#8=IFCAPPLICATION(#7,'0.0.999999','BlenderBIM Add-on','BlenderBIM');
#9=IFCPERSONANDORGANIZATION(#1,#2,$);
#10=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353388,#9,#8,1631353388);
#11=IFCPROJECT('2xwg5dkcT4T8MlEIX1jLjD',#10,'My Project',$,$,$,$,(#20,#27),#15);
#12=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#14=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#15=IFCUNITASSIGNMENT((#14,#12,#13));
#16=IFCCARTESIANPOINT((0.,0.,0.));
#17=IFCDIRECTION((0.,0.,1.));
#18=IFCDIRECTION((1.,0.,0.));
#19=IFCAXIS2PLACEMENT3D(#16,#17,#18);
#20=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#19,$);
#21=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#20,$,.MODEL_VIEW.,$);
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#20,$,.MODEL_VIEW.,$);
#23=IFCCARTESIANPOINT((0.,0.,0.));
#24=IFCDIRECTION((0.,0.,1.));
#25=IFCDIRECTION((1.,0.,0.));
#26=IFCAXIS2PLACEMENT3D(#23,#24,#25);
#27=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#26,$);
#28=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#27,$,.PLAN_VIEW.,$);
#29=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353388,#9,#8,1631353388);
#30=IFCSITE('3cz3qtLYbCURHSIUoySdUS',#29,'My Site',$,$,#56,$,$,$,$,$,$,$,$);
#36=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353388,#9,#8,1631353388);
#37=IFCBUILDING('2dErbdcOP6Nh7ym5xn_$Ec',#36,'My Building',$,$,#63,$,$,$,$,$,$);
#43=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353388,#9,#8,1631353388);
#44=IFCBUILDINGSTOREY('0MbU9rGEH1LAWUFmJ3gCcg',#43,'Ground Floor',$,$,#70,$,$,$,$);
#50=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353388,#9,#8,1631353388);
#51=IFCRELAGGREGATES('1_TFH$83TDGOqH3GdA9HdZ',#50,$,$,#11,(#30));
#52=IFCCARTESIANPOINT((0.,0.,0.));
#53=IFCDIRECTION((0.,0.,1.));
#54=IFCDIRECTION((1.,0.,0.));
#55=IFCAXIS2PLACEMENT3D(#52,#53,#54);
#56=IFCLOCALPLACEMENT($,#55);
#57=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353388,#9,#8,1631353388);
#58=IFCRELAGGREGATES('0rW4qoplT3zBMjr14wc1ok',#57,$,$,#30,(#37));
#59=IFCCARTESIANPOINT((0.,0.,0.));
#60=IFCDIRECTION((0.,0.,1.));
#61=IFCDIRECTION((1.,0.,0.));
#62=IFCAXIS2PLACEMENT3D(#59,#60,#61);
#63=IFCLOCALPLACEMENT(#56,#62);
#64=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353388,#9,#8,1631353388);
#65=IFCRELAGGREGATES('041C6Y06v8$AmAQhXWgREJ',#64,$,$,#37,(#44,#71));
#66=IFCCARTESIANPOINT((0.,0.,0.));
#67=IFCDIRECTION((0.,0.,1.));
#68=IFCDIRECTION((1.,0.,0.));
#69=IFCAXIS2PLACEMENT3D(#66,#67,#68);
#70=IFCLOCALPLACEMENT(#63,#69);
#71=IFCBUILDINGSTOREY('1Pkxs$2EDD3ApZ07AQn5om',#77,'Level 1',$,$,#152,$,$,$,$);
#72=IFCCARTESIANPOINT((0.,0.,0.));
#73=IFCDIRECTION((0.,0.,1.));
#74=IFCDIRECTION((1.,0.,0.));
#75=IFCAXIS2PLACEMENT3D(#72,#73,#74);
#76=IFCLOCALPLACEMENT(#63,#75);
#77=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353453,#9,#8,1631353388);
#78=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353439,#9,#8,1631353439);
#79=IFCSLAB('20Njb8mHv8v9ESSk_1YM2m',#78,'Slab',$,$,#114,#97,$,.BASESLAB.);
#85=IFCINDEXEDPOLYGONALFACE((1,5,7,3));
#86=IFCINDEXEDPOLYGONALFACE((4,3,7,8));
#87=IFCINDEXEDPOLYGONALFACE((8,7,5,6));
#88=IFCINDEXEDPOLYGONALFACE((6,2,4,8));
#89=IFCINDEXEDPOLYGONALFACE((2,1,3,4));
#90=IFCINDEXEDPOLYGONALFACE((6,5,1,2));
#91=IFCCARTESIANPOINTLIST3D(((1.,1.,1.),(1.,1.,-1.),(1.,-1.,1.),(1.,-1.,-1.),(-1.,1.,1.),(-1.,1.,-1.),(-1.,-1.,1.),(-1.,-1.,-1.)));
#92=IFCPOLYGONALFACESET(#91,$,(#85,#86,#87,#88,#89,#90),$);
#93=IFCSHAPEREPRESENTATION(#21,'Body','Tessellation',(#92));
#94=IFCCARTESIANPOINT((-1.,-1.,-1.));
#95=IFCBOUNDINGBOX(#94,2.,2.,2.);
#96=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#95));
#97=IFCPRODUCTDEFINITIONSHAPE($,$,(#96,#93));
#98=IFCCOLOURRGB($,0.800000011920929,0.800000011920929,0.800000011920929);
#99=IFCCOLOURRGB($,0.800000011920929,0.800000011920929,0.800000011920929);
#100=IFCSURFACESTYLERENDERING(#98,0.,#99,$,$,$,$,$,.NOTDEFINED.);
#101=IFCSURFACESTYLE('Material',.BOTH.,(#100));
#102=IFCSTYLEDITEM(#92,(#101),'Material');
#103=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353439,#9,#8,1631353439);
#104=IFCRELCONTAINEDINSPATIALSTRUCTURE('2T4j5EAcn0qQ9vTOZFK9HO',#103,$,$,(#79),#44);
#110=IFCCARTESIANPOINT((0.,0.,0.));
#111=IFCDIRECTION((0.,0.,1.));
#112=IFCDIRECTION((1.,0.,0.));
#113=IFCAXIS2PLACEMENT3D(#110,#111,#112);
#114=IFCLOCALPLACEMENT(#70,#113);
#115=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631353453,#9,#8,1631353443);
#116=IFCWALL('27uW0y7Gr72R8yo2C4vBlV',#115,'Wall',$,$,#157,#134,$,.ELEMENTEDWALL.);
#122=IFCINDEXEDPOLYGONALFACE((1,5,7,3));
#123=IFCINDEXEDPOLYGONALFACE((4,3,7,8));
#124=IFCINDEXEDPOLYGONALFACE((8,7,5,6));
#125=IFCINDEXEDPOLYGONALFACE((6,2,4,8));
#126=IFCINDEXEDPOLYGONALFACE((2,1,3,4));
#127=IFCINDEXEDPOLYGONALFACE((6,5,1,2));
#128=IFCCARTESIANPOINTLIST3D(((1.,1.,1.),(1.,1.,-1.),(1.,-1.,1.),(1.,-1.,-1.),(-1.,1.,1.),(-1.,1.,-1.),(-1.,-1.,1.),(-1.,-1.,-1.)));
#129=IFCPOLYGONALFACESET(#128,$,(#122,#123,#124,#125,#126,#127),$);
#130=IFCSHAPEREPRESENTATION(#21,'Body','Tessellation',(#129));
#131=IFCCARTESIANPOINT((-1.,-1.,-1.));
#132=IFCBOUNDINGBOX(#131,2.,2.,2.);
#133=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#132));
#134=IFCPRODUCTDEFINITIONSHAPE($,$,(#133,#130));
#135=IFCSTYLEDITEM(#129,(#101),'Material');
#136=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353443,#9,#8,1631353443);
#137=IFCRELCONTAINEDINSPATIALSTRUCTURE('1CgivZt6z1l8IgKaTkZMcT',#136,$,$,(#116),#71);
#148=IFCCARTESIANPOINT((0.,0.,3.));
#149=IFCDIRECTION((0.,0.,1.));
#150=IFCDIRECTION((1.,0.,0.));
#151=IFCAXIS2PLACEMENT3D(#148,#149,#150);
#152=IFCLOCALPLACEMENT(#63,#151);
#153=IFCCARTESIANPOINT((0.,0.,-3.));
#154=IFCDIRECTION((0.,0.,1.));
#155=IFCDIRECTION((1.,0.,0.));
#156=IFCAXIS2PLACEMENT3D(#153,#154,#155);
#157=IFCLOCALPLACEMENT(#152,#156);
ENDSEC;
END-ISO-10303-21;