/// <summary>
        /// Exports list of geometries to IFC body representation.
        /// </summary>
        /// <param name="application">The Revit application.</param>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="geometryList">The geometry list.</param>
        /// <param name="tryToExportAsExtrusion">True if try to export as extrusion.</param>
        /// <param name="exportBodyParams">The extrusion creation data.</param>
        /// <returns>The representation handle.</returns>
        public static IFCAnyHandle ExportBody(Autodesk.Revit.ApplicationServices.Application application, ExporterIFC exporterIFC, ElementId categoryId,
           IList<GeometryObject> geometryList, bool tryToExportAsExtrusion,
           IFCExtrusionCreationData exportBodyParams)
        {
            IFCAnyHandle body = IFCAnyHandle.Create();

            int sizeOfGeometry = geometryList.Count;
            if (sizeOfGeometry == 0)
                return body;

            IFCFile file = exporterIFC.GetFile();
            IFCAnyHandle contextOfItems = exporterIFC.Get3DContextHandle();
            double scale = exporterIFC.LinearScale;

            double eps = application.VertexTolerance * scale;

            HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
            if (tryToExportAsExtrusion)
            {
                // 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 = new List<Face>();
                bool allFaces = true;
                foreach (GeometryObject geometryObject in geometryList)
                {
                    if (geometryObject is Face)
                        faces.Add(geometryObject as Face);
                    else
                    {
                        allFaces = false;
                        break;
                    }
                }

                int numExtrusionsToCreate = allFaces ? 1 : sizeOfGeometry;

                try
                {
                    for (int ii = 0; ii < numExtrusionsToCreate && tryToExportAsExtrusion; ii++)
                    {
                        SetBestMaterialIdInExporter(geometryList[ii], exporterIFC);

                        IList<IFCExtrusionData> extrusionList = new List<IFCExtrusionData>();

                        IFCExtrusionAxes axesToExtrudeIn = exportBodyParams != null ? exportBodyParams.PossibleExtrusionAxes : IFCExtrusionAxes.TryDefault;
                        XYZ directionToExtrudeIn = XYZ.Zero;
                        if (exportBodyParams != null && exportBodyParams.HasCustomAxis)
                            directionToExtrudeIn = exportBodyParams.CustomAxis;

                        IFCExtrusionCalculatorOptions extrusionOptions =
                           new IFCExtrusionCalculatorOptions(exporterIFC, axesToExtrudeIn, directionToExtrudeIn,
                              scale);

                        bool canExportAsExtrusion = false;
                        if (allFaces)
                            extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, faces);
                        else
                            extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, geometryList[ii]);

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

                                int numLoops = curveLoops.Count;
                                for (int jj = numLoops - 1; jj > 0; jj--)
                                {
                                    AddOpeningData(exportBodyParams, extrusionList[0], curveLoops[jj]);
                                }
                                extrusionList[0].ClearLoops();
                            }

                            canExportAsExtrusion = false;
                            IFCAnyHandle extrusionHandle = CreateExtrudedSolidFromExtrusionData(exporterIFC, categoryId, extrusionList[0]);
                            if (extrusionHandle.HasValue)
                            {
                                bodyItems.Add(extrusionHandle);

                                IFCExtrusionBasis whichBasis = extrusionList[0].ExtrusionBasis;
                                IList<CurveLoop> curveLoops = extrusionList[0].GetLoops();
                                XYZ extrusionDirection = extrusionList[0].ExtrusionDirection;

                                canExportAsExtrusion = (whichBasis >= 0);

                                if (exportBodyParams != null)
                                {
                                    double zOff = (whichBasis == IFCExtrusionBasis.BasisZ) ? (1.0 - Math.Abs(extrusionDirection[2])) : Math.Abs(extrusionDirection[2]);
                                    double scaledAngle = Math.Asin(zOff) * 180 / Math.PI;
                                    exportBodyParams.Slope = scaledAngle;
                                    exportBodyParams.ScaledLength = extrusionList[0].ScaledExtrusionLength;
                                    exportBodyParams.CustomAxis = extrusionDirection;
                                    for (int kk = 1; kk < extrusionList.Count; kk++)
                                    {
                                        AddOpeningData(exportBodyParams, extrusionList[kk]);
                                    }

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

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

                                    double innerPerimeter = ComputeInnerPerimeterOfCurveLoops(curveLoops);
                                    double outerPerimeter = ComputeOuterPerimeterOfCurveLoops(curveLoops);
                                    if (innerPerimeter > 0.0)
                                        exportBodyParams.ScaledInnerPerimeter = innerPerimeter * scale;
                                    if (outerPerimeter > 0.0)
                                        exportBodyParams.ScaledOuterPerimeter = outerPerimeter * scale;
                                }
                            }
                            else
                            {
                                if (exportBodyParams != null)
                                    exportBodyParams.ClearOpenings();
                            }
                        }
                        else
                        {
                            tryToExportAsExtrusion = false;
                        }
                    }
                }
                catch
                {
                    tryToExportAsExtrusion = false;
                }

                if (tryToExportAsExtrusion)
                {
                    body = RepresentationUtil.CreateSweptSolidRep(exporterIFC, categoryId, contextOfItems, bodyItems, body);
                    return body;
                }
            }

            // We couldn't export it as an extrusion; export as a faceted solid or a surface model.
            DeleteHandles(bodyItems);
            bodyItems.Clear();

            // 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 (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
            {
                if (exportBodyParams != null)
                    transformSetter.InitializeFromBoundingBox(exporterIFC, geometryList, exportBodyParams);

                HashSet<IFCAnyHandle> faceSet = new HashSet<IFCAnyHandle>(); // for export as surface.
                bool exportAsBReps = true;

                foreach (GeometryObject geometryObject in geometryList)
                {
                    SetBestMaterialIdInExporter(geometryObject, exporterIFC);

                    IFCGeometryInfo faceListInfo = IFCGeometryInfo.CreateFaceGeometryInfo(eps);
                    ExporterIFCUtils.CollectGeometryInfo(exporterIFC, faceListInfo, geometryObject, 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)
                        {
                            if ((currentFaceSet.Count < 4) || !CanCreateClosedShell(currentFaceSet))
                            {
                                // fix all previous bodyItems.
                                exportAsBReps = false;
                                foreach (IFCAnyHandle bodyItem in bodyItems)
                                {
                                    if (bodyItem.HasValue)
                                    {
                                        IFCAnyHandle closedShellHnd = IFCGeometryUtils.GetFaceOuterBoundary(bodyItem);
                                        if (closedShellHnd.HasValue)
                                        {
                                            ICollection<IFCAnyHandle> faces = IFCGeometryUtils.GetClosedShellFaces(closedShellHnd);
                                            IFCAnyHandle faceSetHnd = file.CreateConnectedFaceSet(faces);
                                            faceSet.Add(faceSetHnd); // only one item.
                                            closedShellHnd.Delete();
                                        }
                                        bodyItem.Delete();
                                    }
                                }
                                bodyItems.Clear();
                            }
                        }

                        if (exportAsBReps)
                        {
                            IFCAnyHandle faceOuter = file.CreateClosedShell(currentFaceSet);
                            IFCAnyHandle brepHnd = RepresentationUtil.CreateFacetedBRep(exporterIFC, faceOuter);
                            if (brepHnd.HasValue)
                                bodyItems.Add(brepHnd); // only one item.
                        }
                        else
                        {
                            // TODO: add layer assignment info.
                            IFCAnyHandle faceSetHnd = file.CreateConnectedFaceSet(currentFaceSet);
                            faceSet.Add(faceSetHnd); // only one item.
                        }
                    }
                }

                if (faceSet.Count > 0)
                {
                    IFCAnyHandle surfaceModel = file.CreateFaceBasedSurfaceModel(faceSet);
                    bodyItems.Add(surfaceModel); // only one item.
                }

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

                if (exportAsBReps)
                    body = RepresentationUtil.CreateBRepRep(exporterIFC, categoryId, contextOfItems, bodyItems);
                else
                    body = RepresentationUtil.CreateSurfaceRep(exporterIFC, categoryId, contextOfItems, bodyItems, false);

                return body;
            }
        }
        /// <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;
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Exports list of geometries to IFC body representation.
        /// </summary>
        /// <param name="application">The Revit application.</param>
        /// <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(Autodesk.Revit.ApplicationServices.Application application, 
            ExporterIFC exporterIFC,
            Element element, 
            ElementId categoryId,
            IList<GeometryObject> geometryListIn,
            BodyExporterOptions options,
            IFCExtrusionCreationData exportBodyParams) 
        {
            BodyData bodyData = new BodyData();
            if (geometryListIn.Count == 0)
                return bodyData;

            Document document = element.Document;
            bool tryToExportAsExtrusion = options.TryToExportAsExtrusion;
            bool canExportSolidModelRep = false;

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

            double eps = application.VertexTolerance * scale;

            IList<GeometryObject> splitGeometryList = new List<GeometryObject>();

            bool allFaces = true;
            foreach (GeometryObject geomObject in geometryListIn)
            {
                try
                {
                    bool split = false;
                    if (geomObject is Solid)
                    {
                        Solid solid = geomObject as Solid;
                        IList<Solid> splitVolumes = SolidUtils.SplitVolumes(solid);
                        allFaces = false;

                        if (splitVolumes != null && splitVolumes.Count != 0)
                        {
                            split = true;
                            foreach (Solid currSolid in splitVolumes)
                            {
                                splitGeometryList.Add(currSolid);
                                // The geometry element created by SplitVolumesis a copy which will have its own allocated
                                // membership - this needs to be stored and disposed of (see AllocatedGeometryObjectCache
                                // for details)
                                ExporterCacheManager.AllocatedGeometryObjectCache.AddGeometryObject(currSolid);
                            }
                        }
                    }
                    else if (allFaces && !(geomObject is Face))
                        allFaces = false;

                    if (!split)
                        splitGeometryList.Add(geomObject);
                }
                catch
                {
                    splitGeometryList.Add(geomObject);
                }
            }

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

            IList<int> exportAsBRep = new List<int>();
            IList<int> exportAsExtrusion = new List<int>();
            
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                if (tryToExportAsExtrusion)
                {
                    // 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 splitGeometryList)
                        {
                            faces.Add(geometryObject as Face);
                        }
                    }

                    int numExtrusionsToCreate = allFaces ? 1 : splitGeometryList.Count;

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

                        IFCExtrusionAxes axesToExtrudeIn = exportBodyParams != null ? exportBodyParams.PossibleExtrusionAxes : IFCExtrusionAxes.TryDefault;
                        XYZ directionToExtrudeIn = XYZ.Zero;
                        if (exportBodyParams != null && exportBodyParams.HasCustomAxis)
                            directionToExtrudeIn = exportBodyParams.CustomAxis;

                        IFCExtrusionCalculatorOptions extrusionOptions =
                           new IFCExtrusionCalculatorOptions(exporterIFC, axesToExtrudeIn, directionToExtrudeIn, scale);

                        if (allFaces)
                            extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, faces);
                        else
                            extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, splitGeometryList[ii]);

                        if (extrusionList.Count == 0)
                        {
                            if (!canExportSolidModelRep)
                            {
                                tryToExportAsExtrusion = false;
                                break;
                            }
                            exportAsBRep.Add(ii);
                        }
                        else
                        {
                            extrusionLists.Add(extrusionList);
                            exportAsExtrusion.Add(ii);
                        }
                    }

                    int numCreatedExtrusions = extrusionLists.Count;
                    for (int ii = 0; (ii < numCreatedExtrusions) && tryToExportAsExtrusion; ii++)
                    {
                        int geomIndex = exportAsExtrusion[ii];
                        bodyData.AddMaterial(SetBestMaterialIdInExporter(splitGeometryList[geomIndex], exporterIFC));

                        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)
                                {
                                    double zOff = (whichBasis == IFCExtrusionBasis.BasisZ) ? (1.0 - Math.Abs(extrusionDirection[2])) : Math.Abs(extrusionDirection[2]);
                                    double scaledAngle = Math.Asin(zOff) * 180 / Math.PI;
                                    exportBodyParams.Slope = scaledAngle;
                                    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 = height * scale;
                                        exportBodyParams.ScaledWidth = width * scale;
                                    }

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

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

                        if (!exportedAsExtrusion)
                        {
                            if (!canExportSolidModelRep)
                            {
                                tryToExportAsExtrusion = false;
                                break;
                            }
                            exportAsBRep.Add(ii);
                        }
                    }
                }

                if ((exportAsBRep.Count == 0) && tryToExportAsExtrusion)
                {
                    int sz = bodyItems.Count();
                    for (int ii = 0; ii < sz; ii++)
                        BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, bodyItems[ii], materialIdsForExtrusions[ii]);

                    bodyData.RepresentationHnd =
                        RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItems, bodyData.RepresentationHnd);
                }

                if (tryToExportAsExtrusion)
                    tr.Commit();
                else
                    tr.RollBack();

                if ((exportAsBRep.Count == 0) && tryToExportAsExtrusion)
                    return bodyData;
            }
            
            // 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)
                exportAsBRep.Clear();

            // 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.
            // We will only do this if we didn't create any extrusions at all.
            using (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
            {
                if (exportBodyParams != null && (exportAsBRep.Count == 0))
                    bodyData.BrepOffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, splitGeometryList, exportBodyParams);

                return ExportBodyAsBRep(exporterIFC, splitGeometryList, exportAsBRep, bodyItems, element, categoryId, contextOfItems, eps, options, bodyData);
            }
        }
        /// <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;
            
            // This will default to false for now in all cases, as swept solids are not included in CV2.0.
            bool tryToExportAsSweptSolid = options.TryToExportAsSweptSolid;
            
            IFCFile file = exporterIFC.GetFile();
            IFCAnyHandle contextOfItems = exporterIFC.Get3DContextHandle("Body");
            double scale = exporterIFC.LinearScale;

            double eps = element.Document.Application.VertexTolerance * scale;

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

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

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

            bool hasExtrusions = false;
            bool hasSweptSolids = false;

            XYZ unscaledTrfOrig = new XYZ();
            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 (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
                {
                    // We may need to get the original values back, in case the export rolls back.
                    IFCLocalPlacementBackup localPlacementBackup = null;

                    if (options.AllowOffsetTransform && exportBodyParams!= null)
                    {
                        localPlacementBackup = new IFCLocalPlacementBackup(exportBodyParams.GetLocalPlacement());
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, geometryList, exportBodyParams);
                    }

                    if (tryToExportAsExtrusion)
                    {
                        // 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);
                            }
                        }

                        int numExtrusionsToCreate = allFaces ? 1 : geometryList.Count;

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

                            IFCExtrusionAxes axesToExtrudeIn = exportBodyParams != null ? exportBodyParams.PossibleExtrusionAxes : IFCExtrusionAxes.TryDefault;
                            XYZ directionToExtrudeIn = XYZ.Zero;
                            if (exportBodyParams != null && exportBodyParams.HasCustomAxis)
                                directionToExtrudeIn = exportBodyParams.CustomAxis;

                            IFCExtrusionCalculatorOptions extrusionOptions =
                               new IFCExtrusionCalculatorOptions(exporterIFC, axesToExtrudeIn, directionToExtrudeIn, scale);

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

                            if (extrusionList.Count == 0)
                            {
                                if (tryToExportAsSweptSolid)
                                    exportAsSweptSolid.Add(ii);
                                else if (!canExportSolidModelRep)
                                {
                                    tryToExportAsExtrusion = false;
                                    break;
                                }
                                else
                                    exportAsBRep.Add(ii);
                            }
                            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 = height * scale;
                                            exportBodyParams.ScaledWidth = width * scale;
                                        }

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

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

                            if (!exportedAsExtrusion)
                            {
                                if (tryToExportAsSweptSolid)
                                    exportAsSweptSolid.Add(ii);
                                else if (!canExportSolidModelRep)
                                {
                                    tryToExportAsExtrusion = false;
                                    break;
                                }
                                else
                                    exportAsBRep.Add(ii);
                            }
                        }
                    }

                    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;
                            // TODO: allFaces to SweptSolid
                            if (solid != null)
                            {
                                // TODO: other types of Axes
                                XYZ normal = new XYZ(0, 0, 1);
                                SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, solid, normal);
                                if (sweptSolidExporter != null)
                                {
                                    IFCAnyHandle sweptHandle = sweptSolidExporter.RepresentationItem;
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(sweptHandle))
                                    {
                                        bodyItems.Add(sweptHandle);
                                        materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());
                                        exported = true;
                                        if (sweptSolidExporter.IsExtrusion)
                                            hasExtrusions = true;
                                        else
                                            hasSweptSolids = true;
                                    }
                                }
                            }

                            if (!exported)
                                exportAsBRep.Add(ii);
                        }
                    }

                    bool exportSucceeded = (exportAsBRep.Count == 0) && (tryToExportAsExtrusion || tryToExportAsSweptSolid) && (hasExtrusions || hasSweptSolids);
                    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 (hasExtrusions && !hasSweptSolids)
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItems, bodyData.RepresentationHnd);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.SweptSolid;
                        }
                        else if (hasSweptSolids && !hasExtrusions)
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateAdvancedSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItems, bodyData.RepresentationHnd);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.AdvancedSweptSolid;
                        }
                        else
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateSolidModelRep(exporterIFC, element, categoryId, contextOfItems, bodyItems);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.SolidModel;
                        }
                        
                        // TODO: include BRep, CSG, Clipping
                        tr.Commit();
                        return bodyData;
                    }
                    }

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

                        // Revert to the original local placement, and re-set the relative placement, as the rollback may delete either.
                        if (localPlacementBackup != null)
                        {
                           IFCAnyHandle origLocalPlacement = localPlacementBackup.Restore();
                           if (!IFCAnyHandleUtil.IsNullOrHasNoValue(origLocalPlacement))
                               exportBodyParams.SetLocalPlacement(origLocalPlacement);
                        }
                    }
                    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)
                    exportAsBRep.Clear();
            }

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                using (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
                {
                    if (exportBodyParams != null && (exportAsBRep.Count == 0))
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, geometryList, exportBodyParams);

                    BodyData retBodyData = ExportBodyAsBRep(exporterIFC, geometryList, exportAsBRep, bodyItems, element, categoryId, overrideMaterialId, contextOfItems, eps, options, bodyData);
                    if (retBodyData != null)
                        tr.Commit();
                    else
                        tr.RollBack();
                    return retBodyData;
                }
            }
        }