private static HandleAndAnalyzer CreateExtrusionWithClippingAndOpening(ExporterIFC exporterIFC, Element element,
            ElementId catId, Solid solid, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped,
            out bool hasClippingResult, out bool hasBooleanResult, out ElementId materialId)
        {
            completelyClipped = false;
            materialId = ElementId.InvalidElementId;
            hasClippingResult = false;
            hasBooleanResult = false;
            HandleAndAnalyzer nullVal = new HandleAndAnalyzer();
            HandleAndAnalyzer retVal = new HandleAndAnalyzer();

            try
            {
                ExtrusionAnalyzer elementAnalyzer = ExtrusionAnalyzer.Create(solid, plane, projDir);
                retVal.Analyzer = elementAnalyzer;

                Document document = element.Document;
                XYZ planeOrig = plane.Origin;
                XYZ baseLoopOffset = null;

                if (!MathUtil.IsAlmostZero(elementAnalyzer.StartParameter))
                    baseLoopOffset = elementAnalyzer.StartParameter * projDir;

                Face extrusionBase = elementAnalyzer.GetExtrusionBase();

                IList<GeometryUtil.FaceBoundaryType> boundaryTypes;
                IList<CurveLoop> extrusionBoundaryLoops =
                    GeometryUtil.GetFaceBoundaries(extrusionBase, baseLoopOffset, out boundaryTypes);

                // Return if we get any CurveLoops that are complex, as we don't want to export an approximation of the boundary here.
                foreach (GeometryUtil.FaceBoundaryType boundaryType in boundaryTypes)
                {
                    if (boundaryType == GeometryUtil.FaceBoundaryType.Complex)
                        return nullVal;
                }

                // Move base plane to start parameter location.
                Plane extrusionBasePlane = null;
                try
                {
                    extrusionBasePlane = extrusionBoundaryLoops[0].GetPlane();
                }
                catch
                {
                    return nullVal;
                }

                double extrusionLength = elementAnalyzer.EndParameter - elementAnalyzer.StartParameter;
                double baseOffset = extrusionBasePlane.Origin.DotProduct(projDir);
                IFCRange extrusionRange = new IFCRange(baseOffset, extrusionLength + baseOffset);

                double startParam = planeOrig.DotProduct(projDir);
                double endParam = planeOrig.DotProduct(projDir) + extrusionLength;
                if ((range != null) && (startParam >= range.End || endParam <= range.Start))
                {
                    completelyClipped = true;
                    return nullVal;
                }

                double scaledExtrusionDepth = UnitUtil.ScaleLength(extrusionLength);

                string profileName = null;
                if (element != null)
                {
                    ElementType type = element.Document.GetElement(element.GetTypeId()) as ElementType;
                    if (type != null)
                        profileName = type.Name;
                }

                // We use a sub-transaction here in case we are able to generate the base body but not the clippings.
                IFCFile file = exporterIFC.GetFile();
                IFCAnyHandle finalExtrusionBodyItemHnd = null;
                using (IFCTransaction tr = new IFCTransaction(file))
                {
                    // For creating the actual extrusion, we want to use the calculated extrusion plane, not the input plane.
                    IFCAnyHandle extrusionBodyItemHnd = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, profileName,
                        extrusionBoundaryLoops, extrusionBasePlane, projDir, scaledExtrusionDepth);
                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionBodyItemHnd))
                    {
                        retVal.BaseExtrusions.Add(extrusionBodyItemHnd);

                        finalExtrusionBodyItemHnd = extrusionBodyItemHnd;
                        IDictionary<ElementId, ICollection<ICollection<Face>>> elementCutouts =
                            GeometryUtil.GetCuttingElementFaces(element, elementAnalyzer);
                        foreach (KeyValuePair<ElementId, ICollection<ICollection<Face>>> elementCutoutsForElement in elementCutouts)
                        {
                            // process clippings first, then openings
                            ICollection<ICollection<Face>> unhandledElementCutoutsForElement = new List<ICollection<Face>>();
                            Element cuttingElement = document.GetElement(elementCutoutsForElement.Key);
                            foreach (ICollection<Face> elementCutout in elementCutoutsForElement.Value)
                            {
                                ICollection<Face> skippedFaces = null;
                                bool unhandledClipping = false;
                                try
                                {
                                    // The skippedFaces may represent openings that will be dealt with below.
                                    finalExtrusionBodyItemHnd = GeometryUtil.CreateClippingFromFaces(exporterIFC, cuttingElement,
                                        extrusionBasePlane, projDir,
                                        elementCutout, extrusionRange, finalExtrusionBodyItemHnd, out skippedFaces);
                                }
                                catch
                                {
                                    skippedFaces = null;
                                    unhandledClipping = true;
                                }

                                if (finalExtrusionBodyItemHnd == null || unhandledClipping)
                                {
                                    unhandledElementCutoutsForElement.Add(elementCutout);
                                }
                                else
                                {
                                    if (finalExtrusionBodyItemHnd != extrusionBodyItemHnd)
                                        hasClippingResult = true;

                                    // Even if we created a clipping, we may have faces to further process as openings.  
                                    if (skippedFaces != null && skippedFaces.Count != 0)
                                        unhandledElementCutoutsForElement.Add(skippedFaces);
                                }
                            }

                            IFCAnyHandle finalExtrusionClippingBodyItemHnd = finalExtrusionBodyItemHnd;
                            foreach (ICollection<Face> elementCutout in unhandledElementCutoutsForElement)
                            {
                                bool unhandledOpening = false;
                                try
                                {
                                    finalExtrusionBodyItemHnd = GeometryUtil.CreateOpeningFromFaces(exporterIFC, cuttingElement,
                                        extrusionBasePlane, projDir,
                                        elementCutout, extrusionRange, finalExtrusionBodyItemHnd);
                                }
                                catch
                                {
                                    unhandledOpening = true;
                                }

                                if (finalExtrusionBodyItemHnd == null || unhandledOpening)
                                {
                                    // Item is completely clipped.  We use this only when we are certain:
                                    // 1. finalExtrusionBodyItemHnd is null.
                                    // 2. range is not null (i.e., we expect the possibility of clipping)
                                    // 3. unhandledOpening is not true (i.e., we didn't abort the operation).
                                    // If completelyClipped is true, we won't export the item, so we want to make sure
                                    // that we don't actually want to try a backup method instead.
                                    completelyClipped = (finalExtrusionBodyItemHnd == null) && (range != null) && (!unhandledOpening);
                                    tr.RollBack();
                                    return nullVal;
                                }
                                else if (finalExtrusionBodyItemHnd != finalExtrusionClippingBodyItemHnd)
                                {
                                    hasBooleanResult = true;
                                }
                            }
                        }

                        materialId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solid, exporterIFC, element);
                        BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, extrusionBodyItemHnd, materialId);
                    }
                    tr.Commit();
                }

                retVal.Handle = finalExtrusionBodyItemHnd;
                return retVal;
            }
            catch
            {
                return nullVal;
            }
        }
        private static HandleAndAnalyzer CreateExtrusionWithClippingBase(ExporterIFC exporterIFC, Element element,
            ElementId catId, IList<Solid> solids, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped, out HashSet<ElementId> materialIds)
        {
            IFCFile file = exporterIFC.GetFile();
            bool mustUseTessellation = false;

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                completelyClipped = false;
                materialIds = new HashSet<ElementId>();
                HandleAndAnalyzer retVal = new HandleAndAnalyzer();
                HashSet<IFCAnyHandle> extrusionBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionBooleanBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionClippingBodyItems = new HashSet<IFCAnyHandle>();
                foreach (Solid solid in solids)
                {
                    bool hasClippingResult = false;
                    bool hasBooleanResult = false;
                    ElementId materialId = ElementId.InvalidElementId;
                    HandleAndAnalyzer currRetVal = CreateExtrusionWithClippingAndOpening(exporterIFC, element, catId, solid, plane, projDir, range,
                        out completelyClipped, out hasClippingResult, out hasBooleanResult, out materialId);

                    if (currRetVal != null && currRetVal.Handle != null)
                    {
                        // If any of the solid is of the type of Clipping or Boolean and export is for Reference View, exit the loop and collect the geometries entirely
                        //   for TriangulatedFaceSet
                        if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && (hasClippingResult || hasBooleanResult))
                        {
                            mustUseTessellation = true;
                            break;
                        }

                        materialIds.Add(materialId);
                        IFCAnyHandle repHandle = currRetVal.Handle;
                        if (hasBooleanResult) // if both have boolean and clipping result, use boolean one.
                            extrusionBooleanBodyItems.Add(repHandle);
                        else if (hasClippingResult)
                            extrusionClippingBodyItems.Add(repHandle);
                        else
                            extrusionBodyItems.Add(repHandle);
                    }
                    else
                    {
                        tr.RollBack();

                        // TODO: include this cleanup in RollBack(), to avoid issues.
                        ExporterCacheManager.MaterialIdToStyleHandleCache.RemoveInvalidHandles(materialIds, IFCEntityType.IfcSurfaceStyle);
                        ExporterCacheManager.PresentationStyleAssignmentCache.RemoveInvalidHandles(materialIds);
                        return retVal;
                    }

                    // currRetVal will only have one extrusion.  Use the analyzer from the "last" extrusion.  Should only really be used for one extrusion.
                    retVal.Analyzer = currRetVal.Analyzer;
                    retVal.BaseExtrusions.Add(currRetVal.BaseExtrusions[0]);
                }

                IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body");

                // Handle Tessellation here for Reference View export. If all are extrusions, it should not get in here
                if (mustUseTessellation)
                {
                    BodyExporterOptions options = new BodyExporterOptions(true);
                    Document document = element.Document;
                    ElementId materialId = ElementId.InvalidElementId;
                    materialIds.Clear();
                    extrusionBodyItems.Clear();

                    foreach (Solid solid in solids)
                    {
                        IFCAnyHandle triangulatedBodyItem = BodyExporter.ExportBodyAsTriangulatedFaceSet(exporterIFC, element, options, solid);
                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBodyItem))
                            extrusionBodyItems.Add(triangulatedBodyItem);
                        materialId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solid, exporterIFC, element);
                        materialIds.Add(materialId);
                        BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, triangulatedBodyItem, materialId);
                    }
                    retVal.Handle = RepresentationUtil.CreateTessellatedRep(exporterIFC, element, catId, contextOfItemsBody, extrusionBodyItems, null);
                    retVal.ShapeRepresentationType = ShapeRepresentationType.Tessellation;
                }
                else
                {
                    if (extrusionBodyItems.Count > 0 && (extrusionClippingBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                    {
                        retVal.Handle = RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, catId, contextOfItemsBody,
                            extrusionBodyItems, null);
                        retVal.ShapeRepresentationType = ShapeRepresentationType.SweptSolid;
                    }
                    else if (extrusionClippingBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                    {
                        retVal.Handle = RepresentationUtil.CreateClippingRep(exporterIFC, element, catId, contextOfItemsBody,
                            extrusionClippingBodyItems);
                        retVal.ShapeRepresentationType = ShapeRepresentationType.Clipping;
                    }
                    else if (extrusionBooleanBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionClippingBodyItems.Count == 0))
                    {
                        retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                            extrusionBooleanBodyItems);
                        retVal.ShapeRepresentationType = ShapeRepresentationType.CSG;
                    }
                    else
                    {
                        IFCAnyHandle finalBodyItemHnd = null;

                        ICollection<IFCAnyHandle> booleanBodyItems = extrusionClippingBodyItems.Union<IFCAnyHandle>(extrusionBooleanBodyItems).ToList();

                        finalBodyItemHnd = booleanBodyItems.ElementAt(0);
                        booleanBodyItems.Remove(finalBodyItemHnd);

                        // union non-boolean result first with a boolean result
                        foreach (IFCAnyHandle bodyRep in extrusionBodyItems)
                        {
                            finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                                 finalBodyItemHnd, bodyRep);
                        }

                        foreach (IFCAnyHandle bodyRep in booleanBodyItems)
                        {
                            finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                                 finalBodyItemHnd, bodyRep);
                        }

                        extrusionBodyItems.Clear();
                        extrusionBodyItems.Add(finalBodyItemHnd);

                        retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                            extrusionBodyItems);
                        retVal.ShapeRepresentationType = ShapeRepresentationType.CSG;
                    }
                }

                tr.Commit();
                return retVal;
            }
        }
Пример #3
0
        private static HandleAndAnalyzer CreateExtrusionWithClippingBase(ExporterIFC exporterIFC, Element element, 
            ElementId catId, Solid solid, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped)
        
        {
            completelyClipped = false;
            HandleAndAnalyzer nullVal = new HandleAndAnalyzer();
            HandleAndAnalyzer retVal = new HandleAndAnalyzer();

            try
            {
                ExtrusionAnalyzer elementAnalyzer = ExtrusionAnalyzer.Create(solid, plane, projDir);
                retVal.Analyzer = elementAnalyzer;

                IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body");
            
                IFCAnyHandle bodyRep = null;
                Document document = element.Document;
                double scale = exporterIFC.LinearScale;
                XYZ planeOrig = plane.Origin;
                XYZ baseLoopOffset = null;
          
                if (!MathUtil.IsAlmostZero(elementAnalyzer.StartParameter))
                    baseLoopOffset = elementAnalyzer.StartParameter * projDir;
                
                Face extrusionBase = elementAnalyzer.GetExtrusionBase();

                IList<GeometryUtil.FaceBoundaryType> boundaryTypes;
                IList<CurveLoop> extrusionBoundaryLoops =
                    GeometryUtil.GetFaceBoundaries(extrusionBase, baseLoopOffset, out boundaryTypes);

                // Return if we get any CurveLoops that are complex, as we don't want to export an approximation of the boundary here.
                foreach (GeometryUtil.FaceBoundaryType boundaryType in boundaryTypes)
                {
                    if (boundaryType == GeometryUtil.FaceBoundaryType.Complex)
                        return nullVal;
                }

                // Move base plane to start parameter location.
                Plane extrusionBasePlane = null;
                try
                {
                    extrusionBasePlane = extrusionBoundaryLoops[0].GetPlane();
                }
                catch
                {
                    return nullVal;
                }

                double extrusionLength = elementAnalyzer.EndParameter - elementAnalyzer.StartParameter;
                double baseOffset = extrusionBasePlane.Origin.DotProduct(projDir);
                IFCRange extrusionRange = new IFCRange(baseOffset, extrusionLength + baseOffset);

                double startParam = planeOrig.DotProduct(projDir);
                double endParam = planeOrig.DotProduct(projDir) + extrusionLength;
                if ((range != null) && (startParam >= range.End || endParam <= range.Start))
                {
                    completelyClipped = true;
                    return nullVal;
                }

                double scaledExtrusionDepth = extrusionLength * scale;

                string profileName = null;
                if (element != null)
                {
                    ElementType type = element.Document.GetElement(element.GetTypeId()) as ElementType;
                    if (type != null)
                        profileName = type.Name;
                }

                // We use a sub-transaction here in case we are able to generate the base body but not the clippings.
                IFCFile file = exporterIFC.GetFile();
                using (IFCTransaction tr = new IFCTransaction(file))
                {
                    // For creating the actual extrusion, we want to use the calculated extrusion plane, not the input plane.
                    IFCAnyHandle extrusionBodyItemHnd = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, profileName,
                        extrusionBoundaryLoops, extrusionBasePlane, projDir, scaledExtrusionDepth);
                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionBodyItemHnd))
                    {
                        IFCAnyHandle finalExtrusionBodyItemHnd = extrusionBodyItemHnd;
                        IDictionary<ElementId, ICollection<ICollection<Face>>> elementCutouts =
                            GeometryUtil.GetCuttingElementFaces(element, elementAnalyzer);
                        foreach (KeyValuePair<ElementId, ICollection<ICollection<Face>>> elementCutoutsForElement in elementCutouts)
                        {
                            Element cuttingElement = document.GetElement(elementCutoutsForElement.Key);
                            foreach (ICollection<Face> elementCutout in elementCutoutsForElement.Value)
                            {
                                bool unhandledClipping = false;
                                try
                                {
                                    finalExtrusionBodyItemHnd = GeometryUtil.ProcessFaceCollection(exporterIFC, cuttingElement, 
                                        extrusionBasePlane, projDir,
                                        elementCutout, extrusionRange, finalExtrusionBodyItemHnd);
                                }
                                catch
                                {
                                    unhandledClipping = true;
                                }

                                if (finalExtrusionBodyItemHnd == null || unhandledClipping)
                                {
                                    // Item is completely clipped.
                                    completelyClipped = (finalExtrusionBodyItemHnd == null);
                                    tr.RollBack();
                                    return nullVal;
                                }
                            }
                        }

                        IFCAnyHandle extrusionStyledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document,
                            extrusionBodyItemHnd, ElementId.InvalidElementId);

                        HashSet<IFCAnyHandle> extrusionBodyItems = new HashSet<IFCAnyHandle>();
                        extrusionBodyItems.Add(finalExtrusionBodyItemHnd);

                        if (extrusionBodyItemHnd == finalExtrusionBodyItemHnd)
                        {
                            bodyRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, catId, contextOfItemsBody,
                                extrusionBodyItems, null);
                        }
                        else
                        {
                            bodyRep = RepresentationUtil.CreateClippingRep(exporterIFC, element, catId, contextOfItemsBody,
                                extrusionBodyItems);
                        }
                    }
                    tr.Commit();
                }

                retVal.Handle = bodyRep;
                return retVal;
            }
            catch
            {
                return nullVal;
            }
        }
Пример #4
0
        private static HandleAndAnalyzer CreateExtrusionWithClippingBase(ExporterIFC exporterIFC, Element element, 
            ElementId catId, IList<Solid> solids, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped, out HashSet<ElementId> materialIds)
        {
            IFCFile file = exporterIFC.GetFile();
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                completelyClipped = false;
                materialIds = new HashSet<ElementId>();
                HandleAndAnalyzer retVal = new HandleAndAnalyzer();
                HashSet<IFCAnyHandle> extrusionBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionBooleanBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionClippingBodyItems = new HashSet<IFCAnyHandle>();
                foreach (Solid solid in solids)
                {
                    bool hasClippingResult = false;
                    bool hasBooleanResult = false;
                    ElementId materialId = ElementId.InvalidElementId;
                    retVal = CreateExtrsionWithClippingAndOpening(exporterIFC, element, catId, solid, plane, projDir, range,
                        out completelyClipped, out hasClippingResult, out hasBooleanResult, out materialId);

                    if (retVal != null && retVal.Handle != null)
                    {
                        materialIds.Add(materialId);
                        IFCAnyHandle repHandle = retVal.Handle;
                        if (hasBooleanResult) // if both have boolean and clipping result, use boolean one.
                            extrusionBooleanBodyItems.Add(repHandle);
                        else if (hasClippingResult)
                            extrusionClippingBodyItems.Add(repHandle);
                        else
                            extrusionBodyItems.Add(repHandle);

                    }
                    else
                    {
                        tr.RollBack();
                        return retVal;
                    }
                }

                IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body");

                if (extrusionBodyItems.Count > 0 && (extrusionClippingBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBodyItems, null);
                }
                else if (extrusionClippingBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateClippingRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionClippingBodyItems);
                }
                else if (extrusionBooleanBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionClippingBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBooleanBodyItems);
                }
                else
                {
                    IFCAnyHandle finalBodyItemHnd = null;

                    ICollection<IFCAnyHandle> booleanBodyItems = extrusionClippingBodyItems.Union<IFCAnyHandle>(extrusionBooleanBodyItems).ToList();

                    finalBodyItemHnd = booleanBodyItems.ElementAt(0);
                    booleanBodyItems.Remove(finalBodyItemHnd);

                    // union non-boolean result first with a boolean result
                    foreach (IFCAnyHandle bodyRep in extrusionBodyItems)
                    {
                        finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                             finalBodyItemHnd, bodyRep);
                    }

                    foreach (IFCAnyHandle bodyRep in booleanBodyItems)
                    {
                        finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                             finalBodyItemHnd, bodyRep);
                    }

                    extrusionBodyItems.Clear();
                    extrusionBodyItems.Add(finalBodyItemHnd);
                    retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBodyItems);
                }
                tr.Commit();
                return retVal;
            }
        }
Пример #5
0
        private static HandleAndAnalyzer CreateExtrusionWithClippingBase(ExporterIFC exporterIFC, Element element, 
            ElementId catId, IList<Solid> solids, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped, out HashSet<ElementId> materialIds)
        {
            IFCFile file = exporterIFC.GetFile();
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                completelyClipped = false;
                materialIds = new HashSet<ElementId>();
                HandleAndAnalyzer retVal = new HandleAndAnalyzer();
                HashSet<IFCAnyHandle> extrusionBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionBooleanBodyItems = new HashSet<IFCAnyHandle>();
                HashSet<IFCAnyHandle> extrusionClippingBodyItems = new HashSet<IFCAnyHandle>();
                foreach (Solid solid in solids)
                {
                    bool hasClippingResult = false;
                    bool hasBooleanResult = false;
                    ElementId materialId = ElementId.InvalidElementId;
                    HandleAndAnalyzer currRetVal = CreateExtrusionWithClippingAndOpening(exporterIFC, element, catId, solid, plane, projDir, range,
                        out completelyClipped, out hasClippingResult, out hasBooleanResult, out materialId);

                    if (currRetVal != null && currRetVal.Handle != null)
                    {
                        materialIds.Add(materialId);
                        IFCAnyHandle repHandle = currRetVal.Handle;
                        if (hasBooleanResult) // if both have boolean and clipping result, use boolean one.
                            extrusionBooleanBodyItems.Add(repHandle);
                        else if (hasClippingResult)
                            extrusionClippingBodyItems.Add(repHandle);
                        else
                            extrusionBodyItems.Add(repHandle);

                    }
                    else
                    {
                        tr.RollBack();

                        // TODO: include this cleanup in RollBack(), to avoid issues.
                        ExporterCacheManager.MaterialIdToStyleHandleCache.RemoveInvalidHandles(materialIds, IFCEntityType.IfcSurfaceStyle);
                        ExporterCacheManager.PresentationStyleAssignmentCache.RemoveInvalidHandles(materialIds);
                        return retVal;
                    }

                    // currRetVal will only have one extrusion.  Use the analyzer from the "last" extrusion.  Should only really be used for one extrusion.
                    retVal.Analyzer = currRetVal.Analyzer;
                    retVal.BaseExtrusions.Add(currRetVal.BaseExtrusions[0]);
                }

                IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body");

                if (extrusionBodyItems.Count > 0 && (extrusionClippingBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBodyItems, null);
                    retVal.ShapeRepresentationType = ShapeRepresentationType.SweptSolid;
                }
                else if (extrusionClippingBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionBooleanBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateClippingRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionClippingBodyItems);
                    retVal.ShapeRepresentationType = ShapeRepresentationType.Clipping;
                }
                else if (extrusionBooleanBodyItems.Count > 0 && (extrusionBodyItems.Count == 0 && extrusionClippingBodyItems.Count == 0))
                {
                    retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBooleanBodyItems);
                    retVal.ShapeRepresentationType = ShapeRepresentationType.CSG;
                }
                else
                {
                    IFCAnyHandle finalBodyItemHnd = null;

                    ICollection<IFCAnyHandle> booleanBodyItems = extrusionClippingBodyItems.Union<IFCAnyHandle>(extrusionBooleanBodyItems).ToList();

                    finalBodyItemHnd = booleanBodyItems.ElementAt(0);
                    booleanBodyItems.Remove(finalBodyItemHnd);

                    // union non-boolean result first with a boolean result
                    foreach (IFCAnyHandle bodyRep in extrusionBodyItems)
                    {
                        finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                             finalBodyItemHnd, bodyRep);
                    }

                    foreach (IFCAnyHandle bodyRep in booleanBodyItems)
                    {
                        finalBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Union,
                             finalBodyItemHnd, bodyRep);
                    }

                    extrusionBodyItems.Clear();
                    extrusionBodyItems.Add(finalBodyItemHnd);

                    retVal.Handle = RepresentationUtil.CreateCSGRep(exporterIFC, element, catId, contextOfItemsBody,
                        extrusionBodyItems);
                    retVal.ShapeRepresentationType = ShapeRepresentationType.CSG;
                }
                tr.Commit();
                return retVal;
            }
        }