此功能要实现 管线与构筑物(墙、梁、楼板、天花板、屋顶)交点开洞,并且要把管线与洞口关联起来。
首先是开洞,以墙为例
大致思路:1、得到墙体的水平面
2、得到管线与墙面相交的两点,并计算出中点
3、在中点创建洞口族并执行剪切
private void OpeningHole(Element wall,Curve line)
{
//得到墙的面
List<Face> lstFace = Get_ElementFace(wall);
//得到管线与墙的交点
List<XYZ> lstIntersection = Get_Intersection(line, lstFace);
//计算中心点
XYZ ptIntersection = (lstIntersection[0] + lstIntersection[1]) / 2
Transaction trans = new Transaction(doc, "opening");
trans.Start();
try
{
//创建洞口
FamilyInstance instance = doc.Create.NewFamilyInstance(ptIntersection, fs, StructuralType.NonStructural);
//执行剪切
InstanceVoidCutUtils.AddInstanceVoidCut(doc, wall, instance);
trans.Commit();
}
catch
{
trans.RollBack();
}
}
/// <summary>
/// 找到宿主的面
/// </summary>
public static List<Face> Get_ElementFace(Element element)
{
Options opt = new Options();
opt.ComputeReferences = true;
opt.DetailLevel = ViewDetailLevel.Fine;
GeometryElement geometryElement = element.get_Geometry(opt);
Face normalFace = null;
List<Face> lstFace = new List<Face>();
foreach (var go in geometryElement)
{
if(go is Solid)
{
Solid solid = go as Solid;
if (solid != null && solid.Faces.Size > 0)
{
foreach (Face face in solid.Faces)
{
PlanarFace planarFace = face as PlanarFace;
if (planarFace != null)
{
lstFace.Add(face);
}
}
}
}
else if(go is GeometryInstance)
{
GeometryInstance gins = goas GeometryInstance;
if (gins != null)
{
GeometryElement ge = gins.GetInstanceGeometry();
foreach (GeometryObject go in ge)
{
Solid solid = go as Solid;
if (solid != null && solid.Faces.Size > 0)
{
foreach (Face face in solid.Faces)
{
PlanarFace planarFace = face as PlanarFace;
if (planarFace != null)
{
lstFace.Add(face);
}
}
}
}
}
}
}
return lstFace;
}
/// <summary>
/// 获取线与构筑物面的焦点
/// </summary>
public static List<XYZ> Get_Intersection(Curve pipeLine, List<Face> lstFace)
{
List<XYZ> lstIntersection = new List<XYZ>();
foreach (Face item in lstFace)
{
XYZ ptJd = CaculateIntersection(item, pipeLine);
if (ptJd != null)
{
lstIntersection.Add(ptJd);
}
}
return lstIntersection;
}
/// <summary>
/// 求面和线的交点
/// </summary>
public XYZ CaculateIntersection(Face face, Curve curve)
{
XYZ intersection = null;
try
{
IntersectionResultArray resultArray = new IntersectionResultArray();
SetComparisonResult setComparisonResult = face.Intersect(curve, out resultArray);
if (SetComparisonResult.Disjoint != setComparisonResult && resultArray != null)
{
if (!resultArray.IsEmpty)
{
intersection = resultArray.get_Item(0).XYZPoint;
}
}
}
catch (Exception ex)
{ }
return intersection;
}
然后是管线和洞口/套管关联,
大致思路:1、统计需要用到的数据;比如 洞口id,墙体id等等
2、开洞的同时把数据写入管线扩展属性中
3、利用“IUpdater”接口,用户更改模型时会触发,触发后:
①获取记录的数据,并得到洞口和墙体实例 ②计算管线与墙体新交点,并设置给洞口(还有管线的长/宽/半径)
这样就实现了洞口与管线相关联的功能。
//key:洞口id,value:所在墙体id
Dictionary<string,string> dicData = new Dictionary<string,string>();
private void OpeningHole(Element wall,Element duct)
{
//得到风管中心线
LocationCurve lc = duct.Location as LocationCurve;
Curve curve = lc.Curve;
//得到墙的面
List<Face> lstFace = Get_ElementFace(wall);
//得到管线与墙的交点
List<XYZ> lstIntersection = Get_Intersection(curve, lstFace);
//计算中心点
XYZ ptIntersection = (lstIntersection[0] + lstIntersection[1]) / 2
Transaction trans = new Transaction(doc, "opening");
trans.Start();
try
{
//创建洞口
FamilyInstance instance = doc.Create.NewFamilyInstance(ptIntersection, fs, StructuralType.NonStructural);
//执行剪切
InstanceVoidCutUtils.AddInstanceVoidCut(doc, wall, instance);
dicData.Add(instance.Id,wall.Id);
//数据加到扩展属性
Add_Data(duct,dicData);
trans.Commit();
}
catch
{
trans.RollBack();
}
}
注册更新
public class OpeningApplication : IExternalApplication
{
ElementUpdate Updater;
/// <summary>
/// 注册更新
/// </summary>
/// <param name="addInId"></param>
private void Init(AddInId addInId)
{
try
{
//设置触发元素类型
ElementFilter pipe = new ElementCategoryFilter(BuiltInCategory.OST_PipeCurves);
ElementFilter duct = new ElementCategoryFilter(BuiltInCategory.OST_DuctCurves);
List<ElementFilter> lstFilters = new List<ElementFilter>();
lstFilters.Add(pipe);
lstFilters.Add(duct);
//注册更新
Updater = new ElementUpdate(addInId);
UpdaterRegistry.RegisterUpdater(Updater);
LogicalOrFilter filter = new LogicalOrFilter(lstFilters);
UpdaterRegistry.AddTrigger(Updater.GetUpdaterId(), filter, Element.GetChangeTypeAny());
}
catch(Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
public Result OnShutdown(UIControlledApplication application)
{
if(Updater != null)
{
//注销更新
UpdaterRegistry.UnregisterUpdater(Updater.GetUpdaterId());
}
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
Init(application.ActiveAddInId);
return Result.Succeeded;
}
}
实现IUpdater接口
public class ElementUpdate : IUpdater
{
private AddInId addInId = null;
Guid guid = new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF");
public ElementUpdate(AddInId addin)
{
this.addInId = addin;
}
public void Execute(UpdaterData data)
{
Document doc = data.GetDocument();
List<ElementId> lstModefieIds = data.GetModifiedElementIds().ToList();
//更新元素,此接口内不可以开启事务,因为触发这个接口时文档中的修改还未提交
foreach(ElementId eid in lstModefieIds)
{
Element duct = doc.GetElement(eid);
LocationCurve lc = duct.Location as LocationCurve;
Curve curve = lc.Curve;
Dictionary<string,string> dicData = Get_ExtendedData(duct);
foreach(string cid in dicData)
{
FamilyInstance cave = doc.GetElement(new ElementId(Convert.ToInt32(cid))) as FamilyInstance;
Element wall = doc.GetElement(new ElementId(Convert.ToInt32(dicData[cid])));
//得到墙的面
List<Face> lstFace = Get_ElementFace(wall);
//得到管线与墙的交点
List<XYZ> lstIntersection = Get_Intersection(curve, lstFace);
//计算中心点
XYZ ptIntersection = (lstIntersection[0] + lstIntersection[1]) / 2;
LocationPoint lp = cave.Location as LocationPoint;
lp.Point = ptIntersection;
}
}
}
public string GetAdditionalInformation()
{
return "add";
}
public ChangePriority GetChangePriority()
{
return ChangePriority.FloorsRoofsStructuralWalls;
}
public UpdaterId GetUpdaterId()
{
return new UpdaterId(addInId, guid);
}
public string GetUpdaterName()
{
return "update";
}
}
操作扩展属性的方法
/// <summary>
/// 获取指定元素扩展属性数据
/// </summary>
public static Dictionary<string, string> Get_ExtendedData(Element elem)
{
Dictionary<string, string> dicData = new Dictionary<string, string>();
IList<Guid> listGuid = elem.GetEntitySchemaGuids();
if (listGuid.Count == 0)
return dicData;
Schema sa = Schema.Lookup(listGuid[0]);
Entity entity = elem.GetEntity(sa);
IDictionary<string, string> dicMap = entity.Get<IDictionary<string, string>>(sa.GetField("管件集合"));
for (int i = 0; i < dicMap.Count; i++)
{
dicData.Add(dicMap.Keys.ElementAt(i), dicMap.Values.ElementAt(i));
}
return dicData;
}
/// <summary>
/// 覆盖原有扩展属性
/// </summary>
private static void Cover_Data(Element elem, string guid, string name, IDictionary<string, string> data)
{
if (GbvSchema == null)
{
//设置Schema的数据结构框架
SchemaBuilder schemaBuilder = new SchemaBuilder(new Guid(guid));
//设置这个结构框架的可读性和可写性
schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);
//设置这个框架的总名称
schemaBuilder.SetSchemaName(name);
//设置这个框架的类型和数据的名字
schemaBuilder.AddMapField(name, typeof(string), typeof(string));
//把数据结构框架添加到Schema中
GbvSchema = schemaBuilder.Finish();
}
//创建一个新的数据对象
Entity entity = new Entity(GbvSchema);
//得到Revit中对应名字的数据对象
Field field = GbvSchema.GetField(name);
//然后给数据对象entity赋值
entity.Set(field, data);
//最后给墙添加数据
elem.SetEntity(entity);
}
/// <summary>
/// 添加扩展属性
/// </summary>
public static void Add_Data(Element elem, IDictionary<string, string> data)
{
Dictionary<string, string> tempData = Get_ExtendedData(elem);
if (tempData.Count > 0)
{
foreach (string item in tempData.Keys)
{
if (data.Keys.Contains(item))
continue;
data.Add(item, tempData[item]);
}
}
if (data.Count > 0)
{
Cover_Data(elem, "f0c28996-670d-438f-a2be-b81b14aeb8dd", "管件集合", data);
}
}
大概就这些,代码都是删减简化后的,没有运行过 给个参考而已。
有问题可留言。