vis-rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.project +11 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/vis/rails/engine.rb +6 -0
- data/lib/vis/rails/version.rb +5 -0
- data/lib/vis/rails.rb +7 -0
- data/vendor/assets/javascripts/vis.js +1 -0
- data/vendor/assets/stylesheets/vis.css +3 -0
- data/vendor/assets/vis/DataSet.js +936 -0
- data/vendor/assets/vis/DataView.js +281 -0
- data/vendor/assets/vis/EventBus.js +89 -0
- data/vendor/assets/vis/events.js +116 -0
- data/vendor/assets/vis/graph/ClusterMixin.js +1019 -0
- data/vendor/assets/vis/graph/Edge.js +620 -0
- data/vendor/assets/vis/graph/Graph.js +2111 -0
- data/vendor/assets/vis/graph/Groups.js +80 -0
- data/vendor/assets/vis/graph/Images.js +41 -0
- data/vendor/assets/vis/graph/NavigationMixin.js +245 -0
- data/vendor/assets/vis/graph/Node.js +978 -0
- data/vendor/assets/vis/graph/Popup.js +105 -0
- data/vendor/assets/vis/graph/SectorsMixin.js +547 -0
- data/vendor/assets/vis/graph/SelectionMixin.js +515 -0
- data/vendor/assets/vis/graph/dotparser.js +829 -0
- data/vendor/assets/vis/graph/img/downarrow.png +0 -0
- data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
- data/vendor/assets/vis/graph/img/minus.png +0 -0
- data/vendor/assets/vis/graph/img/plus.png +0 -0
- data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
- data/vendor/assets/vis/graph/img/uparrow.png +0 -0
- data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
- data/vendor/assets/vis/graph/shapes.js +225 -0
- data/vendor/assets/vis/module/exports.js +68 -0
- data/vendor/assets/vis/module/header.js +24 -0
- data/vendor/assets/vis/module/imports.js +32 -0
- data/vendor/assets/vis/shim.js +252 -0
- data/vendor/assets/vis/timeline/Controller.js +172 -0
- data/vendor/assets/vis/timeline/Range.js +553 -0
- data/vendor/assets/vis/timeline/Stack.js +192 -0
- data/vendor/assets/vis/timeline/TimeStep.js +449 -0
- data/vendor/assets/vis/timeline/Timeline.js +476 -0
- data/vendor/assets/vis/timeline/component/Component.js +148 -0
- data/vendor/assets/vis/timeline/component/ContentPanel.js +113 -0
- data/vendor/assets/vis/timeline/component/CurrentTime.js +101 -0
- data/vendor/assets/vis/timeline/component/CustomTime.js +255 -0
- data/vendor/assets/vis/timeline/component/Group.js +129 -0
- data/vendor/assets/vis/timeline/component/GroupSet.js +546 -0
- data/vendor/assets/vis/timeline/component/ItemSet.js +612 -0
- data/vendor/assets/vis/timeline/component/Panel.js +112 -0
- data/vendor/assets/vis/timeline/component/RootPanel.js +215 -0
- data/vendor/assets/vis/timeline/component/TimeAxis.js +522 -0
- data/vendor/assets/vis/timeline/component/css/currenttime.css +5 -0
- data/vendor/assets/vis/timeline/component/css/customtime.css +6 -0
- data/vendor/assets/vis/timeline/component/css/groupset.css +59 -0
- data/vendor/assets/vis/timeline/component/css/item.css +93 -0
- data/vendor/assets/vis/timeline/component/css/itemset.css +17 -0
- data/vendor/assets/vis/timeline/component/css/panel.css +14 -0
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +41 -0
- data/vendor/assets/vis/timeline/component/css/timeline.css +2 -0
- data/vendor/assets/vis/timeline/component/item/Item.js +81 -0
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +302 -0
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +237 -0
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +251 -0
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +91 -0
- data/vendor/assets/vis/util.js +673 -0
- data/vis-rails.gemspec +47 -0
- metadata +142 -0
@@ -0,0 +1,620 @@
|
|
1
|
+
/**
|
2
|
+
* @class Edge
|
3
|
+
*
|
4
|
+
* A edge connects two nodes
|
5
|
+
* @param {Object} properties Object with properties. Must contain
|
6
|
+
* At least properties from and to.
|
7
|
+
* Available properties: from (number),
|
8
|
+
* to (number), label (string, color (string),
|
9
|
+
* width (number), style (string),
|
10
|
+
* length (number), title (string)
|
11
|
+
* @param {Graph} graph A graph object, used to find and edge to
|
12
|
+
* nodes.
|
13
|
+
* @param {Object} constants An object with default values for
|
14
|
+
* example for the color
|
15
|
+
*/
|
16
|
+
function Edge (properties, graph, constants) {
|
17
|
+
if (!graph) {
|
18
|
+
throw "No graph provided";
|
19
|
+
}
|
20
|
+
this.graph = graph;
|
21
|
+
|
22
|
+
// initialize constants
|
23
|
+
this.widthMin = constants.edges.widthMin;
|
24
|
+
this.widthMax = constants.edges.widthMax;
|
25
|
+
|
26
|
+
// initialize variables
|
27
|
+
this.id = undefined;
|
28
|
+
this.fromId = undefined;
|
29
|
+
this.toId = undefined;
|
30
|
+
this.style = constants.edges.style;
|
31
|
+
this.title = undefined;
|
32
|
+
this.width = constants.edges.width;
|
33
|
+
this.value = undefined;
|
34
|
+
this.length = constants.edges.length;
|
35
|
+
|
36
|
+
this.from = null; // a node
|
37
|
+
this.to = null; // a node
|
38
|
+
|
39
|
+
// we use this to be able to reconnect the edge to a cluster if its node is put into a cluster
|
40
|
+
// by storing the original information we can revert to the original connection when the cluser is opened.
|
41
|
+
this.originalFromId = [];
|
42
|
+
this.originalToId = [];
|
43
|
+
|
44
|
+
this.connected = false;
|
45
|
+
|
46
|
+
// Added to support dashed lines
|
47
|
+
// David Jordan
|
48
|
+
// 2012-08-08
|
49
|
+
this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
|
50
|
+
|
51
|
+
this.stiffness = undefined; // depends on the length of the edge
|
52
|
+
this.color = constants.edges.color;
|
53
|
+
this.widthFixed = false;
|
54
|
+
this.lengthFixed = false;
|
55
|
+
|
56
|
+
this.setProperties(properties, constants);
|
57
|
+
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Set or overwrite properties for the edge
|
62
|
+
* @param {Object} properties an object with properties
|
63
|
+
* @param {Object} constants and object with default, global properties
|
64
|
+
*/
|
65
|
+
Edge.prototype.setProperties = function(properties, constants) {
|
66
|
+
if (!properties) {
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
70
|
+
if (properties.from !== undefined) {this.fromId = properties.from;}
|
71
|
+
if (properties.to !== undefined) {this.toId = properties.to;}
|
72
|
+
|
73
|
+
if (properties.id !== undefined) {this.id = properties.id;}
|
74
|
+
if (properties.style !== undefined) {this.style = properties.style;}
|
75
|
+
if (properties.label !== undefined) {this.label = properties.label;}
|
76
|
+
if (this.label) {
|
77
|
+
this.fontSize = constants.edges.fontSize;
|
78
|
+
this.fontFace = constants.edges.fontFace;
|
79
|
+
this.fontColor = constants.edges.fontColor;
|
80
|
+
if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;}
|
81
|
+
if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;}
|
82
|
+
if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;}
|
83
|
+
}
|
84
|
+
if (properties.title !== undefined) {this.title = properties.title;}
|
85
|
+
if (properties.width !== undefined) {this.width = properties.width;}
|
86
|
+
if (properties.value !== undefined) {this.value = properties.value;}
|
87
|
+
if (properties.length !== undefined) {this.length = properties.length;}
|
88
|
+
|
89
|
+
// Added to support dashed lines
|
90
|
+
// David Jordan
|
91
|
+
// 2012-08-08
|
92
|
+
if (properties.dash) {
|
93
|
+
if (properties.dash.length !== undefined) {this.dash.length = properties.dash.length;}
|
94
|
+
if (properties.dash.gap !== undefined) {this.dash.gap = properties.dash.gap;}
|
95
|
+
if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;}
|
96
|
+
}
|
97
|
+
|
98
|
+
if (properties.color !== undefined) {this.color = properties.color;}
|
99
|
+
|
100
|
+
// A node is connected when it has a from and to node.
|
101
|
+
this.connect();
|
102
|
+
|
103
|
+
this.widthFixed = this.widthFixed || (properties.width !== undefined);
|
104
|
+
this.lengthFixed = this.lengthFixed || (properties.length !== undefined);
|
105
|
+
this.stiffness = 1 / this.length;
|
106
|
+
|
107
|
+
// set draw method based on style
|
108
|
+
switch (this.style) {
|
109
|
+
case 'line': this.draw = this._drawLine; break;
|
110
|
+
case 'arrow': this.draw = this._drawArrow; break;
|
111
|
+
case 'arrow-center': this.draw = this._drawArrowCenter; break;
|
112
|
+
case 'dash-line': this.draw = this._drawDashLine; break;
|
113
|
+
default: this.draw = this._drawLine; break;
|
114
|
+
}
|
115
|
+
};
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Connect an edge to its nodes
|
119
|
+
*/
|
120
|
+
Edge.prototype.connect = function () {
|
121
|
+
this.disconnect();
|
122
|
+
|
123
|
+
this.from = this.graph.nodes[this.fromId] || null;
|
124
|
+
this.to = this.graph.nodes[this.toId] || null;
|
125
|
+
this.connected = (this.from && this.to);
|
126
|
+
|
127
|
+
if (this.connected) {
|
128
|
+
this.from.attachEdge(this);
|
129
|
+
this.to.attachEdge(this);
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
if (this.from) {
|
133
|
+
this.from.detachEdge(this);
|
134
|
+
}
|
135
|
+
if (this.to) {
|
136
|
+
this.to.detachEdge(this);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
};
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Disconnect an edge from its nodes
|
143
|
+
*/
|
144
|
+
Edge.prototype.disconnect = function () {
|
145
|
+
if (this.from) {
|
146
|
+
this.from.detachEdge(this);
|
147
|
+
this.from = null;
|
148
|
+
}
|
149
|
+
if (this.to) {
|
150
|
+
this.to.detachEdge(this);
|
151
|
+
this.to = null;
|
152
|
+
}
|
153
|
+
|
154
|
+
this.connected = false;
|
155
|
+
};
|
156
|
+
|
157
|
+
/**
|
158
|
+
* get the title of this edge.
|
159
|
+
* @return {string} title The title of the edge, or undefined when no title
|
160
|
+
* has been set.
|
161
|
+
*/
|
162
|
+
Edge.prototype.getTitle = function() {
|
163
|
+
return this.title;
|
164
|
+
};
|
165
|
+
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Retrieve the value of the edge. Can be undefined
|
169
|
+
* @return {Number} value
|
170
|
+
*/
|
171
|
+
Edge.prototype.getValue = function() {
|
172
|
+
return this.value;
|
173
|
+
};
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Adjust the value range of the edge. The edge will adjust it's width
|
177
|
+
* based on its value.
|
178
|
+
* @param {Number} min
|
179
|
+
* @param {Number} max
|
180
|
+
*/
|
181
|
+
Edge.prototype.setValueRange = function(min, max) {
|
182
|
+
if (!this.widthFixed && this.value !== undefined) {
|
183
|
+
var scale = (this.widthMax - this.widthMin) / (max - min);
|
184
|
+
this.width = (this.value - min) * scale + this.widthMin;
|
185
|
+
}
|
186
|
+
};
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Redraw a edge
|
190
|
+
* Draw this edge in the given canvas
|
191
|
+
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
192
|
+
* @param {CanvasRenderingContext2D} ctx
|
193
|
+
*/
|
194
|
+
Edge.prototype.draw = function(ctx) {
|
195
|
+
throw "Method draw not initialized in edge";
|
196
|
+
};
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Check if this object is overlapping with the provided object
|
200
|
+
* @param {Object} obj an object with parameters left, top
|
201
|
+
* @return {boolean} True if location is located on the edge
|
202
|
+
*/
|
203
|
+
Edge.prototype.isOverlappingWith = function(obj) {
|
204
|
+
var distMax = 10;
|
205
|
+
|
206
|
+
var xFrom = this.from.x;
|
207
|
+
var yFrom = this.from.y;
|
208
|
+
var xTo = this.to.x;
|
209
|
+
var yTo = this.to.y;
|
210
|
+
var xObj = obj.left;
|
211
|
+
var yObj = obj.top;
|
212
|
+
|
213
|
+
|
214
|
+
var dist = Edge._dist(xFrom, yFrom, xTo, yTo, xObj, yObj);
|
215
|
+
|
216
|
+
return (dist < distMax);
|
217
|
+
};
|
218
|
+
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Redraw a edge as a line
|
222
|
+
* Draw this edge in the given canvas
|
223
|
+
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
224
|
+
* @param {CanvasRenderingContext2D} ctx
|
225
|
+
* @private
|
226
|
+
*/
|
227
|
+
Edge.prototype._drawLine = function(ctx) {
|
228
|
+
// set style
|
229
|
+
ctx.strokeStyle = this.color;
|
230
|
+
ctx.lineWidth = this._getLineWidth();
|
231
|
+
|
232
|
+
var point;
|
233
|
+
if (this.from != this.to) {
|
234
|
+
// draw line
|
235
|
+
this._line(ctx);
|
236
|
+
|
237
|
+
// draw label
|
238
|
+
if (this.label) {
|
239
|
+
point = this._pointOnLine(0.5);
|
240
|
+
this._label(ctx, this.label, point.x, point.y);
|
241
|
+
}
|
242
|
+
}
|
243
|
+
else {
|
244
|
+
var x, y;
|
245
|
+
var radius = this.length / 4;
|
246
|
+
var node = this.from;
|
247
|
+
if (!node.width) {
|
248
|
+
node.resize(ctx);
|
249
|
+
}
|
250
|
+
if (node.width > node.height) {
|
251
|
+
x = node.x + node.width / 2;
|
252
|
+
y = node.y - radius;
|
253
|
+
}
|
254
|
+
else {
|
255
|
+
x = node.x + radius;
|
256
|
+
y = node.y - node.height / 2;
|
257
|
+
}
|
258
|
+
this._circle(ctx, x, y, radius);
|
259
|
+
point = this._pointOnCircle(x, y, radius, 0.5);
|
260
|
+
this._label(ctx, this.label, point.x, point.y);
|
261
|
+
}
|
262
|
+
};
|
263
|
+
|
264
|
+
/**
|
265
|
+
* Get the line width of the edge. Depends on width and whether one of the
|
266
|
+
* connected nodes is selected.
|
267
|
+
* @return {Number} width
|
268
|
+
* @private
|
269
|
+
*/
|
270
|
+
Edge.prototype._getLineWidth = function() {
|
271
|
+
if (this.from.selected || this.to.selected) {
|
272
|
+
return Math.min(this.width * 2, this.widthMax)*this.graphScaleInv;
|
273
|
+
}
|
274
|
+
else {
|
275
|
+
return this.width*this.graphScaleInv;
|
276
|
+
}
|
277
|
+
};
|
278
|
+
|
279
|
+
/**
|
280
|
+
* Draw a line between two nodes
|
281
|
+
* @param {CanvasRenderingContext2D} ctx
|
282
|
+
* @private
|
283
|
+
*/
|
284
|
+
Edge.prototype._line = function (ctx) {
|
285
|
+
// draw a straight line
|
286
|
+
ctx.beginPath();
|
287
|
+
ctx.moveTo(this.from.x, this.from.y);
|
288
|
+
ctx.lineTo(this.to.x, this.to.y);
|
289
|
+
ctx.stroke();
|
290
|
+
};
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Draw a line from a node to itself, a circle
|
294
|
+
* @param {CanvasRenderingContext2D} ctx
|
295
|
+
* @param {Number} x
|
296
|
+
* @param {Number} y
|
297
|
+
* @param {Number} radius
|
298
|
+
* @private
|
299
|
+
*/
|
300
|
+
Edge.prototype._circle = function (ctx, x, y, radius) {
|
301
|
+
// draw a circle
|
302
|
+
ctx.beginPath();
|
303
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
304
|
+
ctx.stroke();
|
305
|
+
};
|
306
|
+
|
307
|
+
/**
|
308
|
+
* Draw label with white background and with the middle at (x, y)
|
309
|
+
* @param {CanvasRenderingContext2D} ctx
|
310
|
+
* @param {String} text
|
311
|
+
* @param {Number} x
|
312
|
+
* @param {Number} y
|
313
|
+
* @private
|
314
|
+
*/
|
315
|
+
Edge.prototype._label = function (ctx, text, x, y) {
|
316
|
+
if (text) {
|
317
|
+
// TODO: cache the calculated size
|
318
|
+
ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
|
319
|
+
this.fontSize + "px " + this.fontFace;
|
320
|
+
ctx.fillStyle = 'white';
|
321
|
+
var width = ctx.measureText(text).width;
|
322
|
+
var height = this.fontSize;
|
323
|
+
var left = x - width / 2;
|
324
|
+
var top = y - height / 2;
|
325
|
+
|
326
|
+
ctx.fillRect(left, top, width, height);
|
327
|
+
|
328
|
+
// draw text
|
329
|
+
ctx.fillStyle = this.fontColor || "black";
|
330
|
+
ctx.textAlign = "left";
|
331
|
+
ctx.textBaseline = "top";
|
332
|
+
ctx.fillText(text, left, top);
|
333
|
+
}
|
334
|
+
};
|
335
|
+
|
336
|
+
/**
|
337
|
+
* Redraw a edge as a dashed line
|
338
|
+
* Draw this edge in the given canvas
|
339
|
+
* @author David Jordan
|
340
|
+
* @date 2012-08-08
|
341
|
+
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
342
|
+
* @param {CanvasRenderingContext2D} ctx
|
343
|
+
* @private
|
344
|
+
*/
|
345
|
+
Edge.prototype._drawDashLine = function(ctx) {
|
346
|
+
// set style
|
347
|
+
ctx.strokeStyle = this.color;
|
348
|
+
ctx.lineWidth = this._getLineWidth();
|
349
|
+
|
350
|
+
// draw dashed line
|
351
|
+
ctx.beginPath();
|
352
|
+
ctx.lineCap = 'round';
|
353
|
+
if (this.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value
|
354
|
+
{
|
355
|
+
ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
|
356
|
+
[this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
|
357
|
+
}
|
358
|
+
else if (this.dash.length !== undefined && this.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value
|
359
|
+
{
|
360
|
+
ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
|
361
|
+
[this.dash.length,this.dash.gap]);
|
362
|
+
}
|
363
|
+
else //If all else fails draw a line
|
364
|
+
{
|
365
|
+
ctx.moveTo(this.from.x, this.from.y);
|
366
|
+
ctx.lineTo(this.to.x, this.to.y);
|
367
|
+
}
|
368
|
+
ctx.stroke();
|
369
|
+
|
370
|
+
// draw label
|
371
|
+
if (this.label) {
|
372
|
+
var point = this._pointOnLine(0.5);
|
373
|
+
this._label(ctx, this.label, point.x, point.y);
|
374
|
+
}
|
375
|
+
};
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Get a point on a line
|
379
|
+
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
|
380
|
+
* @return {Object} point
|
381
|
+
* @private
|
382
|
+
*/
|
383
|
+
Edge.prototype._pointOnLine = function (percentage) {
|
384
|
+
return {
|
385
|
+
x: (1 - percentage) * this.from.x + percentage * this.to.x,
|
386
|
+
y: (1 - percentage) * this.from.y + percentage * this.to.y
|
387
|
+
}
|
388
|
+
};
|
389
|
+
|
390
|
+
/**
|
391
|
+
* Get a point on a circle
|
392
|
+
* @param {Number} x
|
393
|
+
* @param {Number} y
|
394
|
+
* @param {Number} radius
|
395
|
+
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
|
396
|
+
* @return {Object} point
|
397
|
+
* @private
|
398
|
+
*/
|
399
|
+
Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
|
400
|
+
var angle = (percentage - 3/8) * 2 * Math.PI;
|
401
|
+
return {
|
402
|
+
x: x + radius * Math.cos(angle),
|
403
|
+
y: y - radius * Math.sin(angle)
|
404
|
+
}
|
405
|
+
};
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Redraw a edge as a line with an arrow halfway the line
|
409
|
+
* Draw this edge in the given canvas
|
410
|
+
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
411
|
+
* @param {CanvasRenderingContext2D} ctx
|
412
|
+
* @private
|
413
|
+
*/
|
414
|
+
Edge.prototype._drawArrowCenter = function(ctx) {
|
415
|
+
var point;
|
416
|
+
// set style
|
417
|
+
ctx.strokeStyle = this.color;
|
418
|
+
ctx.fillStyle = this.color;
|
419
|
+
ctx.lineWidth = this._getLineWidth();
|
420
|
+
|
421
|
+
if (this.from != this.to) {
|
422
|
+
// draw line
|
423
|
+
this._line(ctx);
|
424
|
+
|
425
|
+
// draw an arrow halfway the line
|
426
|
+
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
|
427
|
+
var length = 10 + 5 * this.width; // TODO: make customizable?
|
428
|
+
point = this._pointOnLine(0.5);
|
429
|
+
ctx.arrow(point.x, point.y, angle, length);
|
430
|
+
ctx.fill();
|
431
|
+
ctx.stroke();
|
432
|
+
|
433
|
+
// draw label
|
434
|
+
if (this.label) {
|
435
|
+
point = this._pointOnLine(0.5);
|
436
|
+
this._label(ctx, this.label, point.x, point.y);
|
437
|
+
}
|
438
|
+
}
|
439
|
+
else {
|
440
|
+
// draw circle
|
441
|
+
var x, y;
|
442
|
+
var radius = this.length / 4;
|
443
|
+
var node = this.from;
|
444
|
+
if (!node.width) {
|
445
|
+
node.resize(ctx);
|
446
|
+
}
|
447
|
+
if (node.width > node.height) {
|
448
|
+
x = node.x + node.width / 2;
|
449
|
+
y = node.y - radius;
|
450
|
+
}
|
451
|
+
else {
|
452
|
+
x = node.x + radius;
|
453
|
+
y = node.y - node.height / 2;
|
454
|
+
}
|
455
|
+
this._circle(ctx, x, y, radius);
|
456
|
+
|
457
|
+
// draw all arrows
|
458
|
+
var angle = 0.2 * Math.PI;
|
459
|
+
var length = 10 + 5 * this.width; // TODO: make customizable?
|
460
|
+
point = this._pointOnCircle(x, y, radius, 0.5);
|
461
|
+
ctx.arrow(point.x, point.y, angle, length);
|
462
|
+
ctx.fill();
|
463
|
+
ctx.stroke();
|
464
|
+
|
465
|
+
// draw label
|
466
|
+
if (this.label) {
|
467
|
+
point = this._pointOnCircle(x, y, radius, 0.5);
|
468
|
+
this._label(ctx, this.label, point.x, point.y);
|
469
|
+
}
|
470
|
+
}
|
471
|
+
};
|
472
|
+
|
473
|
+
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Redraw a edge as a line with an arrow
|
477
|
+
* Draw this edge in the given canvas
|
478
|
+
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
479
|
+
* @param {CanvasRenderingContext2D} ctx
|
480
|
+
* @private
|
481
|
+
*/
|
482
|
+
Edge.prototype._drawArrow = function(ctx) {
|
483
|
+
// set style
|
484
|
+
ctx.strokeStyle = this.color;
|
485
|
+
ctx.fillStyle = this.color;
|
486
|
+
ctx.lineWidth = this._getLineWidth();
|
487
|
+
|
488
|
+
// draw line
|
489
|
+
var angle, length;
|
490
|
+
if (this.from != this.to) {
|
491
|
+
// calculate length and angle of the line
|
492
|
+
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
|
493
|
+
var dx = (this.to.x - this.from.x);
|
494
|
+
var dy = (this.to.y - this.from.y);
|
495
|
+
var lEdge = Math.sqrt(dx * dx + dy * dy);
|
496
|
+
|
497
|
+
var lFrom = this.from.distanceToBorder(ctx, angle + Math.PI);
|
498
|
+
var pFrom = (lEdge - lFrom) / lEdge;
|
499
|
+
var xFrom = (pFrom) * this.from.x + (1 - pFrom) * this.to.x;
|
500
|
+
var yFrom = (pFrom) * this.from.y + (1 - pFrom) * this.to.y;
|
501
|
+
|
502
|
+
var lTo = this.to.distanceToBorder(ctx, angle);
|
503
|
+
var pTo = (lEdge - lTo) / lEdge;
|
504
|
+
var xTo = (1 - pTo) * this.from.x + pTo * this.to.x;
|
505
|
+
var yTo = (1 - pTo) * this.from.y + pTo * this.to.y;
|
506
|
+
|
507
|
+
ctx.beginPath();
|
508
|
+
ctx.moveTo(xFrom, yFrom);
|
509
|
+
ctx.lineTo(xTo, yTo);
|
510
|
+
ctx.stroke();
|
511
|
+
|
512
|
+
// draw arrow at the end of the line
|
513
|
+
length = 10 + 5 * this.width; // TODO: make customizable?
|
514
|
+
ctx.arrow(xTo, yTo, angle, length);
|
515
|
+
ctx.fill();
|
516
|
+
ctx.stroke();
|
517
|
+
|
518
|
+
// draw label
|
519
|
+
if (this.label) {
|
520
|
+
var point = this._pointOnLine(0.5);
|
521
|
+
this._label(ctx, this.label, point.x, point.y);
|
522
|
+
}
|
523
|
+
}
|
524
|
+
else {
|
525
|
+
// draw circle
|
526
|
+
var node = this.from;
|
527
|
+
var x, y, arrow;
|
528
|
+
var radius = this.length / 4;
|
529
|
+
if (!node.width) {
|
530
|
+
node.resize(ctx);
|
531
|
+
}
|
532
|
+
if (node.width > node.height) {
|
533
|
+
x = node.x + node.width / 2;
|
534
|
+
y = node.y - radius;
|
535
|
+
arrow = {
|
536
|
+
x: x,
|
537
|
+
y: node.y,
|
538
|
+
angle: 0.9 * Math.PI
|
539
|
+
};
|
540
|
+
}
|
541
|
+
else {
|
542
|
+
x = node.x + radius;
|
543
|
+
y = node.y - node.height / 2;
|
544
|
+
arrow = {
|
545
|
+
x: node.x,
|
546
|
+
y: y,
|
547
|
+
angle: 0.6 * Math.PI
|
548
|
+
};
|
549
|
+
}
|
550
|
+
ctx.beginPath();
|
551
|
+
// TODO: do not draw a circle, but an arc
|
552
|
+
// TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
|
553
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
554
|
+
ctx.stroke();
|
555
|
+
|
556
|
+
// draw all arrows
|
557
|
+
length = 10 + 5 * this.width; // TODO: make customizable?
|
558
|
+
ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
|
559
|
+
ctx.fill();
|
560
|
+
ctx.stroke();
|
561
|
+
|
562
|
+
// draw label
|
563
|
+
if (this.label) {
|
564
|
+
point = this._pointOnCircle(x, y, radius, 0.5);
|
565
|
+
this._label(ctx, this.label, point.x, point.y);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
};
|
569
|
+
|
570
|
+
|
571
|
+
|
572
|
+
/**
|
573
|
+
* Calculate the distance between a point (x3,y3) and a line segment from
|
574
|
+
* (x1,y1) to (x2,y2).
|
575
|
+
* http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
|
576
|
+
* @param {number} x1
|
577
|
+
* @param {number} y1
|
578
|
+
* @param {number} x2
|
579
|
+
* @param {number} y2
|
580
|
+
* @param {number} x3
|
581
|
+
* @param {number} y3
|
582
|
+
* @private
|
583
|
+
*/
|
584
|
+
Edge._dist = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
|
585
|
+
var px = x2-x1,
|
586
|
+
py = y2-y1,
|
587
|
+
something = px*px + py*py,
|
588
|
+
u = ((x3 - x1) * px + (y3 - y1) * py) / something;
|
589
|
+
|
590
|
+
if (u > 1) {
|
591
|
+
u = 1;
|
592
|
+
}
|
593
|
+
else if (u < 0) {
|
594
|
+
u = 0;
|
595
|
+
}
|
596
|
+
|
597
|
+
var x = x1 + u * px,
|
598
|
+
y = y1 + u * py,
|
599
|
+
dx = x - x3,
|
600
|
+
dy = y - y3;
|
601
|
+
|
602
|
+
//# Note: If the actual distance does not matter,
|
603
|
+
//# if you only want to compare what this function
|
604
|
+
//# returns to other results of this function, you
|
605
|
+
//# can just return the squared distance instead
|
606
|
+
//# (i.e. remove the sqrt) to gain a little performance
|
607
|
+
|
608
|
+
return Math.sqrt(dx*dx + dy*dy);
|
609
|
+
};
|
610
|
+
|
611
|
+
|
612
|
+
|
613
|
+
/**
|
614
|
+
* This allows the zoom level of the graph to influence the rendering
|
615
|
+
*
|
616
|
+
* @param scale
|
617
|
+
*/
|
618
|
+
Edge.prototype.setScale = function(scale) {
|
619
|
+
this.graphScaleInv = 1.0/scale;
|
620
|
+
};
|