/// <summary> /// The constructor for the class. /// </summary> /// <param name="rebarElement">The element that created the rebar.</param> /// <param name="elementHandle">The associated IFC element handle.</param> /// <param name="levelInfo">The information for the associated level.</param> public DelayedProductWrapper(Element rebarElement, IFCAnyHandle elementHandle, IFCLevelInfo levelInfo) { RebarElement = rebarElement; ElementHandle = elementHandle; LevelInfo = levelInfo; }
/// <summary> /// Calculates the distance to the next level if the UpToLevel parameter is set. /// If the parameter is not set, or the distance is negative, 0 is returned. /// This is not set in IFCLevelInfo as we want this calculation to be done in .NET code. /// </summary> /// <param name="level"> /// The Level. /// </param> public static double calculateDistanceToNextLevel(Document doc, ElementId levelId, IFCLevelInfo levelInfo) { double height = 0.0; Level level = doc.GetElement(levelId) as Level; ElementId nextLevelId = ElementId.InvalidElementId; if (level != null) { Parameter nextLevelParameter = level.get_Parameter(BuiltInParameter.LEVEL_UP_TO_LEVEL); if (nextLevelParameter != null) { Element nextLevelAsElement = doc.GetElement(nextLevelParameter.AsElementId()); if (nextLevelAsElement != null) { Level possibleNextLevel = nextLevelAsElement as Level; if (possibleNextLevel != null && IsBuildingStory(possibleNextLevel)) { double nextLevelElevation = possibleNextLevel.Elevation; double netElevation = nextLevelElevation - level.Elevation; if (netElevation > 0.0) { height = netElevation; nextLevelId = nextLevelParameter.AsElementId(); } } } } } if ((height <= 0.0) && (levelInfo != null)) { height = levelInfo.DistanceToNextLevel; } ExporterCacheManager.LevelInfoCache.Register(levelId, nextLevelId, height); return(height); }
/// <summary> /// Gets the height of a spatial element. /// </summary> /// <param name="spatialElement"> /// The spatial element. /// </param> /// <param name="scale"> /// The scale value. /// </param> /// <param name="levelInfo"> /// The level info. /// </param> /// <returns> /// The height. /// </returns> static double GetHeight(SpatialElement spatialElement, double scale, ElementId levelId, IFCLevelInfo levelInfo) { Document document = spatialElement.Document; ElementId topLevelId; ParameterUtil.GetElementIdValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_LEVEL, out topLevelId); double topOffset; ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_OFFSET, out topOffset); double bottomOffset; ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_LOWER_OFFSET, out bottomOffset); Level bottomLevel = document.GetElement(levelId) as Level; Level topLevel = (levelId == topLevelId) ? bottomLevel : document.GetElement(topLevelId) as Level; double roomHeight = 0.0; if (bottomLevel != null && topLevel != null) { roomHeight = (topLevel.Elevation - bottomLevel.Elevation) + (topOffset - bottomOffset); roomHeight *= scale; } if (MathUtil.IsAlmostZero(roomHeight)) { double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(levelId); if (levelHeight < 0.0) levelHeight = LevelUtil.calculateDistanceToNextLevel(document, levelId, levelInfo); roomHeight = levelHeight * scale; } // For area spaces, we assign a dummy height (1'), as we are not allowed to export IfcSpaces without a volumetric representation. if (MathUtil.IsAlmostZero(roomHeight) && spatialElement is Area) { roomHeight = 1.0; } return roomHeight; }
/// <summary> /// Gets the height of a spatial element. /// </summary> /// <param name="spatialElement">The spatial element.</param> /// <param name="levelId">The level id.</param> /// <param name="levelInfo">The level info.</param> /// <returns> /// The height, scaled in IFC units. /// </returns> static double GetScaledHeight(SpatialElement spatialElement, ElementId levelId, IFCLevelInfo levelInfo) { Document document = spatialElement.Document; bool isArea = spatialElement is Area; ElementId topLevelId = ElementId.InvalidElementId; double topOffset = 0.0; // These values are internally set for areas, but are invalid. Ignore them and just use the level height. if (!isArea) { ParameterUtil.GetElementIdValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_LEVEL, out topLevelId); ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_OFFSET, out topOffset); } double bottomOffset; ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_LOWER_OFFSET, out bottomOffset); Level bottomLevel = document.GetElement(levelId) as Level; Level topLevel = (levelId == topLevelId) ? bottomLevel : document.GetElement(topLevelId) as Level; double roomHeight = 0.0; if (bottomLevel != null && topLevel != null) { roomHeight = (topLevel.Elevation - bottomLevel.Elevation) + (topOffset - bottomOffset); roomHeight = UnitUtil.ScaleLength(roomHeight); } if (MathUtil.IsAlmostZero(roomHeight)) { double levelHeight = ExporterCacheManager.LevelInfoCache.FindHeight(levelId); if (levelHeight < 0.0) levelHeight = LevelUtil.CalculateDistanceToNextLevel(document, levelId, levelInfo); roomHeight = UnitUtil.ScaleLength(levelHeight); } // For area spaces, we assign a dummy height (1 unit), as we are not allowed to export IfcSpaces without a volumetric representation. if (MathUtil.IsAlmostZero(roomHeight) && spatialElement is Area) { roomHeight = 1.0; } return roomHeight; }
/// <summary> /// Exports an element as an IFC assembly. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The element.</param> /// <param name="productWrapper">The ProductWrapper.</param> /// <returns>True if exported successfully, false otherwise.</returns> public static bool ExportAssemblyInstanceElement(ExporterIFC exporterIFC, AssemblyInstance element, ProductWrapper productWrapper) { if (element == null) { return(false); } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { IFCAnyHandle assemblyInstanceHnd = null; string guid = GUIDUtil.CreateGUID(element); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; IFCAnyHandle localPlacement = null; PlacementSetter placementSetter = null; IFCLevelInfo levelInfo = null; bool relateToLevel = true; string ifcEnumType; IFCExportInfoPair exportAs = ExporterUtil.GetExportType(exporterIFC, element, out ifcEnumType); if (exportAs.ExportInstance == IFCEntityType.IfcSystem) { string name = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string description = NamingUtil.GetDescriptionOverride(element, null); string objectType = NamingUtil.GetObjectTypeOverride(element, NamingUtil.GetFamilyAndTypeName(element)); assemblyInstanceHnd = IFCInstanceExporter.CreateSystem(file, guid, ownerHistory, name, description, objectType); // Create classification reference when System has classification filed name assigned to it ClassificationUtil.CreateClassification(exporterIFC, file, element, assemblyInstanceHnd); HashSet <IFCAnyHandle> relatedBuildings = new HashSet <IFCAnyHandle>(); relatedBuildings.Add(ExporterCacheManager.BuildingHandle); IFCAnyHandle relServicesBuildings = IFCInstanceExporter.CreateRelServicesBuildings(file, GUIDUtil.CreateGUID(), ExporterCacheManager.OwnerHistoryHandle, null, null, assemblyInstanceHnd, relatedBuildings); relateToLevel = false; // Already related to the building via IfcRelServicesBuildings. } else { using (placementSetter = PlacementSetter.Create(exporterIFC, element)) { IFCAnyHandle representation = null; // We have limited support for exporting assemblies as other container types. localPlacement = placementSetter.LocalPlacement; levelInfo = placementSetter.LevelInfo; switch (exportAs.ExportInstance) { case IFCEntityType.IfcCurtainWall: //case IFCExportType.IfcCurtainWallType: //string cwPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(element, ifcEnumType, "IfcCurtainWallType"); assemblyInstanceHnd = IFCInstanceExporter.CreateCurtainWall(exporterIFC, element, guid, ownerHistory, localPlacement, representation, ifcEnumType); break; case IFCEntityType.IfcRamp: string rampPredefinedType = RampExporter.GetIFCRampType(ifcEnumType); //rampPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(element, rampPredefinedType, "IfcRampType"); assemblyInstanceHnd = IFCInstanceExporter.CreateRamp(exporterIFC, element, guid, ownerHistory, localPlacement, representation, rampPredefinedType); break; case IFCEntityType.IfcRoof: //string roofPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(element, ifcEnumType, "IfcRoofType"); assemblyInstanceHnd = IFCInstanceExporter.CreateRoof(exporterIFC, element, guid, ownerHistory, localPlacement, representation, ifcEnumType); break; case IFCEntityType.IfcStair: string stairPredefinedType = StairsExporter.GetIFCStairType(ifcEnumType); //stairPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(element, stairPredefinedType, "IfcStairType"); assemblyInstanceHnd = IFCInstanceExporter.CreateStair(exporterIFC, element, guid, ownerHistory, localPlacement, representation, stairPredefinedType); break; case IFCEntityType.IfcWall: //string wallPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(element, ifcEnumType, "IfcWallType"); assemblyInstanceHnd = IFCInstanceExporter.CreateWall(exporterIFC, element, guid, ownerHistory, localPlacement, representation, ifcEnumType); break; default: string objectType = NamingUtil.GetObjectTypeOverride(element, NamingUtil.GetFamilyAndTypeName(element)); IFCElementAssemblyType assemblyPredefinedType = GetPredefinedTypeFromObjectType(objectType); assemblyInstanceHnd = IFCInstanceExporter.CreateElementAssembly(exporterIFC, element, guid, ownerHistory, localPlacement, representation, IFCAssemblyPlace.NotDefined, assemblyPredefinedType); break; } } } if (assemblyInstanceHnd == null) { return(false); } // relateToLevel depends on how the AssemblyInstance is being mapped to IFC, above. productWrapper.AddElement(element, assemblyInstanceHnd, levelInfo, null, relateToLevel); ExporterCacheManager.AssemblyInstanceCache.RegisterAssemblyInstance(element.Id, assemblyInstanceHnd); tr.Commit(); return(true); } }
/// <summary> /// Add a space to the wrapper, with associated level and extrusion data information. /// </summary> /// <param name="handle">The element handle.</param> /// <param name="levelInfo">The level information.</param> /// <param name="data">The extrusion creation data (can be null.)</param> /// <param name="relateToLevel">Relate to the level in the setter, or not.</param> public void AddSpace(IFCAnyHandle handle, IFCLevelInfo levelInfo, IFCExtrusionCreationData data, bool relateToLevel) { m_InternalWrapper.AddSpace(handle, levelInfo, data, relateToLevel); }
/// <summary> /// Adds an annotation handle to associate with the IfcProduct in this wrapper. /// </summary> /// <param name="handle">The annotation handle.</param> /// <param name="levelInfo">The level information, can be null if relateToLevel is false.</param> /// <param name="relateToLevel">Whether the annotation is contained in a level.</param> public void AddAnnotation(IFCAnyHandle handle, IFCLevelInfo levelInfo, bool relateToLevel) { // The internal AddAnnotation takes an optional levelInfo, so we don't need to do a levelInfo null check here. m_InternalWrapper.AddAnnotation(handle, levelInfo, relateToLevel); }
/// <summary> /// Exports the top stories of a multistory ramp. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="ramp">The ramp element.</param> /// <param name="numFlights">The number of flights for a multistory ramp.</param> /// <param name="rampHnd">The stairs container handle.</param> /// <param name="components">The components handles.</param> /// <param name="ecData">The extrusion creation data.</param> /// <param name="componentECData">The extrusion creation data for the components.</param> /// <param name="placementSetter">The placement setter.</param> /// <param name="productWrapper">The ProductWrapper.</param> public static void ExportMultistoryRamp(ExporterIFC exporterIFC, Element ramp, int numFlights, IFCAnyHandle rampHnd, IList <IFCAnyHandle> components, IList <IFCExtrusionCreationData> componentECData, PlacementSetter placementSetter, ProductWrapper productWrapper) { if (numFlights < 2) { return; } double heightNonScaled = GetRampHeight(exporterIFC, ramp); if (heightNonScaled < MathUtil.Eps()) { return; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(rampHnd)) { return; } IFCAnyHandle localPlacement = IFCAnyHandleUtil.GetObjectPlacement(rampHnd); if (IFCAnyHandleUtil.IsNullOrHasNoValue(localPlacement)) { return; } IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle relPlacement = GeometryUtil.GetRelativePlacementFromLocalPlacement(localPlacement); IFCAnyHandle ptHnd = IFCAnyHandleUtil.GetLocation(relPlacement); IList <double> origCoords = IFCAnyHandleUtil.GetCoordinates(ptHnd); IList <IFCAnyHandle> rampLocalPlacementHnds = new List <IFCAnyHandle>(); IList <IFCLevelInfo> levelInfos = new List <IFCLevelInfo>(); for (int ii = 0; ii < numFlights - 1; ii++) { IFCAnyHandle newLevelHnd = null; // We are going to avoid internal scaling routines, and instead scale in .NET. double newOffsetUnscaled = 0.0; IFCLevelInfo currLevelInfo = placementSetter.GetOffsetLevelInfoAndHandle(heightNonScaled * (ii + 1), 1.0, ramp.Document, out newLevelHnd, out newOffsetUnscaled); double newOffsetScaled = UnitUtil.ScaleLength(newOffsetUnscaled); if (currLevelInfo != null) { levelInfos.Add(currLevelInfo); } else { levelInfos.Add(placementSetter.LevelInfo); } XYZ orig; if (ptHnd.HasValue) { orig = new XYZ(origCoords[0], origCoords[1], newOffsetScaled); } else { orig = new XYZ(0.0, 0.0, newOffsetScaled); } rampLocalPlacementHnds.Add(ExporterUtil.CreateLocalPlacement(file, newLevelHnd, orig, null, null)); } IList <List <IFCAnyHandle> > newComponents = new List <List <IFCAnyHandle> >(); for (int ii = 0; ii < numFlights - 1; ii++) { newComponents.Add(new List <IFCAnyHandle>()); } int compIdx = 0; ElementId catId = CategoryUtil.GetSafeCategoryId(ramp); foreach (IFCAnyHandle component in components) { string componentName = IFCAnyHandleUtil.GetStringAttribute(component, "Name"); string componentDescription = IFCAnyHandleUtil.GetStringAttribute(component, "Description"); string componentObjectType = IFCAnyHandleUtil.GetStringAttribute(component, "ObjectType"); string componentElementTag = IFCAnyHandleUtil.GetStringAttribute(component, "Tag"); IFCAnyHandle componentProdRep = IFCAnyHandleUtil.GetInstanceAttribute(component, "Representation"); IList <string> localComponentNames = new List <string>(); IList <IFCAnyHandle> componentPlacementHnds = new List <IFCAnyHandle>(); IFCAnyHandle localLocalPlacement = IFCAnyHandleUtil.GetObjectPlacement(component); IFCAnyHandle localRelativePlacement = (localLocalPlacement == null) ? null : IFCAnyHandleUtil.GetInstanceAttribute(localLocalPlacement, "RelativePlacement"); bool isSubRamp = component.IsSubTypeOf(IFCEntityType.IfcRamp.ToString()); for (int ii = 0; ii < numFlights - 1; ii++) { localComponentNames.Add((componentName == null) ? (ii + 2).ToString() : (componentName + ":" + (ii + 2))); if (isSubRamp) { componentPlacementHnds.Add(ExporterUtil.CopyLocalPlacement(file, rampLocalPlacementHnds[ii])); } else { componentPlacementHnds.Add(IFCInstanceExporter.CreateLocalPlacement(file, rampLocalPlacementHnds[ii], localRelativePlacement)); } } IList <IFCAnyHandle> localComponentHnds = new List <IFCAnyHandle>(); if (isSubRamp) { string componentType = IFCAnyHandleUtil.GetEnumerationAttribute(component, ExporterCacheManager.ExportOptionsCache.ExportAs4 ? "PredefinedType" : "ShapeType"); string localRampType = GetIFCRampType(componentType); for (int ii = 0; ii < numFlights - 1; ii++) { IFCAnyHandle representationCopy = ExporterUtil.CopyProductDefinitionShape(exporterIFC, ramp, catId, componentProdRep); localComponentHnds.Add(IFCInstanceExporter.CreateRamp(file, GUIDUtil.CreateGUID(), ownerHistory, localComponentNames[ii], componentDescription, componentObjectType, componentPlacementHnds[ii], representationCopy, componentElementTag, localRampType)); } } else if (IFCAnyHandleUtil.IsSubTypeOf(component, IFCEntityType.IfcRampFlight)) { for (int ii = 0; ii < numFlights - 1; ii++) { IFCAnyHandle representationCopy = ExporterUtil.CopyProductDefinitionShape(exporterIFC, ramp, catId, componentProdRep); localComponentHnds.Add(IFCInstanceExporter.CreateRampFlight(file, GUIDUtil.CreateGUID(), ownerHistory, localComponentNames[ii], componentDescription, componentObjectType, componentPlacementHnds[ii], representationCopy, componentElementTag, "NOTDEFINED")); } } else if (IFCAnyHandleUtil.IsSubTypeOf(component, IFCEntityType.IfcSlab)) { string componentType = IFCAnyHandleUtil.GetEnumerationAttribute(component, "PredefinedType"); IFCSlabType localLandingType = FloorExporter.GetIFCSlabType(componentType); for (int ii = 0; ii < numFlights - 1; ii++) { IFCAnyHandle representationCopy = ExporterUtil.CopyProductDefinitionShape(exporterIFC, ramp, catId, componentProdRep); localComponentHnds.Add(IFCInstanceExporter.CreateSlab(file, GUIDUtil.CreateGUID(), ownerHistory, localComponentNames[ii], componentDescription, componentObjectType, componentPlacementHnds[ii], representationCopy, componentElementTag, localLandingType.ToString())); } } else if (IFCAnyHandleUtil.IsSubTypeOf(component, IFCEntityType.IfcMember)) { for (int ii = 0; ii < numFlights - 1; ii++) { IFCAnyHandle representationCopy = ExporterUtil.CopyProductDefinitionShape(exporterIFC, ramp, catId, componentProdRep); localComponentHnds.Add(IFCInstanceExporter.CreateMember(file, GUIDUtil.CreateGUID(), ownerHistory, localComponentNames[ii], componentDescription, componentObjectType, componentPlacementHnds[ii], representationCopy, componentElementTag, "STRINGER")); } } for (int ii = 0; ii < numFlights - 1; ii++) { if (localComponentHnds[ii] != null) { newComponents[ii].Add(localComponentHnds[ii]); productWrapper.AddElement(null, localComponentHnds[ii], levelInfos[ii], componentECData[compIdx], false); } } compIdx++; } // finally add a copy of the container. IList <IFCAnyHandle> rampCopyHnds = new List <IFCAnyHandle>(); for (int ii = 0; ii < numFlights - 1; ii++) { string rampName = IFCAnyHandleUtil.GetStringAttribute(rampHnd, "Name"); string rampObjectType = IFCAnyHandleUtil.GetStringAttribute(rampHnd, "ObjectType"); string rampDescription = IFCAnyHandleUtil.GetStringAttribute(rampHnd, "Description"); string rampElementTag = IFCAnyHandleUtil.GetStringAttribute(rampHnd, "Tag"); string rampTypeAsString = null; if (ExporterCacheManager.ExportOptionsCache.ExportAs4) { rampTypeAsString = IFCAnyHandleUtil.GetEnumerationAttribute(rampHnd, "PredefinedType"); } else { rampTypeAsString = IFCAnyHandleUtil.GetEnumerationAttribute(rampHnd, "ShapeType"); } string rampType = GetIFCRampType(rampTypeAsString); string containerRampName = rampName + ":" + (ii + 2); rampCopyHnds.Add(IFCInstanceExporter.CreateRamp(file, GUIDUtil.CreateGUID(), ownerHistory, containerRampName, rampDescription, rampObjectType, rampLocalPlacementHnds[ii], null, rampElementTag, rampType)); productWrapper.AddElement(ramp, rampCopyHnds[ii], levelInfos[ii], null, true); } for (int ii = 0; ii < numFlights - 1; ii++) { StairRampContainerInfo stairRampInfo = new StairRampContainerInfo(rampCopyHnds[ii], newComponents[ii], rampLocalPlacementHnds[ii]); ExporterCacheManager.StairRampContainerInfoCache.AppendStairRampContainerInfo(ramp.Id, stairRampInfo); } }
/// <summary> /// Add a generic element to the wrapper, with associated level and extrusion data information, and create associated internal property sets if option is set. /// </summary> /// <param name="element">The element.</param> /// <param name="handle">The element handle.</param> /// <param name="levelInfo">The level information.</param> /// <param name="data">The extrusion creation data (can be null.)</param> /// <param name="relateToLevel">Relate to the level in the setter, or not.</param> public void AddElement(Element element, IFCAnyHandle handle, IFCLevelInfo levelInfo, IFCExtrusionCreationData data, bool relateToLevel) { // There is a bug in the internal AddElement that requires us to do a levelInfo null check here. bool actuallyRelateToLevel = relateToLevel && (levelInfo != null); m_InternalWrapper.AddElement(handle, levelInfo, data, actuallyRelateToLevel); if (levelInfo == null && relateToLevel) ExporterCacheManager.LevelInfoCache.OrphanedElements.Add(handle); RegisterHandleWithElement(element, handle); }
/// <summary> /// Add a space to the wrapper, with associated level and extrusion data information. /// </summary> /// <param name="element">The element.</param> /// <param name="handle">The element handle.</param> /// <param name="levelInfo">The level information.</param> /// <param name="data">The extrusion creation data (can be null.)</param> /// <param name="relateToLevel">Relate to the level in the setter, or not.</param> public void AddSpace(Element element, IFCAnyHandle handle, IFCLevelInfo levelInfo, IFCExtrusionCreationData data, bool relateToLevel) { bool actuallyRelateToLevel = relateToLevel && (levelInfo != null); m_InternalWrapper.AddSpace(handle, levelInfo, data, actuallyRelateToLevel); if (levelInfo == null && relateToLevel) ExporterCacheManager.LevelInfoCache.OrphanedSpaces.Add(handle); RegisterHandleWithElement(element, handle); }
/// <summary> /// Adds an annotation handle to associate with the IfcProduct in this wrapper. /// </summary> /// <param name="handle">The annotation handle.</param> /// <param name="levelInfo">The level information, can be null if relateToLevel is false.</param> /// <param name="relateToLevel">Whether the annotation is contained in a level.</param> public void AddAnnotation(IFCAnyHandle handle, IFCLevelInfo levelInfo, bool relateToLevel) { m_InternalWrapper.AddAnnotation(handle, levelInfo, relateToLevel); }
/// <summary> /// Add a space to the wrapper, with associated level and extrusion data information. /// </summary> /// <param name="element">The element.</param> /// <param name="handle">The element handle.</param> /// <param name="levelInfo">The level information.</param> /// <param name="data">The extrusion creation data (can be null.)</param> /// <param name="relateToLevel">Relate to the level in the setter, or not.</param> public void AddSpace(Element element, IFCAnyHandle handle, IFCLevelInfo levelInfo, IFCExtrusionCreationData data, bool relateToLevel) { m_InternalWrapper.AddSpace(handle, levelInfo, data, relateToLevel); RegisterHandleWithElement(element, handle); }
/// <summary> /// Export one IFCGrid in one level. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="levelId">The level ID.</param> /// <param name="sameDirectionAxesU">The U axes of grids.</param> /// <param name="sameDirectionAxesV">The V axes of grids.</param> /// <param name="sameDirectionAxesW">The W axes of grids.</param> public static void ExportGrid(ExporterIFC exporterIFC, ElementId levelId, List <Grid> sameDirectionAxesU, List <Grid> sameDirectionAxesV, List <Grid> sameDirectionAxesW) { List <IFCAnyHandle> axesU = null; List <IFCAnyHandle> axesV = null; List <IFCAnyHandle> axesW = null; List <IFCAnyHandle> representations = new List <IFCAnyHandle>(); using (ProductWrapper productWrapper = ProductWrapper.Create(exporterIFC, true)) { IFCFile ifcFile = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(ifcFile)) { GridRepresentationData gridRepresentationData = new GridRepresentationData(); axesU = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesU, representations, gridRepresentationData); axesV = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesV, representations, gridRepresentationData); if (sameDirectionAxesW != null) { axesW = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesW, representations, gridRepresentationData); } IFCAnyHandle contextOfItemsFootPrint = exporterIFC.Get3DContextHandle("FootPrint"); string identifierOpt = "FootPrint"; string representationTypeOpt = "GeometricCurveSet"; int numGridsToExport = gridRepresentationData.m_Grids.Count; if (numGridsToExport == 0) { return; } bool useIFCCADLayer = !string.IsNullOrWhiteSpace(gridRepresentationData.m_IFCCADLayer); IFCAnyHandle shapeRepresentation = null; HashSet <IFCAnyHandle> allCurves = new HashSet <IFCAnyHandle>(); for (int ii = 0; ii < numGridsToExport; ii++) { allCurves.UnionWith(gridRepresentationData.m_curveSets[ii]); } if (useIFCCADLayer) { shapeRepresentation = RepresentationUtil.CreateShapeRepresentation(exporterIFC, contextOfItemsFootPrint, identifierOpt, representationTypeOpt, allCurves, gridRepresentationData.m_IFCCADLayer); } else { ElementId catId = CategoryUtil.GetSafeCategoryId(gridRepresentationData.m_Grids[0]); shapeRepresentation = RepresentationUtil.CreateShapeRepresentation(exporterIFC, gridRepresentationData.m_Grids[0], catId, contextOfItemsFootPrint, identifierOpt, representationTypeOpt, allCurves); } representations.Add(shapeRepresentation); IFCAnyHandle productRep = IFCInstanceExporter.CreateProductDefinitionShape(ifcFile, null, null, representations); IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, levelId); string gridGUID = GUIDUtil.CreateGUID(); // Get the first grid's override name, if cannot find it, use null. string gridName = GetGridName(sameDirectionAxesU, sameDirectionAxesV, sameDirectionAxesW); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); IFCAnyHandle gridLevelHandle = levelInfo.GetBuildingStorey(); IFCAnyHandle levelObjectPlacement = IFCAnyHandleUtil.GetObjectPlacement(gridLevelHandle); double elev = levelInfo.Elevation; double elevation = UnitUtil.ScaleLength(elev); XYZ orig = new XYZ(0.0, 0.0, elevation); IFCAnyHandle copyLevelPlacement = ExporterUtil.CopyLocalPlacement(ifcFile, levelObjectPlacement); IFCAnyHandle ifcGrid = IFCInstanceExporter.CreateGrid(ifcFile, gridGUID, ownerHistory, gridName, null, null, copyLevelPlacement, productRep, axesU, axesV, axesW); productWrapper.AddElement(null, ifcGrid, levelInfo, null, true); transaction.Commit(); } } }
/// <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; } } } } }
/// <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; } } } }
/// <summary> /// Export one IFCGrid in one level. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="levelId">The level ID.</param> /// <param name="sameDirectionAxesU">The U axes of grids.</param> /// <param name="sameDirectionAxesV">The V axes of grids.</param> /// <param name="sameDirectionAxesW">The W axes of grids.</param> public static void ExportGrid(ExporterIFC exporterIFC, ElementId levelId, string gridName, List <Grid> sameDirectionAxesU, List <Grid> sameDirectionAxesV, List <Grid> sameDirectionAxesW) { List <IFCAnyHandle> axesU = null; List <IFCAnyHandle> axesV = null; List <IFCAnyHandle> axesW = null; List <IFCAnyHandle> representations = new List <IFCAnyHandle>(); using (ProductWrapper productWrapper = ProductWrapper.Create(exporterIFC, true)) { IFCFile ifcFile = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(ifcFile)) { GridRepresentationData gridRepresentationData = new GridRepresentationData(); axesU = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesU, representations, gridRepresentationData); axesV = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesV, representations, gridRepresentationData); if (sameDirectionAxesW != null) { axesW = CreateIFCGridAxisAndRepresentations(exporterIFC, productWrapper, sameDirectionAxesW, representations, gridRepresentationData); } IFCAnyHandle contextOfItemsFootPrint = exporterIFC.Get3DContextHandle("FootPrint"); string identifierOpt = "FootPrint"; string representationTypeOpt = "GeometricCurveSet"; int numGridsToExport = gridRepresentationData.m_Grids.Count; if (numGridsToExport == 0) { return; } bool useIFCCADLayer = !string.IsNullOrWhiteSpace(gridRepresentationData.m_IFCCADLayer); IFCAnyHandle shapeRepresentation = null; HashSet <IFCAnyHandle> allCurves = new HashSet <IFCAnyHandle>(); for (int ii = 0; ii < numGridsToExport; ii++) { allCurves.UnionWith(gridRepresentationData.m_curveSets[ii]); } if (useIFCCADLayer) { shapeRepresentation = RepresentationUtil.CreateShapeRepresentation(exporterIFC, contextOfItemsFootPrint, identifierOpt, representationTypeOpt, allCurves, gridRepresentationData.m_IFCCADLayer); } else { ElementId catId = CategoryUtil.GetSafeCategoryId(gridRepresentationData.m_Grids[0]); shapeRepresentation = RepresentationUtil.CreateShapeRepresentation(exporterIFC, gridRepresentationData.m_Grids[0], catId, contextOfItemsFootPrint, identifierOpt, representationTypeOpt, allCurves); } representations.Add(shapeRepresentation); IFCAnyHandle productRep = IFCInstanceExporter.CreateProductDefinitionShape(ifcFile, null, null, representations); // We will associate the grid with its level, unless there are no levels in the file, in which case we'll associate it with the building. IFCLevelInfo levelInfo = ExporterCacheManager.LevelInfoCache.GetLevelInfo(exporterIFC, levelId); bool useLevelInfo = (levelInfo != null); string gridGUID = GUIDUtil.CreateGUID(); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; IFCAnyHandle gridLevelHandle = useLevelInfo ? levelInfo.GetBuildingStorey() : ExporterCacheManager.BuildingHandle; IFCAnyHandle levelObjectPlacement = (gridLevelHandle != null) ? IFCAnyHandleUtil.GetObjectPlacement(gridLevelHandle) : null; IFCAnyHandle copyLevelPlacement = (levelObjectPlacement != null) ? ExporterUtil.CopyLocalPlacement(ifcFile, levelObjectPlacement) : null; IFCAnyHandle ifcGrid = IFCInstanceExporter.CreateGrid(exporterIFC, gridGUID, ownerHistory, gridName, copyLevelPlacement, productRep, axesU, axesV, axesW); productWrapper.AddElement(null, ifcGrid, levelInfo, null, true, null); transaction.Commit(); } } }
/// <summary> /// Adds an IFCLevelInfo to the LevelsByElevation list, also updating the native cache item. /// </summary> /// <param name="exporterIFC"> /// The exporter data object. /// </param> /// <param name="levelId"> /// The level ElementId. /// </param> /// <param name="info"> /// The IFCLevelInfo. /// </param> public void AddLevelInfo(ExporterIFC exporterIFC, ElementId levelId, IFCLevelInfo info) { LevelsByElevation.Add(levelId); exporterIFC.AddBuildingStorey(levelId, info); }
/// <summary> /// Get the height of a spatial element. /// </summary> /// <param name="spatialElement"> /// The spatial element. /// </param> /// <param name="scale"> /// The scale value. /// </param> /// <param name="levelInfo"> /// The level info. /// </param> /// <returns> /// The height. /// </returns> static double GetHeight(SpatialElement spatialElement, double scale, IFCLevelInfo levelInfo) { Document document = spatialElement.Document; double roomHeight = 0.0; double bottomOffset = 0.0; ElementId bottomLevelId = spatialElement.Level.Id; Parameter paramTopLevelId = spatialElement.get_Parameter(BuiltInParameter.ROOM_UPPER_LEVEL); ElementId topLevelId = paramTopLevelId != null ? paramTopLevelId.AsElementId() : ElementId.InvalidElementId; Parameter paramTopOffset = spatialElement.get_Parameter(BuiltInParameter.ROOM_UPPER_OFFSET); double topOffset = paramTopOffset != null ? paramTopOffset.AsDouble() : 0.0; Parameter paramBottomOffset = spatialElement.get_Parameter(BuiltInParameter.ROOM_LOWER_OFFSET); bottomOffset = paramBottomOffset != null ? paramBottomOffset.AsDouble() : 0.0; Level bottomLevel = document.get_Element(bottomLevelId) as Level; Level topLevel = (bottomLevelId == topLevelId) ? bottomLevel : document.get_Element(topLevelId) as Level; if (bottomLevel != null && topLevel != null) { roomHeight = (topLevel.Elevation - bottomLevel.Elevation) + (topOffset - bottomOffset); roomHeight *= scale; } if (MathUtil.IsAlmostZero(roomHeight)) { roomHeight = levelInfo.DistanceToNextLevel * scale; } // For area spaces, we assign a dummy height (1'), as we are not allowed to export IfcSpaces without a volumetric representation. if (MathUtil.IsAlmostZero(roomHeight) && spatialElement is Area) { roomHeight = 1.0; } return roomHeight; }
/// <summary> /// Attempt to determine the local placement of the element based on the element type and initial input. /// </summary> /// <param name="exporterIFC">The ExporterIFC class.</param> /// <param name="elem">The element being exported.</param> /// <param name="familyTrf">The optional family transform.</param> /// <param name="orientationTrf">The optional orientation of the element based on IFC standards or agreements.</param> /// <param name="overrideLevelId">The optional level to place the element, to be used instead of heuristics.</param> private void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyTrf, Transform orientationTrf, ElementId overrideLevelId) { ExporterIFC = exporterIFC; // Convert null value to InvalidElementId. if (overrideLevelId == null) { overrideLevelId = ElementId.InvalidElementId; } Document doc = elem.Document; Element hostElem = elem; ElementId elemId = elem.Id; ElementId newLevelId = overrideLevelId; bool useOverrideOrigin = false; XYZ overrideOrigin = XYZ.Zero; IDictionary <ElementId, IFCLevelInfo> levelInfos = exporterIFC.GetLevelInfos(); if (overrideLevelId == ElementId.InvalidElementId) { if (familyTrf == null) { // Override for CurveElems -- base level calculation on origin of sketch Plane. if (elem is CurveElement) { SketchPlane sketchPlane = (elem as CurveElement).SketchPlane; if (sketchPlane != null) { useOverrideOrigin = true; overrideOrigin = sketchPlane.GetPlane().Origin; } } else { ElementId hostElemId = ElementId.InvalidElementId; // a bit of a hack. If we have a railing, we want it to have the same level base as its host Stair (because of // the way the stairs place railings and stair flights together). if (elem is Railing) { hostElemId = (elem as Railing).HostId; } else if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Assemblies) { hostElemId = elem.AssemblyInstanceId; } if (hostElemId != ElementId.InvalidElementId) { hostElem = doc.GetElement(hostElemId); } newLevelId = hostElem != null ? hostElem.LevelId : ElementId.InvalidElementId; if (newLevelId == ElementId.InvalidElementId) { ExporterIFCUtils.GetLevelIdByHeight(exporterIFC, hostElem); } } } // todo: store. double bottomHeight = double.MaxValue; ElementId bottomLevelId = ElementId.InvalidElementId; if ((newLevelId == ElementId.InvalidElementId) || orientationTrf != null) { // if we have a trf, it might geometrically push the instance to a new level. Check that case. // actually, we should ALWAYS check the bbox vs the settings newLevelId = ElementId.InvalidElementId; XYZ originToUse = XYZ.Zero; bool originIsValid = useOverrideOrigin; if (useOverrideOrigin) { originToUse = overrideOrigin; } else { BoundingBoxXYZ bbox = elem.get_BoundingBox(null); if (bbox != null) { originToUse = bbox.Min; originIsValid = true; } else if (hostElem.Id != elemId) { bbox = hostElem.get_BoundingBox(null); if (bbox != null) { originToUse = bbox.Min; originIsValid = true; } } } // The original heuristic here was that the origin determined the level containment based on exact location: // if the Z of the origin was higher than the current level but lower than the next level, it was contained // on that level. // However, in some places (e.g. Germany), the containment is thought to start just below the level, because floors // are placed before the level, not above. So we have made a small modification so that anything within // 10cm of the 'next' level is on that level. double leveExtension = 10.0 / (12.0 * 2.54); foreach (KeyValuePair <ElementId, IFCLevelInfo> levelInfoPair in levelInfos) { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document if (ExporterCacheManager.ExportOptionsCache.ExportingLink) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) { continue; } } IFCLevelInfo levelInfo = levelInfoPair.Value; double startHeight = levelInfo.Elevation - leveExtension; double height = levelInfo.DistanceToNextLevel; bool useHeight = !MathUtil.IsAlmostZero(height); double endHeight = startHeight + height; if (originIsValid && ((originToUse[2] > (startHeight - MathUtil.Eps())) && (!useHeight || originToUse[2] < (endHeight - MathUtil.Eps())))) { newLevelId = levelInfoPair.Key; } if (startHeight < (bottomHeight + MathUtil.Eps())) { bottomLevelId = levelInfoPair.Key; bottomHeight = startHeight; } } } if (newLevelId == ElementId.InvalidElementId) { newLevelId = bottomLevelId; } } LevelInfo = exporterIFC.GetLevelInfo(newLevelId); if (LevelInfo == null) { foreach (KeyValuePair <ElementId, IFCLevelInfo> levelInfoPair in levelInfos) { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document if (ExporterCacheManager.ExportOptionsCache.ExportingLink) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) { continue; } } LevelInfo = levelInfoPair.Value; break; } //LevelInfo = levelInfos.Values.First<IFCLevelInfo>(); } double elevation = (LevelInfo != null) ? LevelInfo.Elevation : 0.0; IFCAnyHandle levelPlacement = (LevelInfo != null) ? LevelInfo.GetLocalPlacement() : null; IFCFile file = exporterIFC.GetFile(); Transform trf = Transform.Identity; if (familyTrf != null) { XYZ origin, xDir, yDir, zDir; xDir = familyTrf.BasisX; yDir = familyTrf.BasisY; zDir = familyTrf.BasisZ; Transform origOffsetTrf = Transform.Identity; XYZ negLevelOrigin = new XYZ(0, 0, -elevation); origOffsetTrf.Origin = negLevelOrigin; Transform newTrf = origOffsetTrf * familyTrf; origin = newTrf.Origin; trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf = trf.Inverse; origin = UnitUtil.ScaleLength(origin); LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir); } else if (orientationTrf != null) { XYZ origin, xDir, yDir, zDir; xDir = orientationTrf.BasisX; yDir = orientationTrf.BasisY; zDir = orientationTrf.BasisZ; origin = orientationTrf.Origin; XYZ levelOrigin = new XYZ(0, 0, elevation); origin = origin - levelOrigin; trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf.Origin = origin; trf = trf.Inverse; origin = UnitUtil.ScaleLength(origin); LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir); } else { LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, null, null, null); } Transform origOffsetTrf2 = Transform.Identity; XYZ negLevelOrigin2 = new XYZ(0, 0, -elevation); origOffsetTrf2.Origin = negLevelOrigin2; Transform newTrf2 = trf * origOffsetTrf2; ExporterIFC.PushTransform(newTrf2); Offset = elevation; LevelId = newLevelId; }
/// <summary> /// Attempt to determine the local placement of the element based on the element type and initial input. /// </summary> /// <param name="exporterIFC">The ExporterIFC class.</param> /// <param name="elem">The element being exported.</param> /// <param name="familyTrf">The optional family transform.</param> /// <param name="orientationTrf">The optional orientation of the element based on IFC standards or agreements.</param> /// <param name="overrideLevelId">The optional level to place the element, to be used instead of heuristics.</param> private void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyTrf, Transform orientationTrf, ElementId overrideLevelId) { ExporterIFC = exporterIFC; // Convert null value to InvalidElementId. if (overrideLevelId == null) overrideLevelId = ElementId.InvalidElementId; Document doc = elem.Document; Element hostElem = elem; ElementId elemId = elem.Id; ElementId newLevelId = overrideLevelId; bool useOverrideOrigin = false; XYZ overrideOrigin = XYZ.Zero; IDictionary<ElementId, IFCLevelInfo> levelInfos = exporterIFC.GetLevelInfos(); if (overrideLevelId == ElementId.InvalidElementId) { if (familyTrf == null) { // Override for CurveElems -- base level calculation on origin of sketch Plane. if (elem is CurveElement) { SketchPlane sketchPlane = (elem as CurveElement).SketchPlane; if (sketchPlane != null) { useOverrideOrigin = true; overrideOrigin = sketchPlane.GetPlane().Origin; } } else { ElementId hostElemId = ElementId.InvalidElementId; // a bit of a hack. If we have a railing, we want it to have the same level base as its host Stair (because of // the way the stairs place railings and stair flights together). if (elem is Railing) { hostElemId = (elem as Railing).HostId; } else if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Assemblies) { hostElemId = elem.AssemblyInstanceId; } if (hostElemId != ElementId.InvalidElementId) { hostElem = doc.GetElement(hostElemId); } newLevelId = hostElem != null ? hostElem.LevelId : ElementId.InvalidElementId; if (newLevelId == ElementId.InvalidElementId) { ExporterIFCUtils.GetLevelIdByHeight(exporterIFC, hostElem); } } } // todo: store. double bottomHeight = double.MaxValue; ElementId bottomLevelId = ElementId.InvalidElementId; if ((newLevelId == ElementId.InvalidElementId) || orientationTrf != null) { // if we have a trf, it might geometrically push the instance to a new level. Check that case. // actually, we should ALWAYS check the bbox vs the settings newLevelId = ElementId.InvalidElementId; XYZ originToUse = XYZ.Zero; bool originIsValid = useOverrideOrigin; if (useOverrideOrigin) { originToUse = overrideOrigin; } else { BoundingBoxXYZ bbox = elem.get_BoundingBox(null); if (bbox != null) { originToUse = bbox.Min; originIsValid = true; } else if (hostElem.Id != elemId) { bbox = hostElem.get_BoundingBox(null); if (bbox != null) { originToUse = bbox.Min; originIsValid = true; } } } // The original heuristic here was that the origin determined the level containment based on exact location: // if the Z of the origin was higher than the current level but lower than the next level, it was contained // on that level. // However, in some places (e.g. Germany), the containment is thought to start just below the level, because floors // are placed before the level, not above. So we have made a small modification so that anything within // 10cm of the 'next' level is on that level. double leveExtension = 10.0 / (12.0 * 2.54); foreach (KeyValuePair<ElementId, IFCLevelInfo> levelInfoPair in levelInfos) { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document if (ExporterCacheManager.ExportOptionsCache.ExportingLink) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) continue; } IFCLevelInfo levelInfo = levelInfoPair.Value; double startHeight = levelInfo.Elevation - leveExtension; double height = levelInfo.DistanceToNextLevel; bool useHeight = !MathUtil.IsAlmostZero(height); double endHeight = startHeight + height; if (originIsValid && ((originToUse[2] > (startHeight - MathUtil.Eps())) && (!useHeight || originToUse[2] < (endHeight - MathUtil.Eps())))) { newLevelId = levelInfoPair.Key; } if (startHeight < (bottomHeight + MathUtil.Eps())) { bottomLevelId = levelInfoPair.Key; bottomHeight = startHeight; } } } if (newLevelId == ElementId.InvalidElementId) newLevelId = bottomLevelId; } LevelInfo = exporterIFC.GetLevelInfo(newLevelId); if (LevelInfo == null) { foreach (KeyValuePair<ElementId, IFCLevelInfo> levelInfoPair in levelInfos) { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document if (ExporterCacheManager.ExportOptionsCache.ExportingLink) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) continue; } LevelInfo = levelInfoPair.Value; break; } //LevelInfo = levelInfos.Values.First<IFCLevelInfo>(); } double elevation = (LevelInfo != null) ? LevelInfo.Elevation : 0.0; IFCAnyHandle levelPlacement = (LevelInfo != null) ? LevelInfo.GetLocalPlacement() : null; IFCFile file = exporterIFC.GetFile(); Transform trf = Transform.Identity; if (familyTrf != null) { XYZ origin, xDir, yDir, zDir; xDir = familyTrf.BasisX; yDir = familyTrf.BasisY; zDir = familyTrf.BasisZ; Transform origOffsetTrf = Transform.Identity; XYZ negLevelOrigin = new XYZ(0, 0, -elevation); origOffsetTrf.Origin = negLevelOrigin; Transform newTrf = origOffsetTrf * familyTrf; origin = newTrf.Origin; trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf = trf.Inverse; origin = UnitUtil.ScaleLength(origin); LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir); } else if (orientationTrf != null) { XYZ origin, xDir, yDir, zDir; xDir = orientationTrf.BasisX; yDir = orientationTrf.BasisY; zDir = orientationTrf.BasisZ; origin = orientationTrf.Origin; XYZ levelOrigin = new XYZ(0, 0, elevation); origin = origin - levelOrigin; trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf.Origin = origin; trf = trf.Inverse; origin = UnitUtil.ScaleLength(origin); LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir); } else { LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, null, null, null); } Transform origOffsetTrf2 = Transform.Identity; XYZ negLevelOrigin2 = new XYZ(0, 0, -elevation); origOffsetTrf2.Origin = negLevelOrigin2; Transform newTrf2 = trf * origOffsetTrf2; ExporterIFC.PushTransform(newTrf2); Offset = elevation; LevelId = newLevelId; }
/// <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; IFCExportInfoPair 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> /// Adds an IFCLevelInfo to the LevelsByElevation list, also updating the native cache item. /// </summary> /// <param name="exporterIFC">The exporter data object.</param> /// <param name="levelId">The level ElementId.</param> /// <param name="info">The IFCLevelInfo.</param> /// <param name="isBaseBuildingStorey">True if it is the levelId associated with the building storey.</param> public void AddLevelInfo(ExporterIFC exporterIFC, ElementId levelId, IFCLevelInfo info, bool isBaseBuildingStorey) { LevelsByElevation.Add(levelId); if (isBaseBuildingStorey) BuildingStoreysByElevation.Add(levelId); exporterIFC.AddBuildingStorey(levelId, info); }
/// <summary> /// Exports an element as an IFC assembly. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The element.</param> /// <param name="productWrapper">The ProductWrapper.</param> /// <returns>True if exported successfully, false otherwise.</returns> public static bool ExportAssemblyInstanceElement(ExporterIFC exporterIFC, AssemblyInstance element, ProductWrapper productWrapper) { if (element == null) { return(false); } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { IFCAnyHandle assemblyInstanceHnd = null; string guid = GUIDUtil.CreateGUID(element); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); string name = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string description = NamingUtil.GetDescriptionOverride(element, null); string objectType = NamingUtil.GetObjectTypeOverride(element, exporterIFC.GetFamilyName()); IFCAnyHandle localPlacement = null; IFCPlacementSetter placementSetter = null; IFCLevelInfo levelInfo = null; string ifcEnumType; IFCExportType exportAs = ExporterUtil.GetExportType(exporterIFC, element, out ifcEnumType); if (exportAs == IFCExportType.ExportSystem) { assemblyInstanceHnd = IFCInstanceExporter.CreateSystem(file, guid, ownerHistory, name, description, objectType); // Create classification reference when System has classification filed name assigned to it ClassificationUtil.CreateClassification(exporterIFC, file, element, assemblyInstanceHnd); HashSet <IFCAnyHandle> relatedBuildings = new HashSet <IFCAnyHandle>(); relatedBuildings.Add(ExporterCacheManager.BuildingHandle); IFCAnyHandle relServicesBuildings = IFCInstanceExporter.CreateRelServicesBuildings(file, GUIDUtil.CreateGUID(), exporterIFC.GetOwnerHistoryHandle(), null, null, assemblyInstanceHnd, relatedBuildings); } else { using (placementSetter = IFCPlacementSetter.Create(exporterIFC, element, null, null, ExporterUtil.GetBaseLevelIdForElement(element))) { string elementTag = NamingUtil.GetTagOverride(element, NamingUtil.CreateIFCElementId(element)); IFCAnyHandle representation = null; // We have limited support for exporting assemblies as other container types. localPlacement = placementSetter.GetPlacement(); levelInfo = placementSetter.GetLevelInfo(); switch (exportAs) { case IFCExportType.ExportCurtainWall: assemblyInstanceHnd = IFCInstanceExporter.CreateCurtainWall(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag); break; case IFCExportType.ExportRamp: IFCRampType rampPredefinedType = RampExporter.GetIFCRampType(ifcEnumType); assemblyInstanceHnd = IFCInstanceExporter.CreateRamp(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag, rampPredefinedType); break; case IFCExportType.ExportRoof: IFCRoofType roofPredefinedType = RoofExporter.GetIFCRoofType(ifcEnumType); assemblyInstanceHnd = IFCInstanceExporter.CreateRoof(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag, roofPredefinedType); break; case IFCExportType.ExportStair: IFCStairType stairPredefinedType = StairsExporter.GetIFCStairType(ifcEnumType); assemblyInstanceHnd = IFCInstanceExporter.CreateStair(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag, stairPredefinedType); break; case IFCExportType.ExportWall: assemblyInstanceHnd = IFCInstanceExporter.CreateWall(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag); break; default: IFCElementAssemblyType assemblyPredefinedType = GetPredefinedTypeFromObjectType(objectType); assemblyInstanceHnd = IFCInstanceExporter.CreateElementAssembly(file, guid, ownerHistory, name, description, objectType, localPlacement, representation, elementTag, IFCAssemblyPlace.NotDefined, assemblyPredefinedType); break; } } } if (assemblyInstanceHnd == null) { return(false); } bool relateToLevel = (levelInfo != null); productWrapper.AddElement(element, assemblyInstanceHnd, levelInfo, null, relateToLevel); ExporterCacheManager.AssemblyInstanceCache.RegisterAssemblyInstance(element.Id, assemblyInstanceHnd); tr.Commit(); return(true); } }