06.Revit2016二次开发(提高篇)—— 一键生成地板

经过前几章的对API的摸索之后,现在我决定尝试一下具有挑战性的一些小案例了;
一键生成地板,这个功能其实在2016SDK的Sample中有类似的代码样例供参考;
但还是先慢慢来写实现的步骤,并对之前的文章当做总结:

一、首先获取文档;

这一般是基本的操作了,这里就不赘述了;

二、在Revit的项目中选择元素,并过滤墙;

我们需要先在Revit的项目中选择一大片的区域,然后再对这些区域进行过滤,把墙给过滤出来。
我参考了一下样例,也学到了一些技巧,让整个代码变得非常干净整洁:

///过滤器:
static private ElementSet WallFilter(ElementSet miscellanea)
        {
            ElementSet walls = new ElementSet();
            foreach (Element e in miscellanea)
            {
                Wall w = e as Wall;
                if (null != w)
                {
                    walls.Insert(w);
                }
            }

            if (0 == walls.Size)
            {
                throw new InvalidOperationException("请先选择墙。");
            }

            return walls;
        }

分析:

  • 样例代码的做法是把过滤器当做单独的一个方法,写在程序入口之外;如果想要使用过滤器,你直接用这个方法就行了,这样做让主程序非常干净,并且方便修改;
  • 在过滤器使用的最后,判断最终过滤的内容中是否有元素;要是没有元素,则说明用户没有选择目标元素;这个时候可以使用抛出异常的语句;

那么主程序是如何抓住这个异常的呢?没错,就是用try...catch语句;
主程序这个时候应该这样写:


        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            //正常部分:
            try
           {
            //获取文档
            UIApplication uiApplication = commandData.Application;
            Application application = uiApplication.Application;
            UIDocument uiDocument = uiApplication.ActiveUIDocument;
            Document doc = uiDocument.Document;

            //选择元素
            ElementSet selections = new ElementSet();
            foreach (ElementId elementId in commandData.Application.ActiveUIDocument.Selection.GetElementIds())
            {
                selections.Insert(commandData.Application.ActiveUIDocument.Document.GetElement(elementId));
            }
            
            //过滤墙
            ElementSet walls = WallFilter(selections);
            return Result.Succeeded;

           }

            //异常部分:
            catch (Exception e)
            {
                message = e.Message;
                return Autodesk.Revit.UI.Result.Failed;
            }

        }

**请注意try...catch中的内容: **try中是程序正常执行的语句,我们可以看到,try最后一行是return Result.successed;理所当然,catch中都是处理异常情况,把异常的信息告诉message并最后设置成 return Result.Failed;

到这里就已经实现了:如果你没有选择任何元素或者所选元素中没有墙,那么就会弹出错误消息;如果选中,程序会成功运行结束;结果如图:

06.Revit2016二次开发(提高篇)—— 一键生成地板_第1张图片
没有选中元素就运行程序

06.Revit2016二次开发(提高篇)—— 一键生成地板_第2张图片
正常运行

三、提取墙的轮廓线,判断墙是封闭的

单单过滤出墙已经是成功了一小步了;但现在要做的是最关键的一大步;因为判断是否封闭涉及到算法。
这个过程分成两步:

  • 1.提取墙的轮廓,具体代码如下:
private void ObtainProfile(ElementSet walls)
      {
         CurveArray temp = new CurveArray();
         foreach (Wall w in walls)
         {
            LocationCurve curve = w.Location as LocationCurve;
            temp.Append(curve.Curve);
         }
        //将轮廓连接起来的方法(后面再介绍)
         SortCurves(temp);
      }

分析:在Revit API中有专门用来存放线的数组的类CurveArray;
函数传入ElementSet类型的对象,对象里面都是墙(Wall类型);Wall Class的属性之一有Location(属于Location Class),而LocationCurve Class是Location Class的派生类;因此可以使用as进行转换(见《在C#中使用as进行类型转换》);

06.Revit2016二次开发(提高篇)—— 一键生成地板_第3张图片
继承关系图

到这里为止,temp内就全部都是墙的轮廓线了;

  • 2.将墙的轮廓连接起来,方法:SortCurves()
 private void SortCurves(CurveArray lines)
        {
            Autodesk.Revit.DB.XYZ temp = lines.get_Item(0).GetEndPoint(1);
            Curve temCurve = lines.get_Item(0);

            //Profile是自定义的CurveArray;就相当于CurveArray Profile = new CurveArray();
            Profile.Append(temCurve);

            while (Profile.Size != lines.Size) //遍历所有的线;
            {

                //调用GetNext,作用是为当前的曲线获得连接线;
                temCurve = GetNext(lines, temp, temCurve);

                if (Math.Abs(temp.X - temCurve.GetEndPoint(0).X) < PRECISION
                    && Math.Abs(temp.Y - temCurve.GetEndPoint(0).Y) < PRECISION)
                {
                    temp = temCurve.GetEndPoint(1);
                }
                else
                {
                    temp = temCurve.GetEndPoint(0);
                }

                Profile.Append(temCurve);
            }

            if (Math.Abs(temp.X - lines.get_Item(0).GetEndPoint(0).X) > PRECISION
                || Math.Abs(temp.Y - lines.get_Item(0).GetEndPoint(0).Y) > PRECISION
                || Math.Abs(temp.Z - lines.get_Item(0).GetEndPoint(0).Z) > PRECISION)
            {
                throw new InvalidOperationException("选择的墙必须是封闭的!");
            }
        }

再来看看GetNext函数做了什么:

  private Curve GetNext(CurveArray profile, Autodesk.Revit.DB.XYZ connected, Curve line)
      {
         foreach (Curve c in profile)
         {
            if (c.Equals(line))
            {
               continue;
            }
            //0表示起点,1表示终点;
            if ((Math.Abs(c.GetEndPoint(0).X - line.GetEndPoint(1).X) < PRECISION && Math.Abs(c.GetEndPoint(0).Y - line.GetEndPoint(1).Y) < PRECISION && Math.Abs(c.GetEndPoint(0).Z - line.GetEndPoint(1).Z) < PRECISION)
                && (Math.Abs(c.GetEndPoint(1).X - line.GetEndPoint(0).X) < PRECISION && Math.Abs(c.GetEndPoint(1).Y - line.GetEndPoint(0).Y) < PRECISION && Math.Abs(c.GetEndPoint(1).Z - line.GetEndPoint(0).Z) < PRECISION)
                && 2 != profile.Size)
            {
               continue;
            }

            if (Math.Abs(c.GetEndPoint(0).X - connected.X) < PRECISION && Math.Abs(c.GetEndPoint(0).Y - connected.Y) < PRECISION && Math.Abs(c.GetEndPoint(0).Z - connected.Z) < PRECISION)
            {
               return c;
            }
            else if (Math.Abs(c.GetEndPoint(1).X - connected.X) < PRECISION && Math.Abs(c.GetEndPoint(1).Y - connected.Y) < PRECISION && Math.Abs(c.GetEndPoint(1).Z - connected.Z) < PRECISION)
            {
               if (c.GetType().Name.Equals("Line"))
               {
                  Autodesk.Revit.DB.XYZ start = c.GetEndPoint(1);
                  Autodesk.Revit.DB.XYZ end = c.GetEndPoint(0);
                  return Line.CreateBound(start, end);
               }
               else if (c.GetType().Name.Equals("Arc"))
               {
                  int size = c.Tessellate().Count;
                  Autodesk.Revit.DB.XYZ start = c.Tessellate()[0];
                  Autodesk.Revit.DB.XYZ middle = c.Tessellate()[size / 2];
                  Autodesk.Revit.DB.XYZ end = c.Tessellate()[size];

                  return Arc.Create(start, end, middle);
               }
            }
         }

         throw new InvalidOperationException("选择的墙必须是封闭的!");
      }

(未完待续。。。)

你可能感兴趣的:(06.Revit2016二次开发(提高篇)—— 一键生成地板)