private static bool IsInRange(IFCRange range, CurveLoop loop, Plane plane, XYZ extrusionDirection, out bool clipCompletely) { clipCompletely = false; if (range != null) { // This check is only applicable for cuts that are perpendicular to the extrusion direction. // For cuts that aren't, we can't easily tell if this cut is extraneous or not. if (!MathUtil.IsAlmostEqual(Math.Abs(plane.Normal.DotProduct(extrusionDirection)), 1.0)) { return(true); } double eps = MathUtil.Eps(); double parameterValue = plane.Origin.DotProduct(extrusionDirection); if (range.Start > parameterValue - eps) { clipCompletely = true; return(false); } if (range.End < parameterValue + eps) { return(false); } } return(true); }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandle">The parent handle.</param> /// <param name="element">The element.</param> /// <param name="lcs">The local coordinate system.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IFCAnyHandle elementHandle, Element element, Transform lcs, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { IList<IFCAnyHandle> elementHandles = new List<IFCAnyHandle>(); elementHandles.Add(elementHandle); AddOpeningsToElement(exporterIFC, elementHandles, null, element, lcs, scaledWidth, range, setter, localPlacement, localWrapper); }
private static string CreateOpeningGUID(Element openingElem, IFCRange range, int openingIndex, int solidIndex) { // GUID_TODO: Range can be potentially unstable; getting the corresponding level would be // better. string openingId = (range != null) ? "(" + range.Start.ToString() + ":" + range.End.ToString() + ")" : string.Empty; openingId += "Opening:" + openingIndex.ToString() + "Solid:" + solidIndex.ToString(); return(GUIDUtil.GenerateIFCGuidFrom(openingElem, openingId)); }
/// <summary> /// Gets origin, X direction and curve bound from a curve. /// </summary> /// <param name="curve"> /// The curve. /// </param> /// <param name="curveBounds"> /// The output curve bounds. /// </param> /// <param name="xDirection"> /// The output X direction. /// </param> /// <param name="origin"> /// The output origin. /// </param> public static void GetAxisAndRangeFromCurve(Curve curve, out IFCRange curveBounds, out XYZ xDirection, out XYZ origin) { curveBounds = new IFCRange(curve.get_EndParameter(0), curve.get_EndParameter(1)); origin = curve.Evaluate(curveBounds.Start, false); if (curve is Arc) { Arc arc = curve as Arc; xDirection = arc.XDirection; } else { Transform trf = curve.ComputeDerivatives(curveBounds.Start, false); xDirection = trf.get_Basis(0); } }
private static IFCRange GetExtrusionRangeOfCurveLoop(CurveLoop loop, XYZ extrusionDirection) { IFCRange range = new IFCRange(); bool init = false; foreach (Curve curve in loop) { if (!init) { if (curve.IsBound) { IList <XYZ> coords = curve.Tessellate(); foreach (XYZ coord in coords) { double val = coord.DotProduct(extrusionDirection); if (!init) { range.Start = val; range.End = val; init = true; } else { range.Start = Math.Min(range.Start, val); range.End = Math.Max(range.End, val); } } } else { double val = curve.get_EndPoint(0).DotProduct(extrusionDirection); range.Start = val; range.End = val; init = true; } } else { double val = curve.get_EndPoint(0).DotProduct(extrusionDirection); range.Start = Math.Min(range.Start, val); range.End = Math.Max(range.End, val); } } return(range); }
// returns true if either, but not both, the start or end of the extrusion is clipped. private static KeyValuePair <bool, bool> CollectionClipsExtrusionEnds(IList <CurveLoop> curveLoopBoundaries, XYZ extrusionDirection, IFCRange extrusionRange) { bool clipStart = false; bool clipEnd = false; double eps = MathUtil.Eps(); foreach (CurveLoop curveLoop in curveLoopBoundaries) { IFCRange loopRange = GetExtrusionRangeOfCurveLoop(curveLoop, extrusionDirection); if (loopRange.End >= extrusionRange.End - eps) { clipEnd = true; } if (loopRange.Start <= extrusionRange.Start + eps) { clipStart = true; } } KeyValuePair <bool, bool> clipResults = new KeyValuePair <bool, bool>(clipStart, clipEnd); return(clipResults); }
// returns true if either, but not both, the start or end of the extrusion is clipped. private static KeyValuePair<bool, bool> CollectionClipsExtrusionEnds(IList<CurveLoop> curveLoopBoundaries, XYZ extrusionDirection, IFCRange extrusionRange) { bool clipStart = false; bool clipEnd = false; double eps = MathUtil.Eps(); foreach (CurveLoop curveLoop in curveLoopBoundaries) { IFCRange loopRange = GetExtrusionRangeOfCurveLoop(curveLoop, extrusionDirection); if (loopRange.End >= extrusionRange.End - eps) clipEnd = true; if (loopRange.Start <= extrusionRange.Start + eps) clipStart = true; } KeyValuePair<bool, bool> clipResults = new KeyValuePair<bool, bool>(clipStart, clipEnd); return clipResults; }
private static bool IsInRange(IFCRange range, CurveLoop loop, Plane plane, XYZ extrusionDirection, out bool clipCompletely) { clipCompletely = false; if (range != null) { // This check is only applicable for cuts that are perpendicular to the extrusion direction. // For cuts that aren't, we can't easily tell if this cut is extraneous or not. if (!MathUtil.IsAlmostEqual(Math.Abs(plane.Normal.DotProduct(extrusionDirection)), 1.0)) return true; double eps = MathUtil.Eps(); double parameterValue = plane.Origin.DotProduct(extrusionDirection); if (range.Start > parameterValue - eps) { clipCompletely = true; return false; } if (range.End < parameterValue + eps) return false; } return true; }
/// <summary> /// Collects all solids and meshes within a GeometryElement; the solids which consist of multiple closed volumes /// will be split into single closed volume Solids. /// </summary> /// <remarks> /// Added in 2013 to replace the temporary API method ExporterIFCUtils.GetSplitClippedSolidMeshGeometry. /// </remarks> /// <param name="range"> /// The upper and lower levels which act as the clipping boundaries. /// </param> /// <param name="geomElemToUse">The GeometryElement.</param> /// <returns>The collection of solids and meshes.</returns> public static SolidMeshGeometryInfo GetSplitClippedSolidMeshGeometry(GeometryElement geomElemToUse, IFCRange range) { SolidMeshGeometryInfo geometryInfo = GetClippedSolidMeshGeometry(geomElemToUse, range); geometryInfo.SplitSolidsList(); return geometryInfo; }
/// <summary> /// Split associated parts when host element is split by level. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="hostElement">The host element havign associtaed parts.</param> /// <param name="associatedPartsList">The list of associtated parts.</param> private static void SplitParts(ExporterIFC exporterIFC, Element hostElement, List <ElementId> associatedPartsList) { string ifcEnumType; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); // Split the host to find the orphan parts. IList <ElementId> orphanLevels = new List <ElementId>(); IList <ElementId> hostLevels = new List <ElementId>(); IList <IFCRange> hostRanges = new List <IFCRange>(); LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, hostElement, out hostLevels, out hostRanges); orphanLevels = hostLevels; // Split each Parts IList <ElementId> levels = new List <ElementId>(); IList <IFCRange> ranges = new List <IFCRange>(); // Dictionary to storage the level and its parts. Dictionary <ElementId, List <KeyValuePair <Part, IFCRange> > > levelParts = new Dictionary <ElementId, List <KeyValuePair <Part, IFCRange> > >(); foreach (ElementId partId in associatedPartsList) { Part part = hostElement.Document.GetElement(partId) as Part; LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, part, out levels, out ranges); // if the parts are above top level, associate them with nearest bottom level. if (ranges.Count == 0) { ElementId bottomLevelId = FindPartSplitLevel(exporterIFC, part); if (bottomLevelId == ElementId.InvalidElementId) { bottomLevelId = part.LevelId; } if (!levelParts.ContainsKey(bottomLevelId)) { levelParts.Add(bottomLevelId, new List <KeyValuePair <Part, IFCRange> >()); } KeyValuePair <Part, IFCRange> splitPartRange = new KeyValuePair <Part, IFCRange>(part, null); levelParts[bottomLevelId].Add(splitPartRange); continue; } // The parts split by levels are stored in dictionary. for (int ii = 0; ii < ranges.Count; ii++) { if (!levelParts.ContainsKey(levels[ii])) { levelParts.Add(levels[ii], new List <KeyValuePair <Part, IFCRange> >()); } KeyValuePair <Part, IFCRange> splitPartRange = new KeyValuePair <Part, IFCRange>(part, ranges[ii]); levelParts[levels[ii]].Add(splitPartRange); } if (levels.Count > hostLevels.Count) { orphanLevels = orphanLevels.Union <ElementId>(levels).ToList(); } } ExporterCacheManager.HostPartsCache.Register(hostElement.Id, levelParts); // The levels of orphan part. orphanLevels = orphanLevels.Where(number => !hostLevels.Contains(number)).ToList(); List <KeyValuePair <ElementId, IFCRange> > levelRangePairList = new List <KeyValuePair <ElementId, IFCRange> >(); foreach (ElementId orphanLevelId in orphanLevels) { IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, orphanLevelId); if (levelInfo == null) { continue; } double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(orphanLevelId); IFCRange levelRange = new IFCRange(levelInfo.Elevation, levelInfo.Elevation + levelHeight); List <KeyValuePair <Part, IFCRange> > splitPartRangeList = new List <KeyValuePair <Part, IFCRange> >(); splitPartRangeList = ExporterCacheManager.HostPartsCache.Find(hostElement.Id, orphanLevelId); IFCRange highestRange = levelRange; foreach (KeyValuePair <Part, IFCRange> partRange in splitPartRangeList) { if (partRange.Value.End > highestRange.End) { highestRange = partRange.Value; } } levelRangePairList.Add(new KeyValuePair <ElementId, IFCRange>(orphanLevelId, highestRange)); } if (levelRangePairList.Count > 0) { ExporterCacheManager.DummyHostCache.Register(hostElement.Id, levelRangePairList); } }
/// <summary> /// Checks if the wall is clipped completely. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="wallElement"> /// The wall element. /// </param> /// <param name="range"> /// The range of which may clip the wall. /// </param> /// <returns> /// True if the wall is clipped completely, false otherwise. /// </returns> static bool IsWallCompletelyClipped(Wall wallElement, ExporterIFC exporterIFC, IFCRange range) { return ExporterIFCUtils.IsWallCompletelyClipped(wallElement, exporterIFC, range); }
// return null if parent should be completely clipped. // TODO: determine whether or not to use face boundary. private static IFCAnyHandle ProcessClippingFace(ExporterIFC exporterIFC, CurveLoop outerBoundary, Plane boundaryPlane, Plane extrusionBasePlane, XYZ extrusionDirection, IFCRange range, bool useFaceBoundary, IFCAnyHandle bodyItemHnd) { if (outerBoundary == null || boundaryPlane == null) { throw new Exception("Invalid face boundary."); } double clippingSlant = boundaryPlane.Normal.DotProduct(extrusionDirection); if (useFaceBoundary) { if (MathUtil.IsAlmostZero(clippingSlant)) { return(bodyItemHnd); } } bool clipCompletely; if (!IsInRange(range, outerBoundary, boundaryPlane, extrusionDirection, out clipCompletely)) { return(clipCompletely ? null : bodyItemHnd); } if (MathUtil.IsAlmostZero(clippingSlant)) { throw new Exception("Can't create clipping perpendicular to extrusion."); } IFCFile file = exporterIFC.GetFile(); XYZ scaledOrig = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, boundaryPlane.Origin); XYZ scaledNorm = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, boundaryPlane.Normal); XYZ scaledXDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, boundaryPlane.XVec); IFCAnyHandle planeAxisHnd = ExporterUtil.CreateAxis(file, scaledOrig, scaledNorm, scaledXDir); IFCAnyHandle surfHnd = IFCInstanceExporter.CreatePlane(file, planeAxisHnd); IFCAnyHandle clippedBodyItemHnd = null; IFCAnyHandle halfSpaceHnd = null; if (useFaceBoundary) { IFCAnyHandle boundedCurveHnd; if (boundaryPlane != null) { XYZ projScaledOrigin = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, extrusionBasePlane.Origin); XYZ projScaledX = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, extrusionBasePlane.XVec); XYZ projScaledY = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, extrusionBasePlane.YVec); XYZ projScaledNorm = projScaledX.CrossProduct(projScaledY); Plane projScaledPlane = new Plane(projScaledX, projScaledY, projScaledOrigin); IList <UV> polylinePts = TransformAndProjectCurveLoopToPlane(exporterIFC, outerBoundary, projScaledPlane); polylinePts.Add(polylinePts[0]); boundedCurveHnd = ExporterUtil.CreatePolyline(file, polylinePts); IFCAnyHandle boundedAxisHnd = ExporterUtil.CreateAxis(file, projScaledOrigin, projScaledNorm, projScaledX); halfSpaceHnd = IFCInstanceExporter.CreatePolygonalBoundedHalfSpace(file, boundedAxisHnd, boundedCurveHnd, surfHnd, false); } else { throw new Exception("Can't create non-polygonal face boundary."); } } else { halfSpaceHnd = IFCInstanceExporter.CreateHalfSpaceSolid(file, surfHnd, false); } if (halfSpaceHnd == null) { throw new Exception("Can't create clipping."); } clippedBodyItemHnd = IFCInstanceExporter.CreateBooleanClippingResult(file, IFCBooleanOperator.Difference, bodyItemHnd, halfSpaceHnd); return(clippedBodyItemHnd); }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandles">The parent handles.</param> /// <param name="curveLoops">The parent CurveLoops.</param> /// <param name="element">The element.</param> /// <param name="lcs">The local coordinate system.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IList <IFCAnyHandle> elementHandles, IList <CurveLoop> curveLoops, Element element, Transform lcs, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { if (lcs == null && ((curveLoops?.Count ?? 0) > 0)) { // assumption: first curve loop defines the plane. Plane hostObjPlane = curveLoops[0].HasPlane() ? curveLoops[0].GetPlane(): null; if (hostObjPlane != null) { lcs = GeometryUtil.CreateTransformFromPlane(hostObjPlane); } } IList <IFCOpeningData> openingDataList = ExporterIFCUtils.GetOpeningData(exporterIFC, element, lcs, range); IFCFile file = exporterIFC.GetFile(); int openingIndex = 0; foreach (IFCOpeningData openingData in openingDataList) { openingIndex++; Element openingElem = element.Document.GetElement(openingData.OpeningElementId); if (openingElem == null) { openingElem = element; } bool currentWallIsHost = false; FamilyInstance openingFInst = openingElem as FamilyInstance; if (openingFInst != null && openingFInst.Host != null) { if (openingFInst.Host.Id == element.Id) { currentWallIsHost = true; } } // Don't export the opening if WallSweep category has been turned off. // This is currently restricted to WallSweeps because the element responsible for the opening could be a variety of things, // including a line as part of the elevation profile of the wall. // As such, we will restrict which element types we check for CanExportElement. if ((openingElem is WallSweep) && (!ElementFilteringUtil.CanExportElement(exporterIFC, openingElem, true))) { continue; } IList <IFCExtrusionData> extrusionDataList = openingData.GetExtrusionData(); IFCAnyHandle parentHandle = FindParentHandle(elementHandles, curveLoops, extrusionDataList); string predefinedType; IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, openingElem, out predefinedType); bool exportingDoorOrWindow = (exportType.ExportInstance == IFCEntityType.IfcDoor || exportType.ExportType == IFCEntityType.IfcDoorType || exportType.ExportInstance == IFCEntityType.IfcWindow || exportType.ExportType == IFCEntityType.IfcWindowType); bool isDoorOrWindowOpening = IsDoorOrWindowOpening(openingElem, element, exportingDoorOrWindow); if (isDoorOrWindowOpening && currentWallIsHost) { DoorWindowDelayedOpeningCreator delayedCreator = DoorWindowDelayedOpeningCreator.Create(exporterIFC, openingData, scaledWidth, element.Id, parentHandle, setter.LevelId); if (delayedCreator != null) { ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); continue; } } IList <Solid> solids = openingData.GetOpeningSolids(); int solidIndex = 0; foreach (Solid solid in solids) { solidIndex++; using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, null)); extrusionCreationData.ReuseLocalPlacement = true; string openingGUID = CreateOpeningGUID(openingElem, range, openingIndex, solidIndex); CreateOpening(exporterIFC, parentHandle, element, openingElem, openingGUID, solid, scaledWidth, openingData.IsRecess, extrusionCreationData, setter, localWrapper); } } foreach (IFCExtrusionData extrusionData in extrusionDataList) { solidIndex++; if (extrusionData.ScaledExtrusionLength < MathUtil.Eps()) { extrusionData.ScaledExtrusionLength = scaledWidth; } string openingGUID = CreateOpeningGUID(openingElem, range, openingIndex, solidIndex); CreateOpening(exporterIFC, parentHandle, localPlacement, element, openingElem, openingGUID, extrusionData, lcs, openingData.IsRecess, setter, localWrapper); } } }
/// <summary> /// Collects all solids and meshes within a GeometryElement; the solids which consist of multiple closed volumes /// will be split into single closed volume Solids. /// </summary> /// <remarks> /// Added in 2013 to replace the temporary API method ExporterIFCUtils.GetSplitClippedSolidMeshGeometry. /// </remarks> /// <param name="range"> /// The upper and lower levels which act as the clipping boundaries. /// </param> /// <param name="geomElemToUse">The GeometryElement.</param> /// <returns>The collection of solids and meshes.</returns> public static SolidMeshGeometryInfo GetSplitClippedSolidMeshGeometry(GeometryElement geomElemToUse, IFCRange range) { SolidMeshGeometryInfo geometryInfo = GetClippedSolidMeshGeometry(geomElemToUse, range); geometryInfo.SplitSolidsList(); return(geometryInfo); }
/// <summary> /// Collects all meshes within a GeometryElement and all solids clipped between a given IFCRange. /// </summary> /// <remarks> /// Added in 2013 to replace the temporary API method ExporterIFCUtils.GetClippedSolidMeshGeometry. /// </remarks> /// <param name="elem"> /// The Element from which we can obtain a bounding box. Not handled directly in this method, it is used in an internal helper method. /// </param> /// <param name="geomElemToUse"> /// The GeometryElement. /// </param> /// <param name="range"> /// The upper and lower levels which act as the clipping boundaries. /// </param> /// <returns>The collection of solids and meshes.</returns> public static SolidMeshGeometryInfo GetClippedSolidMeshGeometry(GeometryElement geomElemToUse, IFCRange range) { SolidMeshGeometryInfo geometryInfo = GetSolidMeshGeometry(geomElemToUse, Transform.Identity); geometryInfo.ClipSolidsList(geomElemToUse, range); return(geometryInfo); }
/// <summary> /// Processes the geometry of the wall to create an extruded area solid representing the geometry of the wall (including /// any clippings imposed by neighboring elements). /// </summary> /// <param name="exporterIFC"> /// The exporter. /// </param> /// <param name="wallElement"> /// The wall. /// </param> /// <param name="setterOffset"> /// The offset from the placement setter. /// </param> /// <param name="range"> /// The range. This consists of two double values representing the height in Z at the start and the end /// of the range. If the values are identical the entire wall is used. /// </param> /// <param name="zSpan"> /// The overall span in Z of the wall. /// </param> /// <param name="baseBodyItemHnd"> /// The IfcExtrudedAreaSolid handle generated initially for the wall. /// </param> /// <param name="cutPairOpenings"> /// A collection of extruded openings that can be derived from the wall geometry. /// </param> /// <returns> /// IfcEtxtrudedAreaSolid handle. This may be the same handle as was input, or a modified handle derived from the clipping /// geometry. If the function fails this handle will have no value. /// </returns> static IFCAnyHandle AddClippingsToBaseExtrusion(ExporterIFC exporterIFC, Wall wallElement, XYZ setterOffset, IFCRange range, IFCRange zSpan, IFCAnyHandle baseBodyItemHnd, out IList<IFCExtrusionData> cutPairOpenings) { return ExporterIFCUtils.AddClippingsToBaseExtrusion(exporterIFC, wallElement, setterOffset, range, zSpan, baseBodyItemHnd, out cutPairOpenings); }
private static HandleAndAnalyzer CreateExtrusionWithClippingAndOpening(ExporterIFC exporterIFC, Element element, ElementId catId, Solid solid, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped, out bool hasClippingResult, out bool hasBooleanResult, out ElementId materialId) { completelyClipped = false; materialId = ElementId.InvalidElementId; hasClippingResult = false; hasBooleanResult = false; HandleAndAnalyzer nullVal = new HandleAndAnalyzer(); HandleAndAnalyzer retVal = new HandleAndAnalyzer(); try { ExtrusionAnalyzer elementAnalyzer = ExtrusionAnalyzer.Create(solid, plane, projDir); retVal.Analyzer = elementAnalyzer; Document document = element.Document; XYZ planeOrig = plane.Origin; XYZ baseLoopOffset = null; if (!MathUtil.IsAlmostZero(elementAnalyzer.StartParameter)) baseLoopOffset = elementAnalyzer.StartParameter * projDir; Face extrusionBase = elementAnalyzer.GetExtrusionBase(); IList<GeometryUtil.FaceBoundaryType> boundaryTypes; IList<CurveLoop> extrusionBoundaryLoops = GeometryUtil.GetFaceBoundaries(extrusionBase, baseLoopOffset, out boundaryTypes); // Return if we get any CurveLoops that are complex, as we don't want to export an approximation of the boundary here. foreach (GeometryUtil.FaceBoundaryType boundaryType in boundaryTypes) { if (boundaryType == GeometryUtil.FaceBoundaryType.Complex) return nullVal; } // Move base plane to start parameter location. Plane extrusionBasePlane = null; try { extrusionBasePlane = extrusionBoundaryLoops[0].GetPlane(); } catch { return nullVal; } double extrusionLength = elementAnalyzer.EndParameter - elementAnalyzer.StartParameter; double baseOffset = extrusionBasePlane.Origin.DotProduct(projDir); IFCRange extrusionRange = new IFCRange(baseOffset, extrusionLength + baseOffset); double startParam = planeOrig.DotProduct(projDir); double endParam = planeOrig.DotProduct(projDir) + extrusionLength; if ((range != null) && (startParam >= range.End || endParam <= range.Start)) { completelyClipped = true; return nullVal; } double scaledExtrusionDepth = UnitUtil.ScaleLength(extrusionLength); string profileName = null; if (element != null) { ElementType type = element.Document.GetElement(element.GetTypeId()) as ElementType; if (type != null) profileName = type.Name; } // We use a sub-transaction here in case we are able to generate the base body but not the clippings. IFCFile file = exporterIFC.GetFile(); IFCAnyHandle finalExtrusionBodyItemHnd = null; using (IFCTransaction tr = new IFCTransaction(file)) { // For creating the actual extrusion, we want to use the calculated extrusion plane, not the input plane. IFCAnyHandle extrusionBodyItemHnd = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, profileName, extrusionBoundaryLoops, extrusionBasePlane, projDir, scaledExtrusionDepth); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionBodyItemHnd)) { retVal.BaseExtrusions.Add(extrusionBodyItemHnd); finalExtrusionBodyItemHnd = extrusionBodyItemHnd; IDictionary<ElementId, ICollection<ICollection<Face>>> elementCutouts = GeometryUtil.GetCuttingElementFaces(element, elementAnalyzer); foreach (KeyValuePair<ElementId, ICollection<ICollection<Face>>> elementCutoutsForElement in elementCutouts) { // process clippings first, then openings ICollection<ICollection<Face>> unhandledElementCutoutsForElement = new List<ICollection<Face>>(); Element cuttingElement = document.GetElement(elementCutoutsForElement.Key); foreach (ICollection<Face> elementCutout in elementCutoutsForElement.Value) { ICollection<Face> skippedFaces = null; bool unhandledClipping = false; try { // The skippedFaces may represent openings that will be dealt with below. finalExtrusionBodyItemHnd = GeometryUtil.CreateClippingFromFaces(exporterIFC, cuttingElement, extrusionBasePlane, projDir, elementCutout, extrusionRange, finalExtrusionBodyItemHnd, out skippedFaces); } catch { skippedFaces = null; unhandledClipping = true; } if (finalExtrusionBodyItemHnd == null || unhandledClipping) { unhandledElementCutoutsForElement.Add(elementCutout); } else { if (finalExtrusionBodyItemHnd != extrusionBodyItemHnd) hasClippingResult = true; // Even if we created a clipping, we may have faces to further process as openings. if (skippedFaces != null && skippedFaces.Count != 0) unhandledElementCutoutsForElement.Add(skippedFaces); } } IFCAnyHandle finalExtrusionClippingBodyItemHnd = finalExtrusionBodyItemHnd; foreach (ICollection<Face> elementCutout in unhandledElementCutoutsForElement) { bool unhandledOpening = false; try { finalExtrusionBodyItemHnd = GeometryUtil.CreateOpeningFromFaces(exporterIFC, cuttingElement, extrusionBasePlane, projDir, elementCutout, extrusionRange, finalExtrusionBodyItemHnd); } catch { unhandledOpening = true; } if (finalExtrusionBodyItemHnd == null || unhandledOpening) { // Item is completely clipped. We use this only when we are certain: // 1. finalExtrusionBodyItemHnd is null. // 2. range is not null (i.e., we expect the possibility of clipping) // 3. unhandledOpening is not true (i.e., we didn't abort the operation). // If completelyClipped is true, we won't export the item, so we want to make sure // that we don't actually want to try a backup method instead. completelyClipped = (finalExtrusionBodyItemHnd == null) && (range != null) && (!unhandledOpening); tr.RollBack(); return nullVal; } else if (finalExtrusionBodyItemHnd != finalExtrusionClippingBodyItemHnd) { hasBooleanResult = true; } } } materialId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solid, exporterIFC, element); BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, extrusionBodyItemHnd, materialId); } tr.Commit(); } retVal.Handle = finalExtrusionBodyItemHnd; return retVal; } catch { return nullVal; } }
private static IFCAnyHandle FallbackTryToCreateAsExtrusion(ExporterIFC exporterIFC, Wall wallElement, SolidMeshGeometryInfo smCapsule, ElementId catId, Curve curve, Plane plane, double depth, IFCRange zSpan, IFCRange range, IFCPlacementSetter setter, out IList<IFCExtrusionData> cutPairOpenings, out bool isCompletelyClipped) { cutPairOpenings = new List<IFCExtrusionData>(); IFCAnyHandle bodyRep; isCompletelyClipped = false; XYZ localOrig = plane.Origin; bool hasExtrusion = HasElevationProfile(wallElement); if (hasExtrusion) { IList<CurveLoop> loops = GetElevationProfile(wallElement); if (loops.Count == 0) hasExtrusion = false; else { IList<IList<CurveLoop>> sortedLoops = ExporterIFCUtils.SortCurveLoops(loops); if (sortedLoops.Count == 0) return null; // Current limitation: can't handle wall split into multiple disjointed pieces. int numSortedLoops = sortedLoops.Count; if (numSortedLoops > 1) return null; bool ignoreExtrusion = true; bool cantHandle = false; bool hasGeometry = false; for (int ii = 0; (ii < numSortedLoops) && !cantHandle; ii++) { int sortedLoopSize = sortedLoops[ii].Count; if (sortedLoopSize == 0) continue; if (!ExporterIFCUtils.IsCurveLoopConvexWithOpenings(sortedLoops[ii][0], wallElement, range, out ignoreExtrusion)) { if (ignoreExtrusion) { // we need more information. Is there something to export? If so, we'll // ignore the extrusion. Otherwise, we will fail. if (smCapsule.SolidsCount() == 0 && smCapsule.MeshesCount() == 0) { continue; } } else { cantHandle = true; } hasGeometry = true; } else { hasGeometry = true; } } if (!hasGeometry) { isCompletelyClipped = true; return null; } if (cantHandle) return null; } } if (!CanExportWallGeometryAsExtrusion(wallElement, range)) return null; // extrusion direction. XYZ extrusionDir = GetWallHeightDirection(wallElement); // create extrusion boundary. IList<CurveLoop> boundaryLoops = new List<CurveLoop>(); bool alwaysThickenCurve = IsWallBaseRectangular(wallElement, curve); if (!alwaysThickenCurve) { boundaryLoops = GetLoopsFromTopBottomFace(wallElement, exporterIFC); if (boundaryLoops.Count == 0) return null; } else { CurveLoop newLoop = CurveLoop.CreateViaThicken(curve, wallElement.Width, XYZ.BasisZ); if (newLoop == null) return null; if (!newLoop.IsCounterclockwise(XYZ.BasisZ)) newLoop = GeometryUtil.ReverseOrientation(newLoop); boundaryLoops.Add(newLoop); } // origin gets scaled later. XYZ setterOffset = new XYZ(0, 0, setter.Offset + (localOrig[2] - setter.BaseOffset)); IFCAnyHandle baseBodyItemHnd = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, boundaryLoops, plane, extrusionDir, depth); if (IFCAnyHandleUtil.IsNullOrHasNoValue(baseBodyItemHnd)) return null; IFCAnyHandle bodyItemHnd = AddClippingsToBaseExtrusion(exporterIFC, wallElement, setterOffset, range, zSpan, baseBodyItemHnd, out cutPairOpenings); if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyItemHnd)) return null; IFCAnyHandle styledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, wallElement.Document, baseBodyItemHnd, ElementId.InvalidElementId); HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>(); bodyItems.Add(bodyItemHnd); IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body"); if (baseBodyItemHnd.Id == bodyItemHnd.Id) { bodyRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems, null); } else { bodyRep = RepresentationUtil.CreateClippingRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems); } return bodyRep; }
/// <summary> /// This method takes the solidsList and clips all of its solids between the given range. /// </summary> /// <param name="elem"> /// The Element from which we obtain our BoundingBoxXYZ. /// </param> /// <param name="geomElem"> /// The top-level GeometryElement from which to gather X and Y coordinates for the intersecting solid. /// </param> /// <param name="range"> /// The IFCRange whose Z values we use to create an intersecting solid to clip the solids in this class's internal solidsList. /// If range boundaries are equal, method returns, performing no clippings. /// </param> public void ClipSolidsList(GeometryElement geomElem, IFCRange range) { if (geomElem == null) { throw new ArgumentNullException("geomElemToUse"); } if (MathUtil.IsAlmostEqual(range.Start, range.End) || solidsList.Count == 0) { return; } double bottomZ; double boundDifference; if (range.Start < range.End) { bottomZ = range.Start; boundDifference = range.End - range.Start; } else { bottomZ = range.End; boundDifference = range.Start - range.End; } // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); List<Curve> perimeter = new List<Curve>(); perimeter.Add(Line.CreateBound(pointA, pointB)); perimeter.Add(Line.CreateBound(pointB, pointC)); perimeter.Add(Line.CreateBound(pointC, pointD)); perimeter.Add(Line.CreateBound(pointD, pointA)); List<CurveLoop> boxPerimeterList = new List<CurveLoop>(); boxPerimeterList.Add(CurveLoop.Create(perimeter)); Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list List<Solid> clippedSolidsList = new List<Solid>(); Solid currSolid; foreach (Solid solid in solidsList) { try { // ExecuteBooleanOperation can throw if it fails. In this case, just ignore the clipping. currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); if (currSolid != null && currSolid.Volume != 0) { clippedSolidsList.Add(currSolid); } } catch { } } solidsList = clippedSolidsList; }
/// <summary> /// This method takes the solidsList and clips all of its solids between the given range. /// </summary> /// <param name="elem"> /// The Element from which we obtain our BoundingBoxXYZ. /// </param> /// <param name="geomElem"> /// The top-level GeometryElement from which to gather X and Y coordinates for the intersecting solid. /// </param> /// <param name="range"> /// The IFCRange whose Z values we use to create an intersecting solid to clip the solids in this class's internal solidsList. /// If range boundaries are equal, method returns, performing no clippings. /// </param> public void ClipSolidsList(GeometryElement geomElem, IFCRange range) { if (geomElem == null) { throw new ArgumentNullException("geomElemToUse"); } if (MathUtil.IsAlmostEqual(range.Start, range.End) || solidsList.Count == 0) { return; } double bottomZ; double boundDifference; if (range.Start < range.End) { bottomZ = range.Start; boundDifference = range.End - range.Start; } else { bottomZ = range.End; boundDifference = range.Start - range.End; } // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); List <Curve> perimeter = new List <Curve>(); perimeter.Add(Line.CreateBound(pointA, pointB)); perimeter.Add(Line.CreateBound(pointB, pointC)); perimeter.Add(Line.CreateBound(pointC, pointD)); perimeter.Add(Line.CreateBound(pointD, pointA)); List <CurveLoop> boxPerimeterList = new List <CurveLoop>(); boxPerimeterList.Add(CurveLoop.Create(perimeter)); Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list List <Solid> clippedSolidsList = new List <Solid>(); Solid currSolid; foreach (Solid solid in solidsList) { try { // ExecuteBooleanOperation can throw if it fails. In this case, just ignore the clipping. currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); if (currSolid != null && currSolid.Volume != 0) { clippedSolidsList.Add(currSolid); } } catch { } } solidsList = clippedSolidsList; }
/// <summary> /// Attempts to create a clipping, recess, or opening from a collection of faces. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="cuttingElement">The cutting element. This will help determine whether to use a clipping or opening in boundary cases.</param> /// <param name="extrusionBasePlane">The plane of the extrusion base.</param> /// <param name="extrusionDirection">The extrusion direction.</param> /// <param name="faces">The collection of faces.</param> /// <param name="range">The valid range of the extrusion.</param> /// <param name="origBodyRepHnd">The original body representation.</param> /// <returns>The new body representation. If the clipping completely clips the extrusion, this will be null. Otherwise, this /// will be the clipped representation if a clipping was done, or the original representation if not.</returns> public static IFCAnyHandle CreateClippingFromFaces(ExporterIFC exporterIFC, Element cuttingElement, Plane extrusionBasePlane, XYZ extrusionDirection, ICollection<Face> faces, IFCRange range, IFCAnyHandle origBodyRepHnd) { if (IFCAnyHandleUtil.IsNullOrHasNoValue(origBodyRepHnd)) return null; bool polygonalOnly = ExporterCacheManager.ExportOptionsCache.ExportAs2x2; IList<CurveLoop> outerCurveLoops = new List<CurveLoop>(); IList<Plane> outerCurveLoopPlanes = new List<Plane>(); IList<bool> boundaryIsPolygonal = new List<bool>(); bool allPlanes = true; UV faceOriginUV = new UV(0, 0); foreach (Face face in faces) { FaceBoundaryType faceBoundaryType; CurveLoop curveLoop = GetOuterFaceBoundary(face, null, polygonalOnly, out faceBoundaryType); outerCurveLoops.Add(curveLoop); boundaryIsPolygonal.Add(faceBoundaryType == FaceBoundaryType.Polygonal); if (face is PlanarFace) { PlanarFace planarFace = face as PlanarFace; XYZ faceOrigin = planarFace.Origin; XYZ faceNormal = planarFace.ComputeNormal(faceOriginUV); Plane plane = new Plane(faceNormal, faceOrigin); outerCurveLoopPlanes.Add(plane); if (!curveLoop.IsCounterclockwise(faceNormal)) curveLoop.Flip(); } else { outerCurveLoopPlanes.Add(null); allPlanes = false; } } if (allPlanes) { int numFaces = faces.Count; // Special case: one face is a clip plane. if (numFaces == 1) { return ProcessClippingFace(exporterIFC, outerCurveLoops[0], outerCurveLoopPlanes[0], extrusionBasePlane, extrusionDirection, range, false, origBodyRepHnd); } KeyValuePair<bool, bool> clipsExtrusionEnds = CollectionClipsExtrusionEnds(outerCurveLoops, extrusionDirection, range); if (clipsExtrusionEnds.Key == true || clipsExtrusionEnds.Value == true) { // Don't clip for a door, window or opening. if (CreateOpeningForCategory(cuttingElement)) throw new Exception("Unhandled opening."); ICollection<int> facesToSkip = new HashSet<int>(); bool clipStart = (clipsExtrusionEnds.Key == true); bool clipBoth = (clipsExtrusionEnds.Key == true && clipsExtrusionEnds.Value == true); if (!clipBoth) { for (int ii = 0; ii < numFaces; ii++) { double slant = outerCurveLoopPlanes[ii].Normal.DotProduct(extrusionDirection); if (!MathUtil.IsAlmostZero(slant)) { if (clipStart && (slant > 0.0)) throw new Exception("Unhandled clip plane direction."); if (!clipStart && (slant < 0.0)) throw new Exception("Unhandled clip plane direction."); } else { facesToSkip.Add(ii); } } } else { // If we are clipping both the start and end of the extrusion, we have to make sure all of the clipping // planes have the same a non-negative dot product relative to one another. int clipOrientation = 0; for (int ii = 0; ii < numFaces; ii++) { double slant = outerCurveLoopPlanes[ii].Normal.DotProduct(extrusionDirection); if (!MathUtil.IsAlmostZero(slant)) { if (slant > 0.0) { if (clipOrientation < 0) throw new Exception("Unhandled clipping orientations."); clipOrientation = 1; } else { if (clipOrientation > 0) throw new Exception("Unhandled clipping orientations."); clipOrientation = -1; } } else { facesToSkip.Add(ii); } } } IFCAnyHandle newBodyRepHnd = origBodyRepHnd; for (int ii = 0; ii < numFaces; ii++) { if (facesToSkip.Contains(ii)) continue; newBodyRepHnd = ProcessClippingFace(exporterIFC, outerCurveLoops[ii], outerCurveLoopPlanes[ii], extrusionBasePlane, extrusionDirection, range, true, newBodyRepHnd); if (newBodyRepHnd == null) return null; } return newBodyRepHnd; } } //not handled throw new Exception("Unhandled clipping."); }
/// <summary> /// Creates an extrusion with potential clipping from a list of solids corresponding to an element. /// </summary> /// <param name="exporterIFC">The ExporterIFC class.</param> /// <param name="element">The element.</param> /// <param name="catId">The category of the element and/or the solid geometry.</param> /// <param name="solids">The list of solid geometries.</param> /// <param name="plane">The extrusion base plane.</param> /// <param name="projDir">The projection direction.</param> /// <param name="range">The upper and lower limits of the extrusion, in the projection direction.</param> /// <param name="completelyClipped">Returns true if the extrusion is completely outside the range.</param> /// <param name="materialIds">The material ids of the solid geometry.</param> /// <returns>The extrusion handle.</returns> public static IFCAnyHandle CreateExtrusionWithClipping(ExporterIFC exporterIFC, Element element, ElementId catId, IList<Solid> solids, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped, out HashSet<ElementId> materialIds) { HandleAndAnalyzer handleAndAnalyzer = CreateExtrusionWithClippingBase(exporterIFC, element, catId, solids, plane, projDir, range, out completelyClipped, out materialIds); return handleAndAnalyzer.Handle; }
/// <summary> /// Attempts to create a clipping, recess, or opening from a collection of faces. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="cuttingElement">The cutting element. This will help determine whether to use a clipping or opening in boundary cases.</param> /// <param name="extrusionBasePlane">The plane of the extrusion base.</param> /// <param name="extrusionDirection">The extrusion direction.</param> /// <param name="faces">The collection of faces.</param> /// <param name="range">The valid range of the extrusion.</param> /// <param name="origBodyRepHnd">The original body representation.</param> /// <returns>The new body representation. If the clipping completely clips the extrusion, this will be null. Otherwise, this /// will be the clipped representation if a clipping was done, or the original representation if not.</returns> public static IFCAnyHandle ProcessFaceCollection(ExporterIFC exporterIFC, Element cuttingElement, Plane extrusionBasePlane, XYZ extrusionDirection, ICollection <Face> faces, IFCRange range, IFCAnyHandle origBodyRepHnd) { if (IFCAnyHandleUtil.IsNullOrHasNoValue(origBodyRepHnd)) { return(null); } bool polygonalOnly = ExporterCacheManager.ExportOptionsCache.ExportAs2x2; IList <CurveLoop> outerCurveLoops = new List <CurveLoop>(); IList <Plane> outerCurveLoopPlanes = new List <Plane>(); IList <bool> boundaryIsPolygonal = new List <bool>(); bool allPlanes = true; UV faceOriginUV = new UV(0, 0); foreach (Face face in faces) { FaceBoundaryType faceBoundaryType; CurveLoop curveLoop = GetOuterFaceBoundary(face, null, polygonalOnly, out faceBoundaryType); outerCurveLoops.Add(curveLoop); boundaryIsPolygonal.Add(faceBoundaryType == FaceBoundaryType.Polygonal); if (face is PlanarFace) { PlanarFace planarFace = face as PlanarFace; XYZ faceOrigin = planarFace.Origin; XYZ faceNormal = planarFace.ComputeNormal(faceOriginUV); Plane plane = new Plane(faceNormal, faceOrigin); outerCurveLoopPlanes.Add(plane); if (!curveLoop.IsCounterclockwise(faceNormal)) { curveLoop.Flip(); } } else { outerCurveLoopPlanes.Add(null); allPlanes = false; } } if (allPlanes) { int numFaces = faces.Count; // Special case: one face is a clip plane. if (numFaces == 1) { return(ProcessClippingFace(exporterIFC, outerCurveLoops[0], outerCurveLoopPlanes[0], extrusionBasePlane, extrusionDirection, range, false, origBodyRepHnd)); } KeyValuePair <bool, bool> clipsExtrusionEnds = CollectionClipsExtrusionEnds(outerCurveLoops, extrusionDirection, range); if (clipsExtrusionEnds.Key == true || clipsExtrusionEnds.Value == true) { // Don't clip for a door, window or opening. if (CreateOpeningForCategory(cuttingElement)) { throw new Exception("Unhandled opening."); } ICollection <int> facesToSkip = new HashSet <int>(); bool clipStart = (clipsExtrusionEnds.Key == true); bool clipBoth = (clipsExtrusionEnds.Key == true && clipsExtrusionEnds.Value == true); if (!clipBoth) { for (int ii = 0; ii < numFaces; ii++) { double slant = outerCurveLoopPlanes[ii].Normal.DotProduct(extrusionDirection); if (!MathUtil.IsAlmostZero(slant)) { if (clipStart && (slant > 0.0)) { throw new Exception("Unhandled clip plane direction."); } if (!clipStart && (slant < 0.0)) { throw new Exception("Unhandled clip plane direction."); } } else { facesToSkip.Add(ii); } } } else { // If we are clipping both the start and end of the extrusion, we have to make sure all of the clipping // planes have the same a non-negative dot product relative to one another. int clipOrientation = 0; for (int ii = 0; ii < numFaces; ii++) { double slant = outerCurveLoopPlanes[ii].Normal.DotProduct(extrusionDirection); if (!MathUtil.IsAlmostZero(slant)) { if (slant > 0.0) { if (clipOrientation < 0) { throw new Exception("Unhandled clipping orientations."); } clipOrientation = 1; } else { if (clipOrientation > 0) { throw new Exception("Unhandled clipping orientations."); } clipOrientation = -1; } } else { facesToSkip.Add(ii); } } } IFCAnyHandle newBodyRepHnd = origBodyRepHnd; for (int ii = 0; ii < numFaces; ii++) { if (facesToSkip.Contains(ii)) { continue; } newBodyRepHnd = ProcessClippingFace(exporterIFC, outerCurveLoops[ii], outerCurveLoopPlanes[ii], extrusionBasePlane, extrusionDirection, range, true, newBodyRepHnd); if (newBodyRepHnd == null) { return(null); } } return(newBodyRepHnd); } } bool unhandledCases = true; if (unhandledCases) { throw new Exception("Unhandled opening or clipping."); } // We will attempt to "sew" the faces, and see what we have left over. Depending on what we have, we have an opening, recess, or clipping. IList <Edge> boundaryEdges = new List <Edge>(); foreach (Face face in faces) { EdgeArrayArray faceBoundaries = face.EdgeLoops; // We only know how to deal with the outer loop; we'll throw if we have multiple boundaries. if (faceBoundaries.Size != 1) { throw new Exception("Can't process faces with inner boundaries."); } EdgeArray faceBoundary = faceBoundaries.get_Item(0); foreach (Edge edge in faceBoundary) { if (edge.get_Face(0) == null || edge.get_Face(1) == null) { boundaryEdges.Add(edge); } } } return(origBodyRepHnd); }
/// <summary> /// Creates an extrusion with potential clipping from a solid corresponding to an element, and supplies ExtrusionCreationData for the result. /// </summary> /// <param name="exporterIFC">The ExporterIFC class.</param> /// <param name="element">The element.</param> /// <param name="catId">The category of the element and/or the solid geometry.</param> /// <param name="solid">The solid geometry.</param> /// <param name="plane">The extrusion base plane.</param> /// <param name="projDir">The projection direction.</param> /// <param name="range">The upper and lower limits of the extrusion, in the projection direction.</param> /// <param name="completelyClipped">Returns true if the extrusion is completely outside the range.</param> /// <returns>The extrusion handle.</returns> public static HandleAndData CreateExtrusionWithClippingAndProperties(ExporterIFC exporterIFC, Element element, ElementId catId, Solid solid, Plane plane, XYZ projDir, IFCRange range, out bool completelyClipped) { IList<Solid> solids = new List<Solid>(); solids.Add(solid); HashSet<ElementId> materialIds = null; HandleAndAnalyzer handleAndAnalyzer = CreateExtrusionWithClippingBase(exporterIFC, element, catId, solids, plane, projDir, range, out completelyClipped, out materialIds); HandleAndData ret = new HandleAndData(); ret.Handle = handleAndAnalyzer.Handle; ret.BaseExtrusions = handleAndAnalyzer.BaseExtrusions; ret.ShapeRepresentationType = handleAndAnalyzer.ShapeRepresentationType; ret.MaterialIds = materialIds; if (handleAndAnalyzer.Analyzer != null) ret.Data = GetExtrusionCreationDataFromAnalyzer(exporterIFC, projDir, handleAndAnalyzer.Analyzer); return ret; }
/// <summary> /// Export the individual part (IfcBuildingElementPart). /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="partElement">The part element to export.</param> /// <param name="geometryElement">The geometry of part.</param> /// <param name="productWrapper">The IFCProductWrapper object.</param> public static void ExportPart(ExporterIFC exporterIFC, Element partElement, IFCProductWrapper productWrapper, IFCPlacementSetter placementSetter, IFCAnyHandle originalPlacement, IFCRange range, IFCExtrusionAxes ifcExtrusionAxes, Element hostElement, ElementId overrideLevelId, bool asBuildingElement) { if (!ElementFilteringUtil.IsElementVisible(ExporterCacheManager.ExportOptionsCache.FilterViewForExport, partElement)) return; Part part = partElement as Part; if (part == null) return; IFCPlacementSetter standalonePlacementSetter = null; bool standaloneExport = hostElement == null && !asBuildingElement; ElementId partExportLevel = null; if (standaloneExport || asBuildingElement) { if (partElement.Level != null) partExportLevel = partElement.Level.Id; } else { if (part.OriginalCategoryId != hostElement.Category.Id) return; partExportLevel = hostElement.Level.Id; } if (overrideLevelId != null) partExportLevel = overrideLevelId; if (ExporterCacheManager.PartExportedCache.HasExported(partElement.Id, partExportLevel)) return; Options options = GeometryUtil.GetIFCExportGeometryOptions(); View ownerView = partElement.Document.GetElement(partElement.OwnerViewId) as View; if (ownerView != null) options.View = ownerView; GeometryElement geometryElement = partElement.get_Geometry(options); if (geometryElement == null) return; try { IFCFile file = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(file)) { IFCAnyHandle partPlacement = null; if (standaloneExport || asBuildingElement) { Transform orientationTrf = Transform.Identity; standalonePlacementSetter = IFCPlacementSetter.Create(exporterIFC, partElement, null, orientationTrf, partExportLevel); partPlacement = standalonePlacementSetter.GetPlacement(); } else partPlacement = ExporterUtil.CopyLocalPlacement(file, originalPlacement); bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); SolidMeshGeometryInfo solidMeshInfo; if (validRange) { solidMeshInfo = GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) return; } else { solidMeshInfo = GeometryUtil.GetSolidMeshGeometry(geometryElement, Transform.Identity); } using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(partPlacement); extrusionCreationData.ReuseLocalPlacement = false; extrusionCreationData.PossibleExtrusionAxes = ifcExtrusionAxes; IList<Solid> solids = solidMeshInfo.GetSolids(); IList<Mesh> meshes = solidMeshInfo.GetMeshes(); ElementId catId = CategoryUtil.GetSafeCategoryId(partElement); BodyData bodyData = null; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); if (solids.Count > 0 || meshes.Count > 0) { bodyData = BodyExporter.ExportBody(partElement.Document.Application, exporterIFC, partElement, catId, solids, meshes, bodyExporterOptions, extrusionCreationData); } else { IList<GeometryObject> geomlist = new List<GeometryObject>(); geomlist.Add(geometryElement); bodyData = BodyExporter.ExportBody(partElement.Document.Application, exporterIFC, partElement, catId, geomlist, bodyExporterOptions, extrusionCreationData); } IFCAnyHandle bodyRep = bodyData.RepresentationHnd; if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extrusionCreationData.ClearOpenings(); return; } IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); representations.Add(bodyRep); IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); string partGUID = ExporterIFCUtils.CreateGUID(partElement); string origPartName = NamingUtil.CreateIFCName(exporterIFC, -1); string partName = NamingUtil.GetNameOverride(partElement, origPartName); string partDescription = NamingUtil.GetDescriptionOverride(partElement, null); string partObjectType = NamingUtil.GetObjectTypeOverride(partElement, NamingUtil.CreateIFCObjectName(exporterIFC, partElement)); string partElemId = NamingUtil.CreateIFCElementId(partElement); IFCAnyHandle ifcPart = null; if (!asBuildingElement) { ifcPart = IFCInstanceExporter.CreateBuildingElementPart(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId); } else { string ifcEnumType; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); switch (exportType) { case IFCExportType.ExportColumnType: ifcPart = IFCInstanceExporter.CreateColumn(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId); break; case IFCExportType.ExportCovering: IFCCoveringType coveringType = CeilingExporter.GetIFCCoveringType(hostElement, ifcEnumType); ifcPart = IFCInstanceExporter.CreateCovering(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId, coveringType); break; case IFCExportType.ExportFooting: IFCFootingType footingType = FootingExporter.GetIFCFootingType(hostElement, ifcEnumType); ifcPart = IFCInstanceExporter.CreateFooting(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId, footingType); break; case IFCExportType.ExportRoof: IFCRoofType roofType = RoofExporter.GetIFCRoofType(ifcEnumType); ifcPart = IFCInstanceExporter.CreateRoof(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId, roofType); break; case IFCExportType.ExportSlab: IFCSlabType slabType = FloorExporter.GetIFCSlabType(ifcEnumType); ifcPart = IFCInstanceExporter.CreateSlab(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId, slabType); break; case IFCExportType.ExportWall: ifcPart = IFCInstanceExporter.CreateWallStandardCase(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId); break; default: ifcPart = IFCInstanceExporter.CreateBuildingElementProxy(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partElemId, IFCElementComposition.Element); break; } } productWrapper.AddElement(ifcPart, standaloneExport || asBuildingElement ? standalonePlacementSetter : placementSetter, extrusionCreationData, standaloneExport || asBuildingElement); //Add the exported part to exported cache. TraceExportedParts(partElement, partExportLevel, standaloneExport || asBuildingElement ? ElementId.InvalidElementId : hostElement.Id); CategoryUtil.CreateMaterialAssociations(partElement.Document, exporterIFC, ifcPart, bodyData.MaterialIds); transaction.Commit(); } } } finally { if (standalonePlacementSetter != null) standalonePlacementSetter.Dispose(); } }
public static IFCAnyHandle CreateOpeningFromFaces(ExporterIFC exporterIFC, Element cuttingElement, Plane extrusionBasePlane, XYZ extrusionDirection, ICollection<Face> faces, IFCRange range, IFCAnyHandle origBodyRepHnd) { // We will attempt to "sew" the faces, and see what we have left over. Depending on what we have, we have an opening, recess, or clipping. // top and bottom profile curves to create extrusion IDictionary<Face, IList<Curve>> boundaryCurves = new Dictionary<Face, IList<Curve>>(); // curves on same side face to check if they are valid for extrusion IDictionary<Face, IList<Curve>> boundaryCurvesInSameExistingFace = new Dictionary<Face, IList<Curve>>(); foreach (Face face in faces) { EdgeArrayArray faceBoundaries = face.EdgeLoops; // We only know how to deal with the outer loop; we'll throw if we have multiple boundaries. if (faceBoundaries.Size != 1) throw new Exception("Can't process faces with inner boundaries."); EdgeArray faceBoundary = faceBoundaries.get_Item(0); foreach (Edge edge in faceBoundary) { Face face1 = edge.get_Face(0); Face face2 = edge.get_Face(1); Face missingFace = null; Face existingFace = null; if (!faces.Contains(face1)) { missingFace = face1; existingFace = face2; } else if (!faces.Contains(face2)) { missingFace = face2; existingFace = face1; } if (missingFace != null) { Curve curve = edge.AsCurve(); if (!boundaryCurves.ContainsKey(missingFace)) boundaryCurves[missingFace] = new List<Curve>(); boundaryCurves[missingFace].Add(curve); if (!boundaryCurvesInSameExistingFace.ContainsKey(existingFace)) boundaryCurvesInSameExistingFace[existingFace] = new List<Curve>(); boundaryCurvesInSameExistingFace[existingFace].Add(curve); } } } //might be recess if (boundaryCurves.Count == 1) { // boundaryCurves contains one curve loop of top profile of an extrusion // try to find bottom profile IList<Curve> curves1 = boundaryCurves.Values.ElementAt(0); CurveLoop curveloop = CurveLoop.Create(curves1); // find the parallel face XYZ normal = curveloop.GetPlane().Normal; PlanarFace recessFace = null; foreach (Face face in faces) { PlanarFace planarFace = face as PlanarFace; if (planarFace != null && MathUtil.VectorsAreParallel(planarFace.Normal, normal)) { if (recessFace == null) recessFace = planarFace; else throw new Exception("Can't handle."); } } if (recessFace != null) { EdgeArrayArray edgeLoops = recessFace.EdgeLoops; if (edgeLoops.Size != 1) throw new Exception("Can't handle."); EdgeArray edges = edgeLoops.get_Item(0); IList<Edge> recessFaceEdges = new List<Edge>(); foreach (Edge edge in edges) { Face sideFace = edge.get_Face(0); if (sideFace == recessFace) sideFace = edge.get_Face(1); // there should be already one exist during above processing if (!boundaryCurvesInSameExistingFace.ContainsKey(sideFace)) throw new Exception("Can't handle."); boundaryCurvesInSameExistingFace[sideFace].Add(edge.AsCurve()); } } } else if (boundaryCurves.Count == 2) { // might be an internal opening, process them later // do nothing now } else if (boundaryCurves.Count == 3) //might be an opening on an edge { IList<Curve> curves1 = boundaryCurves.Values.ElementAt(0); IList<Curve> curves2 = boundaryCurves.Values.ElementAt(1); IList<Curve> curves3 = boundaryCurves.Values.ElementAt(2); IList<Curve> firstValidCurves = null; IList<Curve> secondValidCurves = null; PlanarFace face1 = boundaryCurves.Keys.ElementAt(0) as PlanarFace; PlanarFace face2 = boundaryCurves.Keys.ElementAt(1) as PlanarFace; PlanarFace face3 = boundaryCurves.Keys.ElementAt(2) as PlanarFace; if (face1 == null || face2 == null || face3 == null) { //Error throw new Exception("Can't handle."); } Face removedFace = null; // find two parallel faces if (MathUtil.VectorsAreParallel(face1.Normal, face2.Normal)) { firstValidCurves = curves1; secondValidCurves = curves2; removedFace = face3; } else if (MathUtil.VectorsAreParallel(face1.Normal, face3.Normal)) { firstValidCurves = curves1; secondValidCurves = curves3; removedFace = face2; } else if (MathUtil.VectorsAreParallel(face2.Normal, face3.Normal)) { firstValidCurves = curves2; secondValidCurves = curves3; removedFace = face1; } // remove the third one and its edge curves if (removedFace != null) { foreach (Curve curve in boundaryCurves[removedFace]) { foreach (KeyValuePair<Face, IList<Curve>> faceEdgePair in boundaryCurvesInSameExistingFace) { if (faceEdgePair.Value.Contains(curve)) boundaryCurvesInSameExistingFace[faceEdgePair.Key].Remove(curve); } } boundaryCurves.Remove(removedFace); } // sew, “closing” them with a simple line IList<IList<Curve>> curvesCollection = new List<IList<Curve>>(); curvesCollection.Add(firstValidCurves); curvesCollection.Add(secondValidCurves); foreach (IList<Curve> curves in curvesCollection) { if (curves.Count < 2) //not valid throw new Exception("Can't handle."); XYZ end0 = curves[0].get_EndPoint(0); XYZ end1 = curves[0].get_EndPoint(1); IList<Curve> processedCurves = new List<Curve>(); processedCurves.Add(curves[0]); curves.Remove(curves[0]); Curve nextCurve = null; Curve preCurve = null; // find the end points on the edges not connected while (curves.Count > 0) { foreach (Curve curve in curves) { XYZ curveEnd0 = curve.get_EndPoint(0); XYZ curveEnd1 = curve.get_EndPoint(1); if (end1.IsAlmostEqualTo(curveEnd0)) { nextCurve = curve; end1 = curveEnd1; break; } else if (end0.IsAlmostEqualTo(curveEnd1)) { preCurve = curve; end0 = curveEnd0; break; } } if (nextCurve != null) { processedCurves.Add(nextCurve); curves.Remove(nextCurve); nextCurve = null; } else if (preCurve != null) { processedCurves.Insert(0, preCurve); curves.Remove(preCurve); preCurve = null; } else throw new Exception("Can't process edges."); } // connect them with a simple line Curve newCurve = Line.get_Bound(end1, end0); processedCurves.Add(newCurve); if (!boundaryCurvesInSameExistingFace.ContainsKey(removedFace)) boundaryCurvesInSameExistingFace[removedFace] = new List<Curve>(); boundaryCurvesInSameExistingFace[removedFace].Add(newCurve); foreach (Curve curve in processedCurves) { curves.Add(curve); } } } else throw new Exception("Can't handle."); // now we should have 2 boundary curve loops IList<Curve> firstCurves = boundaryCurves.Values.ElementAt(0); IList<Curve> secondCurves = boundaryCurves.Values.ElementAt(1); PlanarFace firstFace = boundaryCurves.Keys.ElementAt(0) as PlanarFace; PlanarFace secondFace = boundaryCurves.Keys.ElementAt(1) as PlanarFace; if (firstFace == null || secondFace == null) { //Error, can't handle this throw new Exception("Can't handle."); } if (firstCurves.Count != secondCurves.Count) { //Error, can't handle this throw new Exception("Can't handle."); } CurveLoop curveLoop1 = null; CurveLoop curveLoop2 = null; SortCurves(firstCurves); curveLoop1 = CurveLoop.Create(firstCurves); SortCurves(secondCurves); curveLoop2 = CurveLoop.Create(secondCurves); if (curveLoop1.IsOpen() || curveLoop2.IsOpen() || !curveLoop1.HasPlane() || !curveLoop2.HasPlane()) { //Error, can't handle this throw new Exception("Can't handle."); } Plane plane1 = curveLoop1.GetPlane(); Plane plane2 = curveLoop2.GetPlane(); if (!curveLoop1.IsCounterclockwise(plane1.Normal)) { curveLoop1.Flip(); } if (!curveLoop2.IsCounterclockwise(plane2.Normal)) { curveLoop2.Flip(); } // check planar and parallel and orthogonal to the main extrusion dir if (!MathUtil.VectorsAreParallel(plane1.Normal, plane2.Normal)) { //Error, can't handle this throw new Exception("Can't handle."); } if (!MathUtil.VectorsAreOrthogonal(plane1.Normal, extrusionDirection) || !MathUtil.VectorsAreOrthogonal(plane2.Normal, extrusionDirection)) { //Error, can't handle this throw new Exception("Can't handle."); } //get the distance XYZ origDistance = plane1.Origin - plane2.Origin; double planesDistance = Math.Abs(origDistance.DotProduct(plane1.Normal)); // check the curves on top and bottom profiles are “identical” foreach (KeyValuePair<Face, IList<Curve>> faceEdgeCurvePair in boundaryCurvesInSameExistingFace) { IList<Curve> curves = faceEdgeCurvePair.Value; if (curves.Count != 2) { //Error, can't handle this throw new Exception("Can't handle."); } Curve edgeCurve1 = curves[0]; Curve edgeCurve2 = curves[1]; Face sideFace = faceEdgeCurvePair.Key; if (!MathUtil.IsAlmostEqual(edgeCurve1.get_EndPoint(0).DistanceTo(edgeCurve2.get_EndPoint(1)), planesDistance) || !MathUtil.IsAlmostEqual(edgeCurve1.get_EndPoint(1).DistanceTo(edgeCurve2.get_EndPoint(0)), planesDistance)) { //Error, can't handle this throw new Exception("Can't handle."); } if (edgeCurve1 is Line) { if (!(edgeCurve2 is Line) || !(sideFace is PlanarFace)) { //Error, can't handle this throw new Exception("Can't handle."); } } else if (edgeCurve1 is Arc) { if (!(edgeCurve2 is Arc) || !(sideFace is CylindricalFace)) { //Error, can't handle this throw new Exception("Can't handle."); } Arc arc1 = edgeCurve1 as Arc; Arc arc2 = edgeCurve2 as Arc; if (!MathUtil.IsAlmostEqual(arc1.Center.DistanceTo(arc2.Center), planesDistance)) { //Error, can't handle this throw new Exception("Can't handle."); } XYZ sideFaceAxis = (sideFace as CylindricalFace).Axis; if (!MathUtil.VectorsAreOrthogonal(sideFaceAxis, extrusionDirection)) { throw new Exception("Can't handle."); } } else if (edgeCurve1 is Ellipse) { if (!(edgeCurve2 is Ellipse) || !(sideFace is RuledFace)) { //Error, can't handle this throw new Exception("Can't handle."); } Ellipse ellipse1 = edgeCurve1 as Ellipse; Ellipse ellipse2 = edgeCurve2 as Ellipse; if (!MathUtil.IsAlmostEqual(ellipse1.Center.DistanceTo(ellipse2.Center), planesDistance) || !MathUtil.IsAlmostEqual(ellipse1.RadiusX, ellipse2.RadiusX) || !MathUtil.IsAlmostEqual(ellipse1.RadiusY, ellipse2.RadiusY)) { //Error, can't handle this throw new Exception("Can't handle."); } } else if (edgeCurve1 is HermiteSpline) { if (!(edgeCurve2 is HermiteSpline) || !(sideFace is RuledFace)) { //Error, can't handle this throw new Exception("Can't handle."); } HermiteSpline hermiteSpline1 = edgeCurve1 as HermiteSpline; HermiteSpline hermiteSpline2 = edgeCurve2 as HermiteSpline; IList<XYZ> controlPoints1 = hermiteSpline1.ControlPoints; IList<XYZ> controlPoints2 = hermiteSpline2.ControlPoints; int controlPointCount = controlPoints1.Count; if (controlPointCount != controlPoints2.Count) { //Error, can't handle this throw new Exception("Can't handle."); } for (int i = 0; i < controlPointCount; i++) { if (!MathUtil.IsAlmostEqual(controlPoints1[i].DistanceTo(controlPoints2[controlPointCount - i - 1]), planesDistance)) { //Error, can't handle this throw new Exception("Can't handle."); } } DoubleArray parameters1 = hermiteSpline1.Parameters; DoubleArray parameters2 = hermiteSpline1.Parameters; int parametersCount = parameters1.Size; if (parametersCount != parameters2.Size) { //Error, can't handle this throw new Exception("Can't handle."); } for (int i = 0; i < parametersCount; i++) { if (!MathUtil.IsAlmostEqual(parameters1.get_Item(i), parameters2.get_Item(i))) { //Error, can't handle this throw new Exception("Can't handle."); } } } else if (edgeCurve1 is NurbSpline) { if (!(edgeCurve2 is NurbSpline) || !(sideFace is RuledFace)) { //Error, can't handle this throw new Exception("Can't handle."); } NurbSpline nurbSpline1 = edgeCurve1 as NurbSpline; NurbSpline nurbSpline2 = edgeCurve2 as NurbSpline; IList<XYZ> controlPoints1 = nurbSpline1.CtrlPoints; IList<XYZ> controlPoints2 = nurbSpline2.CtrlPoints; int controlPointCount = controlPoints1.Count; if (controlPointCount != controlPoints2.Count) { //Error, can't handle this throw new Exception("Can't handle."); } for (int i = 0; i < controlPointCount; i++) { if (!MathUtil.IsAlmostEqual(controlPoints1[i].DistanceTo(controlPoints2[controlPointCount - i - 1]), planesDistance)) { //Error, can't handle this throw new Exception("Can't handle."); } } DoubleArray weights1 = nurbSpline1.Weights; DoubleArray weights2 = nurbSpline2.Weights; int weightsCount = weights1.Size; if (weightsCount != weights2.Size) { //Error, can't handle this throw new Exception("Can't handle."); } for (int i = 0; i < weightsCount; i++) { if (!MathUtil.IsAlmostEqual(weights1.get_Item(i), weights2.get_Item(i))) { //Error, can't handle this throw new Exception("Can't handle."); } } DoubleArray knots1 = nurbSpline1.Knots; DoubleArray knots2 = nurbSpline2.Knots; int knotsCount = knots1.Size; if (knotsCount != knots2.Size) { //Error, can't handle this throw new Exception("Can't handle."); } for (int i = 0; i < knotsCount; i++) { if (!MathUtil.IsAlmostEqual(knots1.get_Item(i), knots2.get_Item(i))) { //Error, can't handle this throw new Exception("Can't handle."); } } } else { //Error, can't handle this throw new Exception("Can't handle."); } } XYZ extDir = plane2.Origin - plane1.Origin; XYZ plane1Normal = plane1.Normal; int vecParallel = MathUtil.VectorsAreParallel2(extDir, plane1Normal); if (vecParallel == 1) { extDir = plane1Normal; } else if (vecParallel == -1) { extDir = -plane1Normal; } else throw new Exception("Can't handle."); IList<CurveLoop> origCurveLoops = new List<CurveLoop>(); origCurveLoops.Add(curveLoop1); IFCAnyHandle extrusionHandle = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, origCurveLoops, plane1, extDir, planesDistance * exporterIFC.LinearScale); IFCAnyHandle booleanBodyItemHnd = IFCInstanceExporter.CreateBooleanResult(exporterIFC.GetFile(), IFCBooleanOperator.Difference, origBodyRepHnd, extrusionHandle); return booleanBodyItemHnd; }
/// <summary> /// Split associated parts when host element is split by level. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="hostElement">The host element havign associtaed parts.</param> /// <param name="associatedPartsList">The list of associtated parts.</param> private static void SplitParts(ExporterIFC exporterIFC, Element hostElement, List<ElementId> associatedPartsList) { string ifcEnumType; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); // Split the host to find the orphan parts. IList<ElementId> orphanLevels = new List<ElementId>(); IList<ElementId> hostLevels = new List<ElementId>(); IList<IFCRange> hostRanges = new List<IFCRange>(); LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, hostElement, out hostLevels, out hostRanges); orphanLevels = hostLevels; // Split each Parts IList<ElementId> levels = new List<ElementId>(); IList<IFCRange> ranges = new List<IFCRange>(); // Dictionary to storage the level and its parts. Dictionary<ElementId, List<KeyValuePair<Part, IFCRange>>> levelParts = new Dictionary<ElementId, List<KeyValuePair<Part, IFCRange>>>(); foreach (ElementId partId in associatedPartsList) { Part part = hostElement.Document.GetElement(partId) as Part; LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, part, out levels, out ranges); // if the parts are above top level, associate them with nearest bottom level. if (ranges.Count == 0) { ElementId bottomLevelId = FindPartSplitLevel(exporterIFC, part); if (bottomLevelId == ElementId.InvalidElementId && part.Level != null) bottomLevelId = part.Level.Id; if (!levelParts.ContainsKey(bottomLevelId)) levelParts.Add(bottomLevelId, new List<KeyValuePair<Part, IFCRange>>()); KeyValuePair<Part, IFCRange> splitPartRange = new KeyValuePair<Part, IFCRange>(part, null); levelParts[bottomLevelId].Add(splitPartRange); continue; } // The parts split by levels are stored in dictionary. for (int ii = 0; ii < ranges.Count; ii++) { if (!levelParts.ContainsKey(levels[ii])) levelParts.Add(levels[ii], new List<KeyValuePair<Part, IFCRange>>()); KeyValuePair<Part, IFCRange> splitPartRange = new KeyValuePair<Part, IFCRange>(part, ranges[ii]); levelParts[levels[ii]].Add(splitPartRange); } if (levels.Count > hostLevels.Count) { orphanLevels = orphanLevels.Union<ElementId>(levels).ToList(); } } ExporterCacheManager.HostPartsCache.Register(hostElement.Id, levelParts); // The levels of orphan part. orphanLevels = orphanLevels.Where(number => !hostLevels.Contains(number)).ToList(); List<KeyValuePair<ElementId, IFCRange>> levelRangePairList = new List<KeyValuePair<ElementId, IFCRange>>(); foreach (ElementId orphanLevelId in orphanLevels) { IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, orphanLevelId); if (levelInfo == null) continue; double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(orphanLevelId); IFCRange levelRange = new IFCRange(levelInfo.Elevation, levelInfo.Elevation + levelHeight); List<KeyValuePair<Part, IFCRange>> splitPartRangeList = new List<KeyValuePair<Part, IFCRange>>(); splitPartRangeList = ExporterCacheManager.HostPartsCache.Find(hostElement.Id, orphanLevelId); IFCRange highestRange = levelRange; foreach (KeyValuePair<Part, IFCRange> partRange in splitPartRangeList) { if (partRange.Value.End > highestRange.End) { highestRange = partRange.Value; } } levelRangePairList.Add(new KeyValuePair<ElementId, IFCRange>(orphanLevelId, highestRange)); } if (levelRangePairList.Count > 0) { ExporterCacheManager.DummyHostCache.Register(hostElement.Id, levelRangePairList); } }
/// <summary> /// Collects all meshes within a GeometryElement and all solids clipped between a given IFCRange. /// </summary> /// <remarks> /// Added in 2013 to replace the temporary API method ExporterIFCUtils.GetClippedSolidMeshGeometry. /// </remarks> /// <param name="elem"> /// The Element from which we can obtain a bounding box. Not handled directly in this method, it is used in an internal helper method. /// </param> /// <param name="geomElemToUse"> /// The GeometryElement. /// </param> /// <param name="range"> /// The upper and lower levels which act as the clipping boundaries. /// </param> /// <returns>The collection of solids and meshes.</returns> public static SolidMeshGeometryInfo GetClippedSolidMeshGeometry(GeometryElement geomElemToUse, IFCRange range) { SolidMeshGeometryInfo geometryInfo = GetSolidMeshGeometry(geomElemToUse, Transform.Identity); geometryInfo.ClipSolidsList(geomElemToUse, range); return geometryInfo; }
/// <summary> /// Gets the best material id for the geometry. /// </summary> /// <param name="geometryElement">The geometry object to get the best material id.</param> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="range">The range to get the clipped geometry.</param> /// <returns>The material id.</returns> public static ElementId GetBestMaterialIdForGeometry(GeometryElement geometryElement, ExporterIFC exporterIFC, IFCRange range) { SolidMeshGeometryInfo solidMeshCapsule = null; if (range == null) { solidMeshCapsule = GeometryUtil.GetSolidMeshGeometry(geometryElement, Transform.Identity); } else { solidMeshCapsule = GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); } IList<Solid> solids = solidMeshCapsule.GetSolids(); IList<Mesh> polyMeshes = solidMeshCapsule.GetMeshes(); ElementId id = GetBestMaterialIdForGeometry(solids, polyMeshes); return id; }
private static IFCRange GetExtrusionRangeOfCurveLoop(CurveLoop loop, XYZ extrusionDirection) { IFCRange range = new IFCRange(); bool init = false; foreach (Curve curve in loop) { if (!init) { if (curve.IsBound) { IList<XYZ> coords = curve.Tessellate(); foreach (XYZ coord in coords) { double val = coord.DotProduct(extrusionDirection); if (!init) { range.Start = val; range.End = val; init = true; } else { range.Start = Math.Min(range.Start, val); range.End = Math.Max(range.End, val); } } } else { double val = curve.get_EndPoint(0).DotProduct(extrusionDirection); range.Start = val; range.End = val; init = true; } } else { double val = curve.get_EndPoint(0).DotProduct(extrusionDirection); range.Start = Math.Min(range.Start, val); range.End = Math.Max(range.End, val); } } return range; }
/// <summary> /// Creates a list of ranges to split an element. /// </summary> /// <remarks> /// We may need to split an element (e.g. column) into parts by level. /// </remarks> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="exportType"> /// The export type. /// </param> /// <param name="element"> /// The element. /// </param> /// <param name="levels"> /// The levels to split the element. /// </param> /// <param name="ranges"> /// The ranges to split the element. /// </param> public static void CreateSplitLevelRangesForElement(ExporterIFC exporterIFC, IFCExportType exportType, Element element, out IList <ElementId> levels, out IList <IFCRange> ranges) { levels = new List <ElementId>(); ranges = new List <IFCRange>(); double extension = GetLevelExtension(); if ((exportType == IFCExportType.ExportColumnType || exportType == IFCExportType.ExportWall) && (ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting)) { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); { IFCRange zSpan = new IFCRange(boundingBox.Min.Z, boundingBox.Max.Z); if (zSpan.Start < zSpan.End) { bool firstLevel = true; ElementId skipToNextLevel = ElementId.InvalidElementId; // If the base level of the element is set, we will start "looking" at that level. Anything below the base level will be included with the base level. // We will only do this if the base level is a building story. ElementId firstLevelId = GetBaseLevelIdForElement(element); bool foundFirstLevel = (firstLevelId == ElementId.InvalidElementId); List <ElementId> levelIds = ExporterCacheManager.LevelInfoCache.LevelsByElevation; foreach (ElementId levelId in levelIds) { if (!foundFirstLevel) { if (levelId != firstLevelId) { continue; } else { foundFirstLevel = true; } } if (skipToNextLevel != ElementId.InvalidElementId && levelId != skipToNextLevel) { continue; } IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, levelId); if (levelInfo == null) { continue; } // endBelowLevel if (zSpan.End < levelInfo.Elevation + extension) { continue; } // To calculate the distance to the next level, we check to see if the Level UpToLevel built-in parameter // is set. If so, we calculate the distance by getting the elevation of the UpToLevel level minus the // current elevation, and use it if it is greater than 0. If it is not greater than 0, or the built-in // parameter is not set, we use DistanceToNextLevel. double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(levelId); if (levelHeight < 0.0) { levelHeight = calculateDistanceToNextLevel(element.Document, levelId, levelInfo); } skipToNextLevel = ExporterCacheManager.LevelInfoCache.FindNextLevel(levelId); // startAboveLevel if ((!MathUtil.IsAlmostZero(levelHeight)) && (zSpan.Start > levelInfo.Elevation + levelHeight - extension)) { continue; } bool startBelowLevel = !firstLevel && (zSpan.Start < levelInfo.Elevation - extension); bool endAboveLevel = ((!MathUtil.IsAlmostZero(levelHeight)) && (zSpan.End > levelInfo.Elevation + levelHeight + extension)); if (!startBelowLevel && !endAboveLevel) { break; } IFCRange currentSpan = new IFCRange( startBelowLevel ? levelInfo.Elevation : zSpan.Start, endAboveLevel ? (levelInfo.Elevation + levelHeight) : zSpan.End); ranges.Add(currentSpan); levels.Add(levelId); firstLevel = false; } } } } }
// return null if parent should be completely clipped. // TODO: determine whether or not to use face boundary. private static IFCAnyHandle ProcessClippingFace(ExporterIFC exporterIFC, CurveLoop outerBoundary, Plane boundaryPlane, Plane extrusionBasePlane, XYZ extrusionDirection, IFCRange range, bool useFaceBoundary, IFCAnyHandle bodyItemHnd) { if (outerBoundary == null || boundaryPlane == null) throw new Exception("Invalid face boundary."); double clippingSlant = boundaryPlane.Normal.DotProduct(extrusionDirection); if (useFaceBoundary) { if (MathUtil.IsAlmostZero(clippingSlant)) return bodyItemHnd; } bool clipCompletely; if (!IsInRange(range, outerBoundary, boundaryPlane, extrusionDirection, out clipCompletely)) return clipCompletely ? null : bodyItemHnd; if (MathUtil.IsAlmostZero(clippingSlant)) throw new Exception("Can't create clipping perpendicular to extrusion."); IFCFile file = exporterIFC.GetFile(); XYZ scaledOrig = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, boundaryPlane.Origin); XYZ scaledNorm = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, boundaryPlane.Normal); XYZ scaledXDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, boundaryPlane.XVec); IFCAnyHandle planeAxisHnd = ExporterUtil.CreateAxis(file, scaledOrig, scaledNorm, scaledXDir); IFCAnyHandle surfHnd = IFCInstanceExporter.CreatePlane(file, planeAxisHnd); IFCAnyHandle clippedBodyItemHnd = null; IFCAnyHandle halfSpaceHnd = null; if (useFaceBoundary) { IFCAnyHandle boundedCurveHnd; if (boundaryPlane != null) { XYZ projScaledOrigin = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, extrusionBasePlane.Origin); XYZ projScaledX = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, extrusionBasePlane.XVec); XYZ projScaledY= ExporterIFCUtils.TransformAndScaleVector(exporterIFC, extrusionBasePlane.YVec); XYZ projScaledNorm = projScaledX.CrossProduct(projScaledY); Plane projScaledPlane = new Plane(projScaledX, projScaledY, projScaledOrigin); IList<UV> polylinePts = TransformAndProjectCurveLoopToPlane(exporterIFC, outerBoundary, projScaledPlane); polylinePts.Add(polylinePts[0]); boundedCurveHnd = ExporterUtil.CreatePolyline(file, polylinePts); IFCAnyHandle boundedAxisHnd = ExporterUtil.CreateAxis(file, projScaledOrigin, projScaledNorm, projScaledX); halfSpaceHnd = IFCInstanceExporter.CreatePolygonalBoundedHalfSpace(file, boundedAxisHnd, boundedCurveHnd, surfHnd, false); } else { throw new Exception("Can't create non-polygonal face boundary."); } } else { halfSpaceHnd = IFCInstanceExporter.CreateHalfSpaceSolid(file, surfHnd, false); } if (halfSpaceHnd == null) throw new Exception("Can't create clipping."); clippedBodyItemHnd = IFCInstanceExporter.CreateBooleanClippingResult(file, IFCBooleanOperator.Difference, bodyItemHnd, halfSpaceHnd); return clippedBodyItemHnd; }
/// <summary> /// Export the dummy wall to host an orphan part. It usually happens in the cases of associated parts are higher than split sub-wall. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The wall element.</param> /// <param name="geometryElement">The geometry of wall.</param> /// <param name="origWrapper">The ProductWrapper.</param> /// <param name="overrideLevelId">The ElementId that will crate the dummy wall.</param> /// <param name="range">The IFCRange corresponding to the dummy wall.</param> /// <returns>The handle of dummy wall.</returns> public static IFCAnyHandle ExportDummyWall(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { using (ProductWrapper localWrapper = ProductWrapper.Create(origWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; if (wallElement == null) return null; if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; // get global values. Document doc = element.Document; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(wallElement); if (exportParts && !PartExporter.CanExportElementInPartExport(wallElement, validRange ? overrideLevelId : wallElement.LevelId, validRange)) return null; string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = null; int subElementIndex = ExporterStateManager.GetCurrentRangeIndex(); if (subElementIndex == 0) elemGUID = GUIDUtil.CreateGUID(element); else if (subElementIndex <= ExporterStateManager.RangeIndexSetter.GetMaxStableGUIDs()) elemGUID = GUIDUtil.CreateSubElementGUID(element, subElementIndex + (int)IFCGenericSubElements.SplitInstanceStart - 1); else elemGUID = GUIDUtil.CreateGUID(); string elemName = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemTag = NamingUtil.GetTagOverride(element, NamingUtil.CreateIFCElementId(element)); Transform orientationTrf = Transform.Identity; using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.LocalPlacement; wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, null, elemTag, "NOTDEFINED"); if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; localWrapper.AddElement(element, wallHnd, setter, extraParams, true); ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, wallHnd, element.Id, wallLevelId); } return wallHnd; } }
/// <summary> /// Creates a list of ranges to split an element. /// </summary> /// <remarks> /// We may need to split an element (e.g. column) into parts by level. /// </remarks> /// <param name="exporterIFC">The ExporterIFC object. </param> /// <param name="exportType">The export type. </param> /// <param name="element">The element. </param> /// <param name="levels">The levels to split the element.</param> /// <param name="ranges">The ranges to split the element. These will be non-overlapping.</param> public static void CreateSplitLevelRangesForElement(ExporterIFC exporterIFC, IFCExportType exportType, Element element, out IList <ElementId> levels, out IList <IFCRange> ranges) { levels = new List <ElementId>(); ranges = new List <IFCRange>(); if (!ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting) { return; } double extension = GetLevelExtension(); bool splitByLevel = (exportType == IFCExportType.IfcColumnType || exportType == IFCExportType.IfcWall) || (exportType == IFCExportType.IfcDuctSegmentType); if (!splitByLevel) { return; } BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox == null) { return; } { IFCRange zSpan = new IFCRange(boundingBox.Min.Z, boundingBox.Max.Z); if (zSpan.Start < zSpan.End) { bool firstLevel = true; ElementId skipToNextLevel = ElementId.InvalidElementId; // If the base level of the element is set, we will start "looking" at that level. Anything below the base level will be included with the base level. // We will only do this if the base level is a building story. ElementId firstLevelId = GetBaseLevelIdForElement(element); bool foundFirstLevel = (firstLevelId == ElementId.InvalidElementId); IList <ElementId> levelIds = ExporterCacheManager.LevelInfoCache.BuildingStoreysByElevation; foreach (ElementId levelId in levelIds) { if (!foundFirstLevel) { if (levelId != firstLevelId) { continue; } else { foundFirstLevel = true; } } if (skipToNextLevel != ElementId.InvalidElementId && levelId != skipToNextLevel) { continue; } IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, levelId); if (levelInfo == null) { continue; } // endBelowLevel if (zSpan.End < levelInfo.Elevation + extension) { continue; } // To calculate the distance to the next level, we check to see if the Level UpToLevel built-in parameter // is set. If so, we calculate the distance by getting the elevation of the UpToLevel level minus the // current elevation, and use it if it is greater than 0. If it is not greater than 0, or the built-in // parameter is not set, we use DistanceToNextLevel. double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(levelId); if (levelHeight < 0.0) { levelHeight = CalculateDistanceToNextLevel(element.Document, levelId, levelInfo); } skipToNextLevel = ExporterCacheManager.LevelInfoCache.FindNextLevel(levelId); // startAboveLevel if ((!MathUtil.IsAlmostZero(levelHeight)) && (zSpan.Start > levelInfo.Elevation + levelHeight - extension)) { continue; } bool startBelowLevel = !firstLevel && (zSpan.Start < levelInfo.Elevation - extension); bool endAboveLevel = ((!MathUtil.IsAlmostZero(levelHeight)) && (zSpan.End > levelInfo.Elevation + levelHeight + extension)); if (!startBelowLevel && !endAboveLevel) { break; } IFCRange currentSpan = new IFCRange( startBelowLevel ? levelInfo.Elevation : zSpan.Start, endAboveLevel ? (levelInfo.Elevation + levelHeight) : zSpan.End); // We want our ranges to be non-overlapping. As such, we'll modify the start parameter // to be at least as large as the previous end parameter (if any). If this makes the // range invalid, we won't add it. if (ranges.Count > 0) { IFCRange lastSpan = ranges.Last(); if (lastSpan.End > currentSpan.End - MathUtil.Eps()) { continue; } currentSpan.Start = Math.Max(currentSpan.Start, lastSpan.End); } ranges.Add(currentSpan); levels.Add(levelId); firstLevel = false; } } } }
private static bool WallHasGeometryToExport(Wall wallElement, IList<Solid> solids, IList<Mesh> meshes, IFCRange range, out bool isCompletelyClipped) { isCompletelyClipped = false; bool hasExtrusion = HasElevationProfile(wallElement); if (hasExtrusion) { IList<CurveLoop> loops = GetElevationProfile(wallElement); if (loops.Count == 0) hasExtrusion = false; else { IList<IList<CurveLoop>> sortedLoops = ExporterIFCUtils.SortCurveLoops(loops); if (sortedLoops.Count == 0) return false; // Current limitation: can't handle wall split into multiple disjointed pieces. int numSortedLoops = sortedLoops.Count; if (numSortedLoops > 1) return false; bool ignoreExtrusion = true; bool cantHandle = false; bool hasGeometry = false; for (int ii = 0; (ii < numSortedLoops) && !cantHandle; ii++) { int sortedLoopSize = sortedLoops[ii].Count; if (sortedLoopSize == 0) continue; if (!ExporterIFCUtils.IsCurveLoopConvexWithOpenings(sortedLoops[ii][0], wallElement, range, out ignoreExtrusion)) { if (ignoreExtrusion) { // we need more information. Is there something to export? If so, we'll // ignore the extrusion. Otherwise, we will fail. if (solids.Count == 0 && meshes.Count == 0) continue; } else { cantHandle = true; } hasGeometry = true; } else { hasGeometry = true; } } if (!hasGeometry) { isCompletelyClipped = true; return false; } if (cantHandle) return false; } } return true; }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="element"> /// The element. /// </param> /// <param name="geometryElement"> /// The geometry element. /// </param> /// <param name="origWrapper"> /// The IFCProductWrapper. /// </param> /// <param name="overrideLevelId"> /// The level id. /// </param> /// <param name="range"> /// The range to be exported for the element. /// </param> /// <returns> /// The exported wall handle. /// </returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, IFCProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { using (IFCProductWrapper localWrapper = IFCProductWrapper.Create(origWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; if (wallElement == null && famInstWallElem == null) return null; if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; // get global values. Document doc = element.Document; double scale = exporterIFC.LinearScale; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); IFCAnyHandle contextOfItemsAxis = exporterIFC.Get3DContextHandle("Axis"); IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body"); IFCRange zSpan = new IFCRange(); double depth = 0.0; bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(wallElement); if (exportParts && !PartExporter.CanExportElementInPartExport(wallElement, validRange? overrideLevelId : wallElement.Level.Id, validRange)) return null; // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (wallElement != null && geometryElement != null) { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox == null) return null; zSpan = new IFCRange(boundingBox.Min.Z, boundingBox.Max.Z); // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan.Start, range.Start) : zSpan.Start; double topHeight = validRange ? Math.Min(zSpan.End, range.End) : zSpan.End; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return null; depth *= scale; } IFCAnyHandle axisRep = null; IFCAnyHandle bodyRep = null; bool exportingAxis = false; Curve curve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; bool exportingInplaceOpenings = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); curve = GetWallTrimmedCurve(wallElement, baseCurve); IFCRange curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(curve, out curveBounds, out localXDir, out oldOrig); localOrig = oldOrig; if (baseCurve != null) { if (!validRange || (MathUtil.IsAlmostEqual(range.Start, zSpan.Start))) { XYZ newOrig = baseCurve.Evaluate(curveBounds.Start, false); if (!validRange && (zSpan.Start < newOrig[2] - eps)) localOrig = new XYZ(localOrig.X, localOrig.Y, zSpan.Start); else localOrig = new XYZ(localOrig.X, localOrig.Y, newOrig[2]); } else { localOrig = new XYZ(localOrig.X, localOrig.Y, range.Start); } } double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); curve = GeometryUtil.MoveCurve(curve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.GetPlacement(); Plane plane = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = XYZ.BasisZ; // two representations: axis, body. { if ((centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = "Curve2D"; // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, curve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); Document document = element.Document; if (wallElement != null && exportingAxis && curve != null) { SolidMeshGeometryInfo solidMeshInfo = (range == null) ? GeometryUtil.GetSolidMeshGeometry(geometryElement, Transform.Identity) : GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); IList<Solid> solids = solidMeshInfo.GetSolids(); IList<Mesh> meshes = solidMeshInfo.GetMeshes(); if (solids.Count == 0 && meshes.Count == 0) return null; bool useNewCode = false; if (useNewCode && solids.Count == 1 && meshes.Count == 0) { bool completelyClipped; bodyRep = ExtrusionExporter.CreateExtrusionWithClipping(exporterIFC, wallElement, catId, solids[0], plane, projDir, range, out completelyClipped); if (completelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { exportedAsWallWithAxis = true; exportedBodyDirectly = true; } else { exportedAsWallWithAxis = false; exportedBodyDirectly = false; } } if (!exportedAsWallWithAxis) { // Fallback - use native routines to try to export wall. bool isCompletelyClipped; bodyRep = FallbackTryToCreateAsExtrusion(exporterIFC, wallElement, solidMeshInfo, catId, curve, plane, depth, zSpan, range, setter, out cutPairOpenings, out isCompletelyClipped); if (isCompletelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) exportedAsWallWithAxis = true; } } using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { ElementId matId = ElementId.InvalidElementId; if (!exportedAsWallWithAxis) { SolidMeshGeometryInfo solidMeshCapsule = null; if (wallElement != null) { if (validRange) { solidMeshCapsule = GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); } else { solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } if (solidMeshCapsule.SolidsCount() == 0 && solidMeshCapsule.MeshesCount() == 0) { return null; } } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); solidMeshCapsule = GeometryUtil.GetSolidMeshGeometry(geomElemToUse, trf); } IList<Solid> solids = solidMeshCapsule.GetSolids(); IList<Mesh> meshes = solidMeshCapsule.GetMeshes(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); if ((solids.Count > 0) || (meshes.Count > 0)) { matId = BodyExporter.GetBestMaterialIdForGeometry(solids, meshes); bodyRep = BodyExporter.ExportBody(element.Document.Application, exporterIFC, element, catId, solids, meshes, bodyExporterOptions, extraParams).RepresentationHnd; } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); BodyData bodyData = BodyExporter.ExportBody(element.Document.Application, exporterIFC, element, catId, geomElemList, bodyExporterOptions, extraParams); bodyRep = bodyData.RepresentationHnd; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extraParams.ClearOpenings(); return null; } // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasExtrusionDirection) { extrDirUsed = extraParams.ExtrusionDirection; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = null; IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = (validRange) ? ExporterIFCUtils.CreateGUID() : ExporterIFCUtils.CreateGUID(element); string elemName = NamingUtil.GetNameOverride(element, exporterIFC.GetName()); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemId = NamingUtil.CreateIFCElementId(element); if (exportedAsWallWithAxis) { wallHnd = IFCInstanceExporter.CreateWallStandardCase(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemId); if (exportParts) PartExporter.ExportHostPart(exporterIFC, wallElement, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(wallHnd, setter, extraParams, true); OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } else { double scaledWidth = wallElement.Width * scale; ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, wallElement, scaledWidth, range, setter, localPlacement, localWrapper); } // export Base Quantities if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { CreateWallBaseQuantities(exporterIFC, wallElement, wallHnd, depth); } } else { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemId); if (exportParts) PartExporter.ExportHostPart(exporterIFC, wallElement, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(wallHnd, setter, extraParams, true); // Only export one material for 2x2; for future versions, export the whole list. if (exporterIFC.ExportAs2x2 && (matId != ElementId.InvalidElementId) && !exportParts) { CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, famInstWallElem, 0.0, range, setter, localPlacement, localWrapper); } if (exportedBodyDirectly) OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, element, localWrapper); ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if (wallElement != null && !exportParts) { if (!exporterIFC.ExportAs2x2 || exportedAsWallWithAxis) //will move this check into ExportHostObject HostObjectExporter.ExportHostObjectMaterials(exporterIFC, wallElement, localWrapper.GetAnElement(), geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2); } exporterIFC.RegisterSpaceBoundingElementHandle(wallHnd, element.Id, wallLevelId); return wallHnd; } } } }
private static IFCAnyHandle TryToCreateAsExtrusion(ExporterIFC exporterIFC, Wall wallElement, IList<IList<IFCConnectedWallData>> connectedWalls, IList<Solid> solids, IList<Mesh> meshes, double baseWallElevation, ElementId catId, Curve baseCurve, Curve trimmedCurve, Plane wallLCS, double scaledDepth, IFCRange zSpan, IFCRange range, PlacementSetter setter, out IList<IFCExtrusionData> cutPairOpenings, out bool isCompletelyClipped, out double scaledFootprintArea, out double scaledLength) { cutPairOpenings = new List<IFCExtrusionData>(); IFCAnyHandle bodyRep; scaledFootprintArea = 0; double unscaledLength = trimmedCurve != null ? trimmedCurve.Length : 0; scaledLength = UnitUtil.ScaleLength(unscaledLength); XYZ localOrig = wallLCS.Origin; // Check to see if the wall has geometry given the specified range. if (!WallHasGeometryToExport(wallElement, solids, meshes, range, out isCompletelyClipped)) return null; // This is our major check here that goes into internal code. If we have enough information to faithfully reproduce // the wall as an extrusion with clippings and openings, we will continue. Otherwise, export it as a BRep. if (!CanExportWallGeometryAsExtrusion(wallElement, range)) return null; // extrusion direction. XYZ extrusionDir = GetWallHeightDirection(wallElement); // create extrusion boundary. bool alwaysThickenCurve = IsWallBaseRectangular(wallElement, trimmedCurve); double unscaledWidth = wallElement.Width; IList<CurveLoop> boundaryLoops = GetBoundaryLoopsFromWall(exporterIFC, wallElement, alwaysThickenCurve, trimmedCurve, unscaledWidth); if (boundaryLoops == null || boundaryLoops.Count == 0) return null; double fullUnscaledLength = baseCurve.Length; double unscaledFootprintArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(boundaryLoops); scaledFootprintArea = UnitUtil.ScaleArea(unscaledFootprintArea); // We are going to do a little sanity check here. If the scaledFootprintArea is significantly less than the // width * length of the wall footprint, we probably calculated the area wrong, and will abort. // This could occur because of a door or window that cuts a corner of the wall (i.e., has no wall material on one side). // We want the scaledFootprintArea to be at least (95% of approximateBaseArea - 2 * side wall area). // The "side wall area" is an approximate value that takes into account potential wall joins. // This prevents us from doing extra work for many small walls because of joins. We'll allow 1' (~30 cm) per side for this. double approximateUnscaledBaseArea = unscaledWidth * fullUnscaledLength; if (unscaledFootprintArea < (approximateUnscaledBaseArea * .95 - 2 * unscaledWidth)) { // Can't handle the case where we don't have a simple extrusion to begin with. if (!alwaysThickenCurve) return null; boundaryLoops = GetBoundaryLoopsFromBaseCurve(wallElement, connectedWalls, baseCurve, trimmedCurve, unscaledWidth, scaledDepth); if (boundaryLoops == null || boundaryLoops.Count == 0) return null; } // origin gets scaled later. double baseWallZOffset = localOrig[2] - ((range == null) ? baseWallElevation : Math.Min(range.Start, baseWallElevation)); XYZ modifiedSetterOffset = new XYZ(0, 0, setter.Offset + baseWallZOffset); IFCAnyHandle baseBodyItemHnd = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, boundaryLoops, wallLCS, extrusionDir, scaledDepth); if (IFCAnyHandleUtil.IsNullOrHasNoValue(baseBodyItemHnd)) return null; IFCAnyHandle bodyItemHnd = AddClippingsToBaseExtrusion(exporterIFC, wallElement, modifiedSetterOffset, range, zSpan, baseBodyItemHnd, out cutPairOpenings); if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyItemHnd)) return null; bool hasClipping = bodyItemHnd.Id != baseBodyItemHnd.Id; ElementId matId = HostObjectExporter.GetFirstLayerMaterialId(wallElement); IFCAnyHandle styledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, wallElement.Document, baseBodyItemHnd, matId); HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>(); bodyItems.Add(bodyItemHnd); // Check whether wall has opening. If it has, exporting it in the Reference View will need to be in a tessellated geometry that includes the opening cut IList<IFCOpeningData> openingDataList = ExporterIFCUtils.GetOpeningData(exporterIFC, wallElement, wallLCS, range); bool wallHasOpening = openingDataList.Count > 0; BodyExporterOptions options = new BodyExporterOptions(true); IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body"); if (!hasClipping) { // Check whether wall has opening. If it has, exporting it in Reference View will need to be in a tesselated geometry that includes the opening cut if (ExporterUtil.IsReferenceView() && wallHasOpening) { List<GeometryObject> geomList = new List<GeometryObject>(); bodyItems.Clear(); // Since we will change the geometry, clear existing extrusion data first if (solids.Count > 0) foreach (GeometryObject solid in solids) geomList.Add(solid); if (meshes.Count > 0) foreach (GeometryObject mesh in meshes) geomList.Add(mesh); foreach (GeometryObject geom in geomList) { IFCAnyHandle triangulatedBodyItem = BodyExporter.ExportBodyAsTriangulatedFaceSet(exporterIFC, wallElement, options, geom); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBodyItem)) bodyItems.Add(triangulatedBodyItem); } bodyRep = RepresentationUtil.CreateTessellatedRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems, null); } else bodyRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems, null); } else { // Create TessellatedRep geometry if it is Reference View. if (ExporterUtil.IsReferenceView()) { List<GeometryObject> geomList = new List<GeometryObject>(); // The native function AddClippingsToBaseExtrusion will create the IfcBooleanClippingResult entity and therefore here we need to delete it foreach (IFCAnyHandle item in bodyItems) { item.Dispose(); //Still DOES NOT work, the IfcBooleanClippingResult is still orphaned in the IFC file! } bodyItems.Clear(); // Since we will change the geometry, clear existing extrusion data first if (solids.Count > 0) foreach (GeometryObject solid in solids) geomList.Add(solid); if (meshes.Count > 0) foreach (GeometryObject mesh in meshes) geomList.Add(mesh); foreach (GeometryObject geom in geomList) { IFCAnyHandle triangulatedBodyItem = BodyExporter.ExportBodyAsTriangulatedFaceSet(exporterIFC, wallElement, options, geom); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBodyItem)) bodyItems.Add(triangulatedBodyItem); } bodyRep = RepresentationUtil.CreateTessellatedRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems, null); } else bodyRep = RepresentationUtil.CreateClippingRep(exporterIFC, wallElement, catId, contextOfItemsBody, bodyItems); } return bodyRep; }
/// <summary> /// Export the dummy wall to host an orphan part. It usually happens in the cases of associated parts are higher than split sub-wall. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The wall element.</param> /// <param name="geometryElement">The geometry of wall.</param> /// <param name="origWrapper">The IFCProductWrapper.</param> /// <param name="overrideLevelId">The ElementId that will crate the dummy wall.</param> /// <param name="range">The IFCRange corresponding to the dummy wall.</param> /// <returns>The handle of dummy wall.</returns> public static IFCAnyHandle ExportDummyWall(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, IFCProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { using (IFCProductWrapper localWrapper = IFCProductWrapper.Create(origWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; if (wallElement == null) return null; if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; // get global values. Document doc = element.Document; double scale = exporterIFC.LinearScale; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(wallElement); if (exportParts && !PartExporter.CanExportElementInPartExport(wallElement, validRange ? overrideLevelId : wallElement.Level.Id, validRange)) return null; string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = (validRange) ? ExporterIFCUtils.CreateGUID() : ExporterIFCUtils.CreateGUID(element); string elemName = NamingUtil.GetNameOverride(element, exporterIFC.GetName()); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemId = NamingUtil.CreateIFCElementId(element); Transform orientationTrf = Transform.Identity; using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.GetPlacement(); wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, null, elemId); if (exportParts) PartExporter.ExportHostPart(exporterIFC, wallElement, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; localWrapper.AddElement(wallHnd, setter, extraParams, true); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, element, localWrapper); ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; exporterIFC.RegisterSpaceBoundingElementHandle(wallHnd, element.Id, wallLevelId); } return wallHnd; } }
// Get a list of solids and meshes, but only if we haven't already done so. private static void GetSolidsAndMeshes(GeometryElement geometryElement, IFCRange range, ref IList<Solid> solids, ref IList<Mesh> meshes) { if (solids.Count > 0 || meshes.Count > 0) return; SolidMeshGeometryInfo solidMeshInfo = (range == null) ? GeometryUtil.GetSplitSolidMeshGeometry(geometryElement) : GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); solids = solidMeshInfo.GetSolids(); meshes = solidMeshInfo.GetMeshes(); }
/// <summary> /// Identifies if the base geometry of the wall can be represented as an extrusion. /// </summary> /// <param name="element"> /// The wall element. /// </param> /// <param name="range"> /// The range. This consists of two double values representing the height in Z at the start and the end /// of the range. If the values are identical the entire wall is used. /// </param> /// <returns> /// True if the wall export can be made in the form of an extrusion, false if the /// geometry cannot be assigned to an extrusion. /// </returns> static bool CanExportWallGeometryAsExtrusion(Element element, IFCRange range) { return ExporterIFCUtils.CanExportWallGeometryAsExtrusion(element, range); }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The element.</param> /// <param name="connectedWalls">Information about walls joined to this wall.</param> /// <param name="geometryElement">The geometry element.</param> /// <param name="origWrapper">The ProductWrapper.</param> /// <param name="overrideLevelId">The level id.</param> /// <param name="range">The range to be exported for the element.</param> /// <returns>The exported wall handle.</returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, IList<IList<IFCConnectedWallData>> connectedWalls, GeometryElement geometryElement, ProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { // Check cases where we choose not to export early. ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; FaceWall faceWall = element as FaceWall; bool exportingWallElement = (wallElement != null); bool exportingFamilyInstance = (famInstWallElem != null); bool exportingFaceWall = (faceWall != null); if (!exportingWallElement && !exportingFamilyInstance && !exportingFaceWall) return null; if (exportingWallElement && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; IFCRange zSpan = null; double depth = 0.0; bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(element); if (exportParts && !PartExporter.CanExportElementInPartExport(element, validRange ? overrideLevelId : element.LevelId, validRange)) return null; IList<Solid> solids = new List<Solid>(); IList<Mesh> meshes = new List<Mesh>(); bool exportingInplaceOpenings = false; if (!exportParts) { if (exportingWallElement || exportingFaceWall) { GetSolidsAndMeshes(geometryElement, range, ref solids, ref meshes); if (solids.Count == 0 && meshes.Count == 0) return null; } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); SolidMeshGeometryInfo solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geomElemToUse, trf); solids = solidMeshCapsule.GetSolids(); meshes = solidMeshCapsule.GetMeshes(); } } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { using (ProductWrapper localWrapper = ProductWrapper.Create(origWrapper)) { // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (exportingWallElement && geometryElement != null) { // There is a problem in the API where some walls with vertical structures are overreporting their height, // making it appear as if there are clipping problems on export. We will work around this by getting the // height directly from the solid(s). if (solids.Count > 0 && meshes.Count == 0) { zSpan = GetBoundingBoxOfSolids(solids); } else { BoundingBoxXYZ boundingBox = wallElement.get_BoundingBox(null); if (boundingBox != null) zSpan = GetBoundingBoxZRange(boundingBox); } if (zSpan == null) return null; // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan.Start, range.Start) : zSpan.Start; double topHeight = validRange ? Math.Min(zSpan.End, range.End) : zSpan.End; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return null; depth = UnitUtil.ScaleLength(depth); } else { zSpan = new IFCRange(); } Document doc = element.Document; double baseWallElevation = 0.0; ElementId baseLevelId = PlacementSetter.GetBaseLevelIdForElement(element); if (baseLevelId != ElementId.InvalidElementId) { Element baseLevel = doc.GetElement(baseLevelId); if (baseLevel is Level) baseWallElevation = (baseLevel as Level).Elevation; } IFCAnyHandle axisRep = null; IFCAnyHandle bodyRep = null; bool exportingAxis = false; Curve trimmedCurve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); trimmedCurve = GetWallTrimmedCurve(wallElement, baseCurve); IFCRange curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(trimmedCurve, out curveBounds, out localXDir, out oldOrig); // Move the curve to the bottom of the geometry or the bottom of the range, which is higher. if (baseCurve != null) localOrig = new XYZ(oldOrig.X, oldOrig.Y, validRange ? Math.Max(range.Start, zSpan.Start) : zSpan.Start); else localOrig = oldOrig; double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); trimmedCurve = GeometryUtil.MoveCurve(trimmedCurve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } else { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox != null) { XYZ bBoxMin = boundingBox.Min; XYZ bBoxMax = boundingBox.Max; if (validRange) localOrig = new XYZ(bBoxMin.X, bBoxMin.Y, range.Start); else localOrig = boundingBox.Min; XYZ localXDirMax = null; Transform bTrf = boundingBox.Transform; XYZ localXDirMax1 = new XYZ(bBoxMax.X, localOrig.Y, localOrig.Z); localXDirMax1 = bTrf.OfPoint(localXDirMax1); XYZ localXDirMax2 = new XYZ(localOrig.X, bBoxMax.Y, localOrig.Z); localXDirMax2 = bTrf.OfPoint(localXDirMax2); if (localXDirMax1.DistanceTo(localOrig) >= localXDirMax2.DistanceTo(localOrig)) localXDirMax = localXDirMax1; else localXDirMax = localXDirMax2; localXDir = localXDirMax.Subtract(localOrig); localXDir = localXDir.Normalize(); localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } } IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; double scaledFootprintArea = 0; double scaledLength = 0; using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.LocalPlacement; // The local coordinate system of the wall as defined by IFC for IfcWallStandardCase. Plane wallLCS = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = XYZ.BasisZ; // two representations: axis, body. { if (!exportParts && (centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = "Curve2D"; // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, wallLCS, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, trimmedCurve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); IFCAnyHandle contextOfItemsAxis = exporterIFC.Get3DContextHandle("Axis"); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); if (!exportParts && exportingWallElement && exportingAxis && trimmedCurve != null) { bool isCompletelyClipped; bodyRep = TryToCreateAsExtrusion(exporterIFC, wallElement, connectedWalls, solids, meshes, baseWallElevation, catId, centerCurve, trimmedCurve, wallLCS, depth, zSpan, range, setter, out cutPairOpenings, out isCompletelyClipped, out scaledFootprintArea, out scaledLength); if (isCompletelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) exportedAsWallWithAxis = true; } using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { BodyData bodyData = null; if (!exportedAsWallWithAxis) { extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); // Swept solids are not natively exported as part of CV2.0. // We have removed the UI toggle for this, so that it is by default false, but keep for possible future use. if (ExporterCacheManager.ExportOptionsCache.ExportAdvancedSweptSolids) bodyExporterOptions.TryToExportAsSweptSolid = true; ElementId overrideMaterialId = ElementId.InvalidElementId; if (exportingWallElement) overrideMaterialId = HostObjectExporter.GetFirstLayerMaterialId(wallElement); if (!exportParts) { if ((solids.Count > 0) || (meshes.Count > 0)) { bodyRep = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, solids, meshes, bodyExporterOptions, extraParams).RepresentationHnd; } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, geomElemList, bodyExporterOptions, extraParams); bodyRep = bodyData.RepresentationHnd; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extraParams.ClearOpenings(); return null; } } // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasExtrusionDirection) { extrDirUsed = extraParams.ExtrusionDirection; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = null; if (!exportParts) { IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = null; if ((solids.Count > 0) || (meshes.Count > 0)) boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, solids, meshes, Transform.Identity); else boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) representations.Add(boundingBoxRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); } ElementId matId = ElementId.InvalidElementId; string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = null; int subElementIndex = ExporterStateManager.GetCurrentRangeIndex(); if (subElementIndex == 0) elemGUID = GUIDUtil.CreateGUID(element); else if (subElementIndex <= ExporterStateManager.RangeIndexSetter.GetMaxStableGUIDs()) elemGUID = GUIDUtil.CreateSubElementGUID(element, subElementIndex + (int)IFCGenericSubElements.SplitInstanceStart - 1); else elemGUID = GUIDUtil.CreateGUID(); string elemName = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemTag = NamingUtil.GetTagOverride(element, NamingUtil.CreateIFCElementId(element)); string ifcType = IFCValidateEntry.GetValidIFCType(element, null); // For Foundation and Retaining walls, allow exporting as IfcFooting instead. bool exportAsFooting = false; if (exportingWallElement) { WallType wallType = wallElement.WallType; if (wallType != null) { int wallFunction; if (ParameterUtil.GetIntValueFromElement(wallType, BuiltInParameter.FUNCTION_PARAM, out wallFunction) != null) { if (wallFunction == (int)WallFunction.Retaining || wallFunction == (int)WallFunction.Foundation) { // In this case, allow potential to export foundation and retaining walls as footing. string enumTypeValue = null; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, wallElement, out enumTypeValue); if (exportType == IFCExportType.IfcFooting) exportAsFooting = true; } } } } if (exportedAsWallWithAxis) { if (exportAsFooting) { wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } else { bool exportAsWall = exportParts; if (!exportAsWall) { // (For Reference View export) If the representation returned earlier is of type Tessellation, create IfcWall instead. foreach (IFCAnyHandle pRep in IFCAnyHandleUtil.GetRepresentations(prodRep)) { if (String.Compare(IFCAnyHandleUtil.GetRepresentationType(pRep), "Tessellation") == 0) { exportAsWall = true; break; } } } if (exportAsWall) { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, null, elemTag, ifcType); } else { wallHnd = IFCInstanceExporter.CreateWallStandardCase(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, prodRep, elemTag, ifcType); } } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, null, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } else { double scaledWidth = UnitUtil.ScaleLength(wallElement.Width); OpeningUtil.AddOpeningsToElement(exporterIFC, wallHnd, wallElement, null, scaledWidth, range, setter, localPlacement, localWrapper); } } // export Base Quantities if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { scaledFootprintArea = MathUtil.AreaIsAlmostZero(scaledFootprintArea) ? extraParams.ScaledArea : scaledFootprintArea; scaledLength = MathUtil.IsAlmostZero(scaledLength) ? extraParams.ScaledLength : scaledLength; PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, scaledFootprintArea); } } else { if (exportAsFooting) { wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } else { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { // Only export one material for 2x2; for future versions, export the whole list. if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || exportingFamilyInstance) { matId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solids, meshes, element); if (matId != ElementId.InvalidElementId) CategoryUtil.CreateMaterialAssociation(exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { OpeningUtil.AddOpeningsToElement(exporterIFC, wallHnd, famInstWallElem, null, 0.0, range, setter, localPlacement, localWrapper); } if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } } } ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if ((exportingWallElement || exportingFaceWall) && !exportParts) { HostObject hostObject = null; if (exportingWallElement) hostObject = wallElement; else hostObject = faceWall; if (!ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || exportedAsWallWithAxis) HostObjectExporter.ExportHostObjectMaterials(exporterIFC, hostObject, localWrapper.GetAnElement(), geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2, !exportedAsWallWithAxis); } ExportWallType(exporterIFC, localWrapper, wallHnd, element, matId, exportedAsWallWithAxis, exportAsFooting); SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, wallHnd, element.Id, wallLevelId); tr.Commit(); return wallHnd; } } } } }
/// <summary> /// Export the individual part (IfcBuildingElementPart). /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="partElement">The part element to export.</param> /// <param name="geometryElement">The geometry of part.</param> /// <param name="productWrapper">The ProductWrapper object.</param> public static void ExportPart(ExporterIFC exporterIFC, Element partElement, ProductWrapper productWrapper, PlacementSetter placementSetter, IFCAnyHandle originalPlacement, IFCRange range, IFCExtrusionAxes ifcExtrusionAxes, Element hostElement, ElementId overrideLevelId, bool asBuildingElement) { if (!ElementFilteringUtil.IsElementVisible(partElement)) { return; } Part part = partElement as Part; if (part == null) { return; } if (!asBuildingElement) { // Check the intended IFC entity or type name is in the exclude list specified in the UI Common.Enums.IFCEntityType elementClassTypeEnum; if (Enum.TryParse <Common.Enums.IFCEntityType>("IfcBuildingElementPart", out elementClassTypeEnum)) { if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) { return; } } } else { string ifcEnumType = null; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); // Check the intended IFC entity or type name is in the exclude list specified in the UI Common.Enums.IFCEntityType elementClassTypeEnum; if (Enum.TryParse <Common.Enums.IFCEntityType>(exportType.ToString(), out elementClassTypeEnum)) { if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) { return; } } } PlacementSetter standalonePlacementSetter = null; bool standaloneExport = hostElement == null && !asBuildingElement; ElementId partExportLevel = null; if (standaloneExport || asBuildingElement) { partExportLevel = partElement.LevelId; } else { if (part.OriginalCategoryId != hostElement.Category.Id) { return; } partExportLevel = hostElement.LevelId; } if (overrideLevelId != null) { partExportLevel = overrideLevelId; } if (ExporterCacheManager.PartExportedCache.HasExported(partElement.Id, partExportLevel)) { return; } Options options = GeometryUtil.GetIFCExportGeometryOptions(); View ownerView = partElement.Document.GetElement(partElement.OwnerViewId) as View; if (ownerView != null) { options.View = ownerView; } GeometryElement geometryElement = partElement.get_Geometry(options); if (geometryElement == null) { return; } try { IFCFile file = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(file)) { IFCAnyHandle partPlacement = null; if (standaloneExport || asBuildingElement) { Transform orientationTrf = Transform.Identity; standalonePlacementSetter = PlacementSetter.Create(exporterIFC, partElement, null, orientationTrf, partExportLevel); partPlacement = standalonePlacementSetter.LocalPlacement; } else { partPlacement = ExporterUtil.CreateLocalPlacement(file, originalPlacement, null); } bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); SolidMeshGeometryInfo solidMeshInfo; if (validRange) { solidMeshInfo = GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) { return; } } else { solidMeshInfo = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(partPlacement); extrusionCreationData.ReuseLocalPlacement = false; extrusionCreationData.PossibleExtrusionAxes = ifcExtrusionAxes; IList <Solid> solids = solidMeshInfo.GetSolids(); IList <Mesh> meshes = solidMeshInfo.GetMeshes(); ElementId catId = CategoryUtil.GetSafeCategoryId(partElement); ElementId hostCatId = CategoryUtil.GetSafeCategoryId(hostElement); BodyData bodyData = null; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); if (solids.Count > 0 || meshes.Count > 0) { bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, solids, meshes, bodyExporterOptions, extrusionCreationData); } else { IList <GeometryObject> geomlist = new List <GeometryObject>(); geomlist.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, geomlist, bodyExporterOptions, extrusionCreationData); } IFCAnyHandle bodyRep = bodyData.RepresentationHnd; if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extrusionCreationData.ClearOpenings(); return; } IList <IFCAnyHandle> representations = new List <IFCAnyHandle>(); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) { representations.Add(boundingBoxRep); } IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; string partGUID = GUIDUtil.CreateGUID(partElement); string partName = NamingUtil.GetNameOverride(partElement, NamingUtil.GetIFCName(partElement)); string partDescription = NamingUtil.GetDescriptionOverride(partElement, null); string partObjectType = NamingUtil.GetObjectTypeOverride(partElement, NamingUtil.CreateIFCObjectName(exporterIFC, partElement)); string partTag = NamingUtil.GetTagOverride(partElement, NamingUtil.CreateIFCElementId(partElement)); IFCAnyHandle ifcPart = null; if (!asBuildingElement) { ifcPart = IFCInstanceExporter.CreateBuildingElementPart(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag); } else { string ifcEnumType = null; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); string defaultValue = null; // This replicates old functionality before IFC4 addition, where the default for slab was "FLOOR". // Really the export layer table should be fixed for this case. if (string.IsNullOrWhiteSpace(ifcEnumType) && hostCatId == new ElementId(BuiltInCategory.OST_Floors)) { ifcEnumType = "FLOOR"; } ifcEnumType = IFCValidateEntry.GetValidIFCType(hostElement, ifcEnumType, defaultValue); switch (exportType) { case IFCExportType.IfcColumnType: ifcPart = IFCInstanceExporter.CreateColumn(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; case IFCExportType.IfcCovering: ifcPart = IFCInstanceExporter.CreateCovering(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; case IFCExportType.IfcFooting: ifcPart = IFCInstanceExporter.CreateFooting(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; case IFCExportType.IfcPile: ifcPart = IFCInstanceExporter.CreatePile(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType, null); break; case IFCExportType.IfcRoof: ifcPart = IFCInstanceExporter.CreateRoof(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; case IFCExportType.IfcSlab: ifcPart = IFCInstanceExporter.CreateSlab(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; case IFCExportType.IfcWall: ifcPart = IFCInstanceExporter.CreateWall(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, ifcEnumType); break; default: ifcPart = IFCInstanceExporter.CreateBuildingElementProxy(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, null); break; } } bool containedInLevel = (standaloneExport || asBuildingElement); PlacementSetter whichPlacementSetter = containedInLevel ? standalonePlacementSetter : placementSetter; productWrapper.AddElement(partElement, ifcPart, whichPlacementSetter, extrusionCreationData, containedInLevel); OpeningUtil.CreateOpeningsIfNecessary(ifcPart, partElement, extrusionCreationData, bodyData.OffsetTransform, exporterIFC, extrusionCreationData.GetLocalPlacement(), whichPlacementSetter, productWrapper); //Add the exported part to exported cache. TraceExportedParts(partElement, partExportLevel, standaloneExport || asBuildingElement ? ElementId.InvalidElementId : hostElement.Id); CategoryUtil.CreateMaterialAssociation(exporterIFC, ifcPart, bodyData.MaterialIds); transaction.Commit(); } } } finally { if (standalonePlacementSetter != null) { standalonePlacementSetter.Dispose(); } } }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandles">The parent handles.</param> /// <param name="curveLoops">The parent CurveLoops.</param> /// <param name="element">The element.</param> /// <param name="plane">The plane.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IList<IFCAnyHandle> elementHandles, IList<CurveLoop> curveLoops, Element element, Plane plane, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { IList<IFCOpeningData> openingDataList = ExporterIFCUtils.GetOpeningData(exporterIFC, element, plane, range); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); foreach (IFCOpeningData openingData in openingDataList) { Element openingElem = element.Document.GetElement(openingData.OpeningElementId); if (openingElem == null) openingElem = element; // Don't export the opening if WallSweep category has been turned off. // This is currently restricted to WallSweeps because the element responsible for the opening could be a variety of things, including a line as part of the elevation profile of the wall. // As such, we will restrict which element types we check for CanExportElement. if ((openingElem is WallSweep) && (!ElementFilteringUtil.CanExportElement(exporterIFC, openingElem, true))) continue; IList<IFCExtrusionData> extrusionDataList = openingData.GetExtrusionData(); IFCAnyHandle parentHandle = null; if (elementHandles.Count > 1 && extrusionDataList.Count > 0) { parentHandle = FindParentHandle(elementHandles, curveLoops, extrusionDataList[0].GetLoops()[0]); } if (parentHandle == null) parentHandle = elementHandles[0]; bool isDoorOrWindowOpening = IsDoorOrWindowOpening(exporterIFC, openingElem, element); if (isDoorOrWindowOpening) { DoorWindowDelayedOpeningCreator delayedCreator = DoorWindowDelayedOpeningCreator.Create(exporterIFC, openingData, scaledWidth, element.Id, parentHandle, setter.LevelId); if (delayedCreator != null) { ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); continue; } } bool canUseElementGUID = !isDoorOrWindowOpening; IList<Solid> solids = openingData.GetOpeningSolids(); foreach (Solid solid in solids) { using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, null)); extrusionCreationData.ReuseLocalPlacement = true; string openingGUID = null; if (canUseElementGUID) { openingGUID = GUIDUtil.CreateGUID(openingElem); canUseElementGUID = false; } else openingGUID = GUIDUtil.CreateGUID(); CreateOpening(exporterIFC, parentHandle, element, openingElem, openingGUID, solid, scaledWidth, openingData.IsRecess, extrusionCreationData, setter, localWrapper); } } foreach (IFCExtrusionData extrusionData in extrusionDataList) { if (extrusionData.ScaledExtrusionLength < MathUtil.Eps()) extrusionData.ScaledExtrusionLength = scaledWidth; string openingGUID = null; if (canUseElementGUID) { openingGUID = GUIDUtil.CreateGUID(element); canUseElementGUID = false; } else openingGUID = GUIDUtil.CreateGUID(); CreateOpening(exporterIFC, parentHandle, localPlacement, element, openingElem, openingGUID, extrusionData, plane, openingData.IsRecess, setter, localWrapper); } } }
/// <summary> /// Exports a family instance as a mapped item. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="familyInstance"> /// The family instance to be exported. /// </param> /// <param name="exportType"> /// The export type. /// </param> /// <param name="ifcEnumType"> /// The string value represents the IFC type. /// </param> /// <param name="wrapper"> /// The ProductWrapper. /// </param> /// <param name="overrideLevelId"> /// The level id. /// </param> /// <param name="range"> /// The range of this family instance to be exported. /// </param> public static void ExportFamilyInstanceAsMappedItem(ExporterIFC exporterIFC, FamilyInstance familyInstance, IFCExportType exportType, string ifcEnumType, ProductWrapper wrapper, ElementId overrideLevelId, IFCRange range, IFCAnyHandle parentLocalPlacement) { bool exportParts = PartExporter.CanExportParts(familyInstance); bool isSplit = range != null; if (exportParts && !PartExporter.CanExportElementInPartExport(familyInstance, isSplit ? overrideLevelId : familyInstance.Level.Id, isSplit)) return; Document doc = familyInstance.Document; IFCFile file = exporterIFC.GetFile(); FamilySymbol familySymbol = ExporterIFCUtils.GetOriginalSymbol(familyInstance); if (familySymbol == null) return; ProductWrapper familyProductWrapper = ProductWrapper.Create(wrapper); double scale = exporterIFC.LinearScale; Options options = GeometryUtil.GetIFCExportGeometryOptions(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); HostObject hostElement = familyInstance.Host as HostObject; //hostElement could be null ElementId categoryId = CategoryUtil.GetSafeCategoryId(familySymbol); //string emptyString = ""; string familyName = familySymbol.Name; string objectType = familyName; // A Family Instance can have its own copy of geometry, or use the symbol's copy with a transform. // The routine below tells us whether to use the Instance's copy or the Symbol's copy. bool useInstanceGeometry = ExporterIFCUtils.UsesInstanceGeometry(familyInstance); IList<IFCExtrusionData> cutPairOpeningsForColumns = new List<IFCExtrusionData>(); using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { Transform trf = familyInstance.GetTransform(); // Extra information if we are exporting a door or a window. IFCDoorWindowInfo doorWindowInfo = null; if (exportType == IFCExportType.ExportDoorType) doorWindowInfo = IFCDoorWindowInfo.CreateDoorInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf); else if (exportType == IFCExportType.ExportWindowType) doorWindowInfo = IFCDoorWindowInfo.CreateWindowInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf); FamilyTypeInfo typeInfo = new FamilyTypeInfo(); XYZ extraOffset = XYZ.Zero; bool flipped = doorWindowInfo != null ? doorWindowInfo.IsSymbolFlipped : false; FamilyTypeInfo currentTypeInfo = ExporterCacheManager.TypeObjectsCache.Find(familySymbol.Id, flipped); bool found = currentTypeInfo.IsValid(); Family family = familySymbol.Family; // TODO: this code to be removed by ExtrusionAnalyzer code. bool trySpecialColumnCreation = ((exportType == IFCExportType.ExportColumnType) && (!family.IsInPlace)); IList<GeometryObject> geomObjects = new List<GeometryObject>(); Transform brepOffsetTransform = null; Transform doorWindowTrf = Transform.Identity; // We will create a new mapped type if: // 1. We are exporting part of a column or in-place wall (range != null), OR // 2. We are using the instance's copy of the geometry (that it, it has unique geometry), OR // 3. We haven't already created the type. bool creatingType = ((range != null) || useInstanceGeometry || !found); if (creatingType) { IFCAnyHandle bodyRepresentation = null; IFCAnyHandle planRepresentation = null; // If we are using the instance geometry, ignore the transformation. if (useInstanceGeometry) trf = Transform.Identity; // TODO: this code to be removed by ExtrusionAnalyzer code. if (trySpecialColumnCreation) { XYZ rangeOffset = trf.Origin; IFCFamilyInstanceExtrusionExportResults results; results = ExporterIFCUtils.ExportFamilyInstanceAsExtrusion(exporterIFC, familyInstance, useInstanceGeometry, range, overrideLevelId, extraParams); bodyRepresentation = results.GetExtrusionHandle(); extraOffset = results.ExtraOffset; cutPairOpeningsForColumns = results.GetCutPairOpenings(); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation)) { typeInfo.MaterialIds.Add(results.MaterialId); // add in level for real columns, not in-place ones. Element actualLevel = (overrideLevelId == ElementId.InvalidElementId) ? familyInstance.Level : doc.GetElement(overrideLevelId); if (actualLevel != null) { IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(actualLevel.Id); double nonStoryLevelOffset = LevelUtil.GetNonStoryLevelOffsetIfAny(exporterIFC, actualLevel as Level); if (range != null) { rangeOffset = new XYZ(rangeOffset.X, rangeOffset.Y, levelInfo.Elevation + nonStoryLevelOffset); } } } rangeOffset += extraOffset; trf.Origin = rangeOffset; } IFCAnyHandle dummyPlacement = null; if (doorWindowInfo != null) { doorWindowTrf = ExporterIFCUtils.GetTransformForDoorOrWindow(familyInstance, familySymbol, doorWindowInfo); } else { dummyPlacement = IFCInstanceExporter.CreateLocalPlacement(file, null, ExporterUtil.CreateAxis2Placement3D(file)); extraParams.SetLocalPlacement(dummyPlacement); } bool needToCreate2d = ExporterCacheManager.ExportOptionsCache.ExportAnnotations; bool needToCreate3d = IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation); if (needToCreate2d || needToCreate3d) { using (IFCTransformSetter trfSetter = IFCTransformSetter.Create()) { if (doorWindowInfo != null) { trfSetter.Initialize(exporterIFC, doorWindowTrf); } GeometryElement exportGeometry = useInstanceGeometry ? familyInstance.get_Geometry(options) : familySymbol.get_Geometry(options); if (exportGeometry == null) return; if (needToCreate3d) { SolidMeshGeometryInfo solidMeshCapsule = null; if (range == null) { solidMeshCapsule = GeometryUtil.GetSolidMeshGeometry(exportGeometry, Transform.Identity); } else { solidMeshCapsule = GeometryUtil.GetClippedSolidMeshGeometry(exportGeometry, range); } IList<Solid> solids = solidMeshCapsule.GetSolids(); IList<Mesh> polyMeshes = solidMeshCapsule.GetMeshes(); if (range != null && (solids.Count == 0 && polyMeshes.Count == 0)) return; // no proper split geometry geomObjects = FamilyExporterUtil.RemoveSolidsAndMeshesSetToDontExport(doc, exporterIFC, solids, polyMeshes); if (geomObjects.Count == 0 && (solids.Count > 0 || polyMeshes.Count > 0)) return; bool tryToExportAsExtrusion = (!exporterIFC.ExportAs2x2 || (exportType == IFCExportType.ExportColumnType)); if (exportType == IFCExportType.ExportColumnType) { extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; if (ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2 && solids.Count > 0) { LocationPoint point = familyInstance.Location as LocationPoint; XYZ orig = XYZ.Zero; if (point != null) orig = point.Point; Plane plane = new Plane(XYZ.BasisX, XYZ.BasisY, orig); bool completelyClipped = false; HashSet<ElementId> materialIds = null; bodyRepresentation = ExtrusionExporter.CreateExtrusionWithClipping(exporterIFC, familyInstance, categoryId, solids, plane, XYZ.BasisZ, null, out completelyClipped, out materialIds); typeInfo.MaterialIds = materialIds; } } else { extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryXYZ; } BodyData bodyData = null; if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation)) { BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(tryToExportAsExtrusion); if (geomObjects.Count > 0) { bodyData = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, familyInstance, categoryId, ElementId.InvalidElementId, geomObjects, bodyExporterOptions, extraParams); typeInfo.MaterialIds = bodyData.MaterialIds; } else { IList<GeometryObject> exportedGeometries = new List<GeometryObject>(); exportedGeometries.Add(exportGeometry); bodyData = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, familyInstance, categoryId, ElementId.InvalidElementId, exportedGeometries, bodyExporterOptions, extraParams); } bodyRepresentation = bodyData.RepresentationHnd; brepOffsetTransform = bodyData.BrepOffsetTransform; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation)) { extraParams.ClearOpenings(); return; } } // By default: if exporting IFC2x3 or later, export 2D plan rep of family, if it exists, unless we are exporting Coordination View V2. // This default can be overridden in the export options. if (needToCreate2d) { XYZ curveOffset = new XYZ(0, 0, 0); if (brepOffsetTransform != null) curveOffset = -brepOffsetTransform.Origin / scale; HashSet<IFCAnyHandle> curveSet = new HashSet<IFCAnyHandle>(); { Transform planeTrf = doorWindowTrf.Inverse; Plane plane = new Plane(planeTrf.get_Basis(0), planeTrf.get_Basis(1), planeTrf.Origin); XYZ projDir = new XYZ(0, 0, 1); IFCGeometryInfo IFCGeometryInfo = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, true); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, IFCGeometryInfo, exportGeometry, curveOffset, false); IList<IFCAnyHandle> curves = IFCGeometryInfo.GetCurves(); foreach (IFCAnyHandle curve in curves) curveSet.Add(curve); if (curveSet.Count > 0) { IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle(); IFCAnyHandle curveRepresentationItem = IFCInstanceExporter.CreateGeometricSet(file, curveSet); HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>(); bodyItems.Add(curveRepresentationItem); planRepresentation = RepresentationUtil.CreateGeometricSetRep(exporterIFC, familyInstance, categoryId, "Annotation", contextOfItems2d, bodyItems); } } } } } if (doorWindowInfo != null) { typeInfo.StyleTransform = doorWindowTrf.Inverse; } else { if (!MathUtil.IsAlmostZero(extraOffset.DotProduct(extraOffset))) { Transform newTransform = typeInfo.StyleTransform; XYZ newOrig = newTransform.Origin + extraOffset; newTransform.Origin = newOrig; typeInfo.StyleTransform = newTransform; } typeInfo.StyleTransform = ExporterIFCUtils.GetUnscaledTransform(exporterIFC, extraParams.GetLocalPlacement()); } IFCAnyHandle origin = ExporterUtil.CreateAxis2Placement3D(file); IFCAnyHandle repMap2dHnd = null; IFCAnyHandle repMap3dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, bodyRepresentation); IList<IFCAnyHandle> repMapList = new List<IFCAnyHandle>(); repMapList.Add(repMap3dHnd); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(planRepresentation)) { repMap2dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, planRepresentation); repMapList.Add(repMap2dHnd); } // for Door, Window bool paramTakesPrecedence = false; // For Revit, this is currently always false. bool sizeable = false; // for many HashSet<IFCAnyHandle> propertySets = new HashSet<IFCAnyHandle>(); string guid = GUIDUtil.CreateGUID(familySymbol); string symId = NamingUtil.CreateIFCElementId(familySymbol); // This covers many generic types. If we can't find it in the list here, do custom exports. IFCAnyHandle typeStyle = FamilyExporterUtil.ExportGenericType(file, exportType, ifcEnumType, guid, ownerHistory, objectType, null, null, propertySets, repMapList, symId, objectType, familyInstance, familySymbol); // Cover special cases not covered above. if (IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle)) { switch (exportType) { case IFCExportType.ExportColumnType: { // If we are using the instance GRep, then we have to create a generic GUID for the // column type, as they share the same ElementId. string colGUID = null; string colElemId = null; if (useInstanceGeometry) colGUID = GUIDUtil.CreateGUID(); else colGUID = guid; colElemId = NamingUtil.CreateIFCElementId(familySymbol); string columnType = "Column"; typeStyle = IFCInstanceExporter.CreateColumnType(file, colGUID, ownerHistory, objectType, null, null, propertySets, repMapList, colElemId, objectType, GetColumnType(familyInstance, columnType)); break; } case IFCExportType.ExportDoorType: { string constructionType = string.Empty; ParameterUtil.GetStringValueFromElementOrSymbol(familyInstance, "Construction", out constructionType); IFCAnyHandle doorLining = DoorWindowUtil.CreateDoorLiningProperties(exporterIFC, familyInstance); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(doorLining)) propertySets.Add(doorLining); IList<IFCAnyHandle> doorPanels = DoorWindowUtil.CreateDoorPanelProperties(exporterIFC, doorWindowInfo, familyInstance); propertySets.UnionWith(doorPanels); string doorStyleGUID = GUIDUtil.CreateGUID(); string doorStyleElemId = NamingUtil.CreateIFCElementId(familySymbol); typeStyle = IFCInstanceExporter.CreateDoorStyle(file, doorStyleGUID, ownerHistory, objectType, null, null, propertySets, repMapList, doorStyleElemId, GetDoorStyleOperation(doorWindowInfo.DoorOperationType), GetDoorStyleConstruction(familyInstance, constructionType), paramTakesPrecedence, sizeable); break; } case IFCExportType.ExportSystemFurnitureElementType: { string furnitureId = NamingUtil.CreateIFCElementId(familySymbol); typeStyle = IFCInstanceExporter.CreateSystemFurnitureElementType(file, guid, ownerHistory, objectType, null, null, propertySets, repMapList, furnitureId, objectType); break; } case IFCExportType.ExportWindowType: { Toolkit.IFCWindowStyleOperation operationType = DoorWindowUtil.GetIFCWindowStyleOperation(familySymbol); IFCWindowStyleConstruction constructionType = DoorWindowUtil.GetIFCWindowStyleConstruction(familyInstance, ""); IFCAnyHandle windowLining = DoorWindowUtil.CreateWindowLiningProperties(exporterIFC, familyInstance, null); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(windowLining)) propertySets.Add(windowLining); IList<IFCAnyHandle> windowPanels = DoorWindowUtil.CreateWindowPanelProperties(exporterIFC, familyInstance, null); propertySets.UnionWith(windowPanels); string windowStyleGUID = GUIDUtil.CreateGUID(); string windowStyleElemId = NamingUtil.CreateIFCElementId(familySymbol); typeStyle = IFCInstanceExporter.CreateWindowStyle(file, windowStyleGUID, ownerHistory, objectType, null, null, propertySets, repMapList, windowStyleElemId, constructionType, operationType, paramTakesPrecedence, sizeable); break; } case IFCExportType.ExportBuildingElementProxy: default: { if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd)) typeInfo.Map2DHandle = repMap2dHnd; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd)) typeInfo.Map3DHandle = repMap3dHnd; break; } } } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle)) { CategoryUtil.CreateMaterialAssociations(doc, exporterIFC, typeStyle, typeInfo.MaterialIds); typeInfo.Style = typeStyle; if (((exportType == IFCExportType.ExportColumnType) && trySpecialColumnCreation) || (exportType == IFCExportType.ExportMemberType)) { typeInfo.ScaledArea = extraParams.ScaledArea; typeInfo.ScaledDepth = extraParams.ScaledLength; typeInfo.ScaledInnerPerimeter = extraParams.ScaledInnerPerimeter; typeInfo.ScaledOuterPerimeter = extraParams.ScaledOuterPerimeter; } ClassificationUtil.CreateUniformatClassification(exporterIFC, file, familySymbol, typeStyle); } } else if (!creatingType && (trySpecialColumnCreation)) { // still need to modify instance trf for columns. trf.Origin += GetLevelOffsetForExtrudedColumns(exporterIFC, familyInstance, overrideLevelId, extraParams); } if (found && !typeInfo.IsValid()) { typeInfo = currentTypeInfo; } // we'll pretend we succeeded, but we'll do nothing. if (!typeInfo.IsValid()) return; // add to the map, as long as we are not using range, not using instance geometry, and don't have extra openings. if ((range == null) && !useInstanceGeometry && (extraParams.GetOpenings().Count == 0)) ExporterCacheManager.TypeObjectsCache.Register(familySymbol.Id, flipped, typeInfo); Transform oldTrf = new Transform(trf); XYZ scaledMapOrigin = XYZ.Zero; trf = trf.Multiply(typeInfo.StyleTransform); // create instance. IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>(); { IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle(); IFCAnyHandle contextOfItems3d = exporterIFC.Get3DContextHandle("Body"); // for proxies, we store the IfcRepresentationMap directly since there is no style. IFCAnyHandle style = typeInfo.Style; IList<IFCAnyHandle> repMapList = !IFCAnyHandleUtil.IsNullOrHasNoValue(style) ? GeometryUtil.GetRepresentationMaps(style) : null; int numReps = repMapList != null ? repMapList.Count : 0; IFCAnyHandle repMap2dHnd = typeInfo.Map2DHandle; IFCAnyHandle repMap3dHnd = typeInfo.Map3DHandle; if (IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd) && (numReps > 0)) repMap3dHnd = repMapList[0]; if (IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd) && (numReps > 1)) repMap2dHnd = repMapList[1]; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd)) { IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap3dHnd, scaledMapOrigin)); IFCAnyHandle shapeRep = RepresentationUtil.CreateBodyMappedItemRep(exporterIFC, familyInstance, categoryId, contextOfItems3d, representations); if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep)) return; shapeReps.Add(shapeRep); } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd)) { HashSet<IFCAnyHandle> representations = new HashSet<IFCAnyHandle>(); representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap2dHnd, scaledMapOrigin)); IFCAnyHandle shapeRep = RepresentationUtil.CreatePlanMappedItemRep(exporterIFC, familyInstance, categoryId, contextOfItems2d, representations); if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep)) return; shapeReps.Add(shapeRep); } } IFCAnyHandle boundingBoxRep = null; Transform boundingBoxTrf = (brepOffsetTransform != null) ? brepOffsetTransform.Inverse : Transform.Identity; if (geomObjects.Count > 0) boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geomObjects, boundingBoxTrf); else { boundingBoxTrf = boundingBoxTrf.Multiply(trf.Inverse); boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, familyInstance.get_Geometry(options), boundingBoxTrf); } if (boundingBoxRep != null) shapeReps.Add(boundingBoxRep); IFCAnyHandle rep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps); IFCAnyHandle instanceHandle = null; using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, familyInstance, trf, null, overrideLevelId)) { string instanceGUID = GUIDUtil.CreateGUID(familyInstance); string instanceName = NamingUtil.GetIFCName(familyInstance); string instanceDescription = NamingUtil.GetDescriptionOverride(familyInstance, null); string instanceObjectType = NamingUtil.GetObjectTypeOverride(familyInstance, objectType); string instanceElemId = NamingUtil.CreateIFCElementId(familyInstance); IFCAnyHandle localPlacement = setter.GetPlacement(); IFCAnyHandle overrideLocalPlacement = null; if (parentLocalPlacement != null) { Transform relTrf = ExporterIFCUtils.GetRelativeLocalPlacementOffsetTransform(parentLocalPlacement, localPlacement); Transform inverseTrf = relTrf.Inverse; IFCAnyHandle relativePlacement = ExporterUtil.CreateAxis2Placement3D(file, inverseTrf.Origin, inverseTrf.BasisZ, inverseTrf.BasisX); IFCAnyHandle plateLocalPlacement = IFCInstanceExporter.CreateLocalPlacement(file, parentLocalPlacement, relativePlacement); overrideLocalPlacement = plateLocalPlacement; } instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance, wrapper, setter, extraParams, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, exportParts ? null : rep, instanceElemId, overrideLocalPlacement); if (exportParts) { PartExporter.ExportHostPart(exporterIFC, familyInstance, instanceHandle, familyProductWrapper, setter, setter.GetPlacement(), overrideLevelId); } if (ElementFilteringUtil.IsMEPType(exportType)) ExporterCacheManager.MEPCache.Register(familyInstance, instanceHandle); switch (exportType) { case IFCExportType.ExportColumnType: { IFCAnyHandle placementToUse = localPlacement; if (!useInstanceGeometry) { bool needToCreateOpenings = (cutPairOpeningsForColumns.Count != 0) || OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams); if (needToCreateOpenings) { Transform openingTrf = new Transform(oldTrf); Transform extraRot = new Transform(oldTrf); extraRot.Origin = XYZ.Zero; openingTrf = openingTrf.Multiply(extraRot); openingTrf = openingTrf.Multiply(typeInfo.StyleTransform); IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, openingTrf.Origin * scale, openingTrf.get_Basis(2), openingTrf.get_Basis(0)); IFCAnyHandle openingPlacement = ExporterUtil.CopyLocalPlacement(file, localPlacement); GeometryUtil.SetRelativePlacement(openingPlacement, openingRelativePlacement); placementToUse = openingPlacement; } } OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, cutPairOpeningsForColumns, exporterIFC, placementToUse, setter, wrapper); OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC, placementToUse, setter, wrapper); //export Base Quantities. PropertyUtil.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } case IFCExportType.ExportDoorType: case IFCExportType.ExportWindowType: { double doorHeight = doorWindowInfo.OpeningHeight; if (doorHeight < MathUtil.Eps()) doorHeight = GetMinSymbolHeight(familySymbol); double doorWidth = doorWindowInfo.OpeningWidth; if (doorWidth < MathUtil.Eps()) doorWidth = GetMinSymbolWidth(familySymbol); double height = doorHeight * scale; double width = doorWidth * scale; if (IFCAnyHandleUtil.IsNullOrHasNoValue(doorWindowInfo.GetLocalPlacement())) doorWindowInfo.SetLocalPlacement(localPlacement); IFCAnyHandle doorWindowOrigLocalPlacement = doorWindowInfo.GetLocalPlacement(); Transform relTrf = ExporterIFCUtils.GetRelativeLocalPlacementOffsetTransform(localPlacement, doorWindowOrigLocalPlacement); IFCAnyHandle doorWindowRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, relTrf.Origin, relTrf.BasisZ, relTrf.BasisX); IFCAnyHandle doorWindowLocalPlacement = IFCInstanceExporter.CreateLocalPlacement(file, doorWindowOrigLocalPlacement, doorWindowRelativePlacement); if (exportType == IFCExportType.ExportDoorType) instanceHandle = IFCInstanceExporter.CreateDoor(file, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement, rep, instanceElemId, height, width); else if (exportType == IFCExportType.ExportWindowType) instanceHandle = IFCInstanceExporter.CreateWindow(file, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement, rep, instanceElemId, height, width); wrapper.AddElement(instanceHandle, setter, extraParams, true); exporterIFC.RegisterSpaceBoundingElementHandle(instanceHandle, familyInstance.Id, setter.LevelId); IFCAnyHandle placementToUse = doorWindowLocalPlacement; if (!useInstanceGeometry) { // correct the placement to the symbol space bool needToCreateOpenings = OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams); if (needToCreateOpenings) { Transform openingTrf = Transform.Identity; openingTrf.Origin = new XYZ(0, 0, setter.Offset); openingTrf = openingTrf.Multiply(doorWindowTrf); XYZ scaledOrigin = openingTrf.Origin * exporterIFC.LinearScale; IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, scaledOrigin, openingTrf.BasisZ, openingTrf.BasisX); IFCAnyHandle openingLocalPlacement = IFCInstanceExporter.CreateLocalPlacement(file, doorWindowLocalPlacement, openingRelativePlacement); placementToUse = openingLocalPlacement; } } // only necessary when exporting as possible breps. OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC, placementToUse, setter, wrapper); if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) ExporterIFCUtils.CreateDoorWindowBaseQuantities(exporterIFC, instanceHandle, (doorHeight * scale), (doorWidth * scale)); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } case IFCExportType.ExportMemberType: { OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC, localPlacement, setter, wrapper); //export Base Quantities. PropertyUtil.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo); // TODO: create PropertySet! //createMemberPropertySet(exporter, pFamInst, pWrapper, extraParams); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } case IFCExportType.ExportPlateType: { OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC, localPlacement, setter, wrapper); // TODO: create PropertySet! //createPlatePropertySet(exporter, pFamInst, pWrapper, extraParams); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } case IFCExportType.ExportTransportElementType: { IFCAnyHandle localPlacementToUse; ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse); instanceHandle = IFCInstanceExporter.CreateTransportElement(file, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, localPlacementToUse, rep, instanceElemId, null, null, null); if (roomId == ElementId.InvalidElementId) { wrapper.AddElement(instanceHandle, setter, extraParams, true); } else { exporterIFC.RelateSpatialElement(roomId, instanceHandle); wrapper.AddElement(instanceHandle, setter, extraParams, false); } PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } case IFCExportType.ExportBuildingElementProxy: default: { bool isBuildingElementProxy = (exportType == IFCExportType.ExportBuildingElementProxy); IFCAnyHandle localPlacementToUse; ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse); if (!isBuildingElementProxy) { if (FamilyExporterUtil.IsDistributionControlElementSubType(exportType)) { instanceHandle = IFCInstanceExporter.CreateDistributionControlElement(file, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, localPlacementToUse, rep, instanceElemId, null); if (roomId == ElementId.InvalidElementId) { wrapper.AddElement(instanceHandle, setter, extraParams, true); } else { exporterIFC.RelateSpatialElement(roomId, instanceHandle); wrapper.AddElement(instanceHandle, setter, extraParams, false); } } else if (IFCAnyHandleUtil.IsNullOrHasNoValue(instanceHandle)) { isBuildingElementProxy = true; } } if (isBuildingElementProxy) { Toolkit.IFCElementComposition proxyType = Toolkit.IFCElementComposition.Element; instanceHandle = IFCInstanceExporter.CreateBuildingElementProxy(file, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType, localPlacementToUse, rep, instanceElemId, proxyType); if (roomId == ElementId.InvalidElementId) { wrapper.AddElement(instanceHandle, setter, extraParams, true); } else { exporterIFC.RelateSpatialElement(roomId, instanceHandle); wrapper.AddElement(instanceHandle, setter, extraParams, false); } } IFCAnyHandle placementToUse = localPlacement; if (!useInstanceGeometry) { bool needToCreateOpenings = OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams); if (needToCreateOpenings) { Transform openingTrf = new Transform(oldTrf); Transform extraRot = new Transform(oldTrf); extraRot.Origin = XYZ.Zero; openingTrf = openingTrf.Multiply(extraRot); openingTrf = openingTrf.Multiply(typeInfo.StyleTransform); IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, openingTrf.Origin * scale, openingTrf.get_Basis(2), openingTrf.get_Basis(0)); IFCAnyHandle openingPlacement = ExporterUtil.CopyLocalPlacement(file, localPlacement); GeometryUtil.SetRelativePlacement(openingPlacement, openingRelativePlacement); placementToUse = openingPlacement; } } OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC, placementToUse, setter, wrapper); PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper); break; } } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(instanceHandle)) { if (doorWindowInfo != null) { if (!IFCAnyHandleUtil.IsNullOrHasNoValue(doorWindowInfo.GetOpening())) { string relGUID = GUIDUtil.CreateGUID(); IFCInstanceExporter.CreateRelFillsElement(file, relGUID, ownerHistory, null, null, doorWindowInfo.GetOpening(), instanceHandle); } else if (doorWindowInfo.NeedsOpening) { bool added = doorWindowInfo.SetDelayedFamilyInstance(instanceHandle, localPlacement, doorWindowInfo.AssignedLevelId); if (added) exporterIFC.RegisterDoorWindowForOpeningUpdate(doorWindowInfo); else { // we need to fill a later opening. exporterIFC.RegisterDoorWindowForUncreatedOpening(familyInstance.Id, instanceHandle); } } } if (!exportParts) CategoryUtil.CreateMaterialAssociations(doc, exporterIFC, instanceHandle, typeInfo.MaterialIds); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeInfo.Style)) ExporterCacheManager.TypeRelationsCache.Add(typeInfo.Style, instanceHandle); } } } }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandle">The parent handle.</param> /// <param name="element">The element.</param> /// <param name="plane">The plane.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IFCAnyHandle elementHandle, Element element, Plane plane, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { IList<IFCAnyHandle> elementHandles = new List<IFCAnyHandle>(); elementHandles.Add(elementHandle); AddOpeningsToElement(exporterIFC, elementHandles, null, element, plane, scaledWidth, range, setter, localPlacement, localWrapper); }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandles">The parent handles.</param> /// <param name="curveLoops">The parent CurveLoops.</param> /// <param name="element">The element.</param> /// <param name="lcs">The local coordinate system.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IList<IFCAnyHandle> elementHandles, IList<CurveLoop> curveLoops, Element element, Transform lcs, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { IList<IFCOpeningData> openingDataList = ExporterIFCUtils.GetOpeningData(exporterIFC, element, lcs, range); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; foreach (IFCOpeningData openingData in openingDataList) { Element openingElem = element.Document.GetElement(openingData.OpeningElementId); if (openingElem == null) openingElem = element; bool currentWallIsHost = false; FamilyInstance openingFInst = openingElem as FamilyInstance; if (openingFInst != null && openingFInst.Host != null) { if (openingFInst.Host.Id == element.Id) currentWallIsHost = true; //continue; // If the host is not the current Wall, skip this opening } // Don't export the opening if WallSweep category has been turned off. // This is currently restricted to WallSweeps because the element responsible for the opening could be a variety of things, // including a line as part of the elevation profile of the wall. // As such, we will restrict which element types we check for CanExportElement. if ((openingElem is WallSweep) && (!ElementFilteringUtil.CanExportElement(exporterIFC, openingElem, true))) continue; IList<IFCExtrusionData> extrusionDataList = openingData.GetExtrusionData(); IFCAnyHandle parentHandle = null; if (elementHandles.Count > 1 && extrusionDataList.Count > 0) { parentHandle = FindParentHandle(elementHandles, curveLoops, extrusionDataList[0].GetLoops()[0]); } if (parentHandle == null) parentHandle = elementHandles[0]; bool isDoorOrWindowOpening = IsDoorOrWindowOpening(exporterIFC, openingElem, element); bool insertHasHost = false; bool insertInThisHost = false; if (openingElem is FamilyInstance && element is Wall) { string ifcEnumType; IFCExportInfoPair exportType = ExporterUtil.GetExportType(exporterIFC, openingElem, out ifcEnumType); Element instHost = (openingElem as FamilyInstance).Host; insertHasHost = (instHost != null); insertInThisHost = (insertHasHost && instHost.Id == element.Id); isDoorOrWindowOpening = insertInThisHost && (exportType.ExportInstance == IFCEntityType.IfcDoor || exportType.ExportType == IFCEntityType.IfcDoorType || exportType.ExportInstance == IFCEntityType.IfcWindow || exportType.ExportType == IFCEntityType.IfcWindowType); } if (isDoorOrWindowOpening && currentWallIsHost) { DoorWindowDelayedOpeningCreator delayedCreator = DoorWindowDelayedOpeningCreator.Create(exporterIFC, openingData, scaledWidth, element.Id, parentHandle, setter.LevelId); if (delayedCreator != null) { ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); continue; } } // If the opening is "filled" by another element (either a door or window as // determined above, or an embedded wall, then we can't use the element GUID // for the opening. bool canUseElementGUID = (!insertHasHost || insertInThisHost) && !isDoorOrWindowOpening && !(openingElem is Wall); IList<Solid> solids = openingData.GetOpeningSolids(); foreach (Solid solid in solids) { using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, null)); extrusionCreationData.ReuseLocalPlacement = true; string openingGUID = CreateOpeningGUID(openingElem, canUseElementGUID); canUseElementGUID = false; // Either it was used above, and therefore is now false, or it was already false. CreateOpening(exporterIFC, parentHandle, element, openingElem, openingGUID, solid, scaledWidth, openingData.IsRecess, extrusionCreationData, setter, localWrapper); } } foreach (IFCExtrusionData extrusionData in extrusionDataList) { if (extrusionData.ScaledExtrusionLength < MathUtil.Eps()) extrusionData.ScaledExtrusionLength = scaledWidth; string openingGUID = CreateOpeningGUID(openingElem, canUseElementGUID); canUseElementGUID = false; // Either it was used above, and therefore is now false, or it was already false. CreateOpening(exporterIFC, parentHandle, localPlacement, element, openingElem, openingGUID, extrusionData, lcs, openingData.IsRecess, setter, localWrapper); } } }
/// <summary> /// Export the individual part (IfcBuildingElementPart). /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="partElement">The part element to export.</param> /// <param name="geometryElement">The geometry of part.</param> /// <param name="productWrapper">The ProductWrapper object.</param> /// <param name="placementSetter"></param> /// <param name="originalPlacement"></param> /// <param name="range"></param> /// <param name="ifcExtrusionAxes"></param> /// <param name="hostElement">The host of the part. This can be null.</param> /// <param name="overrideLevelId">The id of the level that the part is one, overridding other sources.</param> /// <param name="asBuildingElement">If true, export the Part as a building element instead of an IfcElementPart.</param> public static void ExportPart(ExporterIFC exporterIFC, Element partElement, ProductWrapper productWrapper, PlacementSetter placementSetter, IFCAnyHandle originalPlacement, IFCRange range, IFCExtrusionAxes ifcExtrusionAxes, Element hostElement, ElementId overrideLevelId, bool asBuildingElement) { if (!ElementFilteringUtil.IsElementVisible(partElement)) { return; } Part part = partElement as Part; if (part == null) { return; } // We don't know how to export a part as a building element if we don't know it's host. if (asBuildingElement && (hostElement == null)) { return; } if (!asBuildingElement) { // Check the intended IFC entity or type name is in the exclude list specified in the UI Common.Enums.IFCEntityType elementClassTypeEnum = Common.Enums.IFCEntityType.IfcBuildingElementPart; if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) { return; } } else { string ifcEnumType = null; IFCExportInfoPair exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); // Check the intended IFC entity or type name is in the exclude list specified in the UI Common.Enums.IFCEntityType elementClassTypeEnum; if (Enum.TryParse <Common.Enums.IFCEntityType>(exportType.ToString(), out elementClassTypeEnum)) { if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) { return; } } } PlacementSetter standalonePlacementSetter = null; bool standaloneExport = hostElement == null || asBuildingElement; ElementId partExportLevelId = (overrideLevelId != null) ? overrideLevelId : null; if (partExportLevelId == null && standaloneExport) { partExportLevelId = partElement.LevelId; } if (partExportLevelId == null) { if (hostElement == null || (part.OriginalCategoryId != hostElement.Category.Id)) { return; } partExportLevelId = hostElement.LevelId; } if (ExporterCacheManager.PartExportedCache.HasExported(partElement.Id, partExportLevelId)) { return; } Options options = GeometryUtil.GetIFCExportGeometryOptions(); View ownerView = partElement.Document.GetElement(partElement.OwnerViewId) as View; if (ownerView != null) { options.View = ownerView; } GeometryElement geometryElement = partElement.get_Geometry(options); if (geometryElement == null) { return; } try { IFCFile file = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(file)) { IFCAnyHandle partPlacement = null; if (standaloneExport) { Transform orientationTrf = Transform.Identity; IFCAnyHandle overrideContainerHnd = null; ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, partElement, out overrideContainerHnd); if (overrideContainerId != ElementId.InvalidElementId && (partExportLevelId == null || partExportLevelId == ElementId.InvalidElementId)) { partExportLevelId = overrideContainerId; } standalonePlacementSetter = PlacementSetter.Create(exporterIFC, partElement, null, orientationTrf, partExportLevelId, overrideContainerHnd); partPlacement = standalonePlacementSetter.LocalPlacement; } else { //partPlacement = ExporterUtil.CreateLocalPlacement(file, null, null); partPlacement = ExporterUtil.CreateLocalPlacement(file, originalPlacement, null); } bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); SolidMeshGeometryInfo solidMeshInfo; if (validRange) { solidMeshInfo = GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) { return; } } else { solidMeshInfo = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(partPlacement); extrusionCreationData.ReuseLocalPlacement = false; extrusionCreationData.PossibleExtrusionAxes = ifcExtrusionAxes; IList <Solid> solids = new List <Solid>();; IList <Mesh> meshes = new List <Mesh>(); IList <GeometryObject> gObjs = FamilyExporterUtil.RemoveInvisibleSolidsAndMeshes(partElement.Document, exporterIFC, solidMeshInfo.GetSolids(), solidMeshInfo.GetMeshes()); foreach (GeometryObject gObj in gObjs) { if (gObj is Solid) { solids.Add(gObj as Solid); } else if (gObj is Mesh) { meshes.Add(gObj as Mesh); } } ElementId catId = CategoryUtil.GetSafeCategoryId(partElement); ElementId hostCatId = CategoryUtil.GetSafeCategoryId(hostElement); BodyData bodyData = null; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); if (solids.Count > 0 || meshes.Count > 0) { bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, solids, meshes, bodyExporterOptions, extrusionCreationData); } else { IList <GeometryObject> geomlist = new List <GeometryObject>(); geomlist.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, geomlist, bodyExporterOptions, extrusionCreationData); } IFCAnyHandle bodyRep = bodyData.RepresentationHnd; if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extrusionCreationData.ClearOpenings(); return; } IList <IFCAnyHandle> representations = new List <IFCAnyHandle>(); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) { representations.Add(boundingBoxRep); } IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; string partGUID = GUIDUtil.CreateGUID(partElement); string ifcEnumType = null; IFCExportInfoPair exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); IFCAnyHandle ifcPart = null; if (!asBuildingElement) { ifcPart = IFCInstanceExporter.CreateBuildingElementPart(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep); } else { switch (exportType.ExportInstance) { case IFCEntityType.IfcColumn: ifcPart = IFCInstanceExporter.CreateColumn(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; case IFCEntityType.IfcCovering: ifcPart = IFCInstanceExporter.CreateCovering(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; case IFCEntityType.IfcFooting: ifcPart = IFCInstanceExporter.CreateFooting(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; case IFCEntityType.IfcPile: ifcPart = IFCInstanceExporter.CreatePile(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType, null); break; case IFCEntityType.IfcRoof: ifcPart = IFCInstanceExporter.CreateRoof(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; case IFCEntityType.IfcSlab: { // TODO: fix this elsewhere. if (ExporterUtil.IsNotDefined(ifcEnumType)) { if (hostCatId == new ElementId(BuiltInCategory.OST_Floors)) { ifcEnumType = "FLOOR"; } else if (hostCatId == new ElementId(BuiltInCategory.OST_Roofs)) { ifcEnumType = "ROOF"; } } ifcPart = IFCInstanceExporter.CreateSlab(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); } break; case IFCEntityType.IfcWall: ifcPart = IFCInstanceExporter.CreateWall(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; default: ifcPart = IFCInstanceExporter.CreateBuildingElementProxy(exporterIFC, partElement, partGUID, ownerHistory, extrusionCreationData.GetLocalPlacement(), prodRep, exportType.ValidatedPredefinedType); break; } } bool containedInLevel = standaloneExport; PlacementSetter whichPlacementSetter = containedInLevel ? standalonePlacementSetter : placementSetter; productWrapper.AddElement(partElement, ifcPart, whichPlacementSetter, extrusionCreationData, containedInLevel, exportType); OpeningUtil.CreateOpeningsIfNecessary(ifcPart, partElement, extrusionCreationData, bodyData.OffsetTransform, exporterIFC, extrusionCreationData.GetLocalPlacement(), whichPlacementSetter, productWrapper); //Add the exported part to exported cache. TraceExportedParts(partElement, partExportLevelId, standaloneExport ? ElementId.InvalidElementId : hostElement.Id); CategoryUtil.CreateMaterialAssociation(exporterIFC, ifcPart, bodyData.MaterialIds); transaction.Commit(); } } } finally { if (standalonePlacementSetter != null) { standalonePlacementSetter.Dispose(); } } }
/// <summary> /// Adds openings to an element. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="elementHandles">The parent handles.</param> /// <param name="curveLoops">The parent CurveLoops.</param> /// <param name="element">The element.</param> /// <param name="plane">The plane.</param> /// <param name="scaledWidth">The width.</param> /// <param name="range">The range.</param> /// <param name="setter">The placement setter.</param> /// <param name="localPlacement">The local placement.</param> /// <param name="localWrapper">The wrapper.</param> public static void AddOpeningsToElement(ExporterIFC exporterIFC, IList <IFCAnyHandle> elementHandles, IList <CurveLoop> curveLoops, Element element, Plane plane, double scaledWidth, IFCRange range, PlacementSetter setter, IFCAnyHandle localPlacement, ProductWrapper localWrapper) { IList <IFCOpeningData> openingDataList = ExporterIFCUtils.GetOpeningData(exporterIFC, element, plane, range); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); foreach (IFCOpeningData openingData in openingDataList) { Element openingElem = element.Document.GetElement(openingData.OpeningElementId); if (openingElem == null) { openingElem = element; } // Don't export the opening if WallSweep category has been turned off. // This is currently restricted to WallSweeps because the element responsible for the opening could be a variety of things, including a line as part of the elevation profile of the wall. // As such, we will restrict which element types we check for CanExportElement. if ((openingElem is WallSweep) && (!ElementFilteringUtil.CanExportElement(exporterIFC, openingElem, true))) { continue; } IList <IFCExtrusionData> extrusionDataList = openingData.GetExtrusionData(); IFCAnyHandle parentHandle = null; if (elementHandles.Count > 1 && extrusionDataList.Count > 0) { parentHandle = FindParentHandle(elementHandles, curveLoops, extrusionDataList[0].GetLoops()[0]); } if (parentHandle == null) { parentHandle = elementHandles[0]; } bool isDoorOrWindowOpening = IsDoorOrWindowOpening(exporterIFC, openingElem, element); if (isDoorOrWindowOpening) { DoorWindowDelayedOpeningCreator delayedCreator = DoorWindowDelayedOpeningCreator.Create(exporterIFC, openingData, scaledWidth, element.Id, parentHandle, setter.LevelId); if (delayedCreator != null) { ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); continue; } } bool canUseElementGUID = !isDoorOrWindowOpening; IList <Solid> solids = openingData.GetOpeningSolids(); foreach (Solid solid in solids) { using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, null)); extrusionCreationData.ReuseLocalPlacement = true; string openingGUID = null; if (canUseElementGUID) { openingGUID = GUIDUtil.CreateGUID(openingElem); canUseElementGUID = false; } else { openingGUID = GUIDUtil.CreateGUID(); } CreateOpening(exporterIFC, parentHandle, element, openingElem, openingGUID, solid, scaledWidth, openingData.IsRecess, extrusionCreationData, setter, localWrapper); } } foreach (IFCExtrusionData extrusionData in extrusionDataList) { if (extrusionData.ScaledExtrusionLength < MathUtil.Eps()) { extrusionData.ScaledExtrusionLength = scaledWidth; } string openingGUID = null; if (canUseElementGUID) { openingGUID = GUIDUtil.CreateGUID(element); canUseElementGUID = false; } else { openingGUID = GUIDUtil.CreateGUID(); } CreateOpening(exporterIFC, parentHandle, localPlacement, element, openingElem, openingGUID, extrusionData, plane, openingData.IsRecess, setter, localWrapper); } } }
/// <summary> /// Export the individual part (IfcBuildingElementPart). /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="partElement">The part element to export.</param> /// <param name="geometryElement">The geometry of part.</param> /// <param name="productWrapper">The ProductWrapper object.</param> public static void ExportPart(ExporterIFC exporterIFC, Element partElement, ProductWrapper productWrapper, IFCPlacementSetter placementSetter, IFCAnyHandle originalPlacement, IFCRange range, IFCExtrusionAxes ifcExtrusionAxes, Element hostElement, ElementId overrideLevelId, bool asBuildingElement) { if (!ElementFilteringUtil.IsElementVisible(partElement)) { return; } Part part = partElement as Part; if (part == null) { return; } IFCPlacementSetter standalonePlacementSetter = null; bool standaloneExport = hostElement == null && !asBuildingElement; ElementId partExportLevel = null; if (overrideLevelId != null) { partExportLevel = overrideLevelId; } else if (standaloneExport || asBuildingElement) { if (partElement.Level != null) { partExportLevel = partElement.Level.Id; } } else { if (part.OriginalCategoryId != hostElement.Category.Id) { return; } partExportLevel = hostElement.Level.Id; } if (ExporterCacheManager.PartExportedCache.HasExported(partElement.Id, partExportLevel)) { return; } Options options = GeometryUtil.GetIFCExportGeometryOptions(); View ownerView = partElement.Document.GetElement(partElement.OwnerViewId) as View; if (ownerView != null) { options.View = ownerView; } GeometryElement geometryElement = partElement.get_Geometry(options); if (geometryElement == null) { return; } try { IFCFile file = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(file)) { IFCAnyHandle partPlacement = null; if (standaloneExport || asBuildingElement) { Transform orientationTrf = Transform.Identity; standalonePlacementSetter = IFCPlacementSetter.Create(exporterIFC, partElement, null, orientationTrf, partExportLevel); partPlacement = standalonePlacementSetter.GetPlacement(); } else { partPlacement = ExporterUtil.CreateLocalPlacement(file, originalPlacement, null); } bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); SolidMeshGeometryInfo solidMeshInfo; if (validRange) { solidMeshInfo = GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) { return; } } else { solidMeshInfo = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } using (IFCExtrusionCreationData extrusionCreationData = new IFCExtrusionCreationData()) { extrusionCreationData.SetLocalPlacement(partPlacement); extrusionCreationData.ReuseLocalPlacement = false; extrusionCreationData.PossibleExtrusionAxes = ifcExtrusionAxes; IList <Solid> solids = solidMeshInfo.GetSolids(); IList <Mesh> meshes = solidMeshInfo.GetMeshes(); ElementId catId = CategoryUtil.GetSafeCategoryId(partElement); BodyData bodyData = null; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); if (solids.Count > 0 || meshes.Count > 0) { bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, solids, meshes, bodyExporterOptions, extrusionCreationData); } else { IList <GeometryObject> geomlist = new List <GeometryObject>(); geomlist.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, partElement, catId, ElementId.InvalidElementId, geomlist, bodyExporterOptions, extrusionCreationData); } IFCAnyHandle bodyRep = bodyData.RepresentationHnd; if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extrusionCreationData.ClearOpenings(); return; } IList <IFCAnyHandle> representations = new List <IFCAnyHandle>(); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) { representations.Add(boundingBoxRep); } IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); string partGUID = GUIDUtil.CreateGUID(partElement); string partName = NamingUtil.GetNameOverride(partElement, NamingUtil.GetIFCName(partElement)); string partDescription = NamingUtil.GetDescriptionOverride(partElement, null); string partObjectType = NamingUtil.GetObjectTypeOverride(partElement, NamingUtil.CreateIFCObjectName(exporterIFC, partElement)); string partTag = NamingUtil.GetTagOverride(partElement, NamingUtil.CreateIFCElementId(partElement)); IFCAnyHandle ifcPart = null; if (!asBuildingElement) { ifcPart = IFCInstanceExporter.CreateBuildingElementPart(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag); } else { string ifcEnumType; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, hostElement, out ifcEnumType); switch (exportType) { case IFCExportType.ExportColumnType: ifcPart = IFCInstanceExporter.CreateColumn(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag); break; case IFCExportType.ExportCovering: IFCCoveringType coveringType = CeilingExporter.GetIFCCoveringType(hostElement, ifcEnumType); ifcPart = IFCInstanceExporter.CreateCovering(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, coveringType); break; case IFCExportType.ExportFooting: IFCFootingType footingType = FootingExporter.GetIFCFootingType(hostElement, ifcEnumType); ifcPart = IFCInstanceExporter.CreateFooting(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, footingType); break; case IFCExportType.ExportRoof: IFCRoofType roofType = RoofExporter.GetIFCRoofType(ifcEnumType); ifcPart = IFCInstanceExporter.CreateRoof(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, roofType); break; case IFCExportType.ExportSlab: IFCSlabType slabType = FloorExporter.GetIFCSlabType(ifcEnumType); ifcPart = IFCInstanceExporter.CreateSlab(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, slabType); break; case IFCExportType.ExportWall: ifcPart = IFCInstanceExporter.CreateWall(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag); break; default: ifcPart = IFCInstanceExporter.CreateBuildingElementProxy(file, partGUID, ownerHistory, partName, partDescription, partObjectType, extrusionCreationData.GetLocalPlacement(), prodRep, partTag, null); break; } } bool containedInLevel = (standaloneExport || asBuildingElement); IFCPlacementSetter whichPlacementSetter = containedInLevel ? standalonePlacementSetter : placementSetter; productWrapper.AddElement(partElement, ifcPart, whichPlacementSetter, extrusionCreationData, containedInLevel); //Add the exported part to exported cache. TraceExportedParts(partElement, partExportLevel, standaloneExport || asBuildingElement ? ElementId.InvalidElementId : hostElement.Id); CategoryUtil.CreateMaterialAssociations(exporterIFC, ifcPart, bodyData.MaterialIds); transaction.Commit(); } } } finally { if (standalonePlacementSetter != null) { standalonePlacementSetter.Dispose(); } } }