@@ -4199,29 +4199,140 @@ private void CreateGlobalCartesianOrigin(ExporterIFC exporterIFC)
41994199 ExporterIFCUtils . SetGlobal2DOriginHandle ( origin2d ) ;
42004200 }
42014201
4202- private HashSet < IFCAnyHandle > RemoveContainedHandlesFromSet ( ICollection < IFCAnyHandle > initialSet )
4202+ private static bool ValidateContainedHandle ( IFCAnyHandle initialHandle )
4203+ {
4204+ if ( ExporterCacheManager . ElementsInAssembliesCache . Contains ( initialHandle ) )
4205+ return false ;
4206+
4207+ try
4208+ {
4209+ if ( ! IFCAnyHandleUtil . HasRelDecomposes ( initialHandle ) )
4210+ return true ;
4211+ }
4212+ catch
4213+ {
4214+ }
4215+
4216+ return false ;
4217+ }
4218+
4219+ /// <summary>
4220+ /// Remove contained or invalid handles from this set.
4221+ /// </summary>
4222+ /// <param name="initialSet">The initial set that may have contained or invalid handles.</param>
4223+ /// <returns>A cleaned set.</returns>
4224+ public static HashSet < IFCAnyHandle > RemoveContainedHandlesFromSet ( ICollection < IFCAnyHandle > initialSet )
42034225 {
42044226 HashSet < IFCAnyHandle > filteredSet = new HashSet < IFCAnyHandle > ( ) ;
42054227
42064228 if ( initialSet != null )
42074229 {
42084230 foreach ( IFCAnyHandle initialHandle in initialSet )
42094231 {
4210- if ( ExporterCacheManager . ElementsInAssembliesCache . Contains ( initialHandle ) )
4211- continue ;
4232+ if ( ValidateContainedHandle ( initialHandle ) )
4233+ filteredSet . Add ( initialHandle ) ;
4234+ }
4235+ }
42124236
4213- try
4237+ return filteredSet ;
4238+ }
4239+
4240+ private class IFCLevelExportInfo
4241+ {
4242+ public IFCLevelExportInfo ( ) { }
4243+
4244+ public IDictionary < ElementId , IList < IFCLevelInfo > > LevelMapping { get ; set ; } =
4245+ new Dictionary < ElementId , IList < IFCLevelInfo > > ( ) ;
4246+
4247+ public IList < IFCLevelInfo > OrphanedLevelInfos { get ; set ; } = new List < IFCLevelInfo > ( ) ;
4248+
4249+ public void UnionLevelInfoRelated ( ElementId toLevelId , IFCLevelInfo fromLevel )
4250+ {
4251+ if ( fromLevel == null )
4252+ return ;
4253+
4254+ if ( toLevelId == ElementId . InvalidElementId )
4255+ {
4256+ OrphanedLevelInfos . Add ( fromLevel ) ;
4257+ return ;
4258+ }
4259+
4260+ IList < IFCLevelInfo > levelMappingList ;
4261+ if ( ! LevelMapping . TryGetValue ( toLevelId , out levelMappingList ) )
4262+ {
4263+ levelMappingList = new List < IFCLevelInfo > ( ) ;
4264+ LevelMapping [ toLevelId ] = levelMappingList ;
4265+ }
4266+ levelMappingList . Add ( fromLevel ) ;
4267+ }
4268+
4269+ public void TransferOrphanedLevelInfo ( ElementId toLevelId )
4270+ {
4271+ if ( toLevelId == ElementId . InvalidElementId )
4272+ return ;
4273+
4274+ if ( OrphanedLevelInfos . Count == 0 )
4275+ return ;
4276+
4277+ IList < IFCLevelInfo > toLevelMappingList ;
4278+ if ( ! LevelMapping . TryGetValue ( toLevelId , out toLevelMappingList ) )
4279+ {
4280+ toLevelMappingList = new List < IFCLevelInfo > ( ) ;
4281+ LevelMapping [ toLevelId ] = toLevelMappingList ;
4282+ }
4283+
4284+ foreach ( IFCLevelInfo orphanedLevelInfo in OrphanedLevelInfos )
4285+ {
4286+ toLevelMappingList . Add ( orphanedLevelInfo ) ;
4287+ }
4288+ OrphanedLevelInfos . Clear ( ) ;
4289+ }
4290+
4291+ public Tuple < HashSet < IFCAnyHandle > , HashSet < IFCAnyHandle > > CollectValidHandlesForLevel (
4292+ ElementId levelId , IFCLevelInfo levelInfo )
4293+ {
4294+ if ( levelId == ElementId . InvalidElementId || levelInfo == null )
4295+ return null ;
4296+
4297+ HashSet < IFCAnyHandle > levelRelatedProducts = new HashSet < IFCAnyHandle > ( ) ;
4298+ levelRelatedProducts . UnionWith ( levelInfo . GetRelatedProducts ( ) ) ;
4299+
4300+ HashSet < IFCAnyHandle > levelRelatedElements = new HashSet < IFCAnyHandle > ( ) ;
4301+ levelRelatedElements . UnionWith ( levelInfo . GetRelatedElements ( ) ) ;
4302+
4303+ IList < IFCLevelInfo > levelMappingList ;
4304+ if ( LevelMapping . TryGetValue ( levelId , out levelMappingList ) )
4305+ {
4306+ foreach ( IFCLevelInfo containedLevelInfo in levelMappingList )
42144307 {
4215- if ( ! IFCAnyHandleUtil . HasRelDecomposes ( initialHandle ) )
4216- filteredSet . Add ( initialHandle ) ;
4308+ if ( containedLevelInfo != null )
4309+ {
4310+ levelRelatedProducts . UnionWith ( containedLevelInfo . GetRelatedProducts ( ) ) ;
4311+ levelRelatedElements . UnionWith ( containedLevelInfo . GetRelatedElements ( ) ) ;
4312+ }
42174313 }
4218- catch
4314+ }
4315+
4316+ return Tuple . Create ( RemoveContainedHandlesFromSet ( levelRelatedProducts ) ,
4317+ RemoveContainedHandlesFromSet ( levelRelatedElements ) ) ;
4318+ }
4319+
4320+ public HashSet < IFCAnyHandle > CollectOrphanedHandles ( )
4321+ {
4322+ HashSet < IFCAnyHandle > orphanedHandles = new HashSet < IFCAnyHandle > ( ) ;
4323+ foreach ( IFCLevelInfo containedLevelInfo in OrphanedLevelInfos )
4324+ {
4325+ if ( containedLevelInfo != null )
42194326 {
4327+ orphanedHandles . UnionWith ( containedLevelInfo . GetRelatedProducts ( ) ) ;
4328+ orphanedHandles . UnionWith ( containedLevelInfo . GetRelatedElements ( ) ) ;
42204329 }
42214330 }
4222- }
42234331
4224- return filteredSet ;
4332+ // RemoveContainedHandlesFromSet will be called before these are used, so
4333+ // don't bother doing it twice.
4334+ return orphanedHandles ;
4335+ }
42254336 }
42264337
42274338 /// <summary>
@@ -4235,66 +4346,33 @@ private void RelateLevels(ExporterIFC exporterIFC, Document document)
42354346 IList < ElementId > levelIds = ExporterCacheManager . LevelInfoCache . LevelsByElevation ;
42364347 IFCFile file = exporterIFC . GetFile ( ) ;
42374348
4349+ ElementId lastValidLevelId = ElementId . InvalidElementId ;
4350+ IFCLevelExportInfo levelInfoMapping = new IFCLevelExportInfo ( ) ;
4351+
42384352 for ( int ii = 0 ; ii < levelIds . Count ; ii ++ )
42394353 {
42404354 ElementId levelId = levelIds [ ii ] ;
42414355 IFCLevelInfo levelInfo = ExporterCacheManager . LevelInfoCache . GetLevelInfo ( exporterIFC , levelId ) ;
42424356 if ( levelInfo == null )
42434357 continue ;
42444358
4245- // remove products that are aggregated (e.g., railings in stairs).
42464359 Element level = document . GetElement ( levelId ) ;
42474360
4248- ICollection < IFCAnyHandle > relatedProductsToCheck = levelInfo . GetRelatedProducts ( ) ;
4249- ICollection < IFCAnyHandle > relatedElementsToCheck = levelInfo . GetRelatedElements ( ) ;
4250-
4251- // get coincident levels, if any.
4252- double currentElevation = levelInfo . Elevation ;
4361+ levelInfoMapping . TransferOrphanedLevelInfo ( levelId ) ;
42534362 int nextLevelIdx = ii + 1 ;
4254- for ( int jj = ii + 1 ; jj < levelIds . Count ; jj ++ , nextLevelIdx ++ )
4255- {
4256- ElementId nextLevelId = levelIds [ jj ] ;
4257- IFCLevelInfo levelInfo2 = ExporterCacheManager . LevelInfoCache . GetLevelInfo ( exporterIFC , nextLevelId ) ;
4258- if ( levelInfo2 == null )
4259- continue ;
4260-
4261- if ( MathUtil . IsAlmostEqual ( currentElevation , levelInfo2 . Elevation ) )
4262- {
4263- foreach ( IFCAnyHandle relatedProduct in levelInfo2 . GetRelatedProducts ( ) )
4264- relatedProductsToCheck . Add ( relatedProduct ) ;
4265-
4266- foreach ( IFCAnyHandle relatedElement in levelInfo2 . GetRelatedElements ( ) )
4267- relatedElementsToCheck . Add ( relatedElement ) ;
4268- }
4269- else
4270- break ;
4271- }
42724363
42734364 // We may get stale handles in here; protect against this.
4274- HashSet < IFCAnyHandle > relatedProducts = RemoveContainedHandlesFromSet ( relatedProductsToCheck ) ;
4275- HashSet < IFCAnyHandle > relatedElements = RemoveContainedHandlesFromSet ( relatedElementsToCheck ) ;
4276-
4277- // skip coincident levels, if any.
4278- for ( int jj = ii + 1 ; jj < nextLevelIdx ; jj ++ )
4279- {
4280- ElementId nextLevelId = levelIds [ jj ] ;
4281- IFCLevelInfo levelInfo2 = ExporterCacheManager . LevelInfoCache . GetLevelInfo ( exporterIFC , nextLevelId ) ;
4282- if ( levelInfo2 == null )
4283- continue ;
4365+ Tuple < HashSet < IFCAnyHandle > , HashSet < IFCAnyHandle > > productsAndElements =
4366+ levelInfoMapping . CollectValidHandlesForLevel ( levelId , levelInfo ) ;
42844367
4285- if ( ! levelInfo . GetBuildingStorey ( ) . Equals ( levelInfo2 . GetBuildingStorey ( ) ) )
4286- IFCAnyHandleUtil . Delete ( levelInfo2 . GetBuildingStorey ( ) ) ;
4287- }
4288- ii = nextLevelIdx - 1 ;
4368+ HashSet < IFCAnyHandle > relatedProducts = productsAndElements . Item1 ;
4369+ HashSet < IFCAnyHandle > relatedElements = productsAndElements . Item2 ;
42894370
4290- if ( relatedProducts . Count == 0 && relatedElements . Count == 0 )
4291- IFCAnyHandleUtil . Delete ( levelInfo . GetBuildingStorey ( ) ) ;
4292- else
4371+ using ( ProductWrapper productWrapper = ProductWrapper . Create ( exporterIFC , false ) )
42934372 {
4294- // We have decided to keep the level - export properties, quantities and classifications.
4295- using ( ProductWrapper productWrapper = ProductWrapper . Create ( exporterIFC , false ) )
4373+ IFCAnyHandle buildingStoreyHandle = levelInfo . GetBuildingStorey ( ) ;
4374+ if ( ! buildingStories . Contains ( buildingStoreyHandle ) )
42964375 {
4297- IFCAnyHandle buildingStoreyHandle = levelInfo . GetBuildingStorey ( ) ;
42984376 buildingStories . Add ( buildingStoreyHandle ) ;
42994377 IFCExportInfoPair exportInfo = new IFCExportInfoPair ( IFCEntityType . IfcBuildingStorey ) ;
43004378
@@ -4307,24 +4385,18 @@ private void RelateLevels(ExporterIFC exporterIFC, Document document)
43074385
43084386 if ( relatedProducts . Count > 0 )
43094387 {
4310- HashSet < IFCAnyHandle > buildingProducts = RemoveContainedHandlesFromSet ( relatedProducts ) ;
4311- if ( buildingProducts . Count > 0 )
4312- {
4313- IFCAnyHandle buildingStorey = levelInfo . GetBuildingStorey ( ) ;
4314- string guid = GUIDUtil . CreateSubElementGUID ( level , ( int ) IFCBuildingStoreySubElements . RelAggregates ) ;
4315- ExporterCacheManager . ContainmentCache . AddRelations ( buildingStorey , guid , relatedProducts ) ;
4316- }
4388+ IFCAnyHandle buildingStorey = levelInfo . GetBuildingStorey ( ) ;
4389+ string guid = GUIDUtil . CreateSubElementGUID ( level , ( int ) IFCBuildingStoreySubElements . RelAggregates ) ;
4390+ ExporterCacheManager . ContainmentCache . AddRelations ( buildingStorey , guid , relatedProducts ) ;
43174391 }
43184392
43194393 if ( relatedElements . Count > 0 )
43204394 {
4321- HashSet < IFCAnyHandle > buildingElements = RemoveContainedHandlesFromSet ( relatedElements ) ;
4322- if ( buildingElements . Count > 0 )
4323- {
4324- string guid = GUIDUtil . CreateSubElementGUID ( level , ( int ) IFCBuildingStoreySubElements . RelContainedInSpatialStructure ) ;
4325- IFCInstanceExporter . CreateRelContainedInSpatialStructure ( file , guid , ExporterCacheManager . OwnerHistoryHandle , null , null , buildingElements , levelInfo . GetBuildingStorey ( ) ) ;
4326- }
4395+ string guid = GUIDUtil . CreateSubElementGUID ( level , ( int ) IFCBuildingStoreySubElements . RelContainedInSpatialStructure ) ;
4396+ IFCInstanceExporter . CreateRelContainedInSpatialStructure ( file , guid , ExporterCacheManager . OwnerHistoryHandle , null , null , relatedElements , levelInfo . GetBuildingStorey ( ) ) ;
43274397 }
4398+
4399+ ii = nextLevelIdx - 1 ;
43284400 }
43294401
43304402 if ( buildingStories . Count > 0 )
@@ -4334,6 +4406,11 @@ private void RelateLevels(ExporterIFC exporterIFC, Document document)
43344406 string guid = GUIDUtil . CreateSubElementGUID ( projectInfo , ( int ) IFCProjectSubElements . RelAggregatesBuildingStories ) ;
43354407 ExporterCacheManager . ContainmentCache . AddRelations ( buildingHnd , guid , buildingStories ) ;
43364408 }
4409+
4410+ // We didn't find a level for this. Put it in the IfcBuilding, IfcSite, or IfcProject later.
4411+ HashSet < IFCAnyHandle > orphanedHandles = levelInfoMapping . CollectOrphanedHandles ( ) ;
4412+
4413+ ExporterCacheManager . LevelInfoCache . OrphanedElements . UnionWith ( orphanedHandles ) ;
43374414 }
43384415
43394416 /// <summary>
0 commit comments