vis-rails 2.0.0 → 2.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 +4 -4
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/javascripts/vis.js +26 -26
- metadata +16 -85
- data/vendor/assets/vis/DataSet.js +0 -926
- data/vendor/assets/vis/DataView.js +0 -283
- data/vendor/assets/vis/graph/Edge.js +0 -957
- data/vendor/assets/vis/graph/Graph.js +0 -2291
- data/vendor/assets/vis/graph/Groups.js +0 -80
- data/vendor/assets/vis/graph/Images.js +0 -41
- data/vendor/assets/vis/graph/Node.js +0 -966
- data/vendor/assets/vis/graph/Popup.js +0 -132
- data/vendor/assets/vis/graph/css/graph-manipulation.css +0 -128
- data/vendor/assets/vis/graph/css/graph-navigation.css +0 -66
- data/vendor/assets/vis/graph/dotparser.js +0 -829
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +0 -1143
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +0 -311
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +0 -576
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +0 -199
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +0 -205
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +0 -552
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +0 -648
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +0 -398
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +0 -64
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +0 -697
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +0 -66
- data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
- data/vendor/assets/vis/graph/img/backIcon.png +0 -0
- data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
- data/vendor/assets/vis/graph/img/cross.png +0 -0
- data/vendor/assets/vis/graph/img/cross2.png +0 -0
- data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/downArrow.png +0 -0
- data/vendor/assets/vis/graph/img/editIcon.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 +0 -225
- data/vendor/assets/vis/graph3d/Graph3d.js +0 -3306
- data/vendor/assets/vis/module/exports.js +0 -65
- data/vendor/assets/vis/module/header.js +0 -24
- data/vendor/assets/vis/module/imports.js +0 -31
- data/vendor/assets/vis/shim.js +0 -252
- data/vendor/assets/vis/timeline/Range.js +0 -532
- data/vendor/assets/vis/timeline/TimeStep.js +0 -466
- data/vendor/assets/vis/timeline/Timeline.js +0 -851
- data/vendor/assets/vis/timeline/component/Component.js +0 -52
- data/vendor/assets/vis/timeline/component/CurrentTime.js +0 -128
- data/vendor/assets/vis/timeline/component/CustomTime.js +0 -182
- data/vendor/assets/vis/timeline/component/Group.js +0 -470
- data/vendor/assets/vis/timeline/component/ItemSet.js +0 -1332
- data/vendor/assets/vis/timeline/component/TimeAxis.js +0 -389
- data/vendor/assets/vis/timeline/component/css/animation.css +0 -33
- data/vendor/assets/vis/timeline/component/css/currenttime.css +0 -5
- data/vendor/assets/vis/timeline/component/css/customtime.css +0 -6
- data/vendor/assets/vis/timeline/component/css/item.css +0 -107
- data/vendor/assets/vis/timeline/component/css/itemset.css +0 -33
- data/vendor/assets/vis/timeline/component/css/labelset.css +0 -36
- data/vendor/assets/vis/timeline/component/css/panel.css +0 -71
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +0 -48
- data/vendor/assets/vis/timeline/component/css/timeline.css +0 -2
- data/vendor/assets/vis/timeline/component/item/Item.js +0 -139
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +0 -230
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +0 -190
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +0 -262
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +0 -57
- data/vendor/assets/vis/timeline/img/delete.png +0 -0
- data/vendor/assets/vis/timeline/stack.js +0 -112
- data/vendor/assets/vis/util.js +0 -990
@@ -1,283 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* DataView
|
3
|
-
*
|
4
|
-
* a dataview offers a filtered view on a dataset or an other dataview.
|
5
|
-
*
|
6
|
-
* @param {DataSet | DataView} data
|
7
|
-
* @param {Object} [options] Available options: see method get
|
8
|
-
*
|
9
|
-
* @constructor DataView
|
10
|
-
*/
|
11
|
-
function DataView (data, options) {
|
12
|
-
this._data = null;
|
13
|
-
this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
|
14
|
-
this._options = options || {};
|
15
|
-
this._fieldId = 'id'; // name of the field containing id
|
16
|
-
this._subscribers = {}; // event subscribers
|
17
|
-
|
18
|
-
var me = this;
|
19
|
-
this.listener = function () {
|
20
|
-
me._onEvent.apply(me, arguments);
|
21
|
-
};
|
22
|
-
|
23
|
-
this.setData(data);
|
24
|
-
}
|
25
|
-
|
26
|
-
// TODO: implement a function .config() to dynamically update things like configured filter
|
27
|
-
// and trigger changes accordingly
|
28
|
-
|
29
|
-
/**
|
30
|
-
* Set a data source for the view
|
31
|
-
* @param {DataSet | DataView} data
|
32
|
-
*/
|
33
|
-
DataView.prototype.setData = function (data) {
|
34
|
-
var ids, i, len;
|
35
|
-
|
36
|
-
if (this._data) {
|
37
|
-
// unsubscribe from current dataset
|
38
|
-
if (this._data.unsubscribe) {
|
39
|
-
this._data.unsubscribe('*', this.listener);
|
40
|
-
}
|
41
|
-
|
42
|
-
// trigger a remove of all items in memory
|
43
|
-
ids = [];
|
44
|
-
for (var id in this._ids) {
|
45
|
-
if (this._ids.hasOwnProperty(id)) {
|
46
|
-
ids.push(id);
|
47
|
-
}
|
48
|
-
}
|
49
|
-
this._ids = {};
|
50
|
-
this._trigger('remove', {items: ids});
|
51
|
-
}
|
52
|
-
|
53
|
-
this._data = data;
|
54
|
-
|
55
|
-
if (this._data) {
|
56
|
-
// update fieldId
|
57
|
-
this._fieldId = this._options.fieldId ||
|
58
|
-
(this._data && this._data.options && this._data.options.fieldId) ||
|
59
|
-
'id';
|
60
|
-
|
61
|
-
// trigger an add of all added items
|
62
|
-
ids = this._data.getIds({filter: this._options && this._options.filter});
|
63
|
-
for (i = 0, len = ids.length; i < len; i++) {
|
64
|
-
id = ids[i];
|
65
|
-
this._ids[id] = true;
|
66
|
-
}
|
67
|
-
this._trigger('add', {items: ids});
|
68
|
-
|
69
|
-
// subscribe to new dataset
|
70
|
-
if (this._data.on) {
|
71
|
-
this._data.on('*', this.listener);
|
72
|
-
}
|
73
|
-
}
|
74
|
-
};
|
75
|
-
|
76
|
-
/**
|
77
|
-
* Get data from the data view
|
78
|
-
*
|
79
|
-
* Usage:
|
80
|
-
*
|
81
|
-
* get()
|
82
|
-
* get(options: Object)
|
83
|
-
* get(options: Object, data: Array | DataTable)
|
84
|
-
*
|
85
|
-
* get(id: Number)
|
86
|
-
* get(id: Number, options: Object)
|
87
|
-
* get(id: Number, options: Object, data: Array | DataTable)
|
88
|
-
*
|
89
|
-
* get(ids: Number[])
|
90
|
-
* get(ids: Number[], options: Object)
|
91
|
-
* get(ids: Number[], options: Object, data: Array | DataTable)
|
92
|
-
*
|
93
|
-
* Where:
|
94
|
-
*
|
95
|
-
* {Number | String} id The id of an item
|
96
|
-
* {Number[] | String{}} ids An array with ids of items
|
97
|
-
* {Object} options An Object with options. Available options:
|
98
|
-
* {String} [type] Type of data to be returned. Can
|
99
|
-
* be 'DataTable' or 'Array' (default)
|
100
|
-
* {Object.<String, String>} [convert]
|
101
|
-
* {String[]} [fields] field names to be returned
|
102
|
-
* {function} [filter] filter items
|
103
|
-
* {String | function} [order] Order the items by
|
104
|
-
* a field name or custom sort function.
|
105
|
-
* {Array | DataTable} [data] If provided, items will be appended to this
|
106
|
-
* array or table. Required in case of Google
|
107
|
-
* DataTable.
|
108
|
-
* @param args
|
109
|
-
*/
|
110
|
-
DataView.prototype.get = function (args) {
|
111
|
-
var me = this;
|
112
|
-
|
113
|
-
// parse the arguments
|
114
|
-
var ids, options, data;
|
115
|
-
var firstType = util.getType(arguments[0]);
|
116
|
-
if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
|
117
|
-
// get(id(s) [, options] [, data])
|
118
|
-
ids = arguments[0]; // can be a single id or an array with ids
|
119
|
-
options = arguments[1];
|
120
|
-
data = arguments[2];
|
121
|
-
}
|
122
|
-
else {
|
123
|
-
// get([, options] [, data])
|
124
|
-
options = arguments[0];
|
125
|
-
data = arguments[1];
|
126
|
-
}
|
127
|
-
|
128
|
-
// extend the options with the default options and provided options
|
129
|
-
var viewOptions = util.extend({}, this._options, options);
|
130
|
-
|
131
|
-
// create a combined filter method when needed
|
132
|
-
if (this._options.filter && options && options.filter) {
|
133
|
-
viewOptions.filter = function (item) {
|
134
|
-
return me._options.filter(item) && options.filter(item);
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
|
-
// build up the call to the linked data set
|
139
|
-
var getArguments = [];
|
140
|
-
if (ids != undefined) {
|
141
|
-
getArguments.push(ids);
|
142
|
-
}
|
143
|
-
getArguments.push(viewOptions);
|
144
|
-
getArguments.push(data);
|
145
|
-
|
146
|
-
return this._data && this._data.get.apply(this._data, getArguments);
|
147
|
-
};
|
148
|
-
|
149
|
-
/**
|
150
|
-
* Get ids of all items or from a filtered set of items.
|
151
|
-
* @param {Object} [options] An Object with options. Available options:
|
152
|
-
* {function} [filter] filter items
|
153
|
-
* {String | function} [order] Order the items by
|
154
|
-
* a field name or custom sort function.
|
155
|
-
* @return {Array} ids
|
156
|
-
*/
|
157
|
-
DataView.prototype.getIds = function (options) {
|
158
|
-
var ids;
|
159
|
-
|
160
|
-
if (this._data) {
|
161
|
-
var defaultFilter = this._options.filter;
|
162
|
-
var filter;
|
163
|
-
|
164
|
-
if (options && options.filter) {
|
165
|
-
if (defaultFilter) {
|
166
|
-
filter = function (item) {
|
167
|
-
return defaultFilter(item) && options.filter(item);
|
168
|
-
}
|
169
|
-
}
|
170
|
-
else {
|
171
|
-
filter = options.filter;
|
172
|
-
}
|
173
|
-
}
|
174
|
-
else {
|
175
|
-
filter = defaultFilter;
|
176
|
-
}
|
177
|
-
|
178
|
-
ids = this._data.getIds({
|
179
|
-
filter: filter,
|
180
|
-
order: options && options.order
|
181
|
-
});
|
182
|
-
}
|
183
|
-
else {
|
184
|
-
ids = [];
|
185
|
-
}
|
186
|
-
|
187
|
-
return ids;
|
188
|
-
};
|
189
|
-
|
190
|
-
/**
|
191
|
-
* Event listener. Will propagate all events from the connected data set to
|
192
|
-
* the subscribers of the DataView, but will filter the items and only trigger
|
193
|
-
* when there are changes in the filtered data set.
|
194
|
-
* @param {String} event
|
195
|
-
* @param {Object | null} params
|
196
|
-
* @param {String} senderId
|
197
|
-
* @private
|
198
|
-
*/
|
199
|
-
DataView.prototype._onEvent = function (event, params, senderId) {
|
200
|
-
var i, len, id, item,
|
201
|
-
ids = params && params.items,
|
202
|
-
data = this._data,
|
203
|
-
added = [],
|
204
|
-
updated = [],
|
205
|
-
removed = [];
|
206
|
-
|
207
|
-
if (ids && data) {
|
208
|
-
switch (event) {
|
209
|
-
case 'add':
|
210
|
-
// filter the ids of the added items
|
211
|
-
for (i = 0, len = ids.length; i < len; i++) {
|
212
|
-
id = ids[i];
|
213
|
-
item = this.get(id);
|
214
|
-
if (item) {
|
215
|
-
this._ids[id] = true;
|
216
|
-
added.push(id);
|
217
|
-
}
|
218
|
-
}
|
219
|
-
|
220
|
-
break;
|
221
|
-
|
222
|
-
case 'update':
|
223
|
-
// determine the event from the views viewpoint: an updated
|
224
|
-
// item can be added, updated, or removed from this view.
|
225
|
-
for (i = 0, len = ids.length; i < len; i++) {
|
226
|
-
id = ids[i];
|
227
|
-
item = this.get(id);
|
228
|
-
|
229
|
-
if (item) {
|
230
|
-
if (this._ids[id]) {
|
231
|
-
updated.push(id);
|
232
|
-
}
|
233
|
-
else {
|
234
|
-
this._ids[id] = true;
|
235
|
-
added.push(id);
|
236
|
-
}
|
237
|
-
}
|
238
|
-
else {
|
239
|
-
if (this._ids[id]) {
|
240
|
-
delete this._ids[id];
|
241
|
-
removed.push(id);
|
242
|
-
}
|
243
|
-
else {
|
244
|
-
// nothing interesting for me :-(
|
245
|
-
}
|
246
|
-
}
|
247
|
-
}
|
248
|
-
|
249
|
-
break;
|
250
|
-
|
251
|
-
case 'remove':
|
252
|
-
// filter the ids of the removed items
|
253
|
-
for (i = 0, len = ids.length; i < len; i++) {
|
254
|
-
id = ids[i];
|
255
|
-
if (this._ids[id]) {
|
256
|
-
delete this._ids[id];
|
257
|
-
removed.push(id);
|
258
|
-
}
|
259
|
-
}
|
260
|
-
|
261
|
-
break;
|
262
|
-
}
|
263
|
-
|
264
|
-
if (added.length) {
|
265
|
-
this._trigger('add', {items: added}, senderId);
|
266
|
-
}
|
267
|
-
if (updated.length) {
|
268
|
-
this._trigger('update', {items: updated}, senderId);
|
269
|
-
}
|
270
|
-
if (removed.length) {
|
271
|
-
this._trigger('remove', {items: removed}, senderId);
|
272
|
-
}
|
273
|
-
}
|
274
|
-
};
|
275
|
-
|
276
|
-
// copy subscription functionality from DataSet
|
277
|
-
DataView.prototype.on = DataSet.prototype.on;
|
278
|
-
DataView.prototype.off = DataSet.prototype.off;
|
279
|
-
DataView.prototype._trigger = DataSet.prototype._trigger;
|
280
|
-
|
281
|
-
// TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5)
|
282
|
-
DataView.prototype.subscribe = DataView.prototype.on;
|
283
|
-
DataView.prototype.unsubscribe = DataView.prototype.off;
|
@@ -1,957 +0,0 @@
|
|
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.hoverWidth = constants.edges.hoverWidth;
|
34
|
-
this.value = undefined;
|
35
|
-
this.length = constants.physics.springLength;
|
36
|
-
this.customLength = false;
|
37
|
-
this.selected = false;
|
38
|
-
this.hover = false;
|
39
|
-
this.smooth = constants.smoothCurves;
|
40
|
-
this.arrowScaleFactor = constants.edges.arrowScaleFactor;
|
41
|
-
|
42
|
-
this.from = null; // a node
|
43
|
-
this.to = null; // a node
|
44
|
-
this.via = null; // a temp node
|
45
|
-
|
46
|
-
// we use this to be able to reconnect the edge to a cluster if its node is put into a cluster
|
47
|
-
// by storing the original information we can revert to the original connection when the cluser is opened.
|
48
|
-
this.originalFromId = [];
|
49
|
-
this.originalToId = [];
|
50
|
-
|
51
|
-
this.connected = false;
|
52
|
-
|
53
|
-
// Added to support dashed lines
|
54
|
-
// David Jordan
|
55
|
-
// 2012-08-08
|
56
|
-
this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
|
57
|
-
|
58
|
-
this.color = {color:constants.edges.color.color,
|
59
|
-
highlight:constants.edges.color.highlight,
|
60
|
-
hover:constants.edges.color.hover};
|
61
|
-
this.widthFixed = false;
|
62
|
-
this.lengthFixed = false;
|
63
|
-
|
64
|
-
this.setProperties(properties, constants);
|
65
|
-
|
66
|
-
this.controlNodesEnabled = false;
|
67
|
-
this.controlNodes = {from:null, to:null, positions:{}};
|
68
|
-
this.connectedNode = null;
|
69
|
-
}
|
70
|
-
|
71
|
-
/**
|
72
|
-
* Set or overwrite properties for the edge
|
73
|
-
* @param {Object} properties an object with properties
|
74
|
-
* @param {Object} constants and object with default, global properties
|
75
|
-
*/
|
76
|
-
Edge.prototype.setProperties = function(properties, constants) {
|
77
|
-
if (!properties) {
|
78
|
-
return;
|
79
|
-
}
|
80
|
-
|
81
|
-
if (properties.from !== undefined) {this.fromId = properties.from;}
|
82
|
-
if (properties.to !== undefined) {this.toId = properties.to;}
|
83
|
-
|
84
|
-
if (properties.id !== undefined) {this.id = properties.id;}
|
85
|
-
if (properties.style !== undefined) {this.style = properties.style;}
|
86
|
-
if (properties.label !== undefined) {this.label = properties.label;}
|
87
|
-
|
88
|
-
if (this.label) {
|
89
|
-
this.fontSize = constants.edges.fontSize;
|
90
|
-
this.fontFace = constants.edges.fontFace;
|
91
|
-
this.fontColor = constants.edges.fontColor;
|
92
|
-
this.fontFill = constants.edges.fontFill;
|
93
|
-
|
94
|
-
if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;}
|
95
|
-
if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;}
|
96
|
-
if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;}
|
97
|
-
if (properties.fontFill !== undefined) {this.fontFill = properties.fontFill;}
|
98
|
-
}
|
99
|
-
|
100
|
-
if (properties.title !== undefined) {this.title = properties.title;}
|
101
|
-
if (properties.width !== undefined) {this.width = properties.width;}
|
102
|
-
if (properties.hoverWidth !== undefined) {this.hoverWidth = properties.hoverWidth;}
|
103
|
-
if (properties.value !== undefined) {this.value = properties.value;}
|
104
|
-
if (properties.length !== undefined) {this.length = properties.length;
|
105
|
-
this.customLength = true;}
|
106
|
-
|
107
|
-
// scale the arrow
|
108
|
-
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;}
|
109
|
-
|
110
|
-
// Added to support dashed lines
|
111
|
-
// David Jordan
|
112
|
-
// 2012-08-08
|
113
|
-
if (properties.dash) {
|
114
|
-
if (properties.dash.length !== undefined) {this.dash.length = properties.dash.length;}
|
115
|
-
if (properties.dash.gap !== undefined) {this.dash.gap = properties.dash.gap;}
|
116
|
-
if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;}
|
117
|
-
}
|
118
|
-
|
119
|
-
if (properties.color !== undefined) {
|
120
|
-
if (util.isString(properties.color)) {
|
121
|
-
this.color.color = properties.color;
|
122
|
-
this.color.highlight = properties.color;
|
123
|
-
}
|
124
|
-
else {
|
125
|
-
if (properties.color.color !== undefined) {this.color.color = properties.color.color;}
|
126
|
-
if (properties.color.highlight !== undefined) {this.color.highlight = properties.color.highlight;}
|
127
|
-
}
|
128
|
-
}
|
129
|
-
|
130
|
-
// A node is connected when it has a from and to node.
|
131
|
-
this.connect();
|
132
|
-
|
133
|
-
this.widthFixed = this.widthFixed || (properties.width !== undefined);
|
134
|
-
this.lengthFixed = this.lengthFixed || (properties.length !== undefined);
|
135
|
-
|
136
|
-
// set draw method based on style
|
137
|
-
switch (this.style) {
|
138
|
-
case 'line': this.draw = this._drawLine; break;
|
139
|
-
case 'arrow': this.draw = this._drawArrow; break;
|
140
|
-
case 'arrow-center': this.draw = this._drawArrowCenter; break;
|
141
|
-
case 'dash-line': this.draw = this._drawDashLine; break;
|
142
|
-
default: this.draw = this._drawLine; break;
|
143
|
-
}
|
144
|
-
};
|
145
|
-
|
146
|
-
/**
|
147
|
-
* Connect an edge to its nodes
|
148
|
-
*/
|
149
|
-
Edge.prototype.connect = function () {
|
150
|
-
this.disconnect();
|
151
|
-
|
152
|
-
this.from = this.graph.nodes[this.fromId] || null;
|
153
|
-
this.to = this.graph.nodes[this.toId] || null;
|
154
|
-
this.connected = (this.from && this.to);
|
155
|
-
|
156
|
-
if (this.connected) {
|
157
|
-
this.from.attachEdge(this);
|
158
|
-
this.to.attachEdge(this);
|
159
|
-
}
|
160
|
-
else {
|
161
|
-
if (this.from) {
|
162
|
-
this.from.detachEdge(this);
|
163
|
-
}
|
164
|
-
if (this.to) {
|
165
|
-
this.to.detachEdge(this);
|
166
|
-
}
|
167
|
-
}
|
168
|
-
};
|
169
|
-
|
170
|
-
/**
|
171
|
-
* Disconnect an edge from its nodes
|
172
|
-
*/
|
173
|
-
Edge.prototype.disconnect = function () {
|
174
|
-
if (this.from) {
|
175
|
-
this.from.detachEdge(this);
|
176
|
-
this.from = null;
|
177
|
-
}
|
178
|
-
if (this.to) {
|
179
|
-
this.to.detachEdge(this);
|
180
|
-
this.to = null;
|
181
|
-
}
|
182
|
-
|
183
|
-
this.connected = false;
|
184
|
-
};
|
185
|
-
|
186
|
-
/**
|
187
|
-
* get the title of this edge.
|
188
|
-
* @return {string} title The title of the edge, or undefined when no title
|
189
|
-
* has been set.
|
190
|
-
*/
|
191
|
-
Edge.prototype.getTitle = function() {
|
192
|
-
return typeof this.title === "function" ? this.title() : this.title;
|
193
|
-
};
|
194
|
-
|
195
|
-
|
196
|
-
/**
|
197
|
-
* Retrieve the value of the edge. Can be undefined
|
198
|
-
* @return {Number} value
|
199
|
-
*/
|
200
|
-
Edge.prototype.getValue = function() {
|
201
|
-
return this.value;
|
202
|
-
};
|
203
|
-
|
204
|
-
/**
|
205
|
-
* Adjust the value range of the edge. The edge will adjust it's width
|
206
|
-
* based on its value.
|
207
|
-
* @param {Number} min
|
208
|
-
* @param {Number} max
|
209
|
-
*/
|
210
|
-
Edge.prototype.setValueRange = function(min, max) {
|
211
|
-
if (!this.widthFixed && this.value !== undefined) {
|
212
|
-
var scale = (this.widthMax - this.widthMin) / (max - min);
|
213
|
-
this.width = (this.value - min) * scale + this.widthMin;
|
214
|
-
}
|
215
|
-
};
|
216
|
-
|
217
|
-
/**
|
218
|
-
* Redraw a edge
|
219
|
-
* Draw this edge in the given canvas
|
220
|
-
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
221
|
-
* @param {CanvasRenderingContext2D} ctx
|
222
|
-
*/
|
223
|
-
Edge.prototype.draw = function(ctx) {
|
224
|
-
throw "Method draw not initialized in edge";
|
225
|
-
};
|
226
|
-
|
227
|
-
/**
|
228
|
-
* Check if this object is overlapping with the provided object
|
229
|
-
* @param {Object} obj an object with parameters left, top
|
230
|
-
* @return {boolean} True if location is located on the edge
|
231
|
-
*/
|
232
|
-
Edge.prototype.isOverlappingWith = function(obj) {
|
233
|
-
if (this.connected) {
|
234
|
-
var distMax = 10;
|
235
|
-
var xFrom = this.from.x;
|
236
|
-
var yFrom = this.from.y;
|
237
|
-
var xTo = this.to.x;
|
238
|
-
var yTo = this.to.y;
|
239
|
-
var xObj = obj.left;
|
240
|
-
var yObj = obj.top;
|
241
|
-
|
242
|
-
var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
|
243
|
-
|
244
|
-
return (dist < distMax);
|
245
|
-
}
|
246
|
-
else {
|
247
|
-
return false
|
248
|
-
}
|
249
|
-
};
|
250
|
-
|
251
|
-
|
252
|
-
/**
|
253
|
-
* Redraw a edge as a line
|
254
|
-
* Draw this edge in the given canvas
|
255
|
-
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
256
|
-
* @param {CanvasRenderingContext2D} ctx
|
257
|
-
* @private
|
258
|
-
*/
|
259
|
-
Edge.prototype._drawLine = function(ctx) {
|
260
|
-
// set style
|
261
|
-
if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
|
262
|
-
else if (this.hover == true) {ctx.strokeStyle = this.color.hover;}
|
263
|
-
else {ctx.strokeStyle = this.color.color;}
|
264
|
-
ctx.lineWidth = this._getLineWidth();
|
265
|
-
|
266
|
-
if (this.from != this.to) {
|
267
|
-
// draw line
|
268
|
-
this._line(ctx);
|
269
|
-
|
270
|
-
// draw label
|
271
|
-
var point;
|
272
|
-
if (this.label) {
|
273
|
-
if (this.smooth == true) {
|
274
|
-
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
|
275
|
-
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
|
276
|
-
point = {x:midpointX, y:midpointY};
|
277
|
-
}
|
278
|
-
else {
|
279
|
-
point = this._pointOnLine(0.5);
|
280
|
-
}
|
281
|
-
this._label(ctx, this.label, point.x, point.y);
|
282
|
-
}
|
283
|
-
}
|
284
|
-
else {
|
285
|
-
var x, y;
|
286
|
-
var radius = this.length / 4;
|
287
|
-
var node = this.from;
|
288
|
-
if (!node.width) {
|
289
|
-
node.resize(ctx);
|
290
|
-
}
|
291
|
-
if (node.width > node.height) {
|
292
|
-
x = node.x + node.width / 2;
|
293
|
-
y = node.y - radius;
|
294
|
-
}
|
295
|
-
else {
|
296
|
-
x = node.x + radius;
|
297
|
-
y = node.y - node.height / 2;
|
298
|
-
}
|
299
|
-
this._circle(ctx, x, y, radius);
|
300
|
-
point = this._pointOnCircle(x, y, radius, 0.5);
|
301
|
-
this._label(ctx, this.label, point.x, point.y);
|
302
|
-
}
|
303
|
-
};
|
304
|
-
|
305
|
-
/**
|
306
|
-
* Get the line width of the edge. Depends on width and whether one of the
|
307
|
-
* connected nodes is selected.
|
308
|
-
* @return {Number} width
|
309
|
-
* @private
|
310
|
-
*/
|
311
|
-
Edge.prototype._getLineWidth = function() {
|
312
|
-
if (this.selected == true) {
|
313
|
-
return Math.min(this.width * 2, this.widthMax)*this.graphScaleInv;
|
314
|
-
}
|
315
|
-
else {
|
316
|
-
if (this.hover == true) {
|
317
|
-
return Math.min(this.hoverWidth, this.widthMax)*this.graphScaleInv;
|
318
|
-
}
|
319
|
-
else {
|
320
|
-
return this.width*this.graphScaleInv;
|
321
|
-
}
|
322
|
-
}
|
323
|
-
};
|
324
|
-
|
325
|
-
/**
|
326
|
-
* Draw a line between two nodes
|
327
|
-
* @param {CanvasRenderingContext2D} ctx
|
328
|
-
* @private
|
329
|
-
*/
|
330
|
-
Edge.prototype._line = function (ctx) {
|
331
|
-
// draw a straight line
|
332
|
-
ctx.beginPath();
|
333
|
-
ctx.moveTo(this.from.x, this.from.y);
|
334
|
-
if (this.smooth == true) {
|
335
|
-
ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y);
|
336
|
-
}
|
337
|
-
else {
|
338
|
-
ctx.lineTo(this.to.x, this.to.y);
|
339
|
-
}
|
340
|
-
ctx.stroke();
|
341
|
-
};
|
342
|
-
|
343
|
-
/**
|
344
|
-
* Draw a line from a node to itself, a circle
|
345
|
-
* @param {CanvasRenderingContext2D} ctx
|
346
|
-
* @param {Number} x
|
347
|
-
* @param {Number} y
|
348
|
-
* @param {Number} radius
|
349
|
-
* @private
|
350
|
-
*/
|
351
|
-
Edge.prototype._circle = function (ctx, x, y, radius) {
|
352
|
-
// draw a circle
|
353
|
-
ctx.beginPath();
|
354
|
-
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
355
|
-
ctx.stroke();
|
356
|
-
};
|
357
|
-
|
358
|
-
/**
|
359
|
-
* Draw label with white background and with the middle at (x, y)
|
360
|
-
* @param {CanvasRenderingContext2D} ctx
|
361
|
-
* @param {String} text
|
362
|
-
* @param {Number} x
|
363
|
-
* @param {Number} y
|
364
|
-
* @private
|
365
|
-
*/
|
366
|
-
Edge.prototype._label = function (ctx, text, x, y) {
|
367
|
-
if (text) {
|
368
|
-
// TODO: cache the calculated size
|
369
|
-
ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
|
370
|
-
this.fontSize + "px " + this.fontFace;
|
371
|
-
ctx.fillStyle = this.fontFill;
|
372
|
-
var width = ctx.measureText(text).width;
|
373
|
-
var height = this.fontSize;
|
374
|
-
var left = x - width / 2;
|
375
|
-
var top = y - height / 2;
|
376
|
-
|
377
|
-
ctx.fillRect(left, top, width, height);
|
378
|
-
|
379
|
-
// draw text
|
380
|
-
ctx.fillStyle = this.fontColor || "black";
|
381
|
-
ctx.textAlign = "left";
|
382
|
-
ctx.textBaseline = "top";
|
383
|
-
ctx.fillText(text, left, top);
|
384
|
-
}
|
385
|
-
};
|
386
|
-
|
387
|
-
/**
|
388
|
-
* Redraw a edge as a dashed line
|
389
|
-
* Draw this edge in the given canvas
|
390
|
-
* @author David Jordan
|
391
|
-
* @date 2012-08-08
|
392
|
-
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
393
|
-
* @param {CanvasRenderingContext2D} ctx
|
394
|
-
* @private
|
395
|
-
*/
|
396
|
-
Edge.prototype._drawDashLine = function(ctx) {
|
397
|
-
// set style
|
398
|
-
if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
|
399
|
-
else if (this.hover == true) {ctx.strokeStyle = this.color.hover;}
|
400
|
-
else {ctx.strokeStyle = this.color.color;}
|
401
|
-
|
402
|
-
ctx.lineWidth = this._getLineWidth();
|
403
|
-
|
404
|
-
// only firefox and chrome support this method, else we use the legacy one.
|
405
|
-
if (ctx.mozDash !== undefined || ctx.setLineDash !== undefined) {
|
406
|
-
ctx.beginPath();
|
407
|
-
ctx.moveTo(this.from.x, this.from.y);
|
408
|
-
|
409
|
-
// configure the dash pattern
|
410
|
-
var pattern = [0];
|
411
|
-
if (this.dash.length !== undefined && this.dash.gap !== undefined) {
|
412
|
-
pattern = [this.dash.length,this.dash.gap];
|
413
|
-
}
|
414
|
-
else {
|
415
|
-
pattern = [5,5];
|
416
|
-
}
|
417
|
-
|
418
|
-
// set dash settings for chrome or firefox
|
419
|
-
if (typeof ctx.setLineDash !== 'undefined') { //Chrome
|
420
|
-
ctx.setLineDash(pattern);
|
421
|
-
ctx.lineDashOffset = 0;
|
422
|
-
|
423
|
-
} else { //Firefox
|
424
|
-
ctx.mozDash = pattern;
|
425
|
-
ctx.mozDashOffset = 0;
|
426
|
-
}
|
427
|
-
|
428
|
-
// draw the line
|
429
|
-
if (this.smooth == true) {
|
430
|
-
ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y);
|
431
|
-
}
|
432
|
-
else {
|
433
|
-
ctx.lineTo(this.to.x, this.to.y);
|
434
|
-
}
|
435
|
-
ctx.stroke();
|
436
|
-
|
437
|
-
// restore the dash settings.
|
438
|
-
if (typeof ctx.setLineDash !== 'undefined') { //Chrome
|
439
|
-
ctx.setLineDash([0]);
|
440
|
-
ctx.lineDashOffset = 0;
|
441
|
-
|
442
|
-
} else { //Firefox
|
443
|
-
ctx.mozDash = [0];
|
444
|
-
ctx.mozDashOffset = 0;
|
445
|
-
}
|
446
|
-
}
|
447
|
-
else { // unsupporting smooth lines
|
448
|
-
// draw dashed line
|
449
|
-
ctx.beginPath();
|
450
|
-
ctx.lineCap = 'round';
|
451
|
-
if (this.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value
|
452
|
-
{
|
453
|
-
ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
|
454
|
-
[this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
|
455
|
-
}
|
456
|
-
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
|
457
|
-
{
|
458
|
-
ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
|
459
|
-
[this.dash.length,this.dash.gap]);
|
460
|
-
}
|
461
|
-
else //If all else fails draw a line
|
462
|
-
{
|
463
|
-
ctx.moveTo(this.from.x, this.from.y);
|
464
|
-
ctx.lineTo(this.to.x, this.to.y);
|
465
|
-
}
|
466
|
-
ctx.stroke();
|
467
|
-
}
|
468
|
-
|
469
|
-
// draw label
|
470
|
-
if (this.label) {
|
471
|
-
var point;
|
472
|
-
if (this.smooth == true) {
|
473
|
-
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
|
474
|
-
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
|
475
|
-
point = {x:midpointX, y:midpointY};
|
476
|
-
}
|
477
|
-
else {
|
478
|
-
point = this._pointOnLine(0.5);
|
479
|
-
}
|
480
|
-
this._label(ctx, this.label, point.x, point.y);
|
481
|
-
}
|
482
|
-
};
|
483
|
-
|
484
|
-
/**
|
485
|
-
* Get a point on a line
|
486
|
-
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
|
487
|
-
* @return {Object} point
|
488
|
-
* @private
|
489
|
-
*/
|
490
|
-
Edge.prototype._pointOnLine = function (percentage) {
|
491
|
-
return {
|
492
|
-
x: (1 - percentage) * this.from.x + percentage * this.to.x,
|
493
|
-
y: (1 - percentage) * this.from.y + percentage * this.to.y
|
494
|
-
}
|
495
|
-
};
|
496
|
-
|
497
|
-
/**
|
498
|
-
* Get a point on a circle
|
499
|
-
* @param {Number} x
|
500
|
-
* @param {Number} y
|
501
|
-
* @param {Number} radius
|
502
|
-
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
|
503
|
-
* @return {Object} point
|
504
|
-
* @private
|
505
|
-
*/
|
506
|
-
Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
|
507
|
-
var angle = (percentage - 3/8) * 2 * Math.PI;
|
508
|
-
return {
|
509
|
-
x: x + radius * Math.cos(angle),
|
510
|
-
y: y - radius * Math.sin(angle)
|
511
|
-
}
|
512
|
-
};
|
513
|
-
|
514
|
-
/**
|
515
|
-
* Redraw a edge as a line with an arrow halfway the line
|
516
|
-
* Draw this edge in the given canvas
|
517
|
-
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
518
|
-
* @param {CanvasRenderingContext2D} ctx
|
519
|
-
* @private
|
520
|
-
*/
|
521
|
-
Edge.prototype._drawArrowCenter = function(ctx) {
|
522
|
-
var point;
|
523
|
-
// set style
|
524
|
-
if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
|
525
|
-
else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;}
|
526
|
-
else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
|
527
|
-
ctx.lineWidth = this._getLineWidth();
|
528
|
-
|
529
|
-
if (this.from != this.to) {
|
530
|
-
// draw line
|
531
|
-
this._line(ctx);
|
532
|
-
|
533
|
-
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
|
534
|
-
var length = (10 + 5 * this.width) * this.arrowScaleFactor;
|
535
|
-
// draw an arrow halfway the line
|
536
|
-
if (this.smooth == true) {
|
537
|
-
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
|
538
|
-
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
|
539
|
-
point = {x:midpointX, y:midpointY};
|
540
|
-
}
|
541
|
-
else {
|
542
|
-
point = this._pointOnLine(0.5);
|
543
|
-
}
|
544
|
-
|
545
|
-
ctx.arrow(point.x, point.y, angle, length);
|
546
|
-
ctx.fill();
|
547
|
-
ctx.stroke();
|
548
|
-
|
549
|
-
// draw label
|
550
|
-
if (this.label) {
|
551
|
-
this._label(ctx, this.label, point.x, point.y);
|
552
|
-
}
|
553
|
-
}
|
554
|
-
else {
|
555
|
-
// draw circle
|
556
|
-
var x, y;
|
557
|
-
var radius = 0.25 * Math.max(100,this.length);
|
558
|
-
var node = this.from;
|
559
|
-
if (!node.width) {
|
560
|
-
node.resize(ctx);
|
561
|
-
}
|
562
|
-
if (node.width > node.height) {
|
563
|
-
x = node.x + node.width * 0.5;
|
564
|
-
y = node.y - radius;
|
565
|
-
}
|
566
|
-
else {
|
567
|
-
x = node.x + radius;
|
568
|
-
y = node.y - node.height * 0.5;
|
569
|
-
}
|
570
|
-
this._circle(ctx, x, y, radius);
|
571
|
-
|
572
|
-
// draw all arrows
|
573
|
-
var angle = 0.2 * Math.PI;
|
574
|
-
var length = (10 + 5 * this.width) * this.arrowScaleFactor;
|
575
|
-
point = this._pointOnCircle(x, y, radius, 0.5);
|
576
|
-
ctx.arrow(point.x, point.y, angle, length);
|
577
|
-
ctx.fill();
|
578
|
-
ctx.stroke();
|
579
|
-
|
580
|
-
// draw label
|
581
|
-
if (this.label) {
|
582
|
-
point = this._pointOnCircle(x, y, radius, 0.5);
|
583
|
-
this._label(ctx, this.label, point.x, point.y);
|
584
|
-
}
|
585
|
-
}
|
586
|
-
};
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
/**
|
591
|
-
* Redraw a edge as a line with an arrow
|
592
|
-
* Draw this edge in the given canvas
|
593
|
-
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
|
594
|
-
* @param {CanvasRenderingContext2D} ctx
|
595
|
-
* @private
|
596
|
-
*/
|
597
|
-
Edge.prototype._drawArrow = function(ctx) {
|
598
|
-
// set style
|
599
|
-
if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
|
600
|
-
else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;}
|
601
|
-
else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
|
602
|
-
|
603
|
-
ctx.lineWidth = this._getLineWidth();
|
604
|
-
|
605
|
-
var angle, length;
|
606
|
-
//draw a line
|
607
|
-
if (this.from != this.to) {
|
608
|
-
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
|
609
|
-
var dx = (this.to.x - this.from.x);
|
610
|
-
var dy = (this.to.y - this.from.y);
|
611
|
-
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
|
612
|
-
|
613
|
-
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI);
|
614
|
-
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength;
|
615
|
-
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
|
616
|
-
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
|
617
|
-
|
618
|
-
|
619
|
-
if (this.smooth == true) {
|
620
|
-
angle = Math.atan2((this.to.y - this.via.y), (this.to.x - this.via.x));
|
621
|
-
dx = (this.to.x - this.via.x);
|
622
|
-
dy = (this.to.y - this.via.y);
|
623
|
-
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
|
624
|
-
}
|
625
|
-
var toBorderDist = this.to.distanceToBorder(ctx, angle);
|
626
|
-
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
|
627
|
-
|
628
|
-
var xTo,yTo;
|
629
|
-
if (this.smooth == true) {
|
630
|
-
xTo = (1 - toBorderPoint) * this.via.x + toBorderPoint * this.to.x;
|
631
|
-
yTo = (1 - toBorderPoint) * this.via.y + toBorderPoint * this.to.y;
|
632
|
-
}
|
633
|
-
else {
|
634
|
-
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
|
635
|
-
yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
|
636
|
-
}
|
637
|
-
|
638
|
-
ctx.beginPath();
|
639
|
-
ctx.moveTo(xFrom,yFrom);
|
640
|
-
if (this.smooth == true) {
|
641
|
-
ctx.quadraticCurveTo(this.via.x,this.via.y,xTo, yTo);
|
642
|
-
}
|
643
|
-
else {
|
644
|
-
ctx.lineTo(xTo, yTo);
|
645
|
-
}
|
646
|
-
ctx.stroke();
|
647
|
-
|
648
|
-
// draw arrow at the end of the line
|
649
|
-
length = (10 + 5 * this.width) * this.arrowScaleFactor;
|
650
|
-
ctx.arrow(xTo, yTo, angle, length);
|
651
|
-
ctx.fill();
|
652
|
-
ctx.stroke();
|
653
|
-
|
654
|
-
// draw label
|
655
|
-
if (this.label) {
|
656
|
-
var point;
|
657
|
-
if (this.smooth == true) {
|
658
|
-
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
|
659
|
-
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
|
660
|
-
point = {x:midpointX, y:midpointY};
|
661
|
-
}
|
662
|
-
else {
|
663
|
-
point = this._pointOnLine(0.5);
|
664
|
-
}
|
665
|
-
this._label(ctx, this.label, point.x, point.y);
|
666
|
-
}
|
667
|
-
}
|
668
|
-
else {
|
669
|
-
// draw circle
|
670
|
-
var node = this.from;
|
671
|
-
var x, y, arrow;
|
672
|
-
var radius = 0.25 * Math.max(100,this.length);
|
673
|
-
if (!node.width) {
|
674
|
-
node.resize(ctx);
|
675
|
-
}
|
676
|
-
if (node.width > node.height) {
|
677
|
-
x = node.x + node.width * 0.5;
|
678
|
-
y = node.y - radius;
|
679
|
-
arrow = {
|
680
|
-
x: x,
|
681
|
-
y: node.y,
|
682
|
-
angle: 0.9 * Math.PI
|
683
|
-
};
|
684
|
-
}
|
685
|
-
else {
|
686
|
-
x = node.x + radius;
|
687
|
-
y = node.y - node.height * 0.5;
|
688
|
-
arrow = {
|
689
|
-
x: node.x,
|
690
|
-
y: y,
|
691
|
-
angle: 0.6 * Math.PI
|
692
|
-
};
|
693
|
-
}
|
694
|
-
ctx.beginPath();
|
695
|
-
// TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
|
696
|
-
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
697
|
-
ctx.stroke();
|
698
|
-
|
699
|
-
// draw all arrows
|
700
|
-
var length = (10 + 5 * this.width) * this.arrowScaleFactor;
|
701
|
-
ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
|
702
|
-
ctx.fill();
|
703
|
-
ctx.stroke();
|
704
|
-
|
705
|
-
// draw label
|
706
|
-
if (this.label) {
|
707
|
-
point = this._pointOnCircle(x, y, radius, 0.5);
|
708
|
-
this._label(ctx, this.label, point.x, point.y);
|
709
|
-
}
|
710
|
-
}
|
711
|
-
};
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
/**
|
716
|
-
* Calculate the distance between a point (x3,y3) and a line segment from
|
717
|
-
* (x1,y1) to (x2,y2).
|
718
|
-
* http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
|
719
|
-
* @param {number} x1
|
720
|
-
* @param {number} y1
|
721
|
-
* @param {number} x2
|
722
|
-
* @param {number} y2
|
723
|
-
* @param {number} x3
|
724
|
-
* @param {number} y3
|
725
|
-
* @private
|
726
|
-
*/
|
727
|
-
Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
|
728
|
-
if (this.from != this.to) {
|
729
|
-
if (this.smooth == true) {
|
730
|
-
var minDistance = 1e9;
|
731
|
-
var i,t,x,y,dx,dy;
|
732
|
-
for (i = 0; i < 10; i++) {
|
733
|
-
t = 0.1*i;
|
734
|
-
x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*this.via.x + Math.pow(t,2)*x2;
|
735
|
-
y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*this.via.y + Math.pow(t,2)*y2;
|
736
|
-
dx = Math.abs(x3-x);
|
737
|
-
dy = Math.abs(y3-y);
|
738
|
-
minDistance = Math.min(minDistance,Math.sqrt(dx*dx + dy*dy));
|
739
|
-
}
|
740
|
-
return minDistance
|
741
|
-
}
|
742
|
-
else {
|
743
|
-
var px = x2-x1,
|
744
|
-
py = y2-y1,
|
745
|
-
something = px*px + py*py,
|
746
|
-
u = ((x3 - x1) * px + (y3 - y1) * py) / something;
|
747
|
-
|
748
|
-
if (u > 1) {
|
749
|
-
u = 1;
|
750
|
-
}
|
751
|
-
else if (u < 0) {
|
752
|
-
u = 0;
|
753
|
-
}
|
754
|
-
|
755
|
-
var x = x1 + u * px,
|
756
|
-
y = y1 + u * py,
|
757
|
-
dx = x - x3,
|
758
|
-
dy = y - y3;
|
759
|
-
|
760
|
-
//# Note: If the actual distance does not matter,
|
761
|
-
//# if you only want to compare what this function
|
762
|
-
//# returns to other results of this function, you
|
763
|
-
//# can just return the squared distance instead
|
764
|
-
//# (i.e. remove the sqrt) to gain a little performance
|
765
|
-
|
766
|
-
return Math.sqrt(dx*dx + dy*dy);
|
767
|
-
}
|
768
|
-
}
|
769
|
-
else {
|
770
|
-
var x, y, dx, dy;
|
771
|
-
var radius = this.length / 4;
|
772
|
-
var node = this.from;
|
773
|
-
if (!node.width) {
|
774
|
-
node.resize(ctx);
|
775
|
-
}
|
776
|
-
if (node.width > node.height) {
|
777
|
-
x = node.x + node.width / 2;
|
778
|
-
y = node.y - radius;
|
779
|
-
}
|
780
|
-
else {
|
781
|
-
x = node.x + radius;
|
782
|
-
y = node.y - node.height / 2;
|
783
|
-
}
|
784
|
-
dx = x - x3;
|
785
|
-
dy = y - y3;
|
786
|
-
return Math.abs(Math.sqrt(dx*dx + dy*dy) - radius);
|
787
|
-
}
|
788
|
-
};
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
/**
|
793
|
-
* This allows the zoom level of the graph to influence the rendering
|
794
|
-
*
|
795
|
-
* @param scale
|
796
|
-
*/
|
797
|
-
Edge.prototype.setScale = function(scale) {
|
798
|
-
this.graphScaleInv = 1.0/scale;
|
799
|
-
};
|
800
|
-
|
801
|
-
|
802
|
-
Edge.prototype.select = function() {
|
803
|
-
this.selected = true;
|
804
|
-
};
|
805
|
-
|
806
|
-
Edge.prototype.unselect = function() {
|
807
|
-
this.selected = false;
|
808
|
-
};
|
809
|
-
|
810
|
-
Edge.prototype.positionBezierNode = function() {
|
811
|
-
if (this.via !== null) {
|
812
|
-
this.via.x = 0.5 * (this.from.x + this.to.x);
|
813
|
-
this.via.y = 0.5 * (this.from.y + this.to.y);
|
814
|
-
}
|
815
|
-
};
|
816
|
-
|
817
|
-
/**
|
818
|
-
* This function draws the control nodes for the manipulator. In order to enable this, only set the this.controlNodesEnabled to true.
|
819
|
-
* @param ctx
|
820
|
-
*/
|
821
|
-
Edge.prototype._drawControlNodes = function(ctx) {
|
822
|
-
if (this.controlNodesEnabled == true) {
|
823
|
-
if (this.controlNodes.from === null && this.controlNodes.to === null) {
|
824
|
-
var nodeIdFrom = "edgeIdFrom:".concat(this.id);
|
825
|
-
var nodeIdTo = "edgeIdTo:".concat(this.id);
|
826
|
-
var constants = {
|
827
|
-
nodes:{group:'', radius:8},
|
828
|
-
physics:{damping:0},
|
829
|
-
clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}}
|
830
|
-
};
|
831
|
-
this.controlNodes.from = new Node(
|
832
|
-
{id:nodeIdFrom,
|
833
|
-
shape:'dot',
|
834
|
-
color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
|
835
|
-
},{},{},constants);
|
836
|
-
this.controlNodes.to = new Node(
|
837
|
-
{id:nodeIdTo,
|
838
|
-
shape:'dot',
|
839
|
-
color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
|
840
|
-
},{},{},constants);
|
841
|
-
}
|
842
|
-
|
843
|
-
if (this.controlNodes.from.selected == false && this.controlNodes.to.selected == false) {
|
844
|
-
this.controlNodes.positions = this.getControlNodePositions(ctx);
|
845
|
-
this.controlNodes.from.x = this.controlNodes.positions.from.x;
|
846
|
-
this.controlNodes.from.y = this.controlNodes.positions.from.y;
|
847
|
-
this.controlNodes.to.x = this.controlNodes.positions.to.x;
|
848
|
-
this.controlNodes.to.y = this.controlNodes.positions.to.y;
|
849
|
-
}
|
850
|
-
|
851
|
-
this.controlNodes.from.draw(ctx);
|
852
|
-
this.controlNodes.to.draw(ctx);
|
853
|
-
}
|
854
|
-
else {
|
855
|
-
this.controlNodes = {from:null, to:null, positions:{}};
|
856
|
-
}
|
857
|
-
}
|
858
|
-
|
859
|
-
/**
|
860
|
-
* Enable control nodes.
|
861
|
-
* @private
|
862
|
-
*/
|
863
|
-
Edge.prototype._enableControlNodes = function() {
|
864
|
-
this.controlNodesEnabled = true;
|
865
|
-
}
|
866
|
-
|
867
|
-
/**
|
868
|
-
* disable control nodes
|
869
|
-
* @private
|
870
|
-
*/
|
871
|
-
Edge.prototype._disableControlNodes = function() {
|
872
|
-
this.controlNodesEnabled = false;
|
873
|
-
}
|
874
|
-
|
875
|
-
/**
|
876
|
-
* This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null.
|
877
|
-
* @param x
|
878
|
-
* @param y
|
879
|
-
* @returns {null}
|
880
|
-
* @private
|
881
|
-
*/
|
882
|
-
Edge.prototype._getSelectedControlNode = function(x,y) {
|
883
|
-
var positions = this.controlNodes.positions;
|
884
|
-
var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2));
|
885
|
-
var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2));
|
886
|
-
|
887
|
-
if (fromDistance < 15) {
|
888
|
-
this.connectedNode = this.from;
|
889
|
-
this.from = this.controlNodes.from;
|
890
|
-
return this.controlNodes.from;
|
891
|
-
}
|
892
|
-
else if (toDistance < 15) {
|
893
|
-
this.connectedNode = this.to;
|
894
|
-
this.to = this.controlNodes.to;
|
895
|
-
return this.controlNodes.to;
|
896
|
-
}
|
897
|
-
else {
|
898
|
-
return null;
|
899
|
-
}
|
900
|
-
}
|
901
|
-
|
902
|
-
|
903
|
-
/**
|
904
|
-
* this resets the control nodes to their original position.
|
905
|
-
* @private
|
906
|
-
*/
|
907
|
-
Edge.prototype._restoreControlNodes = function() {
|
908
|
-
if (this.controlNodes.from.selected == true) {
|
909
|
-
this.from = this.connectedNode;
|
910
|
-
this.connectedNode = null;
|
911
|
-
this.controlNodes.from.unselect();
|
912
|
-
}
|
913
|
-
if (this.controlNodes.to.selected == true) {
|
914
|
-
this.to = this.connectedNode;
|
915
|
-
this.connectedNode = null;
|
916
|
-
this.controlNodes.to.unselect();
|
917
|
-
}
|
918
|
-
}
|
919
|
-
|
920
|
-
/**
|
921
|
-
* this calculates the position of the control nodes on the edges of the parent nodes.
|
922
|
-
*
|
923
|
-
* @param ctx
|
924
|
-
* @returns {{from: {x: number, y: number}, to: {x: *, y: *}}}
|
925
|
-
*/
|
926
|
-
Edge.prototype.getControlNodePositions = function(ctx) {
|
927
|
-
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
|
928
|
-
var dx = (this.to.x - this.from.x);
|
929
|
-
var dy = (this.to.y - this.from.y);
|
930
|
-
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
|
931
|
-
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI);
|
932
|
-
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength;
|
933
|
-
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
|
934
|
-
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
|
935
|
-
|
936
|
-
|
937
|
-
if (this.smooth == true) {
|
938
|
-
angle = Math.atan2((this.to.y - this.via.y), (this.to.x - this.via.x));
|
939
|
-
dx = (this.to.x - this.via.x);
|
940
|
-
dy = (this.to.y - this.via.y);
|
941
|
-
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
|
942
|
-
}
|
943
|
-
var toBorderDist = this.to.distanceToBorder(ctx, angle);
|
944
|
-
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
|
945
|
-
|
946
|
-
var xTo,yTo;
|
947
|
-
if (this.smooth == true) {
|
948
|
-
xTo = (1 - toBorderPoint) * this.via.x + toBorderPoint * this.to.x;
|
949
|
-
yTo = (1 - toBorderPoint) * this.via.y + toBorderPoint * this.to.y;
|
950
|
-
}
|
951
|
-
else {
|
952
|
-
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
|
953
|
-
yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
|
954
|
-
}
|
955
|
-
|
956
|
-
return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}};
|
957
|
-
}
|