/// <summary> /// Gets the element's projection area. /// </summary> /// <param name="elm">A element</param> /// <exception cref="T:Autodesk.Revit.Exceptions.ArgumentNullException">The input element is invalid.</exception> /// <returns>Returns projection area.</returns> public static double GetShadowArea(this Element elm) { if (elm is null) { throw new ArgumentNullException(nameof(elm)); } if (!elm.IsValidObject) { throw new InvalidDataException(nameof(elm)); } if (elm.Id.IntegerValue == -1) { throw new InvalidDataException(nameof(elm)); } var areas = new List <double>(); var geo = elm.get_Geometry(new Options()); foreach (var instance in geo.Select(s => s as GeometryInstance)) { if (instance is null) { continue; } foreach (var item in instance.GetInstanceGeometry()) { var solid = item as Solid; if (null == solid || solid.Faces.Size <= 0) { continue; } var plane = XYZ.BasisZ.CreatePlane(XYZ.Zero); ExtrusionAnalyzer analyzer; try { analyzer = ExtrusionAnalyzer.Create(solid, plane, XYZ.BasisZ); } catch { continue; } if (analyzer is null) { continue; } areas.Add(analyzer.GetExtrusionBase().Area); } } return(areas.Max()); }
/// <summary> /// Gets the element's projection area. /// </summary> /// <param name="elm">A element</param> /// <remarks>Returns projection area, that area unit is square meter.</remarks> /// <exception cref="T:Autodesk.Revit.Exceptions.ArgumentNullException">The input element is invalid.</exception> /// <returns>Returns projection area.</returns> public static double GetShadowArea(this Element elm) { if (elm == null) { throw new ArgumentNullException(nameof(elm)); } var areas = new List <double>(); var geo = elm.get_Geometry(new Options()); foreach (var instance in geo.Select(s => s as GeometryInstance)) { if (instance == null) { continue; } foreach (var item in instance.GetInstanceGeometry()) { var solid = item as Solid; if (null == solid || solid.Faces.Size <= 0) { continue; } var plane = Plane.CreateByOriginAndBasis(XYZ.Zero, XYZ.BasisX, XYZ.BasisY); ExtrusionAnalyzer analyzer; try { analyzer = ExtrusionAnalyzer.Create(solid, plane, XYZ.BasisZ); } catch { continue; } if (analyzer == null) { continue; } areas.Add(analyzer.GetExtrusionBase().Area *FT2_TO_M2); } } return(areas.Max()); }
/// <summary> /// Add all plan view boundary loops from /// given solid to the list of loops. /// The creation application argument is used to /// reverse the extrusion analyser output curves /// in case they are badly oriented. /// </summary> /// <returns>Number of loops added</returns> static int AddLoops( Autodesk.Revit.Creation.Application creapp, JtLoops loops, GeometryObject obj, ref int nExtrusionAnalysisFailures) { int nAdded = 0; Solid solid = obj as Solid; if (null != solid && 0 < solid.Faces.Size) { //Plane plane = new Plane(XYZ.BasisX, // XYZ.BasisY, XYZ.Zero); // 2016 Plane plane = Plane.CreateByOriginAndBasis( XYZ.Zero, XYZ.BasisX, XYZ.BasisY); // 2017 ExtrusionAnalyzer extrusionAnalyzer = null; try { extrusionAnalyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { ++nExtrusionAnalysisFailures; return(nAdded); } Face face = extrusionAnalyzer .GetExtrusionBase(); loops.Add(GetLoop(creapp, face)); ++nAdded; } return(nAdded); }
private static IEnumerable <CurveLoop> GetCountours( Solid solid, Element element) { try { var plane = Plane.CreateByNormalAndOrigin( XYZ.BasisZ, element.get_BoundingBox(null).Min); var analyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ); var face = analyzer.GetExtrusionBase(); return(face.GetEdgesAsCurveLoops()); } catch (InvalidOperationException) { return(Enumerable.Empty <CurveLoop>()); } }
/// <summary> /// Set the ExtrusionBase from the ExtrusionAnalyzer /// </summary> /// <param name="extrusionAnalyzer">Extrusion Analyzer</param> /// <param name="lcs">the LCS</param> /// <param name="baseLoopOffset">offset</param> public FootPrintInfo(ExtrusionAnalyzer extrusionAnalyzer, Transform lcs = null, XYZ baseLoopOffset = null) { Face extrusionBase = extrusionAnalyzer.GetExtrusionBase(); IList <GeometryUtil.FaceBoundaryType> boundaryTypes; if (lcs == null) { lcs = Transform.Identity; } if (baseLoopOffset == null) { baseLoopOffset = XYZ.Zero; } IList <CurveLoop> extrusionBoundaryLoops = GeometryUtil.GetFaceBoundaries(extrusionBase, baseLoopOffset, out boundaryTypes); if (extrusionBoundaryLoops == null || extrusionBoundaryLoops.Count == 0 || extrusionBoundaryLoops[0] == null) { return; } // Move base plane to start parameter location. Plane extrusionBasePlane = null; try { extrusionBasePlane = extrusionBoundaryLoops[0].GetPlane(); } catch { return; } // Only the first CurveLoop will be used as the foorprint ExtrusionBaseLoops = extrusionBoundaryLoops; ExtrusionBaseLCS = lcs; }
/// <summary> /// Group the extra faces in the extrusion by element id, representing clippings, recesses, and openings. /// </summary> /// <param name="elem">The element generating the base extrusion.</param> /// <param name="analyzer">The extrusion analyzer.</param> /// <returns>A list of connected faces for each element id that cuts the extrusion</returns> public static IDictionary<ElementId, ICollection<ICollection<Face>>> GetCuttingElementFaces(Element elem, ExtrusionAnalyzer analyzer) { IDictionary<ElementId, HashSet<Face>> cuttingElementFaces = new Dictionary<ElementId, HashSet<Face>>(); IDictionary<Face, ExtrusionAnalyzerFaceAlignment> allFaces = analyzer.CalculateFaceAlignment(); foreach (KeyValuePair<Face, ExtrusionAnalyzerFaceAlignment> currFace in allFaces) { if (currFace.Value == ExtrusionAnalyzerFaceAlignment.FullyAligned) continue; EdgeArrayArray faceEdges = currFace.Key.EdgeLoops; int numBoundaries = faceEdges.Size; if (numBoundaries == 0) continue; if (numBoundaries > 1) throw new Exception("Can't handle faces with interior boundaries."); ICollection<ElementId> generatingElementIds = elem.GetGeneratingElementIds(currFace.Key); foreach (ElementId generatingElementId in generatingElementIds) { HashSet<Face> elementFaces; if (cuttingElementFaces.ContainsKey(generatingElementId)) { elementFaces = cuttingElementFaces[generatingElementId]; } else { elementFaces = new HashSet<Face>(); cuttingElementFaces[generatingElementId] = elementFaces; } elementFaces.Add(currFace.Key); } } IDictionary<ElementId, ICollection<ICollection<Face>>> cuttingElementFaceCollections = new Dictionary<ElementId, ICollection<ICollection<Face>>>(); foreach (KeyValuePair<ElementId, HashSet<Face>> cuttingElementFaceCollection in cuttingElementFaces) { ICollection<ICollection<Face>> faceCollections = new List<ICollection<Face>>(); // Split into separate collections based on connectivity. while (cuttingElementFaceCollection.Value.Count > 0) { IList<Face> currCollection = new List<Face>(); IEnumerator<Face> cuttingElementFaceCollectionEnumerator = cuttingElementFaceCollection.Value.GetEnumerator(); cuttingElementFaceCollectionEnumerator.MoveNext(); Face currFace = cuttingElementFaceCollectionEnumerator.Current; currCollection.Add(currFace); cuttingElementFaceCollection.Value.Remove(currFace); IList<Face> facesToProcess = new List<Face>(); facesToProcess.Add(currFace); if (cuttingElementFaceCollection.Value.Count > 0) { while (facesToProcess.Count > 0) { currFace = facesToProcess[0]; EdgeArray faceOuterBoundary = currFace.EdgeLoops.get_Item(0); foreach (Edge edge in faceOuterBoundary) { Face adjoiningFace = edge.get_Face(1); if (adjoiningFace.Equals(currFace)) adjoiningFace = edge.get_Face(0); if (cuttingElementFaceCollection.Value.Contains(adjoiningFace)) { currCollection.Add(adjoiningFace); cuttingElementFaceCollection.Value.Remove(adjoiningFace); facesToProcess.Add(adjoiningFace); } } facesToProcess.Remove(facesToProcess[0]); } } faceCollections.Add(currCollection); } cuttingElementFaceCollections[cuttingElementFaceCollection.Key] = faceCollections; } return cuttingElementFaceCollections; }
/// <summary> /// Retrieve all plan view boundary loops from /// all solids of the given element geometry /// united together. /// </summary> internal static JtLoops GetPlanViewBoundaryLoopsGeo( Autodesk.Revit.Creation.Application creapp, GeometryElement geo, ref int nFailures) { Solid union = null; Plane plane = new Plane(XYZ.BasisX, XYZ.BasisY, XYZ.Zero); foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (null != solid && 0 < solid.Faces.Size) { // Some solids, e.g. in the standard // content 'Furniture Chair - Office' // cause an extrusion analyser failure, // so skip adding those. try { ExtrusionAnalyzer extrusionAnalyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { solid = null; ++nFailures; } if (null != solid) { if (null == union) { union = solid; } else { try { union = BooleanOperationsUtils .ExecuteBooleanOperation(union, solid, BooleanOperationsType.Union); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { ++nFailures; } } } } } JtLoops loops = new JtLoops(1); AddLoops(creapp, loops, union, ref nFailures); return(loops); }
/// <summary> /// Generates an IFCExtrusionCreationData from ExtrusionAnalyzer results /// </summary> /// <remarks>This will be used to populate certain property sets.</remarks> /// <param name="exporterIFC">The exporter.</param> /// <param name="projDir">The projection direction of the extrusion.</param> /// <param name="analyzer">The extrusion analyzer.</param> /// <returns>The IFCExtrusionCreationData information.</returns> public static IFCExtrusionCreationData GetExtrusionCreationDataFromAnalyzer(ExporterIFC exporterIFC, XYZ projDir, ExtrusionAnalyzer analyzer) { IFCExtrusionCreationData exportBodyParams = new IFCExtrusionCreationData(); XYZ extrusionDirection = analyzer.ExtrusionDirection; double zOff = MathUtil.IsAlmostEqual(Math.Abs(projDir[2]), 1.0) ? (1.0 - Math.Abs(extrusionDirection[2])) : Math.Abs(extrusionDirection[2]); double scaledAngle = UnitUtil.ScaleAngle(MathUtil.SafeAsin(zOff)); exportBodyParams.Slope = scaledAngle; exportBodyParams.ScaledLength = UnitUtil.ScaleLength(analyzer.EndParameter - analyzer.StartParameter); exportBodyParams.ExtrusionDirection = extrusionDirection; // no opening data support yet. Face extrusionBase = analyzer.GetExtrusionBase(); if (extrusionBase == null) return null; IList<GeometryUtil.FaceBoundaryType> boundaryTypes; IList<CurveLoop> boundaries = GeometryUtil.GetFaceBoundaries(extrusionBase, XYZ.Zero, out boundaryTypes); if (boundaries.Count == 0) return null; Plane plane = null; double height = 0.0, width = 0.0; if (ExtrusionExporter.ComputeHeightWidthOfCurveLoop(boundaries[0], plane, out height, out width)) { exportBodyParams.ScaledHeight = UnitUtil.ScaleLength(height); exportBodyParams.ScaledWidth = UnitUtil.ScaleLength(width); } double area = extrusionBase.Area; if (area > 0.0) { exportBodyParams.ScaledArea = UnitUtil.ScaleArea(area); } double innerPerimeter = ExtrusionExporter.ComputeInnerPerimeterOfCurveLoops(boundaries); double outerPerimeter = ExtrusionExporter.ComputeOuterPerimeterOfCurveLoops(boundaries); if (innerPerimeter > 0.0) exportBodyParams.ScaledInnerPerimeter = UnitUtil.ScaleLength(innerPerimeter); if (outerPerimeter > 0.0) exportBodyParams.ScaledOuterPerimeter = UnitUtil.ScaleLength(outerPerimeter); return exportBodyParams; }
/// <summary> /// Group the extra faces in the extrusion by element id, representing clippings, recesses, and openings. /// </summary> /// <param name="elem">The element generating the base extrusion.</param> /// <param name="analyzer">The extrusion analyzer.</param> /// <returns>A list of connected faces for each element id that cuts the extrusion</returns> public static IDictionary <ElementId, ICollection <ICollection <Face> > > GetCuttingElementFaces(Element elem, ExtrusionAnalyzer analyzer) { IDictionary <ElementId, HashSet <Face> > cuttingElementFaces = new Dictionary <ElementId, HashSet <Face> >(); IDictionary <Face, ExtrusionAnalyzerFaceAlignment> allFaces = analyzer.CalculateFaceAlignment(); foreach (KeyValuePair <Face, ExtrusionAnalyzerFaceAlignment> currFace in allFaces) { if (currFace.Value == ExtrusionAnalyzerFaceAlignment.FullyAligned) { continue; } EdgeArrayArray faceEdges = currFace.Key.EdgeLoops; int numBoundaries = faceEdges.Size; if (numBoundaries == 0) { continue; } if (numBoundaries > 1) { throw new Exception("Can't handle faces with interior boundaries."); } ICollection <ElementId> generatingElementIds = elem.GetGeneratingElementIds(currFace.Key); foreach (ElementId generatingElementId in generatingElementIds) { HashSet <Face> elementFaces; if (cuttingElementFaces.ContainsKey(generatingElementId)) { elementFaces = cuttingElementFaces[generatingElementId]; } else { elementFaces = new HashSet <Face>(); cuttingElementFaces[generatingElementId] = elementFaces; } elementFaces.Add(currFace.Key); } } IDictionary <ElementId, ICollection <ICollection <Face> > > cuttingElementFaceCollections = new Dictionary <ElementId, ICollection <ICollection <Face> > >(); foreach (KeyValuePair <ElementId, HashSet <Face> > cuttingElementFaceCollection in cuttingElementFaces) { ICollection <ICollection <Face> > faceCollections = new List <ICollection <Face> >(); // Split into separate collections based on connectivity. while (cuttingElementFaceCollection.Value.Count > 0) { IList <Face> currCollection = new List <Face>(); IEnumerator <Face> cuttingElementFaceCollectionEnumerator = cuttingElementFaceCollection.Value.GetEnumerator(); cuttingElementFaceCollectionEnumerator.MoveNext(); Face currFace = cuttingElementFaceCollectionEnumerator.Current; currCollection.Add(currFace); cuttingElementFaceCollection.Value.Remove(currFace); IList <Face> facesToProcess = new List <Face>(); facesToProcess.Add(currFace); if (cuttingElementFaceCollection.Value.Count > 0) { while (facesToProcess.Count > 0) { EdgeArray faceOuterBoundary = facesToProcess[0].EdgeLoops.get_Item(0); foreach (Edge edge in faceOuterBoundary) { Face adjoiningFace = edge.get_Face(1); if (adjoiningFace.Equals(currFace)) { adjoiningFace = edge.get_Face(0); } if (cuttingElementFaceCollection.Value.Contains(adjoiningFace)) { currCollection.Add(adjoiningFace); cuttingElementFaceCollection.Value.Remove(adjoiningFace); facesToProcess.Add(adjoiningFace); } } facesToProcess.Remove(facesToProcess[0]); } } faceCollections.Add(currCollection); } cuttingElementFaceCollections[cuttingElementFaceCollection.Key] = faceCollections; } return(cuttingElementFaceCollections); }