From 48cb59897553036e959bcbe6bb188e62f3324e61 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 11 Sep 2021 20:29:14 +1000 Subject: [PATCH] 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. --- src/blenderbim/blenderbim/bim/import_ifc.py | 25 +--- .../blenderbim/bim/module/project/__init__.py | 2 + .../blenderbim/bim/module/project/operator.py | 55 ++++++++ .../blenderbim/bim/module/project/prop.py | 10 ++ .../blenderbim/bim/module/project/ui.py | 15 ++- src/blenderbim/test/bim/bootstrap.py | 21 +++ .../test/bim/module/project/test_operator.py | 34 +++++ src/blenderbim/test/files/basic.ifc | 126 ++++++++++++++++++ 8 files changed, 263 insertions(+), 25 deletions(-) create mode 100644 src/blenderbim/test/files/basic.ifc diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index ba7a501e68..326a4fea29 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -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): diff --git a/src/blenderbim/blenderbim/bim/module/project/__init__.py b/src/blenderbim/blenderbim/bim/module/project/__init__.py index 331e27bbbe..31c0a48143 100644 --- a/src/blenderbim/blenderbim/bim/module/project/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/project/__init__.py @@ -21,6 +21,8 @@ from . import ui, prop, operator classes = ( operator.CreateProject, + operator.LoadProject, + operator.LoadProjectElements, operator.SelectLibraryFile, operator.ChangeLibraryElement, operator.RefreshLibrary, diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 46a18e69d5..4a8a122f94 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -16,8 +16,11 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +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"} diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py index 8038a0dcce..bb5545f6dc 100644 --- a/src/blenderbim/blenderbim/bim/module/project/prop.py +++ b/src/blenderbim/blenderbim/bim/module/project/prop.py @@ -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)) diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py index 30a89dc5f5..5764ef085a 100644 --- a/src/blenderbim/blenderbim/bim/module/project/ui.py +++ b/src/blenderbim/blenderbim/bim/module/project/ui.py @@ -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): diff --git a/src/blenderbim/test/bim/bootstrap.py b/src/blenderbim/test/bim/bootstrap.py index 902815b4f5..facd60a0b1 100644 --- a/src/blenderbim/test/bim/bootstrap.py +++ b/src/blenderbim/test/bim/bootstrap.py @@ -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, diff --git a/src/blenderbim/test/bim/module/project/test_operator.py b/src/blenderbim/test/bim/module/project/test_operator.py index 5cc7b0bae4..db7cdfa226 100644 --- a/src/blenderbim/test/bim/module/project/test_operator.py +++ b/src/blenderbim/test/bim/module/project/test_operator.py @@ -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" + """ diff --git a/src/blenderbim/test/files/basic.ifc b/src/blenderbim/test/files/basic.ifc new file mode 100644 index 0000000000..f239509bdf --- /dev/null +++ b/src/blenderbim/test/files/basic.ifc @@ -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;