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

一、前言

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

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

本文地址: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]-