记得刚接触BIM技术的时候,那么所谓的六大专业,建筑,结构,水,暖,动,电利用Revit建模,就实行了各专业协同的建模方式,利用中心文件作为媒介,使各个专业能够链接其他专业的模型做进一步的工作,所以说到链接,最近整理了一下以前做过的项目——就是在建筑模型里链接结构模型,为结构柱放上建筑柱外皮,也就是我们常说的抹灰,一般是水泥砂浆做的,那么问题来了 如果这里有几百根结构柱需要放置外皮,那么人工来做的话则需要大量的时间,所以想到开发插件快速布置喽!
其实最大的问题是如何获得链接模型的Document 获得到它 我们就可以获得这个模型里每一个构件的参数信息
基本步骤如下:
1,首先使用LoadFamily方法将要放置的建筑柱外皮族载入到项目中(需要开启事务,当然这一步也可以排在最后):
2,其次写一个过滤器,为PickObject或PickObjects服务,这样可以直接选取链接模型的结构柱,过滤器如下:
//拾取链接文档元素过滤器
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;
}
}
}
3,通过pickobject方法 拾取到的构件引用转化为 RevitLinkInstance 进而得到链接模型的Document
4,获得这个实例的参数,如,坐标,截面深度,宽度,标高和偏移量,当然如果细致点的话我门还需要判断它是否是矩形柱还是圆形柱,我们可以根据Face is CylindricalFace判断,本博文暂且不阐述,下边有介绍代码,那么这里要说的就是标高,我们知道结构模型的标高和建筑模型的标高肯定是有所不同的,所以会出现这种情况——结构柱所依赖的标高,建筑模型里没有,如果我们再把结构标高复制监视一遍,那样显得即复杂又琐碎。
其实可以这样做一个转换——获得结构柱在原模型中的 底部高度和顶部高度值,然后在建筑模型里根据任意标高布置,只要设置它的底部和顶部高度值与原模型的一致即可,这样就可以省了很多事了!
5,那就是根据获得所有信息,传递给我们要放置的建筑外皮,实行参数一对一赋值,则大功告成!
代码步骤如下:
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;
}
}
}
如果使用PickObjects或直接整项目过滤结构柱,则可以实现批量布置,如下图
那么根据链接模型对结构楼板开洞 其实道理也是一样的,不管是给排水,暖通,动力,电气都好,使用的方法都是一样的,这里就不写的太详细了 说说步骤吧!
1,一样的我们要获得链接模型的Document,获得链接里的水管也好,风管也罢,获得他们的信息,长宽或者公称直径就是获得参数呗
2,那水管为例,如果这里只做垂直穿过楼板的,这需要获得水管的中心线和楼板上表面的交点可以使用
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;
}
3,获得线和面的交点 也就是开洞的圆心,代码如下:
//获得相交点
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;
}
4,那就是根据水管的公称直径对楼板进行开洞了,当然给楼板开洞时要考虑给套管留有足够的空间。比如公称直径为100mm那么就要考虑开125mm的洞了 当然要按照标准来的 这里只是说说而已
使用Document.Create.NewOpening(floor, curves, true)方法进行开洞操作,这里就不给大家贴图了,自行测试吧!