/// <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; }