.NET项目使用Devexpress控件DiagramControl和QuikGraph类库实现最短路径算法可视化

news2024/11/18 7:24:56

说明:

  • 使用控件:DevExpress V24.1.3(链接:https://pan.baidu.com/s/1FosVrpyE7q_XvwhZK7ad3w?pwd=tw64提取码:tw64)
  • 项目地址:https://github.com/VinciYan/Diagram_NET.git
  • 可以帮助学习和理解数据结构图的最短路径算法等,适用于计算机专业大学生、大学教师和数据结构学习者等
  • 不足之处,还望指出

简介

最短路径算法——Dijkstra算法

Dijkstra算法用于找到从单个源节点到图中所有其他节点的最短路径,适用于加权图(边有权重),但权重必须是非负的

步骤:

  1. 初始化起点到自身的距离为0,其他节点的距离为∞
  2. 将起点标记为已访问,所有其他节点为未访问
  3. 对于当前节点,检查所有相邻节点的未访问节点,计算其到起点的距离。如果这个距离小于当前记录的距离,就更新它
  4. 选择未访问节点中距离起点最近的节点作为下一个当前节点,重复步骤3
  5. 直到所有节点都被访问过

QuikGraph

QuikGraph是一个用于C#的强大且灵活的图数据结构和算法库。它提供了多种图表示形式和常见的图算法,可以轻松地在.NET应用程序中处理图形数据

主要特性

  • 多种图类型:支持有向图、无向图、多重图等多种图类型
  • 丰富的算法:包括遍历、最短路径、最大流、最小生成树等常见的图算法
  • 灵活的接口:通过接口定义图的数据结构,使得用户可以自定义图的实现
  • 良好的性能:经过优化,适用于处理大规模图数据

DevExpress的DiagramControl

DevExpress的DiagramControl是一个强大的控件,允许开发者在WinForms和WPF应用程序中创建、编辑和显示复杂的图表和流程图。它提供了多种工具和功能,帮助用户以交互和直观的方式构建和管理图形化数据表示

主要特性

  1. 支持多种图表类型:包括流程图、组织结构图、状态图等
  2. 丰富的图元库:提供预定义的形状、连接线和图元,用户也可以自定义图元
  3. 交互功能:支持拖放、缩放、旋转、对齐、分组等交互操作
  4. 数据绑定:可以绑定到数据源,动态生成图表
  5. 导入/导出:支持将图表导出为多种格式(如 PNG、JPEG、SVG、PDF 等),以及从多种格式导入
  6. 布局算法:内置多种自动布局算法,如树形布局、层次布局等
  7. 事件处理:丰富的事件模型,方便开发者处理用户交互和自定义行为
  8. 主题支持:与 DevExpress 其他控件一致,支持多种主题和样式

项目重点

需求

  • 节点:能从元件工具栏拖拽节点元件到绘制区域完成绘制节点,在节点属性栏自定义节点编号
  • 边:能够绘制有向边,在边属性栏自定义边的权值
  • 最短路径可视化:完整显示最短路径
  • 额外信息显示:显示节点编号和边的权值

自定义元件工具栏

自定义元件工具栏,工具栏定义我们用于绘制图节点的元件,可以自定义元件的背景图片和显示文字等

然后就可以从工具栏拖拽该元件到右侧绘图区域完成图节点的绘制

在这里插入图片描述

 var stencil = new DevExpress.Diagram.Core.DiagramStencil("CustomStencil", "Custom Shapes");

 //注册DiagramShapeEx
 var itemTool = new FactoryItemTool("CustomShape", () => "Custom Shape", diagram => { DiagramShapeEx customShape = new DiagramShapeEx() { Width = 100, Height = 50 }; return customShape; }, new System.Windows.Size(100, 50), false);
 stencil.RegisterTool(itemTool);

 // 注册ShapeImage
 stencil.RegisterTool(new FactoryItemTool("节点", () => "节点", diagram =>
 {
     return new ShapeImage();
 }, new System.Windows.Size(18, 18)));

 DevExpress.Diagram.Core.DiagramToolboxRegistrator.RegisterStencil(stencil);
 DiagramControl.ItemTypeRegistrator.Register(typeof(DiagramShapeEx), typeof(ShapeImage), typeof(CustomConnector));

自定义节点

可以继承DiagramImage,并自定义连接点,用于边的绘制边时作为边的起点或终点,并且在节点属性栏增加属性“Id”作为节点的唯一编号

public class ShapeImage : DiagramImage, IShape
{
    public ShapeImage()
    {
        Image = Properties.Resource.node;

        #region 设置连接点
        List<PointFloat> points = new List<PointFloat>();
        points.Add(new PointFloat(0f, 0.5f));
        points.Add(new PointFloat(1f, 0.5f));
        points.Add(new PointFloat(0.5f, 0f));
        points.Add(new PointFloat(0.5f, 1f));
        ConnectionPoints = new PointCollection(points);
        #endregion
    }
    [XtraSerializableProperty, Category("自定义"), DisplayName("节点ID")]

    public int Id { get; set; }

    static ShapeImage()
    {
        DiagramControl.ItemTypeRegistrator.Register(typeof(ShapeImage));
    }
}

如图所示,节点6的属性新增了自定义属性“节点ID”

在这里插入图片描述

自定义边

继承DiagramConnector,自定义属性“权值”用于设置边的权值

public class CustomConnector : DiagramConnector, IShape
{
    public CustomConnector()
    {

    }
    [XtraSerializableProperty, Category("边信息"), DisplayName("边编号")]
    public int Id { get; set; }
    [XtraSerializableProperty, Category("边信息"), DisplayName("权值")]
    public int WeightVal { get; set; }

    [XtraSerializableProperty, Category("边信息"), DisplayName("头部节点编号")]
    public int BeginNodeNo { get; set; }


    [XtraSerializableProperty, Category("边信息"), DisplayName("尾部节点编号")]
    public int EndNodeNo { get; set; }
    static CustomConnector()
    {
        DiagramControl.ItemTypeRegistrator.Register(typeof(CustomConnector));
    }
}


在这里插入图片描述

显示信息

默认绘制完成是不显示信息,可以新增显示功能,更方便设置和查看节点编号和边权值等

在这里插入图片描述

基本思路是绘制不可以被鼠标选择的DiagramShape用来显示编号和权值等信息

DiagramItemCollection diagramItemCollection = diagramControl1.Items;
foreach (var item in diagramItemCollection)
{
    if (item is null) continue;
    if (item is CustomConnector)
    {
        var con = (item) as CustomConnector;
        var beginRec = con.ActualBeginPoint;
        var endRec = con.ActualEndPoint;
        var delX = endRec.X - beginRec.X;
        var delY = endRec.Y - beginRec.Y;
        nodeInfoShapes.Add(
           new NodeInfoShape()
           {
               Content = con.WeightVal.ToString(),
               FontColor = Color.Green,
               Width = 15,
               Height = 10,
               CenterX = (float)(endRec.X),
               CenterY = (float)(endRec.Y),
               X = (float)((beginRec.X + endRec.X) * 0.5),
               Y = (float)((beginRec.Y + endRec.Y) * 0.5),
           }
       );
        continue;
    }
    var shape = (item) as IShape;
    if (shape != null)
    {
        var rect = item.Bounds;
        nodeInfoShapes.Add(
            new NodeInfoShape()
            {
                Content = shape.Id.ToString(),
                FontColor = Color.Black,
                Width = 60,
                Height = 20,
                CenterX = (float)(rect.X + rect.Width * 0.5),
                CenterY = (float)(rect.Y - NODE_NAME_Y_OFFSET),
                X = (float)(rect.X + rect.Width * 0.5 - 60.0 * 0.5),
                Y = (float)(rect.Y - NODE_NAME_Y_OFFSET - 20.0 * 0.5),
            }
        );
    }
}
foreach (var item in nodeInfoShapes)
{
    DiagramShape diagramShape = new DiagramShape()
    {
        Content = item.Content,
        Width = item.Width,
        Height = item.Height,
        CanSelect = false,
        CanEdit = false,
        CanResize = false,
        Position = new PointFloat(item.X, item.Y),
        Appearance =
        {
            BackColor = Color.Transparent, FontSizeDelta = -1, ForeColor = item.FontColor,
            BorderSize = 0
        },
        CanSnapToThisItem = false,
        CanAttachConnectorBeginPoint = false,
        CanAttachConnectorEndPoint = false,
        CanCopy = false,
        CanMove = false,
        CanDelete = false,
        CanRotate = false,
        Tag = "info"
    };
    diagramControl1.Items.Add(diagramShape);
}

最短路径算法

这里使用QuikGraph类库来实现

 public static List<PathDistanceRec> GetShortestPath(AdjacencyGraph<string, SEquatableTaggedEdge<string, double>> graph,
     string start, string end)
 {
     List<PathDistanceRec> pathDistanceRecs = new List<PathDistanceRec>();
     // 使用 Dijkstra 算法找到最短路径
     var algorithm = new DijkstraShortestPathAlgorithm<string, SEquatableTaggedEdge<string, double>>(graph, edge => edge.Tag);

     var predecessors = new VertexPredecessorRecorderObserver<string, SEquatableTaggedEdge<string, double>>();
     // 指定起点
     string source = start;
     using (predecessors.Attach(algorithm))
     {
         algorithm.Compute(source);
         foreach (var ver in graph.Vertices)
         {
             if (ver != source && ver == end && algorithm.TryGetDistance(ver, out double distance))
             {
                 List<PathEdge> pathEdges = new List<PathEdge>();
                 List<string> pathList = new List<string>();
                 PathDistanceRec pathDistanceRec = new PathDistanceRec();
                 pathDistanceRec.StartEnd = source + "->" + end;
                 pathDistanceRec.Start = source;
                 pathDistanceRec.End = end;
                 pathDistanceRec.Dis = distance;
                 IEnumerable<SEquatableTaggedEdge<string, double>> path;
                 predecessors.TryGetPath(end, out path);
                 if (path == null) return null;
                 pathDistanceRec.Path = string.Join(",", path);
                 foreach (var step in path)
                 {
                     pathList.Add(step.Source);
                     pathEdges.Add(new PathEdge(step.Source, step.Target));
                 }
                 pathList.Add(end);
                 pathDistanceRecs.Add(pathDistanceRec);
                 pathDistanceRec.PathList = pathList;
                 pathDistanceRec.PathEdges = pathEdges;
             }
         }
     }
     return pathDistanceRecs;
 }

效果

键盘同时按住Ctrl+Shift键,鼠标任意选择起点和终点,程序自动计算出最短路径并可视化最短路径

如下,起点为1,终点为6

在这里插入图片描述

起点为3,终点为6

在这里插入图片描述

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

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

相关文章

【区块链+基础设施】珠三角征信链 | FISCO BCOS应用案例

“珠三角征信链”是中国人民银行广州分行、中国人民银行深圳市中心支行按照中国人民银行总行工作部署&#xff0c;积 极贯彻珠三角一体化发展、粤港澳大湾区建设等国家战略而建设的跨区域征信一体化数据中心枢纽&#xff0c;以 FISCO BCOS 为底链构建应用平台&#xff0c;并由微…

跨越界限,巴比达带你访问远程桌面【内网穿透技术分享】

在远程工作的时代&#xff0c;远程桌面访问成为了许多职场人士的日常。Windows系统默认的远程桌面服务监听在3389端口&#xff0c;但对于内网环境下的机器来说&#xff0c;直接从外部访问这个端口常常面临重重阻碍。不过&#xff0c;有了巴比达内网穿透&#xff0c;这一切都将不…

填志愿选专业,文科男生如何选专业?

又到了高考分数出炉&#xff0c;无数学子收获喜悦的季节&#xff0c;在分数刚出炉时&#xff0c;很多学生表现的异常兴奋&#xff0c;于他们而言&#xff0c;这么多年的努力终于有了收获&#xff0c;自己该考虑选择什么专业了。而毫不夸张的说&#xff0c;很多人在拿到专业目录…

[leetcode]minimum-absolute-difference-in-bst 二叉搜索树的最小绝对差

. - 力扣&#xff08;LeetCode&#xff09; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(null…

RANSAC空间圆拟合实现

由初中的几何知识我们可以知道&#xff0c;确定一个三角形至少需要三个不共线的点&#xff0c;因此确定一个三角形的外接圆至少可用三个点。我们不妨假设三个点坐标为P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)。 圆方程的标准形式为&#xff1a; (xi-x)2(yi-y)2R2 &#xff08;1…

[吃瓜教程]南瓜书第4章决策树

1.决策树的算法原理 从逻辑角度&#xff0c;条件判断语句的组合&#xff1b;从几何角度&#xff0c;根据某种准则划分特征空间&#xff1b; 是一种分治的思想&#xff0c;其最终目的是将样本约分约纯&#xff0c;而划分的核心是在条件的选择或者说是**特征空间的划分标准 ** …

Fooocus模型配置中文教程

很多同学这里不知道该怎么选择。不知道每个模型效果&#xff0c;针对这个整理了一个表格。参考表格就可生成预期效果图。 下载地址&#xff1a; https://download.csdn.net/download/yuanshiren133/89503764

【详解】RV1106移植opencv-mobile库

文章目录 前言一、烧入镜像二、编译项目1.创建项目文件 三、移植四、运行文件五、总结 前言 硬件&#xff1a;瑞芯微Rv1106【Luckfox Pro\Max Pico、网线一根、USB线、串口助手、摄像头 软件&#xff1a;ubuntu 20.4 编译器&#xff1a;arm-rockchip830-linux-uclibcgnueabihf…

Cesium大屏-vue3注册全局组件

1.需求 说明&#xff1a;产品经理要求开发人员在地图大屏上面随意放置组件&#xff0c;并且需要通过数据库更改其组件大小&#xff0c;位置等&#xff1b;适用于大屏组件中场站视角、任意位置标题等。 2.实现 2.1GlobalComponents.vue 说明&#xff1a;containerList可以通…

阿里云物联网应用层开发:第三部分,微信小程序和web客户端实现

文章目录 哔哩哔哩视频教程1、阿里云物联网平台对接微信小程序2、阿里云物联网平台对接web客户端2-1MQTT服务器编写2-2 web端Servlet部分编写 哔哩哔哩视频教程 【阿里云物联网综合开发&#xff0c;STM32ESP8266微信小程序web客户端一篇教程详细讲解】 https://www.bilibili.c…

袋鼠快跳 - 常用网址快捷访问

袋鼠快跳 开源地址&#xff1a;https://github.com/chenbimo/kangaroo-jump 袋鼠快跳&#xff0c;是一个以 简单快捷 为目标的网站快导航 油猴脚本。 本工具的理念就是&#xff0c;用最快的速度访问我们最常用的50个网站。 功能特点 完全免费&#xff0c;以 MIT协议 开源。…

文生图功能介绍

Stable Diffusion WebUI&#xff08;SD WebUI&#xff09;及文生图功能介绍 一、引言 随着人工智能技术的飞速发展&#xff0c;AI绘画作为一种新兴的艺术形式&#xff0c;逐渐走入人们的视野。Stable Diffusion WebUI&#xff08;简称SD WebUI&#xff09;作为AI绘画领域的重…

如何现代的编译和安装内核

前言&#xff1a;本文是在阅读书目时找到了一篇非常高质量的文章。的原文是英文&#xff0c;现在我自己手头翻译了一下&#xff0c;发布到这里。 原文连接&#xff1a;How to compile a Linux kernel in the 21st century | Opensource.com 目录 更新内核的现代方法 安装内…

在线如何快速把图片变小?图片轻松修改大小的3个在线工具

随着现在图片在工作和生活中的广泛使用&#xff0c;在使用图片的时候经常会因为图片太大的问题受到影响&#xff0c;比较简单的一种处理方法可以通过压缩图片的方式来缩小图片大小&#xff0c;那么图片压缩具体该怎么来操作呢&#xff1f;下面就给大家分享几款图片在线压缩工具…

npm安装包报错解决

目录 一&#xff1a;问题回顾 二:问题分析 三&#xff1a;npm降级或者升级 四&#xff1a;npm和node js 关系 一&#xff1a;问题回顾 今天在本地部署一个vue开发的项目&#xff0c;需要在本地看下运行情况&#xff0c;按照常规的操作就是在网站根目录运行npm install 安装…

Android super.img结构及解包和重新组包

Android super.img结构及解包和重新组包 从Android10版本开始&#xff0c;Android系统使用动态分区&#xff0c;system、vendor、 odm等都包含在super.img里面&#xff0c;编译后的最终镜像不再有这些单独的 image&#xff0c;取而代之的是一个总的 super.img. 1. 基础知识 …

LVS-负载均衡

目录 一、概念 二、LVS工作原理 1. ipvs/ipvsadm 2.名词&#xff1a; 三、常用命令 四、工作模式 1.NAT地址转换模式 &#xff08;1&#xff09;工作流程 &#xff08;2&#xff09;特点 &#xff08;3&#xff09;实验过程 a.环境准备&#xff1a; b.修改测试机的…

第二周:计算机网络概述(下)

一、计算机网络性能指标&#xff08;速率、带宽、延迟&#xff09; 1、速率 2、带宽 3、延迟/时延 前面讲分组交换的时候介绍了&#xff0c;有一种延迟叫“传输延迟”&#xff0c;即发送一个报文&#xff0c;从第一个分组的发送&#xff0c;到最后一个分组的发送完成的这段时…

vue3.2及以上 父调子的方法defineExpose定义供父调用的方法及属性

1、定义子类LoginForm&#xff1a; function handleLogin(account, token) {console.log(account,token)}defineExpose({handleLogin,}); 2、父类调用子类组件 const loginFormRef ref(); <LoginForm ref"loginFormRef" />loginFormRef.value.handleLogin(…

仓库管理系统23--用户管理

原创不易&#xff0c;打字不易&#xff0c;截图不易&#xff0c;多多点赞&#xff0c;送人玫瑰&#xff0c;留有余香&#xff0c;财务自由明日实现 1、创建用户管理的用户控件 <UserControl x:Class"West.StoreMgr.View.UserInfoView"xmlns"http://schemas.…