Revit二次开发小技巧(十六)寻找最短路径

news2024/10/5 22:24:52

最近遇到一个需求,指定两个配电箱,然后找到两个配电箱之间最短的桥架路径。运用了Dijkstra算法去计算最短路径,以配电箱实体、三通、四通为节点,简化中间弯头计算的方式。

背景

在这里插入图片描述
选择起点和终点的配电箱,找到最短的桥架路径
在这里插入图片描述

代码文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;


namespace RevitTest.Short
{
    [Transaction(TransactionMode.Manual)]
    public class ShortCmd : IExternalCommand
    {
        /// <summary>
        /// 所有结点集合
        /// </summary>
        List<NodeModel> _allNodelModelList = new List<NodeModel>();
        /// <summary>
        /// 所有结点连接集合
        /// </summary>
        List<NodeLink> _allNodeLinkList = new List<NodeLink>();
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Document doc = uidoc.Document;
            Reference selOneRef = null;
            Reference selTwoRef = null;
            try
            {
                selOneRef = uidoc.Selection.PickObject(ObjectType.Element, "选择起始配电箱");
                selTwoRef = uidoc.Selection.PickObject(ObjectType.Element, "选择终点配电箱");
            }
            catch
            {
            }
            FamilyInstance targetFI = doc.GetElement(selOneRef) as FamilyInstance;
            FamilyInstance endFI = doc.GetElement(selTwoRef) as FamilyInstance;
            NodeInit(doc, targetFI);
            //先构造所有的点数据
            List<NodeLink> shortNLList = new List<NodeLink>();
            for (int i = 0; i < _allNodelModelList.Count; i++)
            {
                NodeLink nwLink = new NodeLink();
                nwLink.OneModel = _allNodelModelList[0];
                nwLink.TwoModel = _allNodelModelList[i];
                if (i == 0) nwLink.LinkLenght = 0;
                else nwLink.LinkLenght = double.MaxValue;
                shortNLList.Add(nwLink);
            }
            while (true)
            {
                //如果终点给标记的时候就弹出了
                if (shortNLList.FirstOrDefault(x => x.TwoModel.Equals(endFI)).IsTag == true) break;
                NodeLink oneLink = shortNLList.Where(x => !x.IsTag).OrderBy(x => x.LinkLenght).FirstOrDefault();
                if (oneLink == null) break;
                List<NodeLink> linkList = _allNodeLinkList.FindAll(x => x.OneModel.Equals(oneLink.TwoModel) || x.TwoModel.Equals(oneLink.TwoModel));
                foreach (var nl in linkList)
                {
                    double lenght = nl.LinkLenght + oneLink.LinkLenght;
                    NodeModel orderNM = nl.OneModel.Equals(oneLink.TwoModel) ? nl.TwoModel : nl.OneModel;
                    NodeLink orderNL = shortNLList.FirstOrDefault(x => x.TwoModel.Equals(orderNM));
                    if (lenght < orderNL.LinkLenght)
                    {
                        orderNL.LinkLenght = lenght;
                        orderNL.LinkMEPCurveList = new List<MEPCurve>();
                        orderNL.LinkMEPCurveList.AddRange(oneLink.LinkMEPCurveList);
                        orderNL.LinkMEPCurveList.AddRange(nl.LinkMEPCurveList);
                    }
                }
                oneLink.IsTag = true;
            }
            NodeLink findNL = shortNLList.FirstOrDefault(x => x.TwoModel.Equals(endFI));
            uidoc.Selection.SetElementIds(findNL.LinkMEPCurveList.Select(x => x.Id).ToList());


            return Result.Succeeded;
        }
        /// <summary>
        /// 结点数据初始化
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="targetFI"></param>
        public void NodeInit(Document doc, FamilyInstance targetFI)
        {
            NodeModel nm = new NodeModel(targetFI);
            if (_allNodelModelList.FirstOrDefault(x => x.Equals(nm)) != null) return;
            //拿到已经连接的连接器
            List<Connector> connList = targetFI.MEPModel.ConnectorManager.Connectors.OfType<Connector>().Where(x => x.IsConnected && x.AllRefs.Cast<Connector>().FirstOrDefault(m => m.Owner.Id == targetFI.Id) == null).ToList();
            nm.ConnectorList = connList;
            _allNodelModelList.Add(nm);
            while (true)
            {
                NodeModel oneNM = _allNodelModelList.FirstOrDefault(x => x.ConnectorList.Count != 0);
                if (oneNM == null) break;
                for (int i = 0; i < oneNM.ConnectorList.Count; i++)
                {
                    Connector con = oneNM.ConnectorList[i];
                    List<MEPCurve> mepCurveList = new List<MEPCurve>();
                    FamilyInstance nodeFI = null;
                    ElementId endElementID = null;
                    //找到对应连接的连接件
                    List<Connector> linkConList = con.AllRefs.Cast<Connector>().Where(x => x.Owner.Id != con.Owner.Id).ToList();
                    //添加连接到的构件
                    var linkElement = doc.GetElement(linkConList.FirstOrDefault().Owner.Id);
                    if (linkElement is FamilyInstance)
                    {
                        mepCurveList.AddRange(GetLinkMepCurveList(linkElement as FamilyInstance, oneNM.NodeFamilyInstance.Id, out nodeFI, out endElementID));
                    }
                    else
                    {
                        mepCurveList.AddRange(GetLinkMepCurveList(linkElement as MEPCurve, oneNM.NodeFamilyInstance.Id, out nodeFI, out endElementID));
                    }

                    #region 计算长度
                    double mepLenght = 0;
                    foreach (var item in mepCurveList)
                    {
                        try
                        {
                            mepLenght += (item.Location as LocationCurve).Curve.Length;
                        }
                        catch
                        {
                        }
                    }
                    #endregion
                    NodeLink nodeLink = new NodeLink();
                    nodeLink.OneModel = oneNM;
                    nodeLink.TwoModel = null;
                    nodeLink.LinkMEPCurveList = mepCurveList;
                    nodeLink.LinkLenght = mepLenght;
                    if (nodeFI != null)
                    {
                        NodeModel nextNM = _allNodelModelList.FirstOrDefault(x => x.Equals(nodeFI));
                        if (nextNM == null)
                        {
                            nextNM = new NodeModel(nodeFI);
                            //拿到所有构件的连接件
                            nextNM.ConnectorList = nodeFI.MEPModel.ConnectorManager.Connectors.Cast<Connector>().Where(x => x.IsConnected).ToList();
                            _allNodelModelList.Add(nextNM);
                        }
                        nodeLink.TwoModel = nextNM;
                        //处理终点的连接器
                        Connector nextEndConn = nextNM.ConnectorList.FirstOrDefault(x => x.AllRefs.OfType<Connector>().FirstOrDefault(m => m.Owner.Id == endElementID) != null);
                        nextNM.ConnectorList.Remove(nextEndConn);
                        nextNM.UserConnectIndexDic.Add(nextEndConn, _allNodeLinkList.Count - 1);
                        //处理起始结点的连接器
                        _allNodeLinkList.Add(nodeLink);
                        oneNM.UserConnectIndexDic.Add(con, _allNodeLinkList.Count - 1);
                    }
                    oneNM.ConnectorList.RemoveAt(i);
                    i--;
                }
            }
        }
        /// <summary>
        /// 计算单条机电线管设备的完整路径
        /// </summary>
        /// <param name="mepCurve"></param>
        /// <param name="linkId"></param>
        /// <param name="fi"></param>
        /// <param name="endConnMCID"></param>
        /// <returns></returns>
        public List<MEPCurve> GetLinkMepCurveList(MEPCurve mepCurve, ElementId linkId, out FamilyInstance fi, out ElementId endConnMCID)
        {
            List<MEPCurve> mepCurveList = new List<MEPCurve> { mepCurve };
            //拿到两端连接件
            List<Connector> connertorList = mepCurve.ConnectorManager.Connectors.Cast<Connector>().Where(x => x.IsConnected && x.AllRefs.Cast<Connector>().FirstOrDefault(m => m.Owner.Id == linkId) == null).ToList();
            Connector con = connertorList.FirstOrDefault();
            if (con == null)
            {
                fi = null;
                endConnMCID = null;
                return mepCurveList;
            }
            //找到对应连接的连接件
            List<Connector> linkConList = con.AllRefs.Cast<Connector>().Where(x => x.Owner.Id != con.Owner.Id).ToList();
            //添加连接到的构件
            var linkFitting = mepCurve.Document.GetElement(linkConList.FirstOrDefault().Owner.Id) as FamilyInstance;
            //拿到管件连接的所有管线
            List<MEPCurve> linkMC = GetLinkMepCurveList(linkFitting, mepCurve.Id, out fi, out endConnMCID);
            mepCurveList.AddRange(linkMC);
            return mepCurveList;
        }
        /// <summary>
        /// 计算单条机电线管设备的完整路径
        /// </summary>
        /// <param name="fitting"></param>
        /// <param name="linkId"></param>
        /// <param name="nodeFI"></param>
        /// <param name="endConnMCID"></param>
        /// <returns></returns>
        public List<MEPCurve> GetLinkMepCurveList(FamilyInstance fitting, ElementId linkId, out FamilyInstance nodeFI, out ElementId endConnMCID)
        {
            List<MEPCurve> mepCurveList = new List<MEPCurve>();
            nodeFI = null;
            endConnMCID = null;
            //拿到所有构件的连接件
            var fittingLinkConList = fitting.MEPModel.ConnectorManager.Connectors.Cast<Connector>().Where(x => x.IsConnected && x.AllRefs.Cast<Connector>().FirstOrDefault(m => m.Owner.Id == linkId) == null);
            //如果遇到有分支的,直接返回
            if (fittingLinkConList.Count() >= 2 || fittingLinkConList.Count() == 0)
            {
                nodeFI = fitting;
                endConnMCID = linkId;
                return mepCurveList;
            }
            foreach (var con in fittingLinkConList)
            {
                //找到对应连接的连接件
                List<Connector> linkConList = con.AllRefs.Cast<Connector>().Where(x => x.Owner.Id != con.Owner.Id).ToList();
                //添加连接到的构件
                var linkElement = fitting.Document.GetElement(linkConList.FirstOrDefault().Owner.Id);
                if (linkElement is FamilyInstance)
                {
                    mepCurveList.AddRange(GetLinkMepCurveList(linkElement as FamilyInstance, fitting.Id, out nodeFI, out endConnMCID));
                }
                else
                {
                    mepCurveList.AddRange(GetLinkMepCurveList(linkElement as MEPCurve, fitting.Id, out nodeFI, out endConnMCID));
                }
            }
            return mepCurveList;
        }
    }
    /// <summary>
    /// 结点类,三通或者四通为一个结点,选择的起点和终点也是一个结点
    /// </summary>
    public class NodeModel : IEquatable<NodeModel>, IEquatable<FamilyInstance>
    {
        /// <summary>
        /// 结点构件
        /// </summary>
        public FamilyInstance NodeFamilyInstance { get; set; }

        /// <summary>
        /// 未使用连接器集合
        /// </summary>
        public List<Connector> ConnectorList { get; set; } = new List<Connector>();


        /// <summary>
        /// 使用过的连接器和对应的连接条索引
        /// </summary>
        public Dictionary<Connector, int> UserConnectIndexDic { get; set; } = new Dictionary<Connector, int>();

        public NodeModel()
        {

        }
        public NodeModel(FamilyInstance fi)
        {
            NodeFamilyInstance = fi;
        }

        public bool Equals(NodeModel other)
        {
            try
            {
                if (other.NodeFamilyInstance == null || this.NodeFamilyInstance == null) return false;
                return other.NodeFamilyInstance.Id.IntegerValue == this.NodeFamilyInstance.Id.IntegerValue;
            }
            catch (Exception)
            {
                return false;
            }

        }

        public bool Equals(FamilyInstance other)
        {
            try
            {
                if (other == null || this.NodeFamilyInstance == null) return false;
                return other.Id.IntegerValue == this.NodeFamilyInstance.Id.IntegerValue;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
    /// <summary>
    /// 两个结点直接相连的类
    /// </summary>
    public class NodeLink
    {
        /// <summary>
        /// 第一个结点
        /// </summary>
        public NodeModel OneModel { get; set; }

        /// <summary>
        /// 第二个结点
        /// </summary>
        public NodeModel TwoModel { get; set; }

        /// <summary>
        /// 两个结点之间的线集合
        /// </summary>
        public List<MEPCurve> LinkMEPCurveList { get; set; } = new List<MEPCurve>();

        /// <summary>
        /// 连接的长度
        /// </summary>
        public double LinkLenght { get; set; } = 0;

        /// <summary>
        /// 是否标记
        /// </summary>
        public bool IsTag { get; set; } = false;

    }
}

步骤讲解

1.构建节点

节点:实体构件、三通、四通
节点筛选的原则:如果一个实体构件的连接器除前连接前管道外,还有两个以上(包含两个)的连接器时,判断为节点,结束一条线的循环。
节点属性(NodeModel)

属性备注
NodeFamilyInstance节点构件
ConnectorList未使用的连接器集合
UserConnectIndexDic字典类型,用来记录对应的连接器和索引

思路:
a.以起点为开始构建循环,然后返回一个FamilyInstance,通过查找总的节点判断是否存在当前FamilyInstance的节点,如果没有需要新建加入到总的节点集合中。
b.循环的结束点在所有的节点的连接器全部都用完时,就跳出。
在这里插入图片描述
c.因此可以得到9个节点,和对应的连接长度NodelLink实例

2. Dijkstra算法

具体思路可以参考以下的博客:
最短路径算法
下面是我简单的理解和总结
a.先简化上面的图形
在这里插入图片描述

b.根据节点和起点来构建一个NodeLink实例,用来记录具体的起点到达每个节点的距离。然后找到和起点相连的节点,选择距离最短的点为下一个起点,并且把旧起点标记为已经使用过。然后每次的循环都是找所有节点中没有标记使用过的节点,并且是距离最短的为下一个节点。这里可能有点绕,通过图片来讲解

找到最近点B,然后A标记为使用过.在这里插入图片描述
以B为起点,去修改与B相连的点的数值。数值替换的原则是,起点到改点的距离小于记录的距离时,附上新值
在这里插入图片描述

把B标记为已经使用,找下一个距离最近未使用过的点C,以C为新起点去更新距离数值
在这里插入图片描述
通过不断循环,到最后连接终点的点都被标记过的时候,就是整个循环路径寻找结束的时候,这样子就可以找出最短的路径了
在这里插入图片描述

写在最后

用来测试的样例较为简单,没有经过大量的项目推敲,可以交给大家去测试和修改,主要是提供一个解题的思路,希望可以帮到你~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RingUI + JCEF开发IDEA插件

文章目录RingUI知识储备示例插件实现逻辑开发环境开发流程新建一个IDEA插件项目新建一个前端项目验证前端项目丰富前端项目丰富插件内容RingUI This collection of UI components aims to provide all the necessary building blocks for web-based products built inside JetB…

向 Windows 高级用户进阶,这 5 款效率工具帮你开路

工欲善其事&#xff0c;必先利其器。作为全球最多人使用的桌面操作系统&#xff0c;Windows 的使用效率与我们的工作学习息息相关。今天&#xff0c;小编就为大家整理了 10 款提高效率的利器&#xff0c;让你的 Windows 更具生产力。 1.文件预览——Seer MacOS 有一个非常方便…

优雅草YYC松鼠短视频2022年12月28日更新v5.1.6版本更新·修复因为消息提醒二开导致菜单栏无法显示·进一步完善推送

优雅草YYC松鼠短视频2022年12月28日更新v5.1.6版本更新修复因为消息提醒二开导致菜单栏无法显示进一步完善推送 更新日志 修复因为消息提醒二开导致菜单栏无法显示 继续进一步开发消息推送 目前消息推送登录以及推送判断返回刷新仍存在问题在开发中 目前goeasy官方也在更新关…

数据劫持基础

数据劫持数据劫持原理。属性描述符Object.defineProperty官网响应式原理![在这里插入图片描述](https://img-blog.csdnimg.cn/b9f900484f314334a0dc6139428b397c.png)getter和setterObject.defineProperty的不足proxy工作原理总结数据劫持原理。 数据劫持&#xff0c;指的是在…

Find My资讯|苹果 Find My 找到因交通事故坠崖的一名女子

美国加州圣贝纳迪诺县消防局在其官方 Facebook 更新动态&#xff0c;表示在接到家属报警&#xff0c;通过苹果 Find My 功能追踪家人的 iPhone&#xff0c;成功营救出在圣贝纳迪诺山区的 18 号高速公路上坠崖的一名妇女。 圣贝纳迪诺县消防局在帖子中表示这名妇女在圣诞节那天…

多线程与高并发(三)

【 day3课前复习 】&#xff1a; 【AtomicInteger】&#xff1a; 原子性——都是用CAS机制来实现。 【 expected , update 】: //有时候也会写三个值——你要修改的那个对象。 expected——期望值。&#xff08;旧值&#xff09; update——更新值。&#xff08;新值&#…

LeetCode刷题复盘笔记—一文搞懂动态规划之剑指 Offer 46. 把数字翻译成字符串问题(动态规划系列第三十四篇)

今日主要总结一下动态规划的一道题目&#xff0c;剑指 Offer 46. 把数字翻译成字符串 题目&#xff1a;剑指 Offer 46. 把数字翻译成字符串 Leetcode题目地址 题目描述&#xff1a; 给定一个数字&#xff0c;我们按照如下规则把它翻译为字符串&#xff1a;0 翻译成 “a” &am…

小型云台机械手的制作

1. 运动功能说明 小型云台机械手&#xff0c;下方的云台可以提供左右旋转和上下摆动的动作&#xff0c;与舵机夹爪配合可以完成简单的抓取和搬运。 2.结构说明 该样机由一个 R207小型舵机云台 上串联了一个 舵机夹爪模组 构成。 3. 运动功能实现 3.1 电子硬件 在这个示例中&a…

Redis集群系列五 —— 分区/片概念

什么是分区 分区就是将所存储的数据按照一定的规则存储在不同的存储服务或介质上&#xff0c;通过降低单服务或介质的数据量级来提升数据处理能力&#xff0c;从而达到拥有数据处理横向扩容的能力的目的。 还可简单的理解为分区就是将数据拆分到多个 Redis 实例的过程&#xf…

21. 合并两个有序链表播报文章

题目描述 这是一道难度为简单的题目&#xff0c;同时&#xff0c;这道题也是Leetcode148题中&#xff0c;链表归并排序中重要的组成部分。 题目描述 题目分析 本题的题目简单易懂&#xff0c;输入为两个有序链表&#xff0c;要求将链表合并为一个有序的链表。在此不在再赘述…

【消息中间件】RocketMQ如何实现Producer的负载均衡

目录 一、前言 二、实现Producer的负载均衡 1、负载均衡选取一条消息队列并且高可用 1.1、模拟随机递增取模消息队列数为5 1.2、模拟随机递增取模消息队列数为6 1.3、判断Broker代理是否可用 2、更新故障项维护startTimestamp字段 2.1、退避运算 2.2、更新故障项维护st…

干货 | 数字经济创新创业——数字经济下的商业模式与解决方案

下文整理自清华大学大数据能力提升项目能力提升模块课程“Innovation & Entrepreneurship for Digital Economy”&#xff08;数字经济创新创业课程)的精彩内容。主讲嘉宾&#xff1a;Kris Singh: CEO at SRII, Palo Alto, CaliforniaVisiting Professor of Tsinghua Unive…

卷积、转置卷积、膨胀卷积学习记录

Conv计算&#xff1a; 计算公示 1、pytorch中默认参数&#xff0c;以conv1d为例 torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue, padding_mode‘zeros’, deviceNone, dtypeNone&#xff09; 2、输出卷积尺寸&am…

MySQL常见内置函数及其使用

目录 1、聚合函数 2、日期函数 3、字符串函数 4、数学函数 5、其它函数 1、聚合函数 函数说明 COUNT([DISTINCT] expr) 返回查询到的数据的 数量SUM([DISTINCT] expr)返回查询到的数据的 总和&#xff0c;不是数字没有意义AVG([DISTINCT] expr)返回查询到的数据的 平均值…

数据蛙恢复软件替代产品有哪些?15款顶尖数据恢复软件清单

数据蛙恢复软件是一款国内数据恢复软件&#xff0c;可以在很多品牌的电脑上使用。但是你可能会遇到数据蛙恢复软件扫描不到需要恢复文件的情况。那么有没有更专业的数据恢复软件可以找到更多误删数据&#xff1f;本文将为你介绍最值的推荐的15个数据蛙恢复软件替代产品。 丢失…

Web兼容性测试的要点

对于网页的兼容性我们主要考虑的是各种浏览器对前台页面的兼容性&#xff0c;因为浏览器对页面的影响是最大的。 现在浏览器的种类越来越多&#xff0c;网页中展现出来的内容也越来越丰富&#xff0c;这些内容包括网页中的字体、图片、动画等&#xff0c;而且有些内容需要网页…

AlexNet学习笔记(2)

里面 有些东西 对于现在来说都是错误的 而且由大量的细节对于现在来说没有必要 而且是过度的enginnering 一篇论文的第一段通常是讲一则故事 我们在做什么研究 哪个方向 有什么东西然后为什么很重要 正则化 regularization好像没有那么重要&#xff0c;并不是最关键的 最关键…

前端监控系统的搭建

UI自动化测试库 puppeteer&#xff1a;https://zhuanlan.zhihu.com/p/524254998 - google出品 cypress - 据说比puppeteer好用 前端监控体系 性能监控 异常监控 行为监控&#xff1a;埋点体系 主动监控 被动监控 前端性能数据捕获&#xff1a; 打点方式&#xff0c;结…

pyqt5 QPainter绘制图形,并旋转

PyQt5 的绘图系统使用户可以在屏幕或打印设备上用相同的 API 绘图&#xff0c;QPainter 是用来进行 绘图操作的类&#xff0c;一般的绘图设备包括 QWidget、QPixmap、QImage 等&#xff0c;这些绘图设备为 QPainter 提供了一个“画布” QWidget 类是所有界面组件的基类&#xf…

【细节注入模型】

Detail-Injection-Model-Inspired Deep Fusion Network for Pansharpening &#xff08;细节注入模型启发的深度融合网络全色锐化算法&#xff09; 全色锐化是一种图像融合方法&#xff0c;其目的是将低空间分辨率的多光谱&#xff08;MS&#xff09;图像与高空间分辨率的全色…