/// <summary>
        /// Export spatial elements, including rooms, areas and spaces. 1st level space boundaries.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The IFCProductWrapper.
        /// </param>
        public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement spatialElement, IFCProductWrapper productWrapper)
        {
            //quick reject
            bool isArea = spatialElement is Area;
            if (isArea)
            {
                if (!IsAreaGrossInterior(exporterIFC, spatialElement))
                    return;
            }

            IFCFile file = exporterIFC.GetFile();
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                ElementId levelId = spatialElement.Level != null ? spatialElement.Level.Id : ElementId.InvalidElementId;
                using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, spatialElement, null, null, levelId))
                {
                    CreateIFCSpace(exporterIFC, spatialElement, productWrapper, setter);

                    // Do not create boundary information, or extra property sets.
                    if (spatialElement is Area)
                    {
                        tr.Commit();
                        return;
                    }

                    if (exporterIFC.SpaceBoundaryLevel == 1)
                    {
                        Document document = spatialElement.Document;
                        IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);
                        double baseHeightNonScaled = levelInfo.Elevation;

                        SpatialElementGeometryResults spatialElemGeomResult = s_SpatialElemGeometryCalculator.CalculateSpatialElementGeometry(spatialElement);

                        Solid spatialElemGeomSolid = spatialElemGeomResult.GetGeometry();
                        FaceArray faces = spatialElemGeomSolid.Faces;
                        foreach (Face face in faces)
                        {
                            IList<SpatialElementBoundarySubface> spatialElemBoundarySubfaces = spatialElemGeomResult.GetBoundaryFaceInfo(face);
                            foreach (SpatialElementBoundarySubface spatialElemBSubface in spatialElemBoundarySubfaces)
                            {
                                if (spatialElemBSubface.SubfaceType == SubfaceType.Side)
                                    continue;

                                if (spatialElemBSubface.GetSubface() == null)
                                    continue;

                                ElementId elemId = spatialElemBSubface.SpatialBoundaryElement.LinkInstanceId;
                                if (elemId == ElementId.InvalidElementId)
                                {
                                    elemId = spatialElemBSubface.SpatialBoundaryElement.HostElementId;
                                }

                                Element boundingElement = document.get_Element(elemId);
                                if (boundingElement == null)
                                    continue;

                                bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);

                                IFCGeometryInfo info = IFCGeometryInfo.CreateSurfaceGeometryInfo(spatialElement.Document.Application.VertexTolerance);

                                Face subFace = spatialElemBSubface.GetSubface();
                                ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, subFace, XYZ.Zero, false);

                                IFCAnyHandle ifcOptionalHnd = IFCAnyHandle.Create();
                                foreach (IFCAnyHandle surfaceHnd in info.GetSurfaces())
                                {
                                    IFCAnyHandle connectionGeometry = file.CreateConnectionSurfaceGeometry(surfaceHnd, ifcOptionalHnd);

                                    IFCSpaceBoundary spaceBoundary = IFCSpaceBoundary.Create(spatialElement.Id, boundingElement.Id, connectionGeometry, IFCSpaceBoundaryType.Physical, isObjectExt);

                                    if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file))
                                        exporterIFC.RegisterIFCSpaceBoundary(spaceBoundary);
                                }
                            }
                        }

                        IList<IList<BoundarySegment>> roomBoundaries = spatialElement.GetBoundarySegments(ExporterIFCUtils.GetSpatialElementBoundaryOptions(exporterIFC, spatialElement));
                        double roomHeight = GetHeight(spatialElement, exporterIFC.LinearScale, levelInfo);
                        XYZ zDir = new XYZ(0, 0, 1);

                        foreach (IList<BoundarySegment> roomBoundaryList in roomBoundaries)
                        {
                            foreach (BoundarySegment roomBoundary in roomBoundaryList)
                            {
                                Element boundingElement = roomBoundary.Element;

                                if (boundingElement == null)
                                    continue;

                                ElementId buildingElemId = boundingElement.Id;
                                Curve trimmedCurve = roomBoundary.Curve;

                                if (trimmedCurve == null)
                                    continue;

                                //trimmedCurve.Visibility = Visibility.Visible; readonly
                                IFCAnyHandle connectionGeometry = ExporterIFCUtils.CreateExtrudedSurfaceFromCurve(
                                   exporterIFC, trimmedCurve, zDir, roomHeight, baseHeightNonScaled);

                                IFCSpaceBoundaryType physOrVirt = IFCSpaceBoundaryType.Physical;
                                if (boundingElement is CurveElement)
                                    physOrVirt = IFCSpaceBoundaryType.Virtual;
                                else if (boundingElement is Autodesk.Revit.DB.Architecture.Room)
                                    physOrVirt = IFCSpaceBoundaryType.Undefined;

                                bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);
                                bool isObjectPhys = (physOrVirt == IFCSpaceBoundaryType.Physical);

                                ElementId actualBuildingElemId = isObjectPhys ? buildingElemId : ElementId.InvalidElementId;
                                IFCSpaceBoundary boundary = IFCSpaceBoundary.Create(spatialElement.Id, actualBuildingElemId, connectionGeometry, physOrVirt, isObjectExt);

                                if (!ProcessIFCSpaceBoundary(exporterIFC, boundary, file))
                                    exporterIFC.RegisterIFCSpaceBoundary(boundary);

                                // try to add doors and windows for host objects if appropriate.
                                if (isObjectPhys && boundingElement is HostObject)
                                {
                                    HostObject hostObj = boundingElement as HostObject;
                                    IList<ElementId> elemIds = hostObj.FindInserts(false, false, false, false);
                                    foreach (ElementId elemId in elemIds)
                                    {
                                        // we are going to do a simple bbox export, not complicated geometry.
                                        Element instElem = document.get_Element(elemId);
                                        if (instElem == null)
                                            continue;

                                        BoundingBoxXYZ instBBox = instElem.get_BoundingBox(null);

                                        // make copy of original trimmed curve.
                                        Curve instCurve = trimmedCurve.Clone();
                                        XYZ instOrig = instCurve.get_EndPoint(0);

                                        // make sure that the insert is on this level.
                                        if (instBBox.Max.Z < instOrig.Z)
                                            continue;
                                        if (instBBox.Min.Z > instOrig.Z + roomHeight)
                                            continue;

                                        double insHeight = Math.Min(instBBox.Max.Z, instOrig.Z + roomHeight) - Math.Max(instOrig.Z, instBBox.Min.Z);
                                        if (insHeight < (1.0 / (12.0 * 16.0)))
                                            continue;

                                        // move base curve to bottom of bbox.
                                        XYZ moveDir = new XYZ(0.0, 0.0, instBBox.Min.Z - instOrig.Z);
                                        Transform moveTrf = Transform.get_Translation(moveDir);
                                        instCurve = instCurve.get_Transformed(moveTrf);

                                        bool isHorizOrVert = false;
                                        if (instCurve is Line)
                                        {
                                            Line instLine = instCurve as Line;
                                            XYZ lineDir = instLine.Direction;
                                            if (MathUtil.IsAlmostEqual(Math.Abs(lineDir.X), 1.0) || (MathUtil.IsAlmostEqual(Math.Abs(lineDir.Y), 1.0)))
                                                isHorizOrVert = true;
                                        }

                                        double[] parameters = new double[2];
                                        double[] origEndParams = new double[2];
                                        if (isHorizOrVert)
                                        {
                                            parameters[0] = instCurve.Project(instBBox.Min).Parameter;
                                            parameters[1] = instCurve.Project(instBBox.Max).Parameter;
                                        }
                                        else
                                        {
                                            FamilyInstance famInst = instElem as FamilyInstance;
                                            if (famInst == null)
                                                continue;

                                            ElementType elementType = document.get_Element(famInst.GetTypeId()) as ElementType;
                                            if (elementType == null)
                                                continue;

                                            BoundingBoxXYZ symBBox = elementType.get_BoundingBox(null);
                                            Curve symCurve = trimmedCurve.Clone();
                                            Transform trf = famInst.GetTransform();
                                            Transform invTrf = trf.Inverse;
                                            Curve trfCurve = symCurve.get_Transformed(invTrf);
                                            parameters[0] = trfCurve.Project(symBBox.Min).Parameter;
                                            parameters[1] = trfCurve.Project(symBBox.Max).Parameter;
                                        }

                                        // ignore if less than 1/16".
                                        if (Math.Abs(parameters[1] - parameters[0]) < 1.0 / (12.0 * 16.0))
                                            continue;
                                        if (parameters[0] > parameters[1])
                                        {
                                            //swap
                                            double tempParam = parameters[0];
                                            parameters[0] = parameters[1];
                                            parameters[1] = tempParam;
                                        }

                                        origEndParams[0] = instCurve.get_EndParameter(0);
                                        origEndParams[1] = instCurve.get_EndParameter(1);

                                        if (origEndParams[0] > parameters[1] - (1.0 / (12.0 * 16.0)))
                                            continue;
                                        if (origEndParams[1] < parameters[0] + (1.0 / (12.0 * 16.0)))
                                            continue;

                                        if (parameters[0] > origEndParams[0])
                                            instCurve.set_EndParameter(0, parameters[0]);
                                        if (parameters[1] < origEndParams[1])
                                            instCurve.set_EndParameter(1, parameters[1]);

                                        IFCAnyHandle insConnectionGeom = ExporterIFCUtils.CreateExtrudedSurfaceFromCurve(exporterIFC, instCurve, zDir,
                                           insHeight, baseHeightNonScaled);

                                        IFCSpaceBoundary instBoundary = IFCSpaceBoundary.Create(spatialElement.Id, elemId, insConnectionGeom, physOrVirt, isObjectExt);
                                        if (!ProcessIFCSpaceBoundary(exporterIFC, instBoundary, file))
                                            exporterIFC.RegisterIFCSpaceBoundary(instBoundary);
                                    }
                                }
                            }
                        }
                    }
                    ExporterIFCUtils.CreateSpatialElementPropertySet(exporterIFC, spatialElement, productWrapper);
                }
                tr.Commit();
            }
        }
        /// <summary>
        /// Creates SpaceBoundary from a bounding element.
        /// </summary>
        /// <param name="file">
        /// The IFC file.
        /// </param>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="boundingElement">
        /// The bounding element.
        /// </param>
        /// <param name="connectionGeometry">
        /// The connection geometry handle.
        /// </param>
        static void CreateIFCSpaceBoundary(IFCFile file, ExporterIFC exporterIFC, SpatialElement spatialElement, Element boundingElement, IFCAnyHandle connectionGeometry)
        {
            IFCSpaceBoundaryType physOrVirt = IFCSpaceBoundaryType.Physical;
            if (boundingElement is CurveElement)
                physOrVirt = IFCSpaceBoundaryType.Virtual;
            else if (boundingElement is Autodesk.Revit.DB.Architecture.Room)
                physOrVirt = IFCSpaceBoundaryType.Undefined;

            bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);

            IFCSpaceBoundary spaceBoundary = IFCSpaceBoundary.Create(spatialElement.Id, boundingElement.Id, connectionGeometry, physOrVirt, isObjectExt);

            if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file))
                exporterIFC.RegisterIFCSpaceBoundary(spaceBoundary);
        }