31

.NET手撸绘制TypeScript类图——下篇

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654076941&%3Bidx=5&%3Bsn=f1cf50031dbb9ebe2bcb8cfbe9e4fc83
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

.NET手撸绘制TypeScript类图——下篇

在上篇的文章中,我们介绍了如何使用 .NET 解析  TypeScript ,这篇将介绍如何使用代码将类图渲染出来。

上篇文章链接: .NET手撸绘制TypeScript类图——上篇

类型定义渲染

不出意外,我们继续使用 FlysEngine 。虽然文字排版没做过,但不试试怎么知道好不好做呢?

正常实时渲染时,画一两行文字可能很容易,但绘制大量文字时,就需要引入一些排版操作了。为了实现排板,首先需要将 ClassDef 类扩充一下——干脆再加个  RenderingClassDef 类,包含一个  ClassDef

它包含了一些位置和大小信息,并提供了一个中间值的变量。之所以这样定义,因为这里存在了一些挺麻烦的过程,比如想想以下操作:

  • 如果我想绘制放在中间的  类名 ,我就必须知道所有行的宽度

  • 如果我想绘制边框,我也必须知道所有行的高度

还好 Direct2DDirectWrite 提供了方块的文字宽度、高度计算属性,通过  .Metrics 即可获取。有了这个,排板过程中,我认为最难处理的是  y 坐标了,它是一个状态机,需要实时去更新、计算  y 坐标的位置,绘制过程如下:

请注意变量 y 的使用,我使用了一个  LINQ 中的  Aggregate ,实时的绘制并统计  y 变量的最新值,让代码简化了不少。

这里我又取巧了,正常文章排板应该是 x 和  y 都需要更新,但这里 每个定义都固定为一行 ,因此我不需要关心  x 的位置。但如果您想搞一些更 的操作,如所有 类型着个色 ,这时只需要同时更新  x 和  y 即可。

此时渲染出来效果如下:

aYfIn2F.png!web

可见 类图 可能太小,我们可能需要局部放大一点,然后类图之间产生了重叠,我们需要拖拽的方式来移动到正确位置。

放大和缩小

由于我们使用了 Direct2D ,无损的高清放大变得非常容易,首先我们需要定义一个变量,并响应鼠标滚轮事件:

其中魔术值 1.1 代表,鼠标每滚动一次,放大  1.1 倍。

另外 mousePos 变量由鼠标移动事件的  X 和  Y 坐标经  worldTransform 的逆变换计算而来:

注意:

矩阵逆变换涉及一些高等数学中的线性代数知识,没必要立即掌握。只需知道矩阵变换可以变换点位置,矩阵逆变换可以恢复原点的位置。

在本文中鼠标移动的坐标是窗体提供的,换算成真实坐标,即需要进行“矩阵逆变换”——这在碰撞检测中很常见。

以防我有需要,我们还再加一个快捷键,按空格即可立即恢复缩放:

然后在 OnDraw 事件中,将  worldTransform 应用起来即可:

运行效果如下(注意放大缩小时,会以鼠标位置为中心点进行):

IJRFZvq.gif

碰撞检测和拖拽

拖拽而已,为什么会和碰撞检测有关呢?

这是因为拖拽时,必须知道鼠标是否处于元素的上方,这就需要碰撞检测了。

首先给 RenderingClassDef 方法加一个  TestPoint() 方法,判断是鼠标是否与绘制位置重叠,这里我使用了  SharpDX 提供的  RectangleF.Contains(Vector2) 方法,具体算法已经不用关心,调用函数即可:

然后在 OnDraw 方法中,做一个判断,如果类方框与鼠标出现重叠,则画一个宽度  2.0 的红色的边框,代码如下:

测试效果如下(注意鼠标位置和红框):

6VJrMzn.gif

碰撞检测做好,就能写代码拖拽了。要实现拖拽,首先需要在 RenderingClassDef 类中定义两个变量,用于保存其起始位置和鼠标起始位置,用于计算鼠标移动距离:

然后在鼠标按下、鼠标移动、鼠标松开时进行判断,如果鼠标按下时处于某个类的方框里面,则记录这两个起始值:

如果鼠标移动时,且有类的方框处于有值的状态,则计算偏移量,并让该方框随着鼠标移动:

如果鼠标松开,则清除该记录值:

此时,运行效果如下:

MVvMrqU.gif

类型间的关系

类型和类型之间是有依赖关系的,这也应该通过图形的方式体现出来。使用 DeviceContext.DrawLine() 方法即可画出线条,注意先画的会被后画的覆盖,因此这个  foreach 需要放在  OnDraw 方法的  foreach 语句之前:

此时,运行效果如下:

eaAFFzi.jpg!web

注意:在真正的 UML 图中,除了依赖关系,继承关系也是需要体现的。而且线条是有箭头、且线条类型也是有讲究的,  Direct2D 支持自定义线条,这些都能做,权当留给各位自己去挑战尝试了。

方框顺序

现在我们不能决定哪个在前,哪个在后,想象中方框可能应该就像窗体一样,客户点击哪个哪个就应该提到最前,这可以通过一个 ZIndex 变量来表示,首先在  RenderingClassDef 类中加一个属性:

然后在鼠标点击事件中,判断如果击中该类的方框,则将 ZIndex 赋值为最大值加1:

然后在 OnDraw 方法的第二个  foreach 循环,改成按  ZIndex 从小到大排序渲染即可:

运行效果如下(注意我的鼠标点击和前后顺序):

zIZbArV.gif

其实这是一个真实的需求,我们公司写代码时要求设计文档,通常我们都使用 ProcessOn 等工具来绘制,但前端开发者通过需要面对好几屏幕的类、方法和属性,然后弄将其名称、参数和类型一一拷贝到该工具中,这是一个需要极大耐心的工作。

“哪里有需求,哪里就有办法”,这个小工具也许能给我们的客户少许帮助,我正准备“说干就干”时——有人提醒我,我们的开发流程要先出文档,再写代码。所以……理论上不应该存在这种工具:joy:

但后来有一天,某同事突然点醒了我,“为什么不能有呢?这就叫 CodeFirst 设计!”——是啊,  EntityFramework 也提供了  CodeFirst 设计,很合理嘛,所以最后,就有了本篇文章:grin:。

微信公众号无法评论,有什么想法各位可以转至我的博客园留言/评论/点赞:https://www.cnblogs.com/sdflysha/p/20191114-ts-uml-with-dotnet-2.html

本文所用到的 完整代码 ,可以在我的  Github 仓库中下载:https://github.com/sdcb/blog-data/tree/master/2019/20191113-ts-uml-with-dotnet

b2Q7ryq.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK