应用程序:FindColumns

Revit 平台:所有版本

Revit 版本:2011.0

首次发布版本:2011.0

编程语言:C#

技能级别:高级

类别:几何

类型:外部命令

主题:嵌入在墙体中的柱子

摘要:

本示例演示如何使用 Revit API 中的 FindReferencesWithContextByDirection 方法查找嵌入在墙体中的柱子。

相关类:

Autodesk.Revit.DB.FilteredElementCollector

Autodesk.Revit.DB.FilteredElementIterator

Autodesk.Revit.DB.Transform

Autodesk.Revit.DB.XYZ

Autodesk.Revit.DB.Document

Autodesk.Revit.UI.Selection.Selection

项目文件:

描述:

1. 确定需要处理的墙壁(如果没有选择,则是所有墙壁)

2. 查找嵌入在每个墙壁中的柱子

   a. 对于线性墙壁

      i. 将射线的高度设为在墙壁水平面以上1英尺

      ii. 找到与墙壁位置曲线切线方向的方向

      iii. 由于墙壁的LocationCurve始终是墙壁中心线,使用墙壁宽度确定通过FindReferencesWithContextByDirection发射“射线”的偏移量,使其刚好到达墙的外侧

      iv. 从一端延伸到另一端运行FindReferencesWithContextByDirection查找返回对象的引用

      v. 检查每个引用以验证它是否是距离FindReferencesWithContextByDirection使用的原点允许的距离内的柱子(不希望超出墙的末端的柱子)

   b. 对于非线性墙壁

      i. 与线性墙壁不同,需要通过将非线性墙壁划分为一系列较短的直线段来近似该墙壁。

      ii. 计算将用于这些短线段长度的增量

      iii. 使用上述线性墙壁的方法检查每个墙壁的每个增量片段上的柱子。

   c. 选择结果柱子

说明:

1. 打开文件FindColumns-Basic.rvt FindColumns-TestCases.rvt

2. 运行外部命令“在墙中查找嵌入的柱子”

3. 如果在运行命令之前选择了一个或多个墙,则Revit将选择嵌入在所选墙中的柱子。

4. 如果在运行命令之前未选择任何墙,则Revit将选择嵌入在任何墙中的所有柱子。

源代码:

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

FindColumns.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Diagnostics;

using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;

namespace Revit.SDK.Samples.FindColumns.CS
{
    /// <summary>
    /// Find all walls that have embedded columns in them, and the ids of those embedded columns.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.ReadOnly)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    public class Command : IExternalCommand
    {
        #region Class Members
        /// <summary>
        /// This is the increment by which the code checks for embedded columns on a curved wall.
        /// </summary>
        private static double WallIncrement = 0.5;  // Check every 1/2'

        /// <summary>
        /// This is a slight offset to ensure that the ray-trace occurs just outside the extents of the wall.
        /// </summary>
        private static double WALL_EPSILON = (1.0 / 8.0) / 12.0;  // 1/8"

        /// <summary>
        /// Dictionary of columns and walls
        /// </summary>
        private Dictionary<ElementId, List<ElementId>> m_columnsOnWall = new Dictionary<ElementId, List<ElementId>>();

        /// <summary>
        /// ElementId list for columns which are on walls
        /// </summary>
        private List<ElementId> m_allColumnsOnWalls = new List<ElementId>();

        /// <summary>
        /// revit application
        /// </summary>
        private Autodesk.Revit.UI.UIApplication m_app;

        /// <summary>
        /// Revit active document
        /// </summary>
        private Autodesk.Revit.DB.Document m_doc;

        /// <summary>
        /// A 3d view 
        /// </summary>
        private View3D m_view3D;
        #endregion

        #region Class Interface Implementation
        /// <summary>
        /// The top level command.
        /// </summary>
        /// <param name="revit">An object that is passed to the external application 
        /// which contains data related to the command, 
        /// such as the application object and active view.</param>
        /// <param name="message">A message that can be set by the external application 
        /// which will be displayed if a failure or cancellation is returned by 
        /// the external command.</param>
        /// <param name="elements">A set of elements to which the external application 
        /// can add elements that are to be highlighted in case of failure or cancellation.</param>
        /// <returns>Return the status of the external command. 
        /// A result of Succeeded means that the API external method functioned as expected. 
        /// Cancelled can be used to signify that the user cancelled the external operation 
        /// at some point. Failure should be returned if the application is unable to proceed with 
        /// the operation.</returns>
        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                                             ref string message,
                                                             Autodesk.Revit.DB.ElementSet elements)
        {
            // Initialization 
            m_app = revit.Application;
            m_doc = revit.Application.ActiveUIDocument.Document;

            // Find a 3D view to use for the ray tracing operation
            Get3DView("{3D}");

            Selection selection = revit.Application.ActiveUIDocument.Selection;
            List<Wall> wallsToCheck = new List<Wall>();

            // If wall(s) are selected, process them.
            if (selection.GetElementIds().Count > 0)
            {
                foreach (Autodesk.Revit.DB.ElementId eId in selection.GetElementIds())
                {
                   Autodesk.Revit.DB.Element e = revit.Application.ActiveUIDocument.Document.GetElement(eId);
                    if (e is Wall)
                    {
                        wallsToCheck.Add((Wall)e);
                    }
                }

                if (wallsToCheck.Count <= 0)
                {
                    message = "No walls were found in the active document selection";
                    return Result.Cancelled;
                }
            }
            // Find all walls in the document and process them.
            else
            {
                FilteredElementCollector collector = new FilteredElementCollector(m_doc);
                FilteredElementIterator iter = collector.OfClass(typeof(Wall)).GetElementIterator();
                while (iter.MoveNext())
                {
                    wallsToCheck.Add((Wall)iter.Current);
                }
            }

            // Execute the check for embedded columns
            CheckWallsForEmbeddedColumns(wallsToCheck);

            // Process the results, in this case set the active selection to contain all embedded columns
            ICollection<ElementId> toSelected = new List<ElementId>(); 
            if (m_allColumnsOnWalls.Count > 0)
            {
                foreach (ElementId id in m_allColumnsOnWalls)
                {
                    ElementId familyInstanceId = id;
                    Autodesk.Revit.DB.Element familyInstance = m_doc.GetElement(familyInstanceId);
                    toSelected.Add(familyInstance.Id);
                }
                selection.SetElementIds(toSelected); 
            }
            return Result.Succeeded;
        }
        #endregion

        #region Class Implementation
        /// <summary>
        /// Check a list of walls for embedded columns.
        /// </summary>
        /// <param name="wallsToCheck">The list of walls to check.</param>
        private void CheckWallsForEmbeddedColumns(List<Wall> wallsToCheck)
        {
            foreach (Wall wall in wallsToCheck)
            {
                CheckWallForEmbeddedColumns(wall);
            }
        }

        /// <summary>
        /// Checks a single wall for embedded columns.
        /// </summary>
        /// <param name="wall">The wall to check.</param>
        private void CheckWallForEmbeddedColumns(Wall wall)
        {
            LocationCurve locationCurve = wall.Location as LocationCurve;
            Curve wallCurve = locationCurve.Curve;
            if (wallCurve is Line)
            {
                LogWallCurve((Line)wallCurve);
                CheckLinearWallForEmbeddedColumns(wall, locationCurve, (Line)wallCurve);
            }
            else
            {
                CheckProfiledWallForEmbeddedColumns(wall, locationCurve, wallCurve);
            }
        }

        /// <summary>
        /// Checks a single linear wall for embedded columns.
        /// </summary>
        /// <param name="wall">The wall to check.</param>
        /// <param name="locationCurve">The location curve extracted from this wall.</param>
        /// <param name="wallCurve">The profile of the wall.</param>
        private void CheckLinearWallForEmbeddedColumns(Wall wall, LocationCurve locationCurve, Curve wallCurve)
        {
            double bottomHeight = GetElevationForRay(wall);

            FindColumnsOnEitherSideOfWall(wall, locationCurve, wallCurve, 0, bottomHeight, wallCurve.Length);
        }

        /// <summary>
        /// Finds columns on either side of the given wall.
        /// </summary>
        /// <param name="wall">The wall.</param>
        /// <param name="locationCurve">The location curve of the wall.</param>
        /// <param name="wallCurve">The profile of the wall.</param>
        /// <param name="parameter">The normalized parameter along the wall profile which is being evaluated.</param>
        /// <param name="elevation">The elevation at which the rays are cast.</param>
        /// <param name="within">The maximum distance away that columns may be found.</param>
        private void FindColumnsOnEitherSideOfWall(Wall wall, LocationCurve locationCurve, Curve wallCurve, double parameter, double elevation, double within)
        {
            XYZ rayDirection = GetTangentAt(wallCurve, parameter);
            XYZ wallLocation = wallCurve.Evaluate(parameter, true);

            XYZ wallDelta = GetWallDeltaAt(wall, locationCurve, parameter);

            XYZ rayStart = new XYZ(wallLocation.X + wallDelta.X, wallLocation.Y + wallDelta.Y, elevation);
            FindColumnsByDirection(rayStart, rayDirection, within, wall);

            rayStart = new XYZ(wallLocation.X - wallDelta.X, wallLocation.Y - wallDelta.Y, elevation);
            FindColumnsByDirection(rayStart, rayDirection, within, wall);
        }

        /// <summary>
        /// Finds columns by projecting rays along a given direction.
        /// </summary>
        /// <param name="rayStart">The origin of the ray.</param>
        /// <param name="rayDirection">The direction of the ray.</param>
        /// <param name="within">The maximum distance away that columns may be found.</param>
        /// <param name="wall">The wall that this search is associated with.</param>
        private void FindColumnsByDirection(XYZ rayStart, XYZ rayDirection, double within, Wall wall)
        {
            ReferenceIntersector referenceIntersector = new ReferenceIntersector(m_view3D);
            IList<ReferenceWithContext> intersectedReferences = referenceIntersector.Find(rayStart, rayDirection);
            FindColumnsWithin(intersectedReferences, within, wall);
        }

        /// <summary>
        /// Checks a single curved/profiled wall for embedded columns.
        /// </summary>
        /// <param name="wall">The wall to check.</param>
        /// <param name="locationCurve">The location curve extracted from this wall.</param>
        /// <param name="wallCurve">The profile of the wall.</param>
        private void CheckProfiledWallForEmbeddedColumns(Wall wall, LocationCurve locationCurve, Curve wallCurve)
        {
            double bottomHeight = GetElevationForRay(wall);

            // Figure out the increment for the normalized parameter based on how long the wall is.  
            double parameterIncrement = WallIncrement / wallCurve.Length;

            // Find columns within 2' of the start of the ray.  Any smaller, and you run the risk of not finding a boundary
            // face of the column within the target range.
            double findColumnWithin = 2;

            // check for columns along every WallIncrement fraction of the wall
            for (double parameter = 0; parameter < 1.0; parameter += parameterIncrement)
            {
                FindColumnsOnEitherSideOfWall(wall, locationCurve, wallCurve, parameter, bottomHeight, findColumnWithin);
            }

        }

        /// <summary>
        /// Obtains the elevation for ray casting evaluation for a given wall.
        /// </summary>
        /// <param name="wall">The wall.</param>
        /// <returns>The elevation.</returns>
        private double GetElevationForRay(Wall wall)
        {
            Level level = m_doc.GetElement(wall.LevelId) as Level;

            // Start at 1 foot above the bottom level
            double bottomHeight = level.Elevation + 1.0;

            return bottomHeight;
        }

        /// <summary>
        /// Obtains the offset to the wall at a given location along the wall's profile.
        /// </summary>
        /// <param name="wall">The wall.</param>
        /// <param name="locationCurve">The location curve of the wall.</param>
        /// <param name="parameter">The normalized parameter along the location curve of the wall.</param>
        /// <returns>An XY vector representing the offset from the wall centerline.</returns>
        private XYZ GetWallDeltaAt(Wall wall, LocationCurve locationCurve, double parameter)
        {
            XYZ wallNormal = GetNormalToWallAt(wall, locationCurve, parameter);
            double wallWidth = wall.Width;

            // The LocationCurve is always the wall centerline, regardless of the setting for the wall Location Line.
            // So the delta to place the ray just outside the wall extents is always 1/2 the wall width + a little extra.
            XYZ wallDelta = new XYZ(wallNormal.X * wallWidth / 2 + WALL_EPSILON, wallNormal.Y * wallWidth / 2 + WALL_EPSILON, 0);

            return wallDelta;
        }

        /// <summary>
        /// Finds column elements which occur within a given set of references within the designated proximity, and stores them to the results. 
        /// </summary>
        /// <param name="references">The references obtained from FindReferencesByDirection()</param> 
        /// <param name="proximity">The maximum proximity.</param>
        /// <param name="wall">The wall from which these references were found.</param>
        private void FindColumnsWithin(IList<ReferenceWithContext> references, double proximity, Wall wall)
        {
            foreach (ReferenceWithContext reference in references)
            {
                // Exclude items too far from the start point.
                if (reference.Proximity < proximity)
                {
                    Autodesk.Revit.DB.Element referenceElement = wall.Document.GetElement(reference.GetReference());
                    if (referenceElement is FamilyInstance)
                    {
                        FamilyInstance familyInstance = (FamilyInstance)referenceElement;
                        ElementId familyInstanceId = familyInstance.Id;
                        ElementId wallId = wall.Id;
                        int categoryIdValue = referenceElement.Category.Id.IntegerValue;
                        if (categoryIdValue == (int)BuiltInCategory.OST_Columns || categoryIdValue == (int)BuiltInCategory.OST_StructuralColumns)
                        {
                            // Add the column to the map of wall->columns
                            if (m_columnsOnWall.ContainsKey(wallId))
                            {
                                List<ElementId> columnsOnWall = m_columnsOnWall[wallId];
                                if (!columnsOnWall.Contains(familyInstanceId))
                                    columnsOnWall.Add(familyInstanceId);
                            }
                            else
                            {
                                List<ElementId> columnsOnWall = new List<ElementId>();
                                columnsOnWall.Add(familyInstanceId);
                                m_columnsOnWall.Add(wallId, columnsOnWall);
                            }
                            // Add the column to the complete list of all embedded columns
                            if (!m_allColumnsOnWalls.Contains(familyInstanceId))
                                m_allColumnsOnWalls.Add(familyInstanceId);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Obtains the tangent of the given curve at the given parameter.
        /// </summary>
        /// <param name="curve">The curve.</param>
        /// <param name="parameter">The normalized parameter.</param>
        /// <returns>The normalized tangent vector.</returns>
        private XYZ GetTangentAt(Curve curve, double parameter)
        {
            Transform t = curve.ComputeDerivatives(parameter, true);
            // BasisX is the tangent vector of the curve.
            return t.BasisX.Normalize();
        }

        /// <summary>
        /// Finds the normal to the wall centerline at the given parameter.
        /// </summary>
        /// <param name="wall">The wall.</param>
        /// <param name="curve">The location curve of the wall.</param>
        /// <param name="parameter">The normalized parameter.</param>
        /// <returns>The normalized normal vector.</returns>
        private XYZ GetNormalToWallAt(Wall wall, LocationCurve curve, double parameter)
        {
            Curve wallCurve = curve.Curve;

            // There is no normal at a given point for a line.  We need to get the normal based on the tangent of the wall location curve.
            if (wallCurve is Line)
            {
                XYZ wallDirection = GetTangentAt(wallCurve, 0);
                XYZ wallNormal = new XYZ(wallDirection.Y, wallDirection.X, 0);
                return wallNormal;
            }
            else
            {
                Transform t = wallCurve.ComputeDerivatives(parameter, true);
                // For non-linear curves, BasisY is the normal vector to the curve.
                return t.BasisY.Normalize();
            }
        }

        /// <summary>
        /// Dump wall's curve(end points) to log
        /// </summary>
        /// <param name="wallCurve">Wall curve to be dumped.</param>
        private void LogWallCurve(Line wallCurve)
        {
            Debug.WriteLine("Wall curve is line: ");

            Debug.WriteLine("Start point: " + XYZToString(wallCurve.GetEndPoint(0)));
            Debug.WriteLine("End point: " + XYZToString(wallCurve.GetEndPoint(1)));
        }

        /// <summary>
        /// Format XYZ to string 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private String XYZToString(XYZ point)
        {
            return "( " + point.X + ", " + point.Y + ", " + point.Z + ")";
        }

        /// <summary>
        /// Get a 3D view from active document
        /// </summary>
        private void Get3DView(string viewName)
        {
            FilteredElementCollector collector = new FilteredElementCollector(m_app.ActiveUIDocument.Document);
            foreach (Autodesk.Revit.DB.View3D v in collector.OfClass(typeof(View3D)).ToElements())
            {
                // skip view template here because view templates are invisible in project browsers
                if (v != null && !v.IsTemplate && v.Name == viewName)
                {
                    m_view3D = v as Autodesk.Revit.DB.View3D;
                    break;
                }
            }
        }
        #endregion
    }
}