一、前言

前面的课程我们添加了诸多形状,但连线还只有直线这一种样式,而且也只能连接形状的中心点。我们本节课就来增加一种很常见的连线样式:贝塞尔曲线。同时也对形状增加多个不同的连接点,不再只连中心了。

相信看完的你,一定会有所收获!

本文地址:https://www.cnblogs.com/lesliexin/p/19033113

二、先看效果

通过视频我们可以看到,我们本节课主要实现3个效果:

1,支持添加贝塞尔曲线

2,所有形状都支持上、下、左、右四个连接点

3,对贝塞尔曲线的控制点进行了优化,效果更好

下面我们就分别来讲解一下。

随便说一下,从本节开始我们的代码就和Gitee上同步了,读者可自行拉取编译。

Gitee地址:https://gitee.com/lesliexin/flowchartdemo

三、效果1:添加贝塞尔曲线

(一)贝塞尔曲线类

首先我们来看一下MSDN上对于贝塞尔曲线的说明:

可以看到共需要4个点:起点、控制点1、控制点2、终点。

我们的连线定义里有开始形状和结束形状,可获取到起点和终点,那么还需要额外定义两个属性——控制点1和控制点2——吗?

答案是:不需要。我们的目的不是做成是PS中的钢笔那样可以可以自由调整控制点来改变连线的样式,我们的目的是提供一个统一规则样式的连线,所以我们只需要按照一定的规则根据起点和终点计算出这两个控制点即可。

我们先定义一个简单的规则:控制点1:起点右侧50像素;控制点2:终点左侧50像素。

规则很简单,也不太完善,我们先照此实现,下文会进行优化。

下面我们就基于连线基类派生出一个贝塞尔曲线类:

可以看到,实现起来还是很简单的。

(二)画布增加对贝塞尔曲线的支持

我们现在的一切都是在画布(FCCanvas)中实现,所以我们来看一下如何在画布中添加对贝塞尔曲线的支持。

我们先来看一下“直线连线”是在画布的哪里使用的:

可以看到是在OnMouseDown事件中判断选择了两个形状后便添加了一条新的直线连线,那么我们就要控制一下,可以根据需要添加直线还是贝塞尔曲线。

我们定义一个属性,来控制添加连线时添加的是什么类型连线:

这里我们定义为string类型,一是方便后续扩展,如果定义为bool、int、甚至是enum,扩展性都不太好,因为后续的连线可能是第三方自定义的,非string类型要么阅读性不好要么扩展性不好;二是方便后续动态反射,这个对于后续开放用户自定义形状是必要的。

然后,我们在OnMouseDown事件中,根据此属性来判断添加哪种类型:

(三),应用画布

首先,我们添加一个下拉框控件,内容为直线和贝塞尔曲线:

在下拉框的改变选中项事件中,我们根据选择的项不同,设置画布的连线类型:

到此,我们就实现了贝塞尔曲线的支持,是不是感觉意外的简单?这就是抽象的好处。

注:从本节开始,课程代码就提交到了GITEE上了,大家可自行拉取编译。

四、效果2:上、下、左、右连接点

我们之前的课程都是直接连接的形状的中心点,效果是一言难尽,主要是方便讲解用。随着我们的课程步入正轨,这点也需要同步解决,我们本节就来扩展一下,支持上、下、左、右四个连接点。

注:近期阶段支持上、下、左、右四个连接点已经足够用了,当然后期我们会开放此限制,完全的由派生类自行定义有多少个连接点、各连接点的坐标又是哪里。

(一)形状基类扩展

因为这是所有形状的改动,所以我们要对形状基类进行扩展。

注:为了讲解课程需要,是重新下定义了个基类: ShapeBaseV2,大家自行实现时直接修改原类即可。下同,不再赘述。

1,连接点枚举

我们定义了四个连接点,从左开始顺时针依次是:左->上->右->下。

2,获取连接点方法

我们同样定义一个带默认实现的虚方法。因为我们现在的形状都是基于一个“矩形”来绘制的,所以默认实现就是矩形的四个边的中点坐标:

我们定义为虚方法,是方便派生形状自行决定连接点的坐标是哪里,像平行四边形,左、右两个连接点就是进行偏移才能在斜边上。

3,移除获取中心点方法

因为现在已经有连线点了,所以不再需要获取中心点的方法,我们直接移除即可。

(二)形状派生类修改

形状基类已经修改,所以我们的派生类也需要同步修改。

1,平行四边形

因为其它形状的四个边都是与矩形区域重合的,所以不需要修改。

哪怕是菱形,其四个顶点正好是矩形区域的四个边的中心点,所以也不需要修改。

只有平行四边形需要重写获取连接点坐标方法,因为左、右两个连接点要进行偏移才能在斜边上:

(三)连线基类修改

上面的形状已经支持了不同的连接点,那么连线也要做同步的修改,主要是要记录开始形状的连接点、和结束形状的连接点。

(四)连线派生修改

我们对连线的修改是获取开始点和结束点坐标的地方,我们之前是调用形状的获取中心点方法,而现在要改成根据连接点获取对应连接点坐标。

1,直线

2,贝塞尔曲线

(五)画布增加连接点支持

当形状和连线都修改好后,我们的画布也要同步添加对连接点的支持。

因为这次是让连线支持连接上、下、左、右不同的连接点,所以我们的核心便是记录下连线开始和结束形状的连接点信息然后在生成连接时将连接点信息赋于连线。

所以我们先定义两个属性:

然后在OnMouseDown中,添加连线时将这两个属性的值赋给连线:

(六)应用

我们下面就来编写程序来看下不同连接点的效果:

1,设计器界面

我们增加两个下拉框,来分别设置开始和结束的连接点是上、下、左、右中的哪个:

注:这种方式看文首的演示视频就会发现用起来很繁琐,不直观。我们要一点点深入,在后面我们就会对其改造成鼠标点击开始形状的某一连接点,然后拖动着去结束形状的某一连接点,松开鼠标完成连线操作,而且在拖动时会实时显示连接的半透明虚线效果。

2,切换连接点

我们在下拉框的选择改变事件中,对画布的连接点属性进行修改:

开始形状的连接点:

结束形状的连接点:

就像上一小节一样,很简单的改动,就支持了不同的连接线,大家可自动拉取代码编译尝试。

五、实现效果3:贝塞尔曲线控制点优化

我们实现了上下左右连接点后会发现(看文首的演示视频可以明白):贝塞尔曲线效果只有在某些情况下效果很好,其它时候曲线的效果都很别扭。这是因为我们在第1小节定义贝塞尔曲线时,两个控制点是写死的,而现在我们支持了上下左右不同的连接点,所以我们的贝塞尔曲线也得根据连接点的不同而同步调整控制点的位置。

我们先看下原本控制点的计算方式:

这种情况只适合:开始形状在结束形状的左侧、开始形状的连接点是‘右’、结束形状的连接点是‘左’。所以我们依此规律,根据连接点的不同而计算不同的坐标:

当然,计算控制点的方法有很多,不同的控制点曲线的效果也不相同,我们这里采取的是比较简单的方式,效果不错。

我们只需要修改贝塞尔曲线的定义即可,其它的画布、程序都不需要改动,就可以看到效果了。

随课代码为了每节课不干扰,所以需要重新修改画布、程序等等。

六、结语

我们本节课实现了一个新的连线样式:贝塞尔曲线,同时还支持了上、下、左、右四个连接点,到此终于有点正式流程图的样子了。

我们看本节的代码就会感受到,添加新功能意外的改动很少,这就是抽象的魅力。

其实到本节课为止,基础的东西就讲完了,我们后面的课程就是要在各个细节处下功夫了,向着正规的流程图去前进。后面的课程,实现思路和逻辑大于技术代码,更多的是如何去实现、去更好的实现想要的效果,而这些,对于任何框架和语言都有用。

下节课,我们就来解决本节课选择连接点繁琐的问题,我们来实现鼠标拖动到连接点来添加连接。敬请期待。

感谢大家的观看,本人水平有限,文章不足之处欢迎大家评论指正。

-[END]-

[原创]《C#高级GDI+实战:从零开发一个流程图》第09章:增加贝塞尔曲线,上、下、左、右连接点的更多相关文章

  1. 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)

    本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...

  2. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

  3. 【Nodejs】326- 从零开发一个node命令行工具

    本文由 IMWeb 社区授权转载自腾讯内部 KM 论坛.点击阅读原文查看 IMWeb 社区更多精彩文章. 什么是命令行工具? 命令行工具(Cmmand Line Interface)简称cli,顾名思 ...

  4. Django实战总结 - 快速开发一个数据库查询工具

    一.简介 Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 只要很少的代码就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 ...

  5. ios 给微信开发一个插件并安装到未越狱的手机上教程

    现来整体说一下思路,首先给越狱的手机开发一个插件并安装上去,然后去越狱手机上找到相应的动态库和对应的微信APP安装包,拷贝出来,然后重新签名,就可以安装到未越狱的手机上了 1.首先你的电脑需要安装th ...

  6. 如何从零开发一个NuGet软件包?

    作者:依乐祝 首发地址:https://www.cnblogs.com/yilezhu/p/14175019.html 我想目前每个.net开发人员都应该知道nuget.org和NuGet软件包吧.但 ...

  7. 【前端vue进阶实战】:从零打造一个流程图、拓扑图项目【Nuxt.js + Element + Vuex】 (一)

    本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 开源js绘图库,打造一个属于自己的在线绘图软件,最终效果:topology.le5le.com .如果你觉得好,欢 ...

  8. 【原创】新手入门一篇就够:从零开发移动端IM

    一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...

  9. 《IM开发新手入门一篇就够:从零开发移动端IM》

        登录 立即注册 TCP/IP详解 资讯 动态 社区 技术精选 首页   即时通讯网›专项技术区›IM开发新手入门一篇就够:从零开发移动端IM   帖子 打赏 分享 发表评论162     想开 ...

  10. Android | 教你如何用华为HMS MLKit SDK 三十分钟在安卓上开发一个微笑抓拍神器

    Android | 只要三十分钟就可以在手机上开发一个微笑抓拍神器!!! 前言 前段时间Richard Yu在发布会上给大家介绍了华为HMS Core4.0,回顾发布会信息请戳: 华为面向全球发布HM ...

随机推荐

  1. SynchronizedHashMap和ConcurrentHashMap的区别

    ConcurrrentHashMap   ConcurrentHashMap 使用锁分离技术来保证在多线程下的性能.它每次锁住一个桶,默认将 hash 表分为 16 个桶,诸如put和remove 等 ...

  2. 代码随想录第十天 | 栈与队列part02

      第五章 栈与队列part02 150. 逆波兰表达式求值 本题不难,但第一次做的话,会很难想到,所以先看视频,了解思路再去做题 题目链接/文章讲解/视频讲解:https://programmerc ...

  3. 【中文】【吴恩达课后编程作业】Course 5 - 序列模型 - 第三周作业 - 机器翻译与触发词检测

    [中文][吴恩达课后编程作业]Course 5 - 序列模型 - 第三周作业 - 机器翻译与触发词检测 上一篇:[课程5 - 第三周测验]※※※※※ [回到目录]※※※※※下一篇:无 致谢: 感谢@e ...

  4. 2025私域运营工具攻略:9款AI+SCRM神器助你留存爆发

    私域流量的战火在2025年依旧熊熊燃烧.相比于烧钱获取公域流量,精细化运营私域用户成为越来越多企业的共识.但真正做得好的运营者都明白,留存和转化不是靠刷屏,而是靠体系和工具支撑. 这篇文章,我们将围绕 ...

  5. 【Java并发编程】面试必备之线程池

    什么是线程池 是一种基于池化思想管理线程的工具. 池化技术:池化技术简单点来说,就是提前保存大量的资源,以备不时之需.比如我们的对象池,数据库连接池等. 线程池好处 我们为什么要使用线程池,直接new ...

  6. 使用hive数据查询小结

    业务背景: 公司大数据查询需要通过hive查询和分析一些数据 产品提出业务分析需求: 我的处理方式: 接到需求就想怎么写SQL语句,然后不断调整SQL语句进行验证,最后这个需求写了170行的SQL语句 ...

  7. 有知道CAE软件Hypermesh的配置要求吗?

    Altair的Hypermesh是一款先进的有限元分析软件,用于高效地处理和模拟复杂的三维几何形状.作为一款仿真软件,Hypermesh的CPU和GPU配置是非常重要的. 首先,对于Hypermesh ...

  8. DotTrace系列:6. 程序异常诊断 和 Request慢处理

    一:背景 1. 讲故事 在我分析的众多dump中,有一些CPU爆高是因为高频的抛 Exception 导致,比如下面这张图,有 19 个线程都在抛 xxxResultException 异常. 从卦中 ...

  9. C++ set/multiset容器 学习总结

    -------------------------------------set/multiset容器 set/multiset特性 set/multiset的特性是所有元素会根据元素的值自动进行排序 ...

  10. C++ 习惯RAII思想

    什么是 RAII RAII(资源获取即初始化,Resource Acquisition Is Initialization),作为 C++ 的一个重要编程范式,已经被贯彻于标准库的各个角落.RAII ...