Revit二次开发---碰撞检测

本文提供了一种在Revit API中实现碰撞检测的方法,通过使用快速过滤器和慢速过滤器结合的方式,提高了检测效率并减少了资源消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记录一下,写的比较简单 核心代码就这点。

没仔细测,反正目前检测的都是和rvt自带碰撞检测结果一样的。

List<ElementId> left;//要检测碰撞的元素id
List<ElementId> right;//要检测碰撞的元素id
string sResult="";
foreach(ElementId eid left)
{
    Element elem = doc.GetElement(eid);
    FilteredElementCollector collector = new FilteredElementCollector(doc,right);//创建收集器,指定收集范围
    ElementIntersectsElementFilter filter = new ElementIntersectsElementFilter(elem);//用API过滤相交元素
    List<Element> lstElem = collector.WherePasses(filter).ToElements().ToList();
    if(lstElem.Count > 0)
    {
        foreach(Element collision_elem lstElem)
        {
            sInfo += elem.Name+" Id:"+elem.Id+" ----- "+collision_elem.Name+" Id:"+collision_elem.Id +"\n";
        }
        //处理两个集合具有相同id时,防止重复检测
        if(right.Contains(eid))
            right.Remove(eid);
    }
}
TaskDialog.Show("result",sInfo);

纯手敲,可能有误,大家自行修改或回复。

-----------更新---------------

如果检测一个很大的模型,List<Element> lstElem = collector.WherePasses(filter).ToElements().ToList();   

运行到这一句代码时会非常的耗时,出现界面假死的状态。想通过进度条显示检测进度,让界面变得更有好。

用这种方法就不行了,因为我获取不到检测的进度(不知道大家能获取到吗?)

网上查询发现也有遇到同样问题的,并且给出了解决方案:

1.用boundingboxIntersectFilter和boundingboxIsInsideFilter 快速过滤出碰撞元素(这两个都属于快速过滤器),但是过滤出来的元素会不准确,我们要把包含但不相交的元素从集合中删去。

2.用ElementIntersectsSolidFilter(慢速过滤器)进行确认是否真的碰撞

List<ElementId> left;
List<ElementId> right;
List<ElementId> realEids;//真实碰撞的id
//记录key 和哪些元素检测过
Dictionary<ElementId,List<ElementId>> m_dicCheck;
int iCount = 0;//快速过滤出的id总数
foreach(ElementId eid left)
{//此循环可以显示检测总进度
    //防止重复检测
    List<ElementId> temp = right;
    if(m_dicCheck.Count > 0)
    {
        foreach(ElementId id in m_dicCheck.Keys)
        {
            if(m_dicCheck[id].Contains(eid))
                temp.Remove(id);
        }
        m_dicCheck.Add(eid,temp);
    }
    else
    {
        m_dicCheck.Add(eid,temp);
    }
    Element elem = doc.GetElement(eid);
    BoundingBoxXYZ boxXYZ = elem.get_BoundingBox(doc.ActiveView);
    Outline ol = new Outline(boxXYZ.Min,boxXYZ.Max);
    BoundingBoxIntersectsFilter boxIntersectsFilter = new BoundingBoxIntersectsFilter(ol);
    BoundingBoxIsInsideFilter boxIsInsideFilter = new BoundingBoxIsInsideFilter(ol);
    LogicalOrFilter filter = new LogicalOrFilter(boxIntersectsFilter,boxIsInsideFilter);
    FilteredElememntCollector collector = new FilteredElememntCollector(doc,temp);
    //用快速过滤器得到所有相交/包含的元素,并去掉集合中重复的id
    List<ElementId> lstEid = collector.WherePasses(filter).ToElementIds().Distinct().ToList();
    //去掉检测元素自身
    lstEid = lstEid.Where(e=>e != eid).ToList();
    iCount = lstEid.Count;
    //得到当前检测元素的所有solid
    List<Solid> lstSolid = GetElementSolid(elem);
    if(lstSolid.Count == 0)
        continue;
    //然后就是循环判断是否真实碰撞
    foreach(Solid solid lstSolid)
    {//此循环可以显示单个元素检测进度
        FilteredElementCollector collisionCollector = new FilteredElementCollector(doc,lstEid);
        ElementIntersectsSolidFilter collisionFilter = new ElementIntersectsSolidFilter(solid);
        List<ElementId> Ids = collisionCollector.WherePasses(collisionFilter).ToElementIds().ToList();
        //释放过滤器和收集器,否则创建太多会内存溢出
        collisionCollector.Dispose();
        collisionFilter.Dispose();
        if(Ids.Count > 0)
        {
            realEids.AddRange(Ids);
            if(tempEids.Count == iCount)
                break;
            //将过滤出来的id从lstEid集合中删除,缩小检测范围
            foreach(ElementId itemId Ids)
            {
                if(lstEid.Contains(itemId)) lstEid.Remove(itemId);
            }
        }
    }
    //
    //真实碰撞的元素id在这里realEids, 在此执行对结果的操作
    //
    realEids.Clear();
}

获取Element的实体Solid

private List<Solid> GetElementSolid(Element elem)
{
    List<Solid> lstSolid = new List<Solid>();
    Options opt = new Option();
    opt.ComputeReferences = true;
    opt.IncludeNonVisbleObjects = true;
    GeometryElement ge = element.get_Geomtry(opt);
    if(ge != null)
        lstSolid.AddRange(GetSolid(ge));
    return lstSolid;
}
private List<Solid> GetSolid(GeometryElement ge)
{
    List lstSolid = new List<Solid>();
    foreach(GeomtryObject go in ge)
    {
        if(go is Solid)
        {
            Solid solid = go as Solid;
            if(solid.SurfaceArea > 0 && solid.Volume > 0 && solid.Faces.Size > 1 && solid.Edges.Size > 1)
                lstSolid.Add(solid);
        }
        else if(go is GeometryInstance)
        {
            GeomtryElement ge = (go as GeometryInstance).GetInstanceGeometry();
            lstSolid.AddRange(GetSolid(ge));            
        }
    }
    return lstSolid;
}

手敲,有误自己改或回复。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘿呦嘿呦嘿呦嘿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值