From d654110fc1959a3d5862b2a3bca026f7cbb2a3a1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 1 Aug 2022 11:23:35 +1000 Subject: [PATCH 1/4] Support removing representations from IFC models that do not correctly follow the convention that mapped representations come from types --- src/blenderbim/blenderbim/core/geometry.py | 4 ++-- src/blenderbim/test/core/test_geometry.py | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/blenderbim/blenderbim/core/geometry.py b/src/blenderbim/blenderbim/core/geometry.py index 65742a684e..e4ac978016 100644 --- a/src/blenderbim/blenderbim/core/geometry.py +++ b/src/blenderbim/blenderbim/core/geometry.py @@ -120,8 +120,8 @@ def get_representation_ifc_parameters(geometry, obj=None, should_sync_changes_fi def remove_representation(ifc, geometry, obj=None, representation=None): element = ifc.get_entity(obj) - if geometry.is_mapped_representation(representation) or geometry.is_type_product(element): - type = geometry.get_element_type(element) + type = geometry.get_element_type(element) + if type and (geometry.is_mapped_representation(representation) or geometry.is_type_product(element)): representation = geometry.resolve_mapped_representation(representation) data = geometry.get_representation_data(representation) if data and geometry.has_data_users(data): diff --git a/src/blenderbim/test/core/test_geometry.py b/src/blenderbim/test/core/test_geometry.py index a22267cf8f..4eee95cc11 100644 --- a/src/blenderbim/test/core/test_geometry.py +++ b/src/blenderbim/test/core/test_geometry.py @@ -320,9 +320,9 @@ class TestGetRepresentationIfcParameters: class TestRemoveRepresentation: def test_removing_an_actively_used_mapped_representation_by_remapping_usages_to_an_empty(self, ifc, geometry): ifc.get_entity("obj").should_be_called().will_return("element") + geometry.get_element_type("element").should_be_called().will_return("type") geometry.is_mapped_representation("mapped_rep").should_be_called().will_return(False) geometry.is_type_product("element").should_be_called().will_return(True) - geometry.get_element_type("element").should_be_called().will_return("type") geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") geometry.get_representation_data("representation").should_be_called().will_return("data") geometry.has_data_users("data").should_be_called().will_return(True) @@ -337,16 +337,29 @@ class TestRemoveRepresentation: def test_removing_an_unused_mapped_representation(self, ifc, geometry): ifc.get_entity("obj").should_be_called().will_return("element") - geometry.is_mapped_representation("mapped_rep").should_be_called().will_return(True) geometry.get_element_type("element").should_be_called().will_return("type") + geometry.is_mapped_representation("mapped_rep").should_be_called().will_return(True) geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") geometry.get_representation_data("representation").should_be_called().will_return(None) ifc.run("geometry.unassign_representation", product="type", representation="representation").should_be_called() ifc.run("geometry.remove_representation", representation="representation").should_be_called() subject.remove_representation(ifc, geometry, obj="obj", representation="mapped_rep") + def test_remove_a_mapped_representation_by_an_element_with_no_type(self, ifc, geometry): + ifc.get_entity("obj").should_be_called().will_return("element") + geometry.get_element_type("element").should_be_called().will_return(None) + geometry.get_representation_data("representation").should_be_called().will_return("data") + geometry.has_data_users("data").should_be_called().will_return(True) + geometry.replace_object_with_empty("obj").should_be_called() + ifc.run( + "geometry.unassign_representation", product="element", representation="representation" + ).should_be_called() + ifc.run("geometry.remove_representation", representation="representation").should_be_called() + subject.remove_representation(ifc, geometry, obj="obj", representation="representation") + def test_removing_an_actively_used_representation(self, ifc, geometry): ifc.get_entity("obj").should_be_called().will_return("element") + geometry.get_element_type("element").should_be_called().will_return("type") geometry.is_mapped_representation("representation").should_be_called().will_return(False) geometry.is_type_product("element").should_be_called().will_return(False) geometry.get_representation_data("representation").should_be_called().will_return("data") @@ -360,6 +373,7 @@ class TestRemoveRepresentation: def test_removing_an_unused_representation(self, ifc, geometry): ifc.get_entity("obj").should_be_called().will_return("element") + geometry.get_element_type("element").should_be_called().will_return("type") geometry.is_mapped_representation("representation").should_be_called().will_return(False) geometry.is_type_product("element").should_be_called().will_return(False) geometry.get_representation_data("representation").should_be_called().will_return(None) From 5b5a4649c5bedeb00c6ae4c026528ad7fe7c61b1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 1 Aug 2022 13:05:39 +1000 Subject: [PATCH 2/4] Fix #2310. Manually patch applicable class errors in official IFC pset templates. --- .../ifcopenshell/util/schema/Pset_IFC4_ADD2.ifc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/util/schema/Pset_IFC4_ADD2.ifc b/src/ifcopenshell-python/ifcopenshell/util/schema/Pset_IFC4_ADD2.ifc index 370ce2ec16..75ef40f33d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/schema/Pset_IFC4_ADD2.ifc +++ b/src/ifcopenshell-python/ifcopenshell/util/schema/Pset_IFC4_ADD2.ifc @@ -7,7 +7,7 @@ ENDSEC; DATA; #1= IFCPROJECT('3QGWbhaEj3pBvtVSPml_3_',$,'IFC4 Property Set Templates',$,$,$,$,$,$); -#2= IFCRELDECLARES('1zDnNP0LPDzu65koq6JWBn',$,$,$,#1,(#3,#31,#61,#77,#119,#156,#313,#358,#451,#516,#541,#559,#577,#613,#840,#982,#1016,#1081,#1154,#1221,#1338,#1413,#1444,#1516,#1551,#1750,#1753,#1826,#1865,#1920,#1974,#2030,#2072,#2087,#2108,#2206,#2327,#2413,#2486,#2502,#2599,#2709,#2741,#2876,#2899,#3007,#3182,#3400,#3521,#3620,#3697,#3820,#3908,#3995,#4132,#4269,#4452,#4594,#4727,#4884,#4952,#4955,#4969,#5037,#5065,#5107,#5142,#5192,#5237,#5251,#5286,#5306,#5379,#5399,#5483,#5518,#5687,#5719,#5759,#5855,#5881,#5977,#6031,#6057,#6083,#6137,#6156,#6201,#6225,#6245,#6362,#6430,#6515,#6527,#6602,#6697,#6790,#6827,#6839,#6929,#7032,#7135,#7189,#7236,#7264,#7279,#7326,#7359,#7406,#7439,#7472,#7519,#7545,#7592,#7652,#7703,#7732,#7759,#7762,#7815,#7835,#7847,#7885,#7891,#7897,#7906,#7937,#7974,#8026,#8169,#8178,#8202,#8220,#8255,#8296,#8323,#8343,#8379,#8394,#8409,#8477,#8487,#8496,#8511,#8537,#8614,#8644,#8663,#8682,#8701,#8720,#8738,#8775,#8795,#8827,#8916,#8944,#9017,#9072,#9122,#9134,#9166,#9198,#9230,#9245,#9251,#9283,#9295,#9307,#9333,#9345,#9351,#9371,#9397,#9409,#9415,#9434,#9446,#9458,#9470,#9490,#9502,#9521,#9533,#9545,#9571,#9583,#9619,#9659,#9674,#9709,#9734,#9740,#9746,#9752,#9758,#9764,#9770,#9776,#9819,#9828,#9843,#9852,#9871,#9908,#9996,#10040,#10173,#10210,#10264,#10301,#10373,#10417,#10449,#10475,#10501,#10527,#10559,#10603,#10635,#10706,#10718,#10828,#10860,#10954,#11080,#11086,#11118,#11183,#11189,#11221,#11250,#11281,#11290,#11322,#11324,#11491,#11544,#11661,#11693,#11813,#11932,#12046,#12160,#12192,#12234,#12279,#12324,#12369,#12440,#12506,#12570,#12600,#12724,#12823,#12891,#13011,#13056,#13120,#13187,#13279,#13343,#13374,#13443,#13509,#13541,#13571,#13640,#13652,#13664,#13687,#13721,#13808,#13841,#13866,#13887,#13910,#13931,#13937,#14014,#14047,#14092,#14181,#14363,#14369,#14375,#14390,#14396,#14411,#14417,#14423,#14432,#14438,#14444,#14450,#14456,#14465,#14471,#14477,#14483,#14489,#14495,#14501,#14510,#14516,#14522,#14555,#14708,#14762,#14816,#15144,#15226,#15306,#15374,#15525,#15547,#15560,#15628,#15646,#15723,#15731,#15754,#15829,#15934,#16007,#16125,#16161,#16229,#16271,#16283,#16326,#16456,#16474,#16616,#16631,#16652,#16811,#16831,#16868,#16908,#16914,#16934,#16954,#16999,#17019,#17044,#17132,#17147,#17212,#17270,#17300,#17372,#17430,#17550,#17627,#17738,#17788,#17899,#17911,#17991,#18052,#18083,#18106,#18125,#18170,#18182,#18212,#18221,#18271,#18319,#18327,#18340,#18432,#18462,#18505,#18518,#18531,#18544,#18592,#18631,#18652,#18665,#18723,#18736,#18749,#18793,#18826,#18886,#18895,#18960,#19122,#19143,#19178,#19213,#19244,#19248,#19411,#19431,#19486,#19506,#19526,#19626,#19671,#19721,#19763,#19793,#19813,#19823,#19962,#19972,#20061,#20081,#20091,#20117,#20145,#20160,#20170,#20221,#20233,#20239,#20245,#20257,#20263,#20269,#20275,#20281,#20287,#20293,#20299,#20305,#20323,#20341,#20347,#20353,#20359,#20365,#20371,#20377,#20383,#20389,#20410,#20431,#20437,#20449,#20461,#20470,#20476,#20482,#20488,#20540,#20570,#20636,#20716,#20841,#20895,#20934,#20969,#21032,#21062,#21108,#21144,#21224,#21296,#21346,#21405,#21435,#21465,#21583,#21618,#21732,#21849,#21884,#21904,#21939,#21945,#21951,#21957,#21963,#21969,#21999,#22078,#22117,#22156,#22180,#22271,#22298,#22333,#22358,#22414,#22439,#22514,#22562,#22595,#22633,#22659,#22687,#22726,#22753,#22757,#22772,#22784,#22805,#22829,#22844,#22862,#22880,#22910,#22934,#22949,#22973,#23003,#23060,#23102,#23150,#23222)); +#2= IFCRELDECLARES('1zDnNP0LPDzu65koq6JWBn',$,$,$,#1,(#3,#31,#61,#77,#119,#156,#358,#451,#516,#541,#559,#613,#840,#982,#1016,#1081,#1154,#1221,#1338,#1413,#1444,#1516,#1551,#1750,#1753,#1826,#1865,#1920,#1974,#2030,#2072,#2087,#2108,#2206,#2327,#2413,#2486,#2502,#2599,#2709,#2741,#2876,#2899,#3007,#3182,#3400,#3521,#3620,#3697,#3820,#3908,#3995,#4132,#4269,#4452,#4594,#4727,#4884,#4952,#4955,#4969,#5037,#5065,#5107,#5142,#5192,#5237,#5251,#5286,#5306,#5379,#5399,#5483,#5518,#5687,#5719,#5759,#5855,#5881,#5977,#6031,#6057,#6083,#6137,#6156,#6201,#6225,#6245,#6362,#6430,#6515,#6527,#6602,#6697,#6790,#6827,#6839,#6929,#7032,#7135,#7189,#7236,#7264,#7279,#7326,#7359,#7406,#7439,#7472,#7519,#7545,#7592,#7652,#7703,#7732,#7759,#7762,#7815,#7835,#7847,#7885,#7891,#7897,#7906,#7937,#7974,#8026,#8169,#8178,#8202,#8220,#8255,#8296,#8323,#8343,#8379,#8394,#8409,#8477,#8487,#8496,#8511,#8537,#8614,#8644,#8663,#8682,#8701,#8720,#8738,#8775,#8795,#8827,#8916,#8944,#9017,#9072,#9122,#9134,#9166,#9198,#9230,#9245,#9251,#9283,#9295,#9307,#9333,#9345,#9351,#9371,#9397,#9409,#9415,#9434,#9446,#9458,#9470,#9490,#9502,#9521,#9533,#9545,#9571,#9583,#9619,#9659,#9674,#9709,#9734,#9740,#9746,#9752,#9758,#9764,#9770,#9776,#9819,#9828,#9843,#9852,#9871,#9908,#9996,#10040,#10173,#10210,#10264,#10301,#10373,#10417,#10449,#10475,#10501,#10527,#10559,#10603,#10635,#10706,#10718,#10828,#10860,#10954,#11080,#11086,#11118,#11183,#11189,#11221,#11250,#11281,#11290,#11322,#11324,#11491,#11544,#11661,#11693,#11813,#11932,#12046,#12160,#12192,#12234,#12279,#12324,#12369,#12440,#12506,#12570,#12600,#12724,#12823,#12891,#13011,#13056,#13120,#13187,#13279,#13343,#13374,#13443,#13509,#13541,#13571,#13640,#13652,#13664,#13687,#13721,#13808,#13841,#13866,#13887,#13910,#13931,#13937,#14014,#14047,#14092,#14181,#14363,#14369,#14375,#14390,#14396,#14411,#14417,#14423,#14432,#14438,#14444,#14450,#14456,#14465,#14471,#14477,#14483,#14489,#14495,#14501,#14510,#14516,#14522,#14555,#14708,#14762,#14816,#15144,#15226,#15306,#15374,#15525,#15547,#15560,#15628,#15646,#15723,#15731,#15754,#15829,#15934,#16007,#16125,#16161,#16229,#16271,#16283,#16326,#16456,#16474,#16616,#16631,#16652,#16811,#16831,#16868,#16908,#16914,#16934,#16954,#16999,#17019,#17044,#17132,#17147,#17212,#17270,#17300,#17372,#17430,#17550,#17627,#17738,#17788,#17899,#17911,#17991,#18052,#18083,#18106,#18125,#18170,#18182,#18212,#18221,#18271,#18319,#18327,#18340,#18432,#18462,#18505,#18518,#18531,#18544,#18592,#18631,#18652,#18665,#18723,#18736,#18749,#18793,#18826,#18886,#18895,#18960,#19122,#19143,#19178,#19213,#19244,#19248,#19411,#19431,#19486,#19506,#19526,#19626,#19671,#19721,#19763,#19793,#19813,#19823,#19962,#19972,#20061,#20081,#20091,#20117,#20145,#20160,#20170,#20221,#20233,#20239,#20245,#20257,#20263,#20269,#20275,#20281,#20287,#20293,#20299,#20305,#20323,#20341,#20347,#20353,#20359,#20365,#20371,#20377,#20383,#20389,#20410,#20431,#20437,#20449,#20461,#20470,#20476,#20482,#20488,#20540,#20570,#20636,#20716,#20841,#20895,#20934,#20969,#21032,#21062,#21108,#21144,#21224,#21296,#21346,#21405,#21435,#21465,#21583,#21618,#21732,#21849,#21884,#21904,#21939,#21945,#21951,#21957,#21963,#21969,#21999,#22078,#22117,#22156,#22180,#22271,#22298,#22333,#22358,#22414,#22439,#22514,#22562,#22595,#22633,#22659,#22687,#22726,#22753,#22757,#22772,#22784,#22805,#22829,#22844,#22862,#22880,#22910,#22934,#22949,#22973,#23003,#23060,#23102,#23150,#23222)); #3= IFCPROPERTYSETTEMPLATE('3trzS0qRmHuO00025QrE$V',$,'Pset_ActorCommon','A property set that enables further classification of actors, including the ability to give a number of actors to be designated as a population, the number being specified as a property to be dealt with as a single value rather than having to aggregate a number of instances of IfcActor.',.PSET_TYPEDRIVENOVERRIDE.,'IfcActor',(#10,#17,#24)); #4= IFCLIBRARYREFERENCE($,$,'Actor Common',$,'en',$); #5= IFCRELASSOCIATESLIBRARY('3Sj0T8qcX67xNXu1cvvrJw',$,$,$,(#3),#4); @@ -318,7 +318,6 @@ DATA; #310= IFCRELASSOCIATESLIBRARY('31D1zec155FPDy9bldVM5S',$,$,$,(#302),#309); #311= IFCLIBRARYREFERENCE($,$,'\X2\B79CB4DCB9C8D06C\X0\ \X2\AD6CBD84\X0\','\X2\C774\X0\ \X2\AC74BB3CC740\X0\ \X2\C5EDC0ACC801C778\X0\ \X2\AC74BB3C\X0\ \X2\C788B294C9C0\X0\ \X2\C5ECBD80B97C\X0\ \X2\B098D0C0B0B4B294\X0\ \X2\AC12\X0\','ko-KR',$); #312= IFCRELASSOCIATESLIBRARY('2AEB4Sbnv4N9F3LtPgkoo6',$,$,$,(#302),#311); -#313= IFCPROPERTYSETTEMPLATE('0P5PgQe5XA1Rk37$BdUJdH',$,'Pset_BuildingElementCommon',$,$,'IfcBuildingElement',(#314,#325,#336,#347)); #314= IFCSIMPLEPROPERTYTEMPLATE('3wicPbQZz5Ru6Zjj9F$4Lp',$,'IsExternal','Indication whether the element is designed for use in the exterior (TRUE) or not (FALSE). If (TRUE) it is an external element and faces the outside of the building.',.P_SINGLEVALUE.,'IfcBoolean','',$,$,$,$,.READWRITE.); #315= IFCLIBRARYREFERENCE($,$,'Au\S\_enbauteil','Angabe, ob dieses Bauteil ein Aussenbauteil ist (JA) oder ein Innenbauteil (NEIN). Als Aussenbauteil grenzt es an den Aussenraum (oder Erdreich, oder Wasser).','de-DE',$); #316= IFCRELASSOCIATESLIBRARY('1EqK31rnP8CwXGTFf4olyw',$,$,$,(#314),#315); @@ -546,7 +545,7 @@ DATA; #538= IFCRELASSOCIATESLIBRARY('1OXpNZn952bOJuR1pIHt3g',$,$,$,(#536),#537); #539= IFCLIBRARYREFERENCE($,$,'CommentaireUsage','Information sur l''usage des b\S\btiments voisins','fr-FR',$); #540= IFCRELASSOCIATESLIBRARY('0eYUfGP195qvKGZK9G8oXh',$,$,$,(#536),#539); -#541= IFCPROPERTYSETTEMPLATE('2QshPSbtHEWeYz4t$IJ_5k',$,'Pset_CivilElementCommon','Properties common to the definition of all occurrence and type objects of civil element.',$,$,(#542,#543)); +#541= IFCPROPERTYSETTEMPLATE('2QshPSbtHEWeYz4t$IJ_5k',$,'Pset_CivilElementCommon','Properties common to the definition of all occurrence and type objects of civil element.',.PSET_TYPEDRIVENOVERRIDE.,'IfcCivilElement',(#542,#543)); #542= IFCSIMPLEPROPERTYTEMPLATE('1mSb87I6b0egBV57bTxz1d',$,'Reference',$,.P_SINGLEVALUE.,$,$,$,$,$,$,.READWRITE.); #543= IFCSIMPLEPROPERTYTEMPLATE('2KCc_Rd5D99x5nZnZcj2$j',$,'Status',$,.P_ENUMERATEDVALUE.,'IfcLabel',$,#544,$,$,$,.READWRITE.); #544= IFCPROPERTYENUMERATION('PEnum_ElementStatus',(IFCLABEL('NEW'),IFCLABEL('EXISTING'),IFCLABEL('DEMOLISH'),IFCLABEL('TEMPORARY'),IFCLABEL('OTHER'),IFCLABEL('NOTKNOWN'),IFCLABEL('UNSET')),$); @@ -564,7 +563,7 @@ DATA; #556= IFCRELASSOCIATESLIBRARY('24m5X46AbDzezI9ZGB4iLZ',$,$,$,(#543),#555); #557= IFCLIBRARYREFERENCE($,'UNSET','(unset)','Value has not been specified.','en',$); #558= IFCRELASSOCIATESLIBRARY('0doKkNzXD98RcxgzjgnjuO',$,$,$,(#543),#557); -#559= IFCPROPERTYSETTEMPLATE('0FFkv1bQb5xBumBg6nZobE',$,'Pset_ElementAssemblyCommon','Properties common to the definition of all occurrence and type objects of element assembly.',$,$,(#560,#561)); +#559= IFCPROPERTYSETTEMPLATE('0FFkv1bQb5xBumBg6nZobE',$,'Pset_ElementAssemblyCommon','Properties common to the definition of all occurrence and type objects of element assembly.',.PSET_TYPEDRIVENOVERRIDE.,'IfcElementAssembly',(#560,#561)); #560= IFCSIMPLEPROPERTYTEMPLATE('2KCLcfF8r35gUW6qE2nkg0',$,'Reference',$,.P_SINGLEVALUE.,$,$,$,$,$,$,.READWRITE.); #561= IFCSIMPLEPROPERTYTEMPLATE('2KCc_Rd5D99x5nZnZcj2$j',$,'Status',$,.P_ENUMERATEDVALUE.,'IfcLabel',$,#562,$,$,$,.READWRITE.); #562= IFCPROPERTYENUMERATION('PEnum_ElementStatus',(IFCLABEL('NEW'),IFCLABEL('EXISTING'),IFCLABEL('DEMOLISH'),IFCLABEL('TEMPORARY'),IFCLABEL('OTHER'),IFCLABEL('NOTKNOWN'),IFCLABEL('UNSET')),$); @@ -582,7 +581,6 @@ DATA; #574= IFCRELASSOCIATESLIBRARY('0nKEMCXdT1E9i164FE48p_',$,$,$,(#561),#573); #575= IFCLIBRARYREFERENCE($,'UNSET','(unset)','Value has not been specified.','en',$); #576= IFCRELASSOCIATESLIBRARY('12y6u5dOz6aQrCiE93fe4L',$,$,$,(#561),#575); -#577= IFCPROPERTYSETTEMPLATE('1$kUwEqwL2BvJ_iHWNGoOf',$,'Pset_ElementCommon','This property set serves as a placeholder for common properties to assist in translation, and is not currently published (the property set type is set to NOTDEFINED).',$,'IfcElement',(#578,#589)); #578= IFCSIMPLEPROPERTYTEMPLATE('33frgWW4D3euBRuniN2rSn',$,'Reference','Reference ID for this specified type in this project (e.g. type ''A-1''), Also referred to as "construction type". It should be provided as an alternative to the name of the "object type", if the software does not support object types.',.P_SINGLEVALUE.,'IfcIdentifier','',$,$,$,$,.READWRITE.); #579= IFCLIBRARYREFERENCE($,$,'Bauteiltyp','Bezeichnung zur Zusammenfassung gleichartiger Bauteile zu einem Bauteiltyp (auch Konstruktionstyp genannt). Alternativ zum Namen des "Typobjekts", insbesondere wenn die Software keine Typen unterst\S\|tzt.','de-DE',$); #580= IFCRELASSOCIATESLIBRARY('2AGVGoTTL4LRu9JRR5MaXQ',$,$,$,(#578),#579); @@ -1755,7 +1753,7 @@ DATA; #1747= IFCRELASSOCIATESLIBRARY('1YNcMDyzjEnfJoOMX6zEhC',$,$,$,(#1739),#1746); #1748= IFCLIBRARYREFERENCE($,$,'\X2\C911C559C2DD\X0\ \X2\C5D0C5B4CEE8\X0\','\X2\ACF5AC04C774\X0\ \X2\C911C559\X0\ \X2\C2DD\X0\ \X2\ACF5C870B97C\X0\ \X2\C694CCADD560C9C0\X0\ \X2\C5ECBD80B97C\X0\ \X2\B098D0C0B0B4B294\X0\ \X2\BD80C6B8\X0\ \X2\AC12C785B2C8B2E4\X0\. "\X2\ACF5C870\X0\"\X2\C18DC131C774\X0\ TRUE\X2\B85C\X0\ \X2\C124C815B41C\X0\ \X2\ACBDC6B0C5D0B9CC\X0\ \X2\C8FCC5B4C9C4B2E4\X0\.','ko-KR',$); #1749= IFCRELASSOCIATESLIBRARY('1Cny$1o4bCowGEleAjaHMM',$,$,$,(#1739),#1748); -#1750= IFCPROPERTYSETTEMPLATE('0FrzQVlZv4rBwQtsDewSnh',$,'Pset_SpatialZoneCommon',$,$,$,(#1751,#1752)); +#1750= IFCPROPERTYSETTEMPLATE('0FrzQVlZv4rBwQtsDewSnh',$,'Pset_SpatialZoneCommon',$,.PSET_TYPEDRIVENOVERRIDE.,'IfcSpatialZone',(#1751,#1752)); #1751= IFCSIMPLEPROPERTYTEMPLATE('09uhUrGP96Dg7LCTKoeQZI',$,'Reference',$,.P_SINGLEVALUE.,$,$,$,$,$,$,.READWRITE.); #1752= IFCSIMPLEPROPERTYTEMPLATE('0wumB732b2A8dsysfeuKf4',$,'IsExternal','Indication whether the element is designed for use in the exterior (TRUE) or not (FALSE). If (TRUE) it is an external zone at the outside of the building.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #1753= IFCPROPERTYSETTEMPLATE('3X3v_0qX4HuO00025QrE$V',$,'Pset_TransportElementCommon','Properties common to the definition of all occurrences of IfcTransportElement or IfcTransportElementType',.PSET_TYPEDRIVENOVERRIDE.,'IfcTransportElement',(#1762,#1773,#1797,#1806,#1815)); @@ -11327,7 +11325,7 @@ DATA; #11319= IFCRELASSOCIATESLIBRARY('2lYH$TOOfC3PAmK0lZ0a6m',$,$,$,(#11302),#11318); #11320= IFCLIBRARYREFERENCE($,'UNSET','(unset)','Value has not been specified.','en',$); #11321= IFCRELASSOCIATESLIBRARY('2gGIJnDh9809qUyYb9$YmL',$,$,$,(#11302),#11320); -#11322= IFCPROPERTYSETTEMPLATE('3pPX0Yyqz8w978GQpU2GcH',$,'Pset_ElectricFlowStorageDevicePHistory','Electric flow storage device performance history common attributes.',$,$,(#11323)); +#11322= IFCPROPERTYSETTEMPLATE('3pPX0Yyqz8w978GQpU2GcH',$,'Pset_ElectricFlowStorageDevicePHistory','Electric flow storage device performance history common attributes.',.PSET_PERFORMANCEDRIVEN.,'IfcElectricFlowStorageDevice',(#11323)); #11323= IFCSIMPLEPROPERTYTEMPLATE('2YT$iVpSfFp9mfXZHHYn1c',$,'Level','The fraction of usable energy stored.',.P_SINGLEVALUE.,'IfcNormalisedRatioMeasure',$,$,$,$,$,.READWRITE.); #11324= IFCPROPERTYSETTEMPLATE('0BuG00qU0HuO00025QrE$V',$,'Pset_ElectricFlowStorageDeviceTypeCommon','The characteristics of the supply associated with an electrical device occurrence acting as a source of supply to an electrical distribution system NOTE: Properties within this property set should ONLY be used in circumstances when an electrical supply is applied. The property set, the properties contained and their values are not applicable to a circumstance where the sypply is not being applied to the eletrical system or is temporarily disconnected. All properties within this property set are considered to represent a steady state situation.',.PSET_TYPEDRIVENOVERRIDE.,'IfcElectricFlowStorageDevice',(#11329,#11336,#11356,#11363,#11370,#11377,#11403,#11410,#11417,#11424,#11431,#11438,#11445,#11452,#11459,#11466,#11473,#11480,#11487,#11488,#11489,#11490)); #11325= IFCLIBRARYREFERENCE($,$,'Electric Flow Storage Device Type Common',$,'en',$); From 29c98a5996a14fc1503304e9fdc96cd7977eee75 Mon Sep 17 00:00:00 2001 From: Vukas Pajic Date: Mon, 1 Aug 2022 10:16:27 +0200 Subject: [PATCH 3/4] Support for nested IfcGroups --- .../blenderbim/bim/module/group/__init__.py | 5 + .../blenderbim/bim/module/group/operator.py | 108 +++++++++++++++--- .../blenderbim/bim/module/group/prop.py | 11 +- .../blenderbim/bim/module/group/ui.py | 37 +++++- .../blenderbim/bim/module/search/operator.py | 48 ++++++-- .../ifcopenshell/api/group/data.py | 2 + 6 files changed, 182 insertions(+), 29 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/group/__init__.py b/src/blenderbim/blenderbim/bim/module/group/__init__.py index 1b765a0a94..bc16858fd2 100644 --- a/src/blenderbim/blenderbim/bim/module/group/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/group/__init__.py @@ -21,8 +21,10 @@ from . import ui, prop, operator classes = ( operator.LoadGroups, + operator.ToggleGroup, operator.DisableGroupEditingUI, operator.AddGroup, + operator.AddGroupToGroup, operator.EditGroup, operator.RemoveGroup, operator.ToggleAssigningGroup, @@ -32,6 +34,7 @@ classes = ( operator.DisableEditingGroup, operator.SelectGroupProducts, operator.UpdateGroup, + prop.ExpandedGroups, prop.Group, prop.BIMGroupProperties, ui.BIM_PT_groups, @@ -43,7 +46,9 @@ classes = ( def register(): bpy.types.Scene.BIMGroupProperties = bpy.props.PointerProperty(type=prop.BIMGroupProperties) + bpy.types.Scene.ExpandedGroups = bpy.props.PointerProperty(type=prop.ExpandedGroups) def unregister(): del bpy.types.Scene.BIMGroupProperties + del bpy.types.Scene.ExpandedGroups diff --git a/src/blenderbim/blenderbim/bim/module/group/operator.py b/src/blenderbim/blenderbim/bim/module/group/operator.py index c48ea81bcd..aee07af28a 100644 --- a/src/blenderbim/blenderbim/bim/module/group/operator.py +++ b/src/blenderbim/blenderbim/bim/module/group/operator.py @@ -23,23 +23,73 @@ import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.group.data import Data from ifcopenshell.util.selector import Selector +import json class LoadGroups(bpy.types.Operator): bl_idname = "bim.load_groups" bl_label = "Load Groups" bl_options = {"REGISTER", "UNDO"} + is_refresh: bpy.props.BoolProperty(default=False) def execute(self, context): - props = context.scene.BIMGroupProperties - props.groups.clear() + self.props = context.scene.BIMGroupProperties + self.expanded_groups = json.loads(context.scene.ExpandedGroups.json_string) + self.props.groups.clear() + self.ifc = IfcStore.get_file() + if not self.is_refresh: + context.scene.ExpandedGroups.json_string = "{}" + for ifc_definition_id, group in Data.groups.items(): - new = props.groups.add() - new.ifc_definition_id = ifc_definition_id - new.name = group["Name"] - new.selection_query = group["Description"].split("*selector*")[1] if group["Description"] else "" - props.is_editing = True + if not group["HasAssignments"]: + new = self.props.groups.add() + new.ifc_definition_id = ifc_definition_id + new.name = group["Name"] + new.selection_query = group["Description"].split("*selector*")[1] if group["Description"] else "" + new.has_children = True if len(group["IsGroupedBy"]) != 0 else False + new.tree_depth = 0 + + if self.is_refresh: + self.recursively_load_sub_groups(ifc_definition_id, new) + + self.props.is_editing = True bpy.ops.bim.disable_editing_group() + Data.load(IfcStore.get_file()) + return {"FINISHED"} + + def recursively_load_sub_groups(self, ifc_definition_id, parent): + sub_groups = ([k for k,v in Data.products.items() if self.ifc.by_id(k).is_a("IfcGroup") and ifc_definition_id in v]) + if str(ifc_definition_id) not in self.expanded_groups or sub_groups == []: + return + else: + parent.is_expanded = True + for group in sub_groups: + new = self.props.groups.add() + new.ifc_definition_id = group + new.name = Data.groups[group]["Name"] + new.selection_query = Data.groups[group]["Description"].split("*selector*")[1] if Data.groups[group]["Description"] else "" + new.has_children = True if len(Data.groups[group]["IsGroupedBy"]) != 0 else False + new.tree_depth = parent.tree_depth + 1 + self.recursively_load_sub_groups(new.ifc_definition_id, new) + + +class ToggleGroup(bpy.types.Operator): + bl_idname = "bim.toggle_group" + bl_label = "Toggle Group" + bl_options = {"REGISTER", "UNDO"} + ifc_definition_id: bpy.props.IntProperty() + index: bpy.props.IntProperty() + option: bpy.props.StringProperty(name="Expand or Collapse") + + def execute(self, context): + context.scene.BIMGroupProperties.groups[self.index].is_expanded = True if self.option == "Expand" else False + json_string = json.loads(context.scene.ExpandedGroups.json_string) + if self.option == "Expand": + json_string.setdefault(str(self.ifc_definition_id), None) + else: + json_string.pop(str(self.ifc_definition_id), None) + context.scene.ExpandedGroups.json_string = json.dumps(json_string) + bpy.ops.bim.load_groups(is_refresh=True) return {"FINISHED"} @@ -56,7 +106,7 @@ class DisableGroupEditingUI(bpy.types.Operator): class AddGroup(bpy.types.Operator): bl_idname = "bim.add_group" - bl_label = "Add Group" + bl_label = "Add New Group" bl_options = {"REGISTER", "UNDO"} def execute(self, context): @@ -65,15 +115,43 @@ class AddGroup(bpy.types.Operator): def _execute(self, context): result = ifcopenshell.api.run("group.add_group", IfcStore.get_file()) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_groups() + + bpy.ops.bim.load_groups(is_refresh=True) bpy.ops.bim.enable_editing_group(group=result.id()) return {"FINISHED"} + + +class AddGroupToGroup(bpy.types.Operator): + bl_idname = "bim.add_group_to_group" + bl_label = "Add Group to Group" + bl_options = {"REGISTER", "UNDO"} + group: bpy.props.IntProperty(name="Group ID") + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + result = ifcopenshell.api.run("group.add_group", self.file) + ifcopenshell.api.run( + "group.assign_group", + IfcStore.get_file(), + **{ + "product": [result], + "group" : self.file.by_id(self.group) + } + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.load_groups(is_refresh=True) + bpy.ops.bim.disable_group_editing_ui() + return {"FINISHED"} class EditGroup(bpy.types.Operator): bl_idname = "bim.edit_group" bl_label = "Edit Group" bl_options = {"REGISTER", "UNDO"} + copy_from_selector: bpy.props.BoolProperty(name="Copy from Selector", default=False) def execute(self, context): return IfcStore.execute_ifc_operator(self, context) @@ -86,12 +164,16 @@ class EditGroup(bpy.types.Operator): attributes[attribute.name] = None else: attributes[attribute.name] = attribute.string_value + + if self.copy_from_selector: + attributes["Description"] = context.scene.IFCSelector.selection_query + self.file = IfcStore.get_file() ifcopenshell.api.run( "group.edit_group", self.file, **{"group": self.file.by_id(props.active_group_id), "attributes": attributes} ) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_groups() + bpy.ops.bim.load_groups(is_refresh=True) return {"FINISHED"} @@ -109,7 +191,7 @@ class RemoveGroup(bpy.types.Operator): self.file = IfcStore.get_file() ifcopenshell.api.run("group.remove_group", self.file, **{"group": self.file.by_id(self.group)}) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_groups() + bpy.ops.bim.load_groups(is_refresh=True) return {"FINISHED"} @@ -122,9 +204,7 @@ class EnableEditingGroup(bpy.types.Operator): def execute(self, context): props = context.scene.BIMGroupProperties props.group_attributes.clear() - blenderbim.bim.helper.import_attributes("IfcGroup", props.group_attributes, Data.groups[self.group]) - props.active_group_id = self.group return {"FINISHED"} @@ -247,5 +327,5 @@ class UpdateGroup(bpy.types.Operator): } ) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_groups() + bpy.ops.bim.load_groups(is_refresh=True) return {"FINISHED"} \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/group/prop.py b/src/blenderbim/blenderbim/bim/module/group/prop.py index e97f024584..0f00a985a8 100644 --- a/src/blenderbim/blenderbim/bim/module/group/prop.py +++ b/src/blenderbim/blenderbim/bim/module/group/prop.py @@ -18,7 +18,10 @@ import bpy from blenderbim.bim.prop import StrProperty, Attribute +from blenderbim.bim.helper import import_attributes +from ifcopenshell.api.group.data import Data from bpy.types import PropertyGroup +import json from bpy.props import ( PointerProperty, StringProperty, @@ -31,12 +34,17 @@ from bpy.props import ( ) +class ExpandedGroups(StrProperty): + json_string: StringProperty(name="JSON String", default="{}") + class Group(PropertyGroup): name: StringProperty(name="Name") ifc_definition_id: IntProperty(name="IFC Definition ID") selection_query: StringProperty(name="Selection Query") + is_expanded: BoolProperty(name="Is Expanded", default=False) + has_children: BoolProperty(name="Has Children") + tree_depth: IntProperty(name="Tree Depth") - class BIMGroupProperties(PropertyGroup): group_attributes: CollectionProperty(name="Group Attributes", type=Attribute) is_editing: BoolProperty(name="Is Editing", default=False) @@ -44,3 +52,4 @@ class BIMGroupProperties(PropertyGroup): groups: CollectionProperty(name="Groups", type=Group) active_group_index: IntProperty(name="Active Group Index") active_group_id: IntProperty(name="Active Group Id") + \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/group/ui.py b/src/blenderbim/blenderbim/bim/module/group/ui.py index 2c7525132d..4f747e0867 100644 --- a/src/blenderbim/blenderbim/bim/module/group/ui.py +++ b/src/blenderbim/blenderbim/bim/module/group/ui.py @@ -115,9 +115,23 @@ class BIM_PT_object_groups(Panel): class BIM_UL_groups(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): if item: row = layout.row(align=True) + for i in range(0, item.tree_depth): + row.label(text="", icon="BLANK1") + if item.has_children: + op = row.operator( + "bim.toggle_group", + icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT", + text="", + emboss=False) + op.ifc_definition_id = item.ifc_definition_id + op.index = index + op.option = "Collapse" if item.is_expanded else "Expand" + else: + row.label(text="", icon="BLANK1") + row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name) group_id = item.ifc_definition_id if context.scene.BIMGroupProperties.active_group_id == group_id: @@ -128,6 +142,8 @@ class BIM_UL_groups(UIList): elif context.scene.BIMGroupProperties.active_group_id: op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") op.group = group_id + op = row.operator("bim.add_group_to_group", text="", icon="ADD") + op.group = group_id op = row.operator("bim.remove_group", text="", icon="X") op.group = group_id if item.selection_query != "": @@ -139,6 +155,8 @@ class BIM_UL_groups(UIList): op.group = group_id op = row.operator("bim.enable_editing_group", text="", icon="GREASEPENCIL") op.group = group_id + op = row.operator("bim.add_group_to_group", text="", icon="ADD") + op.group = group_id op = row.operator("bim.remove_group", text="", icon="X") op.group = group_id if item.selection_query != "": @@ -148,10 +166,23 @@ class BIM_UL_groups(UIList): class BIM_UL_object_groups(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): if item: row = layout.row(align=True) - row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name) + for i in range(0, item.tree_depth): + row.label(text="", icon="BLANK1") + if item.has_children: + op = row.operator( + "bim.toggle_group", + icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT", + text="", + emboss=False) + op.ifc_definition_id = item.ifc_definition_id + op.index = index + op.option = "Collapse" if item.is_expanded else "Expand" + else: + row.label(text="", icon="BLANK1") + row.label(text=item.name) op = row.operator("bim.remove_group", text="", icon="X") op.group = item.ifc_definition_id op = row.operator("bim.assign_group", text="", icon="ADD") diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py index 5ff9547041..76c87499fe 100644 --- a/src/blenderbim/blenderbim/bim/module/search/operator.py +++ b/src/blenderbim/blenderbim/bim/module/search/operator.py @@ -25,6 +25,7 @@ from ifcopenshell.util.selector import Selector import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore from blenderbim.bim.helper import close_operator_panel +from blenderbim.bim.module.group import ui from itertools import cycle from bpy.types import PropertyGroup, Operator from bpy.props import ( @@ -646,23 +647,48 @@ class AddToIfcGroup(Operator): bl_idname = "bim.add_to_ifc_group" bl_label = "Add to IFC Group" group_name: StringProperty(name="Group Name") - + def invoke(self, context, event): + bpy.ops.bim.load_groups() return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context): - layout = self.layout - layout.prop(self, "group_name") + self.props = context.scene.BIMGroupProperties + row = self.layout.row() + row.operator("bim.add_group") + + self.layout.template_list( + "BIM_UL_groups", + "", + self.props, + "groups", + self.props, + "active_group_index", + ) + + if self.props.active_group_id: + for attribute in self.props.group_attributes: + if attribute.name in ["Name", "Description"]: + row = self.layout.row(align=True) + row.prop(attribute, "string_value", text=attribute.name) def execute(self, context): - self.file = IfcStore.get_file() - ifc_selector = context.scene.IfcSelectorProperties - selector_query_syntax = ifc_selector.selector_query_syntax + active_group_index = self.props.active_group_index + ifc_definition_id = self.props.groups[active_group_index].ifc_definition_id - group = ifcopenshell.api.run("group.add_group", self.file, **{"Name": self.group_name, "Description": f'*selector*{selector_query_syntax}*selector*'}) - objects = Selector.parse(self.file, selector_query_syntax) - - ifcopenshell.api.run("group.assign_group", self.file, **{"product": objects, "group": group}) - Data.load(IfcStore.get_file()) + bpy.ops.bim.enable_editing_group(group=ifc_definition_id) + selector_query_syntax = context.scene.IfcSelectorProperties.selector_query_syntax + + for attribute in self.props.group_attributes: + if attribute.name == "Description": + if "*selector*" not in attribute.string_value: + attribute.string_value += f" *selector*{selector_query_syntax}*selector*" + else: + new_description = attribute.string_value.split("*selector*") + new_description[1] = selector_query_syntax + attribute.string_value = "*selector*".join(new_description) + + bpy.ops.bim.edit_group() + bpy.ops.bim.disable_group_editing_ui() return {"FINISHED"} diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/data.py b/src/ifcopenshell-python/ifcopenshell/api/group/data.py index bce76c6a59..ccf548b0ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/data.py @@ -38,6 +38,8 @@ class Data: for product in rel.RelatedObjects: cls.products.setdefault(product.id(), []).append(group.id()) data = group.get_info() + data["HasAssignments"] = group.HasAssignments + data["IsGroupedBy"] = group.IsGroupedBy del data["OwnerHistory"] cls.groups[group.id()] = data cls.is_loaded = True From 67993071a67aa20d6bceea67ed929e4bcc22401c Mon Sep 17 00:00:00 2001 From: Vukas Pajic Date: Mon, 1 Aug 2022 10:32:05 +0200 Subject: [PATCH 4/4] UIlist no longer closes after adding group to group --- src/blenderbim/blenderbim/bim/module/group/operator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/group/operator.py b/src/blenderbim/blenderbim/bim/module/group/operator.py index aee07af28a..516717b9f1 100644 --- a/src/blenderbim/blenderbim/bim/module/group/operator.py +++ b/src/blenderbim/blenderbim/bim/module/group/operator.py @@ -143,7 +143,6 @@ class AddGroupToGroup(bpy.types.Operator): ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_groups(is_refresh=True) - bpy.ops.bim.disable_group_editing_ui() return {"FINISHED"} @@ -328,4 +327,4 @@ class UpdateGroup(bpy.types.Operator): ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_groups(is_refresh=True) - return {"FINISHED"} \ No newline at end of file + return {"FINISHED"}