一.缘起:
最近留言板里有粉丝让我写一篇关于柱子一键翻模的案例,我答应粉丝的就一定会行动,这不趁着周末赶紧码代码和测试,赶紧来分享这篇文章。
二.缘来:
~~~~~~~正文由此开始~~~~~~
说起一键翻模,咱要先理清一下思路,如何进行翻模,现在市场上的工具非常多,各种方法都有,我总结的有以下几种方法可供选择:
第一种办法比较灵活,但涉及CAD开发以及需要数据传输比较麻烦(主要是帅编的CAD开发最近刚自学,还不熟练~~哈哈哈哈哈~~~);第二种办法是适合设计部内部使用的,需要知道各个图层的命名规则,这样可以省去点选图层的步骤;第三种是这次讲解使用的方法,适合对图纸图层不太熟悉,需要手动去拾取图层的办法。
这是一张帅编自己(瞎鸡儿)画的一张柱子的CAD图,目的是了解CAD链接底图的几何元素都是什么样的。接下来我们用lookup来看一下整体结构
我就不贴全了,红框部分就是链接CAD的图纸内容,通过点击此处可以进一步查询到链接的CAD数据结构,如下图所示:
该页面展示了链接的CAD图纸的全部数据内容,和任何Revit构件元素一样,都包含了类型、属性、几何等数据内容,点击红框的几何部分可以进一步查看几何数据结构,如下图所示:
先解释绿框部分,这里是当前视图的显示模式例如精细,中等,粗略
这里主要是对于一些族来说为了表达效果,会在不同模式底下来显示不同的线条或者样式,对于链接的CAD底图来说并不存在这个问题,因此可以不用管它。点击红框部分进行下一步查看,如下图所示:
可以从上图看到有5个pl线,正好对应的是帅编链接的CAD图纸的五个柱子,每一个pl线就是一个柱子线轮廓的所有数据(所以研究方法的时候要排除干扰是最重要的,不要一上来就链接一张施工图,然后里面一堆数据,那样根本搞不清楚)
我们接下来要做的就是通过代码来拿去这些pl线的数据,Let we continue
(认识我的人都夸我英语很出色)
1.引用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Data;
using Autodesk.Revit;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.ApplicationServices;
using System.Data.SqlClient;
还记得最早的模板吗?这里还是运用的模板内容,但是有一些引用需要添加,黑色显示的部分为使用到的引用。
2.获取CAD对象
Reference r = uiDoc.Selection.PickObject(ObjectType.PointOnElement);//获取对象
string ss = r.ConvertToStableRepresentation(doc);
Element elem = doc.GetElement(r);
GeometryElement geoElem = elem.get_Geometry(new Options());//几何图元
GeometryObject geoObj = elem.GetGeometryObjectFromReference(r);//几何对象
用来获取CAD链接的对象,可以通过点击拾取的方式,获得单独的一根线所在的图层。
可以通过官方文档看到几何对象的整体结构包含的全部信息,包括点、线、面、几何元素、几何实例、pl线等。
3.获取所在图层信息
//获取选中的cad图层
Category targetCategory = null;
ElementId graphicsStyleId = null;
if (geoObj.GraphicsStyleId != ElementId.InvalidElementId)
{
graphicsStyleId = geoObj.GraphicsStyleId;
GraphicsStyle gs = doc.GetElement(geoObj.GraphicsStyleId) as GraphicsStyle;//获得所选对象图层
if (gs != null)
{
targetCategory = gs.GraphicsStyleCategory;//图层
var name = gs.GraphicsStyleCategory.Name;//图层名字 }
}
//隐藏选中的cad图层
if (targetCategory != null)
{
doc.ActiveView.SetVisibility(targetCategory, false);
}
通过点选获取CAD柱线所在的图层可以取得该图层的线图元及所在的图层名字,并且可以通过图元类别进行隐藏。这种好处是不需要写代码来获取链接CAD及辨别柱所在的图层名称,直接拾取可以避免很多对图纸图层的不熟悉及改变造成的麻烦。
以上获取图层名字及隐藏图层为另加,与本次案例无关,隐藏CAD图层反而不好看出翻模效果,图层名字并没有太多用处。因此这两个功能只是写到这就加上看一下效果,不需要该功能的粉丝可以不添加。
4.坐标转换(敲黑板,划重点,重点!)
GeometryInstance geomInstance = gObj as GeometryInstance;
Transform transform = geomInstance.Transform;
官方文档对坐标转换的介绍如下:
GeometryInstance代表Revit在默认配置中存储的一组几何,然后由于元素的属性而转换到正确的位置。遇到GeometryInstances的最常见情况是在Family实例中。Revit使用GeometryInstances允许它存储给定族的几何图形的单个副本,并在多个实例中重复使用。请注意,并非所有Family实例都将包含GeometryInstances。当Revit需要为给定实例制作族几何的唯一副本时(由于局部连接,相交以及与实例放置相关的其他因素的影响),将不会遇到GeometryInstance;相反,实体几何将在层次结构的顶层找到。GeometryInstance提供了通过GetSymbolGeometry()和GetInstanceGeometry()方法读取其几何的功能。这些方法返回另一个Autodesk.Revit.DB.GeometryElement,可以像第一级返回一样对其进行解析。
其实说白了,就是当你链接CAD底图后,后面又解锁了CAD链接的底图将其移动,但是Revit记录的是你一开始自动插入的坐标点,如果不通过坐标转换,那么生成的构件会在一开始插入点的坐标,而不是你移动链接后的坐标。
可以通过lookup在这里看见坐标系统原点的区别,现在用图来进行说明:
(1)一开始点链接图纸自动原点对齐链接图纸。
(2)可以看见图纸的原点就是原点(0,0,0)并没有任何不同
(3)现在我将底图解锁移动1000个单位。
(4)咱们再来查看,会发现原点坐标出现了变化
如果不使用坐标转换,那么你创建的模型就还会在原点处,而不是在你移动后的底图位置出现。结果如下图所示:
5.获取pl线数据
if (null != geomInstance)
{
foreach (var insObj in geomInstance.SymbolGeometry)//坐标空间
{
if (insObj.GraphicsStyleId.IntegerValue != graphicsStyleId.IntegerValue)
continue;
//线类型
if (insObj.GetType().ToString() == "Autodesk.Revit.DB.PolyLine")
{
PolyLine polyLine = insObj as PolyLine;
IList<XYZ> points = polyLine.GetCoordinates();//获取坐标点
XYZ pMax = polyLine.GetOutline().MaximumPoint;
XYZ pMin = polyLine.GetOutline().MinimumPoint;
//长和宽
double b = Math.Abs(pMin.X - pMax.X);
double h = Math.Abs(pMin.Y - pMax.Y);
XYZ pp = pMax.Add(pMin) / 2;//柱子的中点坐标
pp = transform.OfPoint(pp);//坐标转换
通过线型名称对应来获得线的坐标然后通过数学转化获得柱子长宽数据及坐标原点的位置。
由于柱子可以横着放也可以竖着放,所以一开始我使用找最大最小线作为长宽来创建柱子并不好用,横放的柱子需要手动调整,因此改变了算法,直接通过最大最小值即对角线的坐标进行确定长和宽以及柱子坐标中点。
柱子横着放和竖着放无法判断谁是长和宽
6.创建柱子
private void CreatColu(Document doc, XYZ point, double b, double h)
{
FilteredElementCollector fil = new FilteredElementCollector(doc);
fil.OfClass(typeof(FamilySymbol));
string bh = CutDecimalWithN(b * 304.8, 4).ToString() + " " + "x" + " " + CutDecimalWithN(h * 304.8, 4);
List<FamilySymbol> listFa = new List<FamilySymbol>();
foreach (FamilySymbol fa in fil)
{
if (fa.GetParameters("族名称")[0].AsString() == "砼矩形柱")
{
listFa.Add(fa);
}
}
int i = 0;
bool bo = false;
int j = 0;
for (i = 0; i < listFa.Count; i++)
{
if (bh == listFa[i].Name)
{
bo = true;
j = i;
}
}
if (bo == true)
{
doc.Create.NewFamilyInstance(point, listFa[j], StructuralType.Column);
}
else
{
FamilySymbol fam = listFa[0];
ElementType coluType = fam.Duplicate(bh);
coluType.GetParameters("截面宽度")[0].Set(b);
coluType.GetParameters("截面高度")[0].Set(h);
FamilySymbol fs = coluType as FamilySymbol;
doc.Create.NewFamilyInstance(point, fs, StructuralType.Column);
}
通过创建族实例的方式来创建柱子,这里有个新知识点就是当模型里没有该柱子的类型时,需要创建新的柱子类型,通过FamilySymbol的Duplicate来进行复制添加,就和软件复制操作一样。
FamilySymbol fam = listFa[0];
ElementType coluType = fam.Duplicate(bh);
强调一下:
string bh = CutDecimalWithN(b * 304.8, 4).ToString() + " " + "x" + " " + CutDecimalWithN(h * 304.8, 4);
CutDecimalWithN这个方法是我写的一个用来截取小数点的,一般柱子都是整数,没什么用,我加这个是自己测试(瞎鸡儿)画的CAD柱子线有几个不是整数,所以加的,可以不要。
下面是全部的源代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Data;
using Autodesk.Revit;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.ApplicationServices;
using System.Data.SqlClient;
//using Teigha.Geometry;
//using Teigha.Runtime;
//using Teigha.DatabaseServices;
namespace zpxzpx
{ [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
class GenerateColu : IExternalCommand
{
public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
{
//创建应用程序对象
Autodesk.Revit.ApplicationServices.Application app = commandData.Application.Application;
//创建文档对象
Autodesk.Revit.DB.Document doc = commandData.Application.ActiveUIDocument.Document;
//创建应用程序对象
Autodesk.Revit.UI.UIApplication uiapp = commandData.Application;
//创建文档对象
Autodesk.Revit.UI.UIDocument uiDoc = uiapp.ActiveUIDocument;
//开始事务
Autodesk.Revit.DB.Transaction ts = new Autodesk.Revit.DB.Transaction(doc, "柱子翻模");
ts.Start();
Reference r = uiDoc.Selection.PickObject(ObjectType.PointOnElement);//获取对象
string ss = r.ConvertToStableRepresentation(doc);//转化为字符串
Element elem = doc.GetElement(r);
GeometryElement geoElem = elem.get_Geometry(new Options());//几何图元
GeometryObject geoObj = elem.GetGeometryObjectFromReference(r);//几何对象
//获取选中的cad图层
Category targetCategory = null;
ElementId graphicsStyleId = null;
//判断所选取的几何对象样式不为元素无效值
if (geoObj.GraphicsStyleId != ElementId.InvalidElementId)
{
graphicsStyleId = geoObj.GraphicsStyleId;
GraphicsStyle gs = doc.GetElement(geoObj.GraphicsStyleId) as GraphicsStyle;//获得所选对象图形样式
if (gs != null)
{
targetCategory = gs.GraphicsStyleCategory;//图层
var name = gs.GraphicsStyleCategory.Name;//图层名字 }
}
//隐藏选中的cad图层
if (targetCategory != null)
{
doc.ActiveView.SetVisibility(targetCategory, false);
}
CurveArray curveArray = new CurveArray();
List<double> listdb = new List<double>();
foreach (var gObj in geoElem)
{
GeometryInstance geomInstance = gObj as GeometryInstance;
//GeometryInstance代表Revit在默认配置中存储的一组几何,然后由于元素的属性而转换到正确的位置。
//遇到GeometryInstances的最常见情况是在Family实例中。Revit使用GeometryInstances允许它存储给定族的几何图形的单个副本,并在多个实例中重复使用。
//请注意,并非所有Family实例都将包含GeometryInstances。
//当Revit需要为给定实例制作族几何的唯一副本时(由于局部连接,相交以及与实例放置相关的其他因素的影响),将不会遇到GeometryInstance;
//相反,实体几何将在层次结构的顶层找到。GeometryInstance提供了通过GetSymbolGeometry()和GetInstanceGeometry()方法读取其几何的功能。
//这些方法返回另一个Autodesk.Revit.DB.GeometryElement,可以像第一级返回一样对其进行解析。
//坐标转换。如果选择的是“自动-中心到中心”,或者移动了importInstance,需要进行坐标转换
Transform transform = geomInstance.Transform;
if (null != geomInstance)
{
foreach (var insObj in geomInstance.SymbolGeometry)//坐标空间
{
if (insObj.GraphicsStyleId.IntegerValue != graphicsStyleId.IntegerValue)
continue;
//线类型
if (insObj.GetType().ToString() == "Autodesk.Revit.DB.PolyLine")
{
PolyLine polyLine = insObj as PolyLine;
IList<XYZ> points = polyLine.GetCoordinates();//获取坐标点
XYZ pMax = polyLine.GetOutline().MaximumPoint;
XYZ pMin = polyLine.GetOutline().MinimumPoint;
//长和宽
double b = Math.Abs(pMin.X - pMax.X);
double h = Math.Abs(pMin.Y - pMax.Y);
XYZ pp = pMax.Add(pMin) / 2;//柱子的中点坐标
pp = transform.OfPoint(pp);//坐标转换
CreatColu(doc, pp, b, h);//生成柱子
}
}
}
}
ts.Commit();
return Result.Succeeded;
}
//生成柱子
private void CreatColu(Document doc, XYZ point, double b, double h)
{
FilteredElementCollector fil = new FilteredElementCollector(doc);
fil.OfClass(typeof(FamilySymbol));
string bh = CutDecimalWithN(b * 304.8, 4).ToString() + " " + "x" + " " + CutDecimalWithN(h * 304.8, 4);
List<FamilySymbol> listFa = new List<FamilySymbol>();
foreach (FamilySymbol fa in fil)
{
if (fa.GetParameters("族名称")[0].AsString() == "砼矩形柱")
{
listFa.Add(fa);
}
}
int i = 0;
bool bo = false;
int j = 0;
for (i = 0; i < listFa.Count; i++)
{
if (bh == listFa[i].Name)
{
bo = true;
j = i;
}
}
if (bo == true)
{
doc.Create.NewFamilyInstance(point, listFa[j], StructuralType.Column);
}
else
{
FamilySymbol fam = listFa[0];
ElementType coluType = fam.Duplicate(bh);
coluType.GetParameters("截面宽度")[0].Set(b);
coluType.GetParameters("截面高度")[0].Set(h);
FamilySymbol fs = coluType as FamilySymbol;
doc.Create.NewFamilyInstance(point, fs, StructuralType.Column);
}
}
好了来测试一下吧:
一键翻模走起:
三维里的样子:
找了一个真实的例子,试了一下,完美到飞起。。。。。。。。
是不是超级完美
缺点:本案例无法正确生成异形柱及旋转角度的柱子。旋转角度的柱子可以使用pl线的数据算出向量角度然后修改柱子的向量角度或者算出旋转角度进行旋转。异形柱就比较麻烦需要创建族再放置到项目中才行,有时间再慢慢研究。