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