/// <summary> /// Gets the level info related to an offset of the element's local placement. /// </summary> /// <param name="offset">The vertical offset to the local placement.</param> /// <param name="scale">The linear scale.</param> /// <param name="pPlacementHnd">The handle to the new local placement.</param> /// <param name="pScaledOffsetFromNewLevel">The scaled offset from the new level.</param> /// <returns>The level info.</returns> public IFCLevelInfo GetOffsetLevelInfoAndHandle(double offset, double scale, Document document, out IFCAnyHandle placementHnd, out double scaledOffsetFromNewLevel) { placementHnd = null; scaledOffsetFromNewLevel = 0; double newHeight = Offset + offset; IDictionary <ElementId, IFCLevelInfo> levelInfos = m_ExporterIFC.GetLevelInfos(); 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 = document.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) { continue; } } IFCLevelInfo levelInfo = levelInfoPair.Value; double startHeight = levelInfo.Elevation; if (startHeight > newHeight + MathUtil.Eps()) { continue; } double height = levelInfo.DistanceToNextLevel; bool useHeight = !MathUtil.IsAlmostZero(height); if (!useHeight) { scaledOffsetFromNewLevel = (newHeight - startHeight) * scale; placementHnd = levelInfo.GetLocalPlacement(); return(levelInfo); } double endHeight = startHeight + height; if (newHeight < endHeight - MathUtil.Eps()) { scaledOffsetFromNewLevel = (newHeight - startHeight) * scale; placementHnd = levelInfo.GetLocalPlacement(); return(levelInfo); } } return(null); }
void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyTrf, Transform orientationTrf, ElementId overrideLevelId) { m_ExporterIFC = exporterIFC; overrideLevelId = 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; } } m_LevelInfo = exporterIFC.GetLevelInfo(newLevelId); if (m_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; } } m_LevelInfo = levelInfoPair.Value; break; } //m_LevelInfo = levelInfos.Values.First<IFCLevelInfo>(); } double elevation = m_LevelInfo.Elevation; IFCAnyHandle levelPlacement = m_LevelInfo.GetLocalPlacement(); 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); m_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); m_LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir); } else { m_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; m_ExporterIFC.PushTransform(newTrf2); m_Offset = elevation; m_LevelId = newLevelId; }