实现UML 序列图 -- Csdn MarkDown 第六篇 技术篇

这一篇开始讨论代码层面的问题。主要是markdown的功能的设计实现。

这一篇先从markdown外围的代码开始讲起:
UML序列图是从 https://github.com/bramp/js-sequence-diagrams
copy得到的。

当如这个UML代码还用到了两个JS,功能库和画图.

<script src="underscore-min.js"></script>
<script src="raphael-min.js"></script>

首先讲讲这个JS最核心的类:
[ https://github.com/bramp/js-sequence-diagrams/blob/master/src/sequence-
diagram.js ](https://github.com/bramp/js-sequence-diagrams/blob/master/src
/sequence-diagram.js)

首先这个类分为其实使用两种风格的。一种就是现在CSDN采用的风格,叫做simple. 另外一种是手绘风格,叫做hand。定义如下:

var themes = {
simple : RaphaelTheme,
hand : HandRaphaelTheme
};
Diagram.prototype.drawSVG = function (container, options) {
var default_options = {
theme: 'hand'
};
options = _.defaults(options || {}, default_options);
if (!(options.theme in themes))
throw new Error("Unsupported theme: " + options.theme);
var drawing = new themes[options.theme](this);
drawing.draw(container);
}; // end of drawSVG

drawSVG是最外层JS要画图调用的方法。例如:

<div id="diagram">Diagram will be placed here</div>
<script src="sequence-diagram-min.js"></script>
<script> 
  var diagram = Diagram.parse("A->B: Does something");
  diagram.drawSVG('diagram');
</script>

下面就可以画出图形。所以drawSVG是核心入口。

drawSVG的实现过程中,最重要的一句:

drawing.draw(container);

这一句调用的draw function定义如下:

    draw : function(container) {
        var diagram = this.diagram;
        this.init_paper(container);
        this.init_font();
        this.layout();
        var title_height = this._title ? this._title.height : 0;
        this._paper.setStart();
        this._paper.setSize(diagram.width, diagram.height);
        var y = DIAGRAM_MARGIN + title_height;
        this.draw_title();
        this.draw_actors(y);
        this.draw_signals(y + this._actors_height);
        this._paper.setFinish();
},

这几句看起来平淡无奇,就是初始化背景,初始化字体,初始化各种,然后画标题,画角色(也就是序列图中的实体),画调用箭头。

这里面看似简单,但是最为繁琐的就是:

this.layout();

布局:要遍历所有的要画到画布上的东西,要提前预知,各个东西的长宽,然后根据长宽,各个东西相距的距离,算出画布的尺寸。这段JS并没有做到固定长宽后,各个东西等
比缩放。

这个做完之后,开始画角色(也就是实体)

Created with Raphaël 2.1.2 A A 这就是角色A 包括上下两个方块和中间的竖线

再然后,两个角色之间是有间距的。例如:

Created with Raphaël 2.1.2 距离是什么? A A B B 我们两个角色之间的间距 包括上下两个方块和中间的竖线

间距的计算并不是很容易,首先确定每个角色的基本的高和宽,然后根据将信号signal计算在内(signal 包括两种,一种是 箭头关系,另外一种是 注释框)所
以大家可以看到注释框和箭头也是有从上到下的顺序的。signal的宽度和字的个数以及宽度均有关系(这里计算较为复杂,此处略去太多字…有兴趣的看源码)。

然后顺序遍历,依次确定A,B,C…各个角色所在的绝对坐标。

每一个signal都是有高度的,将所有的signal 高度加和,就得到了下端角色方框的坐标。然后连线就大功告成。

看源码发现了许多没有的功能…

例如 UML中增加title
以及UML中注释框除了Right, Left, 还有Over, 对于Over还可以置于多个角色之上。

title 和 Over

1
2
3
4
5
title: 距离是什么?

A->B: 我们两个角色之间的间距
Note Over A,B: 包括上下两个方块和中间的竖线
Note Over A: I'm A

表现:

Created with Raphaël 2.1.2 距离是什么? A A B B 我们两个角色之间的间距 包括上下两个方块和中间的竖线
I’m A

[ https://github.com/bramp/js-sequence-diagrams/blob/master/src/jquery-
plugin.js ](https://github.com/bramp/js-sequence-diagrams/blob/master/src
/jquery-plugin.js)

这里写图片描述

这个类中,如果没有定义option属性,那么就只能用simple的形式了。感觉CSDN的dev好像没有定义这个option. 我这边也无能为力。

[ https://github.com/bramp/js-sequence-diagrams/blob/master/src/diagram.js]

这个类没有什么好讲的,就是通过parser构造出来的实体类。

剩下的两个gammar JS. 这种词法解析的放到后面一篇再说. 网络实在不好,啥都timeout, 另外也该睡觉了。