/// <summary>
        /// Exports list of geometries to IFC body representation.
        /// </summary>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="geometryListIn">The geometry list.</param>
        /// <param name="options">The settings for how to export the body.</param>
        /// <param name="exportBodyParams">The extrusion creation data.</param>
        /// <returns>The BodyData containing the handle, offset and material ids.</returns>
        public static BodyData ExportBody(ExporterIFC exporterIFC,
            Element element,
            ElementId categoryId,
            ElementId overrideMaterialId,
            IList<GeometryObject> geometryList,
            BodyExporterOptions options,
            IFCExtrusionCreationData exportBodyParams)
        {
            BodyData bodyData = new BodyData();
            if (geometryList.Count == 0)
                return bodyData;

            Document document = element.Document;
            bool tryToExportAsExtrusion = options.TryToExportAsExtrusion;
            bool canExportSolidModelRep = tryToExportAsExtrusion && ExporterCacheManager.ExportOptionsCache.CanExportSolidModelRep;

            bool useCoarseTessellation = (ExporterCacheManager.ExportOptionsCache.LevelOfDetail < 4);
            bool allowAdvancedBReps = ExporterCacheManager.ExportOptionsCache.ExportAs4 && !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView;

            // We will try to export as a swept solid if the option is set, and we are either exporting to a schema that allows it,
            // or we are using a coarse tessellation, in which case we will export the swept solid as an optimzed BRep.
            bool tryToExportAsSweptSolid = options.TryToExportAsSweptSolid && (allowAdvancedBReps || useCoarseTessellation);

            // We will allow exporting swept solids as BReps or TriangulatedFaceSet if we are exporting to a schema before IFC4, or to a Reference View MVD, 
            // and we allow coarse representations.  In the future, we may allow more control here.
            // Note that we disable IFC4 because in IFC4, we will export it as a true swept solid instead, except for the Reference View MVD.
            bool tryToExportAsSweptSolidAsTessellation = tryToExportAsSweptSolid && useCoarseTessellation && !allowAdvancedBReps;

            IFCFile file = exporterIFC.GetFile();
            IFCAnyHandle contextOfItems = exporterIFC.Get3DContextHandle("Body");

            double eps = UnitUtil.ScaleLength(element.Document.Application.VertexTolerance);

            bool allFaces = true;
            foreach (GeometryObject geomObject in geometryList)
            {
                if (!(geomObject is Face))
                {
                    allFaces = false;
                    break;
                }
            }

            IList<IFCAnyHandle> bodyItems = new List<IFCAnyHandle>();
            IList<ElementId> materialIdsForExtrusions = new List<ElementId>();

            // This is a list of geometries that can be exported using the coarse facetation of the SweptSolidExporter.
            IList<KeyValuePair<int, SimpleSweptSolidAnalyzer>> exportAsBRep = new List<KeyValuePair<int, SimpleSweptSolidAnalyzer>>();

            IList<int> exportAsSweptSolid = new List<int>();
            IList<int> exportAsExtrusion = new List<int>();

            bool hasExtrusions = false;
            bool hasSweptSolids = false;
            bool hasSweptSolidsAsBReps = false;
            ShapeRepresentationType hasRepresentationType = ShapeRepresentationType.Undefined;

            BoundingBoxXYZ bbox = GeometryUtil.GetBBoxOfGeometries(geometryList);
            XYZ unscaledTrfOrig = new XYZ();

            int numItems = geometryList.Count;
            bool tryExtrusionAnalyzer = tryToExportAsExtrusion && (options.ExtrusionLocalCoordinateSystem != null) && (numItems == 1) && (geometryList[0] is Solid);
            bool supportOffsetTransformForExtrusions = !(tryExtrusionAnalyzer || tryToExportAsSweptSolidAsTessellation);
            bool useOffsetTransformForExtrusions = (options.AllowOffsetTransform && supportOffsetTransformForExtrusions && (exportBodyParams != null));

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                // generate "bottom corner" of bbox; create new local placement if passed in.
                // need to transform, but not scale, this point to make it the new origin.
                using (TransformSetter transformSetter = TransformSetter.Create())
                {
                    if (useOffsetTransformForExtrusions)
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, bbox, exportBodyParams, out unscaledTrfOrig);

                    // If we passed in an ExtrusionLocalCoordinateSystem, and we have 1 Solid, we will try to create an extrusion using the ExtrusionAnalyzer.
                    // If we succeed, we will skip the rest of the routine, otherwise we will try with the backup extrusion method.
                    // This doesn't yet create fallback information for solid models that are hybrid extrusions and BReps.
                    if (tryToExportAsExtrusion)
                    {
                        if (tryExtrusionAnalyzer)
                        {
                            using (IFCTransaction extrusionTransaction = new IFCTransaction(file))
                            {
                                Plane extrusionPlane = new Plane(
                                        options.ExtrusionLocalCoordinateSystem.BasisY,
                                        options.ExtrusionLocalCoordinateSystem.BasisZ,
                                        options.ExtrusionLocalCoordinateSystem.Origin);
                                XYZ extrusionDirection = options.ExtrusionLocalCoordinateSystem.BasisX;

                                bool completelyClipped;
                                HandleAndData extrusionData = ExtrusionExporter.CreateExtrusionWithClippingAndProperties(exporterIFC, element,
                                    CategoryUtil.GetSafeCategoryId(element), geometryList[0] as Solid, extrusionPlane,
                                    extrusionDirection, null, out completelyClipped);
                                if (!completelyClipped && !IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionData.Handle) && extrusionData.BaseExtrusions != null && extrusionData.BaseExtrusions.Count == 1)
                                {
                                    HashSet<ElementId> materialIds = extrusionData.MaterialIds;

                                    // We skip setting and getting the material id from the exporter as unnecessary.
                                    ElementId matIdFromGeom = (materialIds != null && materialIds.Count > 0) ? materialIds.First() : ElementId.InvalidElementId;
                                    ElementId matId = (overrideMaterialId != ElementId.InvalidElementId) ? overrideMaterialId : matIdFromGeom;

                                    bodyItems.Add(extrusionData.BaseExtrusions[0]);
                                    materialIdsForExtrusions.Add(matId);
                                    if (matId != ElementId.InvalidElementId)
                                        bodyData.AddMaterial(matId);
                                    bodyData.RepresentationHnd = extrusionData.Handle;
                                    bodyData.ShapeRepresentationType = extrusionData.ShapeRepresentationType;

                                    if (exportBodyParams != null && extrusionData.Data != null)
                                    {
                                        exportBodyParams.Slope = extrusionData.Data.Slope;
                                        exportBodyParams.ScaledLength = extrusionData.Data.ScaledLength;
                                        exportBodyParams.ExtrusionDirection = extrusionData.Data.ExtrusionDirection;

                                        exportBodyParams.ScaledHeight = extrusionData.Data.ScaledHeight;
                                        exportBodyParams.ScaledWidth = extrusionData.Data.ScaledWidth;

                                        exportBodyParams.ScaledArea = extrusionData.Data.ScaledArea;
                                        exportBodyParams.ScaledInnerPerimeter = extrusionData.Data.ScaledInnerPerimeter;
                                        exportBodyParams.ScaledOuterPerimeter = extrusionData.Data.ScaledOuterPerimeter;
                                    }

                                    hasExtrusions = true;
                                    extrusionTransaction.Commit();
                                }
                                else
                                {
                                    extrusionTransaction.RollBack();
                                }
                            }
                        }

                        // Only try if ExtrusionAnalyzer wasn't called, or failed.
                        if (!hasExtrusions)
                        {
                            // Check to see if we have Geometries or GFaces.
                            // We will have the specific all GFaces case and then the generic case.
                            IList<Face> faces = null;

                            if (allFaces)
                            {
                                faces = new List<Face>();
                                foreach (GeometryObject geometryObject in geometryList)
                                {
                                    faces.Add(geometryObject as Face);
                                }
                            }

                            // Options used if we try to export extrusions.
                            IFCExtrusionAxes axesToExtrudeIn = exportBodyParams != null ? exportBodyParams.PossibleExtrusionAxes : IFCExtrusionAxes.TryDefault;
                            XYZ directionToExtrudeIn = XYZ.Zero;
                            if (exportBodyParams != null && exportBodyParams.HasCustomAxis)
                                directionToExtrudeIn = exportBodyParams.CustomAxis;

                            double lengthScale = UnitUtil.ScaleLengthForRevitAPI();
                            IFCExtrusionCalculatorOptions extrusionOptions =
                               new IFCExtrusionCalculatorOptions(exporterIFC, axesToExtrudeIn, directionToExtrudeIn, lengthScale);

                            int numExtrusionsToCreate = allFaces ? 1 : geometryList.Count;

                            IList<IList<IFCExtrusionData>> extrusionLists = new List<IList<IFCExtrusionData>>();
                            for (int ii = 0; ii < numExtrusionsToCreate; ii++)
                            {
                                IList<IFCExtrusionData> extrusionList = new List<IFCExtrusionData>();

                                if (tryToExportAsExtrusion)
                                {
                                    if (allFaces)
                                        extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, faces);
                                    else
                                        extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, geometryList[ii]);
                                }

                                if (extrusionList.Count == 0)
                                {
                                    // If we are trying to create swept solids, we will keep going, but we won't try to create more extrusions unless we are also exporting a solid model.
                                    if (tryToExportAsSweptSolid)
                                    {
                                        if (!canExportSolidModelRep)
                                            tryToExportAsExtrusion = false;
                                        exportAsSweptSolid.Add(ii);
                                    }
                                    else if (!canExportSolidModelRep)
                                    {
                                        tryToExportAsExtrusion = false;
                                        break;
                                    }
                                    else
                                        exportAsBRep.Add(new KeyValuePair<int, SimpleSweptSolidAnalyzer>(ii, null));
                                }
                                else
                                {
                                    extrusionLists.Add(extrusionList);
                                    exportAsExtrusion.Add(ii);
                                }
                            }

                            int numCreatedExtrusions = extrusionLists.Count;
                            for (int ii = 0; ii < numCreatedExtrusions && tryToExportAsExtrusion; ii++)
                            {
                                int geomIndex = exportAsExtrusion[ii];

                                ElementId matId = SetBestMaterialIdInExporter(geometryList[geomIndex], element, overrideMaterialId, exporterIFC);
                                if (matId != ElementId.InvalidElementId)
                                    bodyData.AddMaterial(matId);

                                if (exportBodyParams != null && exportBodyParams.AreInnerRegionsOpenings)
                                {
                                    IList<CurveLoop> curveLoops = extrusionLists[ii][0].GetLoops();
                                    XYZ extrudedDirection = extrusionLists[ii][0].ExtrusionDirection;

                                    int numLoops = curveLoops.Count;
                                    for (int jj = numLoops - 1; jj > 0; jj--)
                                    {
                                        ExtrusionExporter.AddOpeningData(exportBodyParams, extrusionLists[ii][0], curveLoops[jj]);
                                        extrusionLists[ii][0].RemoveLoopAt(jj);
                                    }
                                }

                                bool exportedAsExtrusion = false;
                                IFCExtrusionBasis whichBasis = extrusionLists[ii][0].ExtrusionBasis;
                                if (whichBasis >= 0)
                                {
                                    IFCAnyHandle extrusionHandle = ExtrusionExporter.CreateExtrudedSolidFromExtrusionData(exporterIFC, element, extrusionLists[ii][0]);
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionHandle))
                                    {
                                        bodyItems.Add(extrusionHandle);
                                        materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());

                                        IList<CurveLoop> curveLoops = extrusionLists[ii][0].GetLoops();
                                        XYZ extrusionDirection = extrusionLists[ii][0].ExtrusionDirection;

                                        if (exportBodyParams != null)
                                        {
                                            exportBodyParams.Slope = GeometryUtil.GetSimpleExtrusionSlope(extrusionDirection, whichBasis);
                                            exportBodyParams.ScaledLength = extrusionLists[ii][0].ScaledExtrusionLength;
                                            exportBodyParams.ExtrusionDirection = extrusionDirection;
                                            for (int kk = 1; kk < extrusionLists[ii].Count; kk++)
                                            {
                                                ExtrusionExporter.AddOpeningData(exportBodyParams, extrusionLists[ii][kk]);
                                            }

                                            Plane plane = null;
                                            double height = 0.0, width = 0.0;
                                            if (ExtrusionExporter.ComputeHeightWidthOfCurveLoop(curveLoops[0], plane, out height, out width))
                                            {
                                                exportBodyParams.ScaledHeight = UnitUtil.ScaleLength(height);
                                                exportBodyParams.ScaledWidth = UnitUtil.ScaleLength(width);
                                            }

                                            double area = ExporterIFCUtils.ComputeAreaOfCurveLoops(curveLoops);
                                            if (area > 0.0)
                                            {
                                                exportBodyParams.ScaledArea = UnitUtil.ScaleArea(area);
                                            }

                                            double innerPerimeter = ExtrusionExporter.ComputeInnerPerimeterOfCurveLoops(curveLoops);
                                            double outerPerimeter = ExtrusionExporter.ComputeOuterPerimeterOfCurveLoops(curveLoops);
                                            if (innerPerimeter > 0.0)
                                                exportBodyParams.ScaledInnerPerimeter = UnitUtil.ScaleLength(innerPerimeter);
                                            if (outerPerimeter > 0.0)
                                                exportBodyParams.ScaledOuterPerimeter = UnitUtil.ScaleLength(outerPerimeter);
                                        }
                                        exportedAsExtrusion = true;
                                        hasExtrusions = true;
                                    }
                                }

                                if (!exportedAsExtrusion)
                                {
                                    if (tryToExportAsSweptSolid)
                                        exportAsSweptSolid.Add(ii);
                                    else if (!canExportSolidModelRep)
                                    {
                                        tryToExportAsExtrusion = false;
                                        break;
                                    }
                                    else
                                        exportAsBRep.Add(new KeyValuePair<int, SimpleSweptSolidAnalyzer>(ii, null));
                                }
                            }
                        }
                    }

                    if (tryToExportAsSweptSolid)
                    {
                        int numCreatedSweptSolids = exportAsSweptSolid.Count;
                        for (int ii = 0; (ii < numCreatedSweptSolids) && tryToExportAsSweptSolid; ii++)
                        {
                            bool exported = false;
                            int geomIndex = exportAsSweptSolid[ii];
                            Solid solid = geometryList[geomIndex] as Solid;
                            SimpleSweptSolidAnalyzer simpleSweptSolidAnalyzer = null;
                            // TODO: allFaces to SweptSolid
                            if (solid != null)
                            {
                                // TODO: give normal hint below if we have an idea.
                                simpleSweptSolidAnalyzer = SweptSolidExporter.CanExportAsSweptSolid(exporterIFC, solid, null);

                                // If we are exporting as a BRep, we will keep the analyzer for later, if it isn't null.
                                if (simpleSweptSolidAnalyzer != null)
                                {
                                    if (!tryToExportAsSweptSolidAsTessellation)
                                    {
                                        SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, simpleSweptSolidAnalyzer, solid);
                                        IFCAnyHandle sweptHandle = (sweptSolidExporter != null) ? sweptSolidExporter.RepresentationItem : null;

                                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(sweptHandle))
                                        {
                                            bodyItems.Add(sweptHandle);
                                            materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());
                                            exported = true;
                                            hasRepresentationType = sweptSolidExporter.RepresentationType;

                                            // These are the only two valid cases for true sweep export: either an extrusion or a sweep.
                                            // We don't expect regular BReps or triangulated face sets here.
                                            if (sweptSolidExporter.isSpecificRepresentationType(ShapeRepresentationType.SweptSolid))
                                                hasExtrusions = true;
                                            else if (sweptSolidExporter.isSpecificRepresentationType(ShapeRepresentationType.AdvancedSweptSolid))
                                                hasSweptSolids = true;
                                        }
                                        else
                                            simpleSweptSolidAnalyzer = null;    // Didn't work for some reason.
                                    }
                                }
                            }

                            if (!exported)
                            {
                                exportAsBRep.Add(new KeyValuePair<int, SimpleSweptSolidAnalyzer>(geomIndex, simpleSweptSolidAnalyzer));
                                hasSweptSolidsAsBReps |= (simpleSweptSolidAnalyzer != null);
                            }
                        }
                    }

                    bool exportSucceeded = (exportAsBRep.Count == 0) && (tryToExportAsExtrusion || tryToExportAsSweptSolid)
                                && (hasExtrusions || hasSweptSolids || hasRepresentationType != ShapeRepresentationType.Undefined);
                    if (exportSucceeded || canExportSolidModelRep)
                    {
                        int sz = bodyItems.Count();
                        for (int ii = 0; ii < sz; ii++)
                            BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, bodyItems[ii], materialIdsForExtrusions[ii]);

                        if (exportSucceeded)
                        {
                            if (bodyData.RepresentationHnd == null)
                            {
                                HashSet<IFCAnyHandle> bodyItemSet = new HashSet<IFCAnyHandle>();
                                bodyItemSet.UnionWith(bodyItems);
                                if (hasExtrusions && !hasSweptSolids)
                                {
                                    bodyData.RepresentationHnd =
                                        RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, bodyData.RepresentationHnd);
                                    bodyData.ShapeRepresentationType = ShapeRepresentationType.SweptSolid;
                                }
                                else if (hasSweptSolids && !hasExtrusions)
                                {
                                    bodyData.RepresentationHnd =
                                        RepresentationUtil.CreateAdvancedSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, bodyData.RepresentationHnd);
                                    bodyData.ShapeRepresentationType = ShapeRepresentationType.AdvancedSweptSolid;
                                }
                                else if (hasRepresentationType == ShapeRepresentationType.Tessellation)
                                {
                                    bodyData.RepresentationHnd =
                                        RepresentationUtil.CreateTessellatedRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, bodyData.RepresentationHnd);
                                    bodyData.ShapeRepresentationType = ShapeRepresentationType.Tessellation;
                                }
                                else
                                {
                                    bodyData.RepresentationHnd =
                                        RepresentationUtil.CreateSolidModelRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet);
                                    bodyData.ShapeRepresentationType = ShapeRepresentationType.SolidModel;
                                }
                            }

                            // TODO: include BRep, CSG, Clipping

                            XYZ lpOrig = ((bodyData != null) && (bodyData.OffsetTransform != null)) ? bodyData.OffsetTransform.Origin : new XYZ();
                            transformSetter.CreateLocalPlacementFromOffset(exporterIFC, bbox, exportBodyParams, lpOrig, unscaledTrfOrig);
                            tr.Commit();
                            return bodyData;
                        }
                    }

                    // If we are going to export a solid model, keep the created items.
                    if (!canExportSolidModelRep)
                        tr.RollBack();
                    else
                        tr.Commit();
                }

                // We couldn't export it as an extrusion; export as a solid, brep, or a surface model.
                if (!canExportSolidModelRep)
                {
                    exportAsExtrusion.Clear();
                    bodyItems.Clear();
                    if (exportBodyParams != null)
                        exportBodyParams.ClearOpenings();
                }

                if (exportAsExtrusion.Count == 0)
                {
                    // We used to clear exportAsBRep, but we need the SimpleSweptSolidAnalyzer information, so we will fill out the rest.
                    int numGeoms = geometryList.Count;
                    IList<KeyValuePair<int, SimpleSweptSolidAnalyzer>> newExportAsBRep = new List<KeyValuePair<int, SimpleSweptSolidAnalyzer>>(numGeoms);
                    int exportAsBRepCount = exportAsBRep.Count;
                    int currIndex = 0;
                    for (int ii = 0; ii < numGeoms; ii++)
                    {
                        if ((currIndex < exportAsBRepCount) && (ii == exportAsBRep[currIndex].Key))
                        {
                            newExportAsBRep.Add(exportAsBRep[currIndex]);
                            currIndex++;
                        }
                        else
                            newExportAsBRep.Add(new KeyValuePair<int, SimpleSweptSolidAnalyzer>(ii, null));
                    }
                    exportAsBRep = newExportAsBRep;
                }
            }

            // If we created some extrusions that we are using (e.g., creating a solid model), and we didn't use an offset transform for the extrusions, don't do it here either.
            bool supportOffsetTransformForBreps = !hasSweptSolidsAsBReps;
            bool disallowOffsetTransformForBreps = (exportAsExtrusion.Count > 0) && !useOffsetTransformForExtrusions;
            bool useOffsetTransformForBReps = options.AllowOffsetTransform && supportOffsetTransformForBreps && !disallowOffsetTransformForBreps;

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                using (TransformSetter transformSetter = TransformSetter.Create())
                {
                    // Need to do extra work to support offset transforms if we are using the sweep analyzer.
                    if (useOffsetTransformForBReps)
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, bbox, exportBodyParams, out unscaledTrfOrig);

                    BodyData brepBodyData =
                        ExportBodyAsBRep(exporterIFC, geometryList, exportAsBRep, bodyItems, element, categoryId, overrideMaterialId, contextOfItems, eps, options, bodyData);
                    if (brepBodyData == null)
                        tr.RollBack();
                    else
                    {
                        XYZ lpOrig = ((bodyData != null) && (bodyData.OffsetTransform != null)) ? bodyData.OffsetTransform.Origin : new XYZ();
                        transformSetter.CreateLocalPlacementFromOffset(exporterIFC, bbox, exportBodyParams, lpOrig, unscaledTrfOrig);
                        tr.Commit();
                    }
                    return brepBodyData;
                }
            }
        }
        // NOTE: the useMappedGeometriesIfPossible and useGroupsIfPossible options are experimental and do not yet work well.
        // In shipped code, these are always false, and should be kept false until API support routines are proved to be reliable.
        private static BodyData ExportBodyAsBRep(ExporterIFC exporterIFC, IList<GeometryObject> splitGeometryList,
            IList<KeyValuePair<int, SimpleSweptSolidAnalyzer>> exportAsBRep, IList<IFCAnyHandle> bodyItems,
            Element element, ElementId categoryId, ElementId overrideMaterialId, IFCAnyHandle contextOfItems, double eps,
            BodyExporterOptions options, BodyData bodyDataIn)
        {
            bool exportAsBReps = true;
            bool hasTriangulatedGeometry = false;
            bool hasAdvancedBrepGeometry = false;
            IFCFile file = exporterIFC.GetFile();
            Document document = element.Document;

            // Can't use the optimization functions below if we already have partially populated our body items with extrusions.
            int numExtrusions = bodyItems.Count;
            bool useMappedGeometriesIfPossible = options.UseMappedGeometriesIfPossible && (numExtrusions != 0);
            bool useGroupsIfPossible = options.UseGroupsIfPossible && (numExtrusions != 0);

            IList<HashSet<IFCAnyHandle>> currentFaceHashSetList = new List<HashSet<IFCAnyHandle>>();
            IList<int> startIndexForObject = new List<int>();

            BodyData bodyData = new BodyData(bodyDataIn);

            IDictionary<SolidMetrics, HashSet<Solid>> solidMappingGroups = null;
            IList<KeyValuePair<int, Transform>> solidMappings = null;
            IList<ElementId> materialIds = new List<ElementId>();

            // This should currently be always false in shipped code.
            if (useMappedGeometriesIfPossible)
            {
                IList<GeometryObject> newGeometryList = null;
                useMappedGeometriesIfPossible = GatherMappedGeometryGroupings(splitGeometryList, out newGeometryList, out solidMappingGroups, out solidMappings);
                if (useMappedGeometriesIfPossible && (newGeometryList != null))
                    splitGeometryList = newGeometryList;
            }

            BodyGroupKey groupKey = null;
            BodyGroupData groupData = null;
            // This should currently be always false in shipped code.
            if (useGroupsIfPossible)
            {
                BodyData bodyDataOut = null;
                useGroupsIfPossible = ProcessGroupMembership(exporterIFC, file, element, categoryId, contextOfItems, splitGeometryList, bodyData,
                    out groupKey, out groupData, out bodyDataOut);
                if (bodyDataOut != null)
                    return bodyDataOut;
                if (useGroupsIfPossible)
                    useMappedGeometriesIfPossible = true;
            }

            bool isCoarse = (options.TessellationLevel == BodyExporterOptions.BodyTessellationLevel.Coarse);

            int numBRepsToExport = exportAsBRep.Count;
            bool selectiveBRepExport = (numBRepsToExport > 0);
            int numGeoms = selectiveBRepExport ? numBRepsToExport : splitGeometryList.Count;

            bool canExportAsAdvancedGeometry = ExporterCacheManager.ExportOptionsCache.ExportAs4DesignTransferView;
            bool canExportAsTessellatedFaceSet = ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView;

            // We will cycle through all of the geometries one at a time, doing the best export we can for each.
            for (int index = 0; index < numGeoms; index++)
            {
                int brepIndex = selectiveBRepExport ? exportAsBRep[index].Key : index;
                SimpleSweptSolidAnalyzer currAnalyzer = selectiveBRepExport ? exportAsBRep[index].Value : null;

                GeometryObject geomObject = selectiveBRepExport ? splitGeometryList[brepIndex] : splitGeometryList[index];

                // A simple test to see if the geometry is a valid solid.  This will save a lot of time in CanCreateClosedShell later.
                if (exportAsBReps && (geomObject is Solid))
                {
                    try
                    {
                        // We don't care what the value is here.  What we care about is whether or not it can be calculated.  If it can't be calculated,
                        // it is probably not a valid solid.
                        double volume = (geomObject as Solid).Volume;

                        // Current code should already prevent 0 volume solids from coming here, but may as well play it safe.
                        if (volume <= MathUtil.Eps())
                            exportAsBReps = false;
                    }
                    catch
                    {
                        exportAsBReps = false;
                    }
                }

                startIndexForObject.Add(currentFaceHashSetList.Count);

                ElementId materialId = SetBestMaterialIdInExporter(geomObject, element, overrideMaterialId, exporterIFC);
                materialIds.Add(materialId);
                bodyData.AddMaterial(materialId);

                bool alreadyExported = false;

                // First, see if this could be represented as a simple swept solid.
                if (exportAsBReps && (currAnalyzer != null))
                {
                    SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, currAnalyzer, geomObject);
                    HashSet<IFCAnyHandle> facetHnds = (sweptSolidExporter != null) ? sweptSolidExporter.Facets : null;
                    if (facetHnds != null && facetHnds.Count != 0)
                    {
                        currentFaceHashSetList.Add(facetHnds);
                        alreadyExported = true;
                    }
                }

                // Next, try to represent as an AdvancedBRep.
                if (!alreadyExported && canExportAsAdvancedGeometry)
                {
                    IFCAnyHandle advancedBrepBodyItem = ExportBodyAsAdvancedBrep(exporterIFC, element, options, geomObject);
                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(advancedBrepBodyItem))
                    {
                        bodyItems.Add(advancedBrepBodyItem);
                        alreadyExported = true;
                        hasAdvancedBrepGeometry = true;
                    }
                }

                // If we are using the Reference View, try a triangulated face set.
                // In theory, we could export a tessellated face set for geometry in the Design Transfer View that failed above.
                // However, FacetedBReps do hold more information (and aren't only triangles).
                if (!alreadyExported && canExportAsTessellatedFaceSet)
                {
                    IFCAnyHandle triangulatedBodyItem = ExportBodyAsTriangulatedFaceSet(exporterIFC, element, options, geomObject);
                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBodyItem))
                    {
                        bodyItems.Add(triangulatedBodyItem);
                        alreadyExported = true;
                        hasTriangulatedGeometry = true;
                    }

                    // We should log here that we couldn't export a geometry.  We aren't allowed to use the traditional methods below.
                    if (!alreadyExported)
                        continue;
                }

                // If the above options do not generate any body, do the traditional step for Brep
                if (!alreadyExported && (exportAsBReps || isCoarse))
                    alreadyExported = ExportBodyAsSolid(exporterIFC, element, options, currentFaceHashSetList, geomObject);

                // If all else fails, use the internal routine to go through the faces.  This will likely create a surface model.
                if (!alreadyExported)
                {
                    IFCGeometryInfo faceListInfo = IFCGeometryInfo.CreateFaceGeometryInfo(eps, isCoarse);
                    ExporterIFCUtils.CollectGeometryInfo(exporterIFC, faceListInfo, geomObject, XYZ.Zero, false);

                    IList<ICollection<IFCAnyHandle>> faceSetList = faceListInfo.GetFaces();

                    int numBReps = faceSetList.Count;
                    if (numBReps == 0)
                        continue;

                    foreach (ICollection<IFCAnyHandle> currentFaceSet in faceSetList)
                    {
                        if (currentFaceSet.Count == 0)
                            continue;

                        if (exportAsBReps)
                        {
                            bool canExportAsClosedShell = (currentFaceSet.Count >= 4);
                            if (canExportAsClosedShell)
                            {
                                if ((geomObject is Mesh) && (numBReps == 1))
                                {
                                    // use optimized version.
                                    canExportAsClosedShell = CanCreateClosedShell(geomObject as Mesh);
                                }
                                else
                                {
                                    canExportAsClosedShell = CanCreateClosedShell(currentFaceSet);
                                }
                            }

                            if (!canExportAsClosedShell)
                            {
                                exportAsBReps = false;

                                // We'll need to invalidate the extrusions we created and replace them with BReps.
                                if (selectiveBRepExport && (numGeoms != splitGeometryList.Count))
                                {
                                    for (int fixIndex = 0; fixIndex < numExtrusions; fixIndex++)
                                    {
                                        bodyItems[0].Delete();
                                        bodyItems.RemoveAt(0);
                                    }
                                    numExtrusions = 0;
                                    numGeoms = splitGeometryList.Count;
                                    int currBRepIndex = 0;
                                    for (int fixIndex = 0; fixIndex < numGeoms; fixIndex++)
                                    {
                                        bool outOfRange = (currBRepIndex >= numBRepsToExport);
                                        if (!outOfRange && (exportAsBRep[currBRepIndex].Key == fixIndex))
                                        {
                                            currBRepIndex++;
                                            continue;
                                        }
                                        SimpleSweptSolidAnalyzer fixAnalyzer = outOfRange ? null : exportAsBRep[currBRepIndex].Value;
                                        exportAsBRep.Add(new KeyValuePair<int, SimpleSweptSolidAnalyzer>(fixIndex, fixAnalyzer));
                                    }
                                    numBRepsToExport = exportAsBRep.Count;
                                }
                            }
                        }

                        currentFaceHashSetList.Add(new HashSet<IFCAnyHandle>(currentFaceSet));
                    }
                }
            }

            if (hasTriangulatedGeometry)
            {
                HashSet<IFCAnyHandle> bodyItemSet = new HashSet<IFCAnyHandle>();
                bodyItemSet.UnionWith(bodyItems);
                if (bodyItemSet.Count > 0)
                {
                    bodyData.RepresentationHnd = RepresentationUtil.CreateTessellatedRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, null);
                    bodyData.ShapeRepresentationType = ShapeRepresentationType.Tessellation;
                }
            }
            else if (hasAdvancedBrepGeometry)
            {
                HashSet<IFCAnyHandle> bodyItemSet = new HashSet<IFCAnyHandle>();
                bodyItemSet.UnionWith(bodyItems);
                if (bodyItemSet.Count > 0)
                {
                    bodyData.RepresentationHnd = RepresentationUtil.CreateAdvancedBRepRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, null);
                    bodyData.ShapeRepresentationType = ShapeRepresentationType.AdvancedBrep;
                }
            }
            else
            {
                startIndexForObject.Add(currentFaceHashSetList.Count);  // end index for last object.

                IList<IFCAnyHandle> repMapItems = new List<IFCAnyHandle>();

                int size = currentFaceHashSetList.Count;
                if (exportAsBReps)
                {
                    int matToUse = -1;
                    for (int ii = 0; ii < size; ii++)
                    {
                        if (startIndexForObject[matToUse + 1] == ii)
                            matToUse++;
                        HashSet<IFCAnyHandle> currentFaceHashSet = currentFaceHashSetList[ii];
                        ElementId currMatId = materialIds[matToUse];

                        IFCAnyHandle faceOuter = IFCInstanceExporter.CreateClosedShell(file, currentFaceHashSet);
                        IFCAnyHandle brepHnd = RepresentationUtil.CreateFacetedBRep(exporterIFC, document, faceOuter, currMatId);

                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(brepHnd))
                        {
                            if (useMappedGeometriesIfPossible)
                            {
                                IFCAnyHandle currMappedRepHnd = CreateBRepRepresentationMap(exporterIFC, file, element, categoryId, contextOfItems, brepHnd);
                                repMapItems.Add(currMappedRepHnd);

                                IFCAnyHandle mappedItemHnd = ExporterUtil.CreateDefaultMappedItem(file, currMappedRepHnd);
                                bodyItems.Add(mappedItemHnd);
                            }
                            else
                                bodyItems.Add(brepHnd);
                        }
                    }
                }
                else
                {
                    IDictionary<ElementId, HashSet<IFCAnyHandle>> faceSets = new Dictionary<ElementId, HashSet<IFCAnyHandle>>();
                    int matToUse = -1;
                    for (int ii = 0; ii < size; ii++)
                    {
                        HashSet<IFCAnyHandle> currentFaceHashSet = currentFaceHashSetList[ii];
                        if (startIndexForObject[matToUse + 1] == ii)
                            matToUse++;

                        IFCAnyHandle faceSetHnd = IFCInstanceExporter.CreateConnectedFaceSet(file, currentFaceHashSet);
                        if (useMappedGeometriesIfPossible)
                        {
                            IFCAnyHandle currMappedRepHnd = CreateSurfaceRepresentationMap(exporterIFC, file, element, categoryId, contextOfItems, faceSetHnd);
                            repMapItems.Add(currMappedRepHnd);

                            IFCAnyHandle mappedItemHnd = ExporterUtil.CreateDefaultMappedItem(file, currMappedRepHnd);
                            bodyItems.Add(mappedItemHnd);
                        }
                        else
                        {
                            HashSet<IFCAnyHandle> surfaceSet = null;
                            if (faceSets.TryGetValue(materialIds[matToUse], out surfaceSet))
                            {
                                surfaceSet.Add(faceSetHnd);
                            }
                            else
                            {
                                surfaceSet = new HashSet<IFCAnyHandle>();
                                surfaceSet.Add(faceSetHnd);
                                faceSets[materialIds[matToUse]] = surfaceSet;
                            }
                        }
                    }

                    if (faceSets.Count > 0)
                    {
                        foreach (KeyValuePair<ElementId, HashSet<IFCAnyHandle>> faceSet in faceSets)
                        {
                            IFCAnyHandle surfaceModel = IFCInstanceExporter.CreateFaceBasedSurfaceModel(file, faceSet.Value);
                            BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, surfaceModel, faceSet.Key);

                            bodyItems.Add(surfaceModel);
                        }
                    }
                }

                if (bodyItems.Count == 0)
                    return bodyData;

                // Add in mapped items.
                if (useMappedGeometriesIfPossible && (solidMappings != null))
                {
                    foreach (KeyValuePair<int, Transform> mappedItem in solidMappings)
                    {
                        for (int idx = startIndexForObject[mappedItem.Key]; idx < startIndexForObject[mappedItem.Key + 1]; idx++)
                        {
                            IFCAnyHandle mappedItemHnd = ExporterUtil.CreateMappedItemFromTransform(file, repMapItems[idx], mappedItem.Value);
                            bodyItems.Add(mappedItemHnd);
                        }
                    }
                }

                HashSet<IFCAnyHandle> bodyItemSet = new HashSet<IFCAnyHandle>();
                bodyItemSet.UnionWith(bodyItems);
                if (useMappedGeometriesIfPossible)
                {
                    bodyData.RepresentationHnd = RepresentationUtil.CreateBodyMappedItemRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet);
                }
                else if (exportAsBReps)
                {
                    if (numExtrusions > 0)
                        bodyData.RepresentationHnd = RepresentationUtil.CreateSolidModelRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet);
                    else
                        bodyData.RepresentationHnd = RepresentationUtil.CreateBRepRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet);
                }
                else
                    bodyData.RepresentationHnd = RepresentationUtil.CreateSurfaceRep(exporterIFC, element, categoryId, contextOfItems, bodyItemSet, false, null);

                if (useGroupsIfPossible && (groupKey != null) && (groupData != null))
                {
                    groupData.Handles = repMapItems;
                    ExporterCacheManager.GroupElementGeometryCache.Register(groupKey, groupData);
                }

                bodyData.ShapeRepresentationType = ShapeRepresentationType.Brep;
            }

            return bodyData;
        }