Revit二次开发之细说链接文件的应用(布置建筑柱外皮,楼板开洞)

标签:
365it |
分类: Revit二次开发 |
记得刚接触BIM技术的时候,那么所谓的六大专业,建筑,结构,水,暖,动,电利用Revit建模,就实行了各专业协同的建模方式,利用中心文件作为媒介,使各个专业能够链接其他专业的模型做进一步的工作,所以说到链接,最近整理了一下以前做过的项目——就是在建筑模型里链接结构模型,为结构柱放上建筑柱外皮,也就是我们常说的抹灰,一般是水泥砂浆做的,那么问题来了
如果这里有几百根结构柱需要放置外皮,那么人工来做的话则需要大量的时间,所以想到开发插件快速布置喽!
//拾取链接文档元素过滤器
public class
ColumnSelectionFilter : ISelectionFilter
{
public RevitLinkInstance instance = null;
public bool AllowElement(Element elem)
{
instance =
elem as RevitLinkInstance;
if
(instance != null)
{
return true;
}
return
false;
}
public bool AllowReference(Reference reference,
XYZ position)
{
if
(instance == null)
{
return false;
}
else
{
Document linkdocument =
instance.GetLinkDocument();
FamilyInstance familyinstance
= linkdocument.GetElement(reference.LinkedElementId) as
FamilyInstance;
//这里简单限制一下筛选条件
if
((familyinstance.Symbol.Family.Name.Contains("混凝土") &&
familyinstance.Symbol.Family.Name.Contains("柱")))
{
return true;
}
return false;
}
}
}
其实可以这样做一个转换——获得结构柱在原模型中的
底部高度和顶部高度值,然后在建筑模型里根据任意标高布置,只要设置它的底部和顶部高度值与原模型的一致即可,这样就可以省了很多事了!
public class Test : IExternalCommand
{
public Result Execute(ExternalCommandData
commandData, ref string message, ElementSet elements)
{
UIApplication uiapp = commandData.Application;
UIDocument
uidoc = uiapp.ActiveUIDocument;
Document
doc = uidoc.Document;
//首先将我们要用到的族载入到项目里 这里是用绝对路径
LoadFamily(doc);
Reference
refer = uidoc.Selection.PickObject(ObjectType.LinkedElement, new
ColumnSelectionFilter(), "请选择混凝土柱");
//首先转化为RevitLinkInstance
就是为了获取连接文件的Document,当然还可以通过其他方式获取比如过滤项目的所有文档
//如果IsLinked==true就是链接文档,这个只针对单链接文档 如果是多链接文档则要增加一些筛选条件了
//Document
linkDoc = null;
//foreach
(Document document in doc.Application.Documents)
//{
//
if (document.IsLinked
&& document.PathName.Contains("GT"))
//
{
//
linkDoc = document;
//
}
//}
RevitLinkInstance linkInstance = doc.GetElement(refer) as
RevitLinkInstance;
Document
linkDoc = linkInstance.GetLinkDocument();
//获得标准的族实例
FamilyInstance instance = linkDoc.GetElement(refer.LinkedElementId)
as FamilyInstance;
//目前获取到了
链接文件文档document也可以获得实例的各种参数
//接下来就要获取链接柱的标高和偏移量了
// 原始结构柱
顶标高
Parameter
paramTop =
instance.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM);
//顶部偏移量
Parameter
paramTopoffset =
instance.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM);
//底标高
Parameter
paramBase =
instance.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM);
//底部偏移量
Parameter
paramBaseoffset =
instance.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM);
//设置两个变量用来接收 链接柱的长宽参数值
double b =
0, h = 0;
//获得链接柱的长宽参数
GetOriginalRectangel(instance, ref b, ref
h);
//获取链接柱的当前位置坐标XYZ
XYZ xyz =
(instance.Location as LocationPoint).Point;
//到这里我们获得了链接柱的基本信息 包括截面长宽,标高和偏移量 接下来就是要设置我们要放置的建筑柱外皮族了
//首先获得对应的类型
FamilySymbol symbol = new
FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).OfCategory(BuiltInCategory.OST_Columns).Where(o
=> o.Name == "建筑柱_矩形_水泥砂浆").First() as FamilySymbol;
//获得标高,如果不用标高的话 生成的柱子的标高是只读的 这不符合我们的需求 这里获取最低的标高
Level
level = new
FilteredElementCollector(doc).OfClass(typeof(Level)).OrderBy(o
=> (o as Level).ProjectElevation).First() as Level;
//2016版要对未使用的族类型要进行激活
//这里要开启事务
因为文档要发生改变了
using
(Transaction ts = new Transaction(doc, "add"))
{
ts.Start();
if (symbol.IsActive ==
false)
{
symbol.Activate();
}
//创建实例
FamilyInstance NewInstance =
doc.Create.NewFamilyInstance(xyz, symbol, level,
StructuralType.NonStructural);
//设置长宽
最后一个参数是建筑柱外皮的厚度这里给20mm
SetRectangeWidth(NewInstance,
h, b, 20 / 304.8);
//设置标高偏移量,这里的document应该是链接文件的而不是当前项目的,因为我们要获得链接柱的信息 所以使用
linkDoc
SetColumnParameter(linkDoc,
NewInstance, paramTop, paramTopoffset, paramBase,
paramBaseoffset,level);
//设置材质 这里为了显而易见 就设置成黄色
ColumnMaterial(doc,
NewInstance, "涂料 - 黄色");
ts.Commit();
}
return
Result.Succeeded;
}
//载入建筑柱外皮族文件
public void LoadFamily(Document doc)
{
try
{
Transaction ts = new
Transaction(doc, "Load");
ts.Start();
doc.LoadFamily(@"C:\ProgramData\Autodesk\Revit\Addins\2016\BIM建筑工具\BIM\AEfamily\建筑柱_矩形_水泥砂浆.rfa");
ts.Commit();
}
catch {
}
}
//获得链接文件矩形柱的宽度,深度,我这里使用的参数为
b和h
//其实我们可以根据柱子的几何模型 获得它的顶面边的长度和宽度,但是有一点 如果这个矩形柱旋转过
还要进一步的判断
//这里只提供一种常规的做法
public void GetOriginalRectangel(FamilyInstance
instance, ref double b, ref double h)
{
foreach
(Parameter p in instance.Symbol.Parameters)
{
if (p.Definition.Name ==
"b")
{
b = p.AsDouble();
}
if (p.Definition.Name ==
"h")
{
h = p.AsDouble();
}
}
}
//设置建筑柱 矩形的长宽
public void SetRectangeWidth(FamilyInstance
familyInstance, double Deep, double Width, double deep)
{
foreach
(Parameter p in familyInstance.Parameters)
{
if (p.Definition.Name ==
"内皮深度")
{
p.Set(Deep);
foreach (Parameter p1 in
familyInstance.Parameters)
{
if
(p1.Definition.Name == "外皮深度")
{
p1.Set(deep + Deep);
break;
}
}
break;
}
}
foreach
(Parameter p in familyInstance.Parameters)
{
if (p.Definition.Name ==
"内皮宽度")
{
p.Set(Width);
foreach (Parameter p1 in
familyInstance.Parameters)
{
if
(p1.Definition.Name == "外皮宽度")
{
p1.Set(deep + Width);
break;
}
}
break;
}
}
}
//设置建筑柱外皮材质
public void ColumnMaterial(Document doc,
FamilyInstance family, string materialName)
{
Material
mate = new
FilteredElementCollector(doc).OfClass(typeof(Material)).Where(o
=> o.Name == materialName).First() as Material;
foreach
(Parameter p in family.Parameters)
{
if (p.Definition.Name ==
"柱外皮材质")
{
p.Set(mate.Id);
break;
}
}
}
//设置建筑柱的标高和偏移量
private void SetColumnParameter(Document
linkDoc, FamilyInstance family, Parameter paramTop, Parameter
paramTopffset, Parameter paramBase, Parameter paramBaseoffset,Level
level)
{
Parameter
OriginparamTop =
family.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM);
Parameter
OriginparamTopoffset =
family.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM);
Parameter
OriginparamBase =
family.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM);
Parameter
OriginparamBaseoffset =
family.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM);
OriginparamTop.Set(level.Id);
Level
leTop = linkDoc.GetElement(paramTop.AsElementId()) as Level;
OriginparamTopoffset.Set(leTop.ProjectElevation +
paramTopffset.AsDouble());
OriginparamBase.Set(level.Id);
Level
leBase = linkDoc.GetElement(paramBase.AsElementId()) as
Level;
OriginparamBaseoffset.Set(leBase.ProjectElevation +
paramBaseoffset.AsDouble());
}
//判断 链接实例是矩形柱还是圆形柱 这里简单用截面去判断,这里只做简单的循环遍历
public bool DetermineShape(FamilyInstance
instance, UIApplication app)
{
Options
option = app.Application.Create.NewGeometryOptions();
GeometryElement groElement = instance.get_Geometry(option);
foreach
(GeometryObject geoObj in groElement)
{
if (geoObj is Solid
&& geoObj != null)
{
Solid solid = geoObj as Solid;
if (solid.Faces.Size > 1)
{
foreach
(Face face in solid.Faces)
{
PlanarFace pf = face as
PlanarFace;
//判断截面形状
if (face is
CylindricalFace)
{
return true;
}
}
}
}
}
return
false;
}
}
//拾取链接文档元素过滤器
public class
ColumnSelectionFilter : ISelectionFilter
{
public RevitLinkInstance instance = null;
public bool AllowElement(Element elem)
{
instance =
elem as RevitLinkInstance;
if
(instance != null)
{
return true;
}
return
false;
}
public bool AllowReference(Reference reference,
XYZ position)
{
if
(instance == null)
{
return false;
}
else
{
Document linkdocument =
instance.GetLinkDocument();
FamilyInstance familyinstance
= linkdocument.GetElement(reference.LinkedElementId) as
FamilyInstance;
if
((familyinstance.Symbol.Family.Name.Contains("混凝土") &&
familyinstance.Symbol.Family.Name.Contains("柱")))
{
return true;
}
return false;
}
}
}
Face.Intersect(Curve, out
IntersectionResultArray)方法,当然在此之前你要获得楼板的上表面或下表面和
链接水管的Curve那么这些不是很难 如下:
//获得管道的曲线与 楼板面做碰撞相交,其实可以更简单的获取
public Curve FindPipeCurve(Pipe pipe)
{
IList list
= new List();
ConnectorSetIterator csi =
pipe.ConnectorManager.Connectors.ForwardIterator();
while
(csi.MoveNext())
{
Connector conn = csi.Current
as Connector;
list.Add(conn.Origin);
}
Curve
curve = Line.CreateBound(list.ElementAt(0), list.ElementAt(1)) as
Curve;
return
curve;
}
//获得结构楼板的上表面
public Face FindFloorFace(Floor floor)
{
Face
normalFace = null;
//Options
option = new Options();
//option.ComputeReferences = true;
//GeometryElement element = floor.get_Geometry(option);
//foreach
(GeometryObject obj in element)
//{
//
Solid solid = obj as
Solid;
//
if (solid != null &&
solid.Faces.Size > 0)
//
{
//
foreach (Face face in solid.Faces)
//
{
//
PlanarFace
pf = face as PlanarFace;
//
if (pf !=
null)
//
{
//
if (pf.ComputeNormal(new
UV(pf.Origin.X, pf.Origin.Y)).Normalize().Z == 1)
//
{
//
normalFace = face;
//
break;
//
}
//
}
//
}
//
}
//}
normalFace
= floor.GetGeometryObjectFromReference(
HostObjectUtils.GetTopFaces(floor)[0]) as Face;
return
normalFace;
}
//获得相交点
public XYZ FindXYZ(Face face, Curve curve)
{
IntersectionResultArray intersectionR = new
IntersectionResultArray();
SetComparisonResult comparisonR;
comparisonR = face.Intersect(curve, out intersectionR);
XYZ
intersectionResult = null;
if
(SetComparisonResult.Disjoint != comparisonR)
{
if
(!intersectionR.IsEmpty)
{
intersectionResult =
intersectionR.get_Item(0).XYZPoint;
}
}
return
intersectionResult;
}
使用Document.Create.NewOpening(floor, curves,
true)方法进行开洞操作,这里就不给大家贴图了,自行测试吧!
其实最大的问题是如何获得链接模型的Document 获得到它 我们就可以获得这个模型里每一个构件的参数信息
基本步骤如下:
1,首先使用LoadFamily方法将要放置的建筑柱外皮族载入到项目中(需要开启事务,当然这一步也可以排在最后):
2,其次写一个过滤器,为PickObject或PickObjects服务,这样可以直接选取链接模型的结构柱,过滤器如下:
3,通过pickobject方法
拾取到的构件引用转化为 RevitLinkInstance
进而得到链接模型的Document
4,获得这个实例的参数,如,坐标,截面深度,宽度,标高和偏移量,当然如果细致点的话我门还需要判断它是否是矩形柱还是圆形柱,我们可以根据Face
is
CylindricalFace判断,本博文暂且不阐述,下边有介绍代码,那么这里要说的就是标高,我们知道结构模型的标高和建筑模型的标高肯定是有所不同的,所以会出现这种情况——结构柱所依赖的标高,建筑模型里没有,如果我们再把结构标高复制监视一遍,那样显得即复杂又琐碎。
5,那就是根据获得所有信息,传递给我们要放置的建筑外皮,实行参数一对一赋值,则大功告成!
代码步骤如下:
http://s12/mw690/006AhjOXzy75sMFS5gT8b&690
http://s13/mw690/006AhjOXzy75sMFYaq8ac&690
如果使用PickObjects或直接整项目过滤结构柱,则可以实现批量布置,如下图
http://s10/mw690/006AhjOXzy75sMG18Bb39&690
http://s13/mw690/006AhjOXzy75sMFYaq8ac&690
如果使用PickObjects或直接整项目过滤结构柱,则可以实现批量布置,如下图
http://s10/mw690/006AhjOXzy75sMG18Bb39&690
那么根据链接模型对结构楼板开洞
其实道理也是一样的,不管是给排水,暖通,动力,电气都好,使用的方法都是一样的,这里就不写的太详细了 说说步骤吧!
1,一样的我们要获得链接模型的Document,获得链接里的水管也好,风管也罢,获得他们的信息,长宽或者公称直径就是获得参数呗
2,那水管为例,如果这里只做垂直穿过楼板的,这需要获得水管的中心线和楼板上表面的交点可以使用
3,获得线和面的交点 也就是开洞的圆心,代码如下:
4,那就是根据水管的公称直径对楼板进行开洞了,当然给楼板开洞时要考虑给套管留有足够的空间。比如公称直径为100mm那么就要考虑开125mm的洞了
当然要按照标准来的 这里只是说说而已
后一篇:Revit二次开发之坐标转换