@things-factory/integration-ui 7.0.31 → 7.0.35

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. package/client/analysis/graph-data.ts +34 -0
  2. package/client/analysis/graph-viewer-old.ts +1097 -0
  3. package/client/analysis/graph-viewer-style.ts +5 -1
  4. package/client/analysis/graph-viewer.ts +245 -942
  5. package/client/analysis/node.ts +73 -0
  6. package/client/analysis/relationship.ts +19 -0
  7. package/client/analysis/utils.ts +41 -0
  8. package/client/pages/integration-analysis.ts +160 -71
  9. package/dist-client/analysis/graph-data.d.ts +36 -0
  10. package/dist-client/analysis/graph-data.js +2 -0
  11. package/dist-client/analysis/graph-data.js.map +1 -0
  12. package/dist-client/analysis/graph-viewer-old.d.ts +110 -0
  13. package/dist-client/analysis/graph-viewer-old.js +808 -0
  14. package/dist-client/analysis/graph-viewer-old.js.map +1 -0
  15. package/dist-client/analysis/graph-viewer-style.js +5 -1
  16. package/dist-client/analysis/graph-viewer-style.js.map +1 -1
  17. package/dist-client/analysis/graph-viewer.d.ts +25 -99
  18. package/dist-client/analysis/graph-viewer.js +189 -703
  19. package/dist-client/analysis/graph-viewer.js.map +1 -1
  20. package/dist-client/analysis/node.d.ts +4 -0
  21. package/dist-client/analysis/node.js +59 -0
  22. package/dist-client/analysis/node.js.map +1 -0
  23. package/dist-client/analysis/relationship.d.ts +4 -0
  24. package/dist-client/analysis/relationship.js +13 -0
  25. package/dist-client/analysis/relationship.js.map +1 -0
  26. package/dist-client/analysis/utils.d.ts +20 -0
  27. package/dist-client/analysis/utils.js +31 -0
  28. package/dist-client/analysis/utils.js.map +1 -0
  29. package/dist-client/pages/integration-analysis.d.ts +8 -3
  30. package/dist-client/pages/integration-analysis.js +153 -70
  31. package/dist-client/pages/integration-analysis.js.map +1 -1
  32. package/dist-client/tsconfig.tsbuildinfo +1 -1
  33. package/dist-server/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +6 -6
@@ -0,0 +1,808 @@
1
+ import * as d3 from 'd3';
2
+ export class GraphViewer {
3
+ constructor(_selector, _options) {
4
+ this.classes2colors = {};
5
+ this.justLoaded = false;
6
+ this.numClasses = 0;
7
+ this.options = {
8
+ arrowSize: 4,
9
+ colors: this.colors(),
10
+ highlight: undefined,
11
+ infoPanel: true,
12
+ minCollision: undefined,
13
+ graphData: undefined,
14
+ dataUrl: undefined,
15
+ nodeOutlineFillColor: undefined,
16
+ nodeRadius: 25,
17
+ relationshipColor: '#a5abb6',
18
+ zoomFit: false
19
+ };
20
+ this.init(_selector, _options);
21
+ }
22
+ appendGraph(container) {
23
+ this.svg = container
24
+ .append('svg')
25
+ .attr('width', '100%')
26
+ .attr('height', '100%')
27
+ .attr('class', 'graph-viewer')
28
+ .call(d3.zoom().on('zoom', (event, d) => {
29
+ var scale = event.transform.k, translate = [event.transform.x, event.transform.y];
30
+ if (this.svgTranslate) {
31
+ translate[0] += this.svgTranslate[0];
32
+ translate[1] += this.svgTranslate[1];
33
+ }
34
+ if (this.svgScale) {
35
+ scale *= this.svgScale;
36
+ }
37
+ this.svg.attr('transform', 'translate(' + translate[0] + ', ' + translate[1] + ') scale(' + scale + ')');
38
+ }))
39
+ .on('dblclick.zoom', null)
40
+ .append('g')
41
+ .attr('width', '100%')
42
+ .attr('height', '100%');
43
+ this.svgRelationships = this.svg.append('g').attr('class', 'relationships');
44
+ this.svgNodes = this.svg.append('g').attr('class', 'nodes');
45
+ }
46
+ appendImageToNode(node) {
47
+ return node
48
+ .append('image')
49
+ .attr('height', d => {
50
+ return this.icon(d) ? '24px' : '30px';
51
+ })
52
+ .attr('x', d => {
53
+ return this.icon(d) ? '5px' : '-15px';
54
+ })
55
+ .attr('xlink:href', d => {
56
+ return this.image(d);
57
+ })
58
+ .attr('y', d => {
59
+ return this.icon(d) ? '5px' : '-16px';
60
+ })
61
+ .attr('width', d => {
62
+ return this.icon(d) ? '24px' : '30px';
63
+ });
64
+ }
65
+ appendInfoPanel(container) {
66
+ return container.append('div').attr('class', 'graph-info');
67
+ }
68
+ appendInfoElement(cls, isNode, property, value) {
69
+ var elem = this.info.append('a');
70
+ elem
71
+ .attr('href', '#')
72
+ .attr('class', cls)
73
+ .html('<strong>' + property + '</strong>' + (value ? ': ' + value : ''));
74
+ if (!value) {
75
+ elem
76
+ .style('background-color', d => {
77
+ return this.options.nodeOutlineFillColor
78
+ ? this.options.nodeOutlineFillColor
79
+ : isNode
80
+ ? this.class2color(property)
81
+ : this.defaultColor();
82
+ })
83
+ .style('border-color', d => {
84
+ return this.options.nodeOutlineFillColor
85
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
86
+ : isNode
87
+ ? this.class2darkenColor(property)
88
+ : this.defaultDarkenColor();
89
+ })
90
+ .style('color', d => {
91
+ return this.options.nodeOutlineFillColor ? this.class2darkenColor(this.options.nodeOutlineFillColor) : '#fff';
92
+ });
93
+ }
94
+ }
95
+ appendInfoElementClass(cls, node) {
96
+ this.appendInfoElement(cls, true, node);
97
+ }
98
+ appendInfoElementProperty(cls, property, value) {
99
+ this.appendInfoElement(cls, false, property, value);
100
+ }
101
+ appendInfoElementRelationship(cls, relationship) {
102
+ this.appendInfoElement(cls, false, relationship);
103
+ }
104
+ appendNode() {
105
+ return this.node
106
+ .enter()
107
+ .append('g')
108
+ .attr('class', d => {
109
+ var highlight, i, classes = 'node', label = d.labels[0];
110
+ if (this.icon(d)) {
111
+ classes += ' node-icon';
112
+ }
113
+ if (this.image(d)) {
114
+ classes += ' node-image';
115
+ }
116
+ if (this.options.highlight) {
117
+ for (i = 0; i < this.options.highlight.length; i++) {
118
+ highlight = this.options.highlight[i];
119
+ if (d.labels[0] === highlight.class && d.properties[highlight.property] === highlight.value) {
120
+ classes += ' node-highlighted';
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ return classes;
126
+ })
127
+ .on('click', (event, d) => {
128
+ d.fx = d.fy = null;
129
+ if (typeof this.options.onNodeClick === 'function') {
130
+ this.options.onNodeClick(d);
131
+ }
132
+ })
133
+ .on('dblclick', (event, d) => {
134
+ this.stickNode(event, d);
135
+ if (typeof this.options.onNodeDoubleClick === 'function') {
136
+ this.options.onNodeDoubleClick(d);
137
+ }
138
+ })
139
+ .on('mouseenter', (event, d) => {
140
+ if (this.info) {
141
+ this.updateInfo(d);
142
+ }
143
+ if (typeof this.options.onNodeMouseEnter === 'function') {
144
+ this.options.onNodeMouseEnter(d);
145
+ }
146
+ })
147
+ .on('mouseleave', (event, d) => {
148
+ if (this.info) {
149
+ this.clearInfo();
150
+ }
151
+ if (typeof this.options.onNodeMouseLeave === 'function') {
152
+ this.options.onNodeMouseLeave(d);
153
+ }
154
+ })
155
+ .call(d3
156
+ .drag()
157
+ .on('start', this.dragStarted.bind(this))
158
+ .on('drag', this.dragged.bind(this))
159
+ .on('end', this.dragEnded.bind(this)));
160
+ }
161
+ appendNodeToGraph() {
162
+ var n = this.appendNode();
163
+ this.appendRingToNode(n);
164
+ // this.appendBoxToNode(n)
165
+ this.appendOutlineToNode(n);
166
+ if (this.options.icons) {
167
+ this.appendTextToNode(n);
168
+ }
169
+ if (this.options.images) {
170
+ this.appendImageToNode(n);
171
+ }
172
+ return n;
173
+ }
174
+ appendOutlineToNode(node) {
175
+ const outline = node
176
+ .append('circle')
177
+ .attr('class', 'outline')
178
+ .attr('r', this.options.nodeRadius)
179
+ .style('fill', d => {
180
+ return this.options.nodeOutlineFillColor ? this.options.nodeOutlineFillColor : this.class2color(d.labels[0]);
181
+ })
182
+ .style('stroke', d => {
183
+ return this.options.nodeOutlineFillColor
184
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
185
+ : this.class2darkenColor(d.labels[0]);
186
+ })
187
+ .append('title')
188
+ .text(d => {
189
+ return this.toString(d);
190
+ });
191
+ node
192
+ .append('text')
193
+ .attr('class', 'node-text')
194
+ .attr('x', 0)
195
+ .attr('y', 52)
196
+ .attr('text-anchor', 'middle') // 텍스트 중앙 정렬
197
+ .attr('fill', 'black') // 텍스트 색상을 검은색으로 변경
198
+ .text(d => d.text);
199
+ return outline;
200
+ }
201
+ appendBoxToNode(node) {
202
+ const rect = node
203
+ .append('rect')
204
+ .attr('class', 'node-rect')
205
+ .attr('width', 160) // 사각형의 너비
206
+ .attr('height', 30) // 사각형의 높이
207
+ .attr('x', -80) // 사각형의 중심을 기준으로 x 위치 조정
208
+ .attr('y', -15) // 사각형의 중심을 기준으로 y 위치 조정
209
+ .style('fill', d => {
210
+ return this.options.nodeOutlineFillColor ? this.options.nodeOutlineFillColor : this.class2color(d.labels[0]);
211
+ })
212
+ .style('stroke', d => {
213
+ return this.options.nodeOutlineFillColor
214
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
215
+ : this.class2darkenColor(d.labels[0]);
216
+ })
217
+ .append('title')
218
+ .text(d => {
219
+ return this.toString(d);
220
+ });
221
+ node
222
+ .append('text')
223
+ .attr('class', 'node-text')
224
+ .attr('x', 0)
225
+ .attr('y', 4)
226
+ .attr('text-anchor', 'middle') // 텍스트 중앙 정렬
227
+ .attr('fill', 'black') // 텍스트 색상을 검은색으로 변경
228
+ .text(d => d.text);
229
+ return rect;
230
+ }
231
+ appendRingToNode(node) {
232
+ return node
233
+ .append('circle')
234
+ .attr('class', 'ring')
235
+ .attr('r', this.options.nodeRadius * 1.16)
236
+ .append('title')
237
+ .text(d => {
238
+ return this.toString(d);
239
+ });
240
+ }
241
+ appendTextToNode(node) {
242
+ return node
243
+ .append('text')
244
+ .attr('fill', '#ffffff')
245
+ .attr('pointer-events', 'none')
246
+ .attr('text-anchor', 'middle')
247
+ .attr('y', '24px')
248
+ .attr('font-family', 'Material Symbols Outlined')
249
+ .attr('font-size', '48px')
250
+ .attr('text-anchor', 'middle')
251
+ .attr('alignment-baseline', 'top')
252
+ .text(d => this.icon(d));
253
+ }
254
+ appendRelationship() {
255
+ return this.relationship
256
+ .enter()
257
+ .append('g')
258
+ .attr('class', 'relationship')
259
+ .on('dblclick', (event, d) => {
260
+ if (typeof this.options.onRelationshipDoubleClick === 'function') {
261
+ this.options.onRelationshipDoubleClick(d);
262
+ }
263
+ })
264
+ .on('mouseenter', (event, d) => {
265
+ if (this.info) {
266
+ this.updateInfo(d);
267
+ }
268
+ });
269
+ }
270
+ appendOutlineToRelationship(r) {
271
+ return r.append('path').attr('class', 'outline').attr('fill', '#a5abb6').attr('stroke', 'none');
272
+ }
273
+ appendOverlayToRelationship(r) {
274
+ return r.append('path').attr('class', 'overlay');
275
+ }
276
+ appendTextToRelationship(r) {
277
+ return r
278
+ .append('text')
279
+ .attr('class', 'text')
280
+ .attr('fill', '#000000')
281
+ .attr('font-size', '8px')
282
+ .attr('pointer-events', 'none')
283
+ .attr('text-anchor', 'middle')
284
+ .text(d => {
285
+ return d.type;
286
+ });
287
+ }
288
+ appendRelationshipToGraph() {
289
+ var relationship = this.appendRelationship(), text = this.appendTextToRelationship(relationship), outline = this.appendOutlineToRelationship(relationship), overlay = this.appendOverlayToRelationship(relationship);
290
+ return {
291
+ outline: outline,
292
+ overlay: overlay,
293
+ relationship: relationship,
294
+ text: text
295
+ };
296
+ }
297
+ class2color(cls) {
298
+ var color = this.classes2colors[cls];
299
+ if (!color) {
300
+ // color = this.options.colors[Math.min(numClasses, this.options.colors.length - 1)];
301
+ color = this.options.colors[this.numClasses % this.options.colors.length];
302
+ this.classes2colors[cls] = color;
303
+ this.numClasses++;
304
+ }
305
+ return color;
306
+ }
307
+ class2darkenColor(cls) {
308
+ return d3.rgb(this.class2color(cls)).darker(1);
309
+ }
310
+ clearInfo() {
311
+ this.info.html('');
312
+ }
313
+ color() {
314
+ return this.options.colors[(this.options.colors.length * Math.random()) << 0];
315
+ }
316
+ colors() {
317
+ // d3.schemeCategory10,
318
+ // d3.schemeCategory20,
319
+ return [
320
+ '#68bdf6', // light blue
321
+ '#6dce9e', // green #1
322
+ '#faafc2', // light pink
323
+ '#f2baf6', // purple
324
+ '#ff928c', // light red
325
+ '#fcea7e', // light yellow
326
+ '#ffc766', // light orange
327
+ '#405f9e', // navy blue
328
+ '#a5abb6', // dark gray
329
+ '#78cecb', // green #2,
330
+ '#b88cbb', // dark purple
331
+ '#ced2d9', // light gray
332
+ '#e84646', // dark red
333
+ '#fa5f86', // dark pink
334
+ '#ffab1a', // dark orange
335
+ '#fcda19', // dark yellow
336
+ '#797b80', // black
337
+ '#c9d96f', // pistacchio
338
+ '#47991f', // green #3
339
+ '#70edee', // turquoise
340
+ '#ff75ea' // pink
341
+ ];
342
+ }
343
+ contains(array, id) {
344
+ var filter = array.filter(function (elem) {
345
+ return elem.id === id;
346
+ });
347
+ return filter.length > 0;
348
+ }
349
+ defaultColor() {
350
+ return this.options.relationshipColor;
351
+ }
352
+ defaultDarkenColor() {
353
+ return d3.rgb(this.options.colors[this.options.colors.length - 1]).darker(1);
354
+ }
355
+ dragEnded(event, d) {
356
+ if (!event.active) {
357
+ this.simulation.alphaTarget(0);
358
+ }
359
+ if (typeof this.options.onNodeDragEnd === 'function') {
360
+ this.options.onNodeDragEnd(d);
361
+ }
362
+ }
363
+ dragged(event, d) {
364
+ this.stickNode(event, d);
365
+ }
366
+ dragStarted(event, d) {
367
+ if (!event.active) {
368
+ this.simulation.alphaTarget(0.3).restart();
369
+ }
370
+ d.fx = d.x;
371
+ d.fy = d.y;
372
+ if (typeof this.options.onNodeDragStart === 'function') {
373
+ this.options.onNodeDragStart(d);
374
+ }
375
+ }
376
+ extend(obj1, obj2) {
377
+ var obj = {};
378
+ this.merge(obj, obj1);
379
+ this.merge(obj, obj2);
380
+ return obj;
381
+ }
382
+ icon(d) {
383
+ return d.icon;
384
+ }
385
+ image(d) {
386
+ var i, imagesForLabel, img, imgLevel, label, labelPropertyValue, property, value;
387
+ if (this.options.images) {
388
+ imagesForLabel = this.options.imageMap[d.labels[0]];
389
+ if (imagesForLabel) {
390
+ imgLevel = 0;
391
+ for (i = 0; i < imagesForLabel.length; i++) {
392
+ labelPropertyValue = imagesForLabel[i].split('|');
393
+ switch (labelPropertyValue.length) {
394
+ case 3:
395
+ value = labelPropertyValue[2];
396
+ /* falls through */
397
+ case 2:
398
+ property = labelPropertyValue[1];
399
+ /* falls through */
400
+ case 1:
401
+ label = labelPropertyValue[0];
402
+ }
403
+ if (d.labels[0] === label &&
404
+ (!property || d.properties[property] !== undefined) &&
405
+ (!value || d.properties[property] === value)) {
406
+ if (labelPropertyValue.length > imgLevel) {
407
+ img = this.options.images[imagesForLabel[i]];
408
+ imgLevel = labelPropertyValue.length;
409
+ }
410
+ }
411
+ }
412
+ }
413
+ }
414
+ return img;
415
+ }
416
+ init(_selector, _options) {
417
+ this.merge(this.options, _options);
418
+ if (this.options.icons) {
419
+ this.options.showIcons = true;
420
+ }
421
+ if (!this.options.minCollision) {
422
+ this.options.minCollision = this.options.nodeRadius * 2;
423
+ }
424
+ this.selector = _selector;
425
+ this.container = d3.select(this.selector);
426
+ // this.container.attr('class', 'graph-container').html('')
427
+ if (this.options.infoPanel) {
428
+ this.info = this.appendInfoPanel(this.container);
429
+ }
430
+ this.appendGraph(this.container);
431
+ this.simulation = this.initSimulation();
432
+ if (this.options.graphData) {
433
+ this.loadGraphData();
434
+ }
435
+ else if (this.options.dataUrl) {
436
+ this.loadGraphDataFromUrl(this.options.dataUrl);
437
+ }
438
+ else {
439
+ console.error('Error: both graphData and dataUrl are empty!');
440
+ }
441
+ }
442
+ initSimulation() {
443
+ const x = this.svg.node().parentElement.parentElement.clientWidth / 2;
444
+ const y = this.svg.node().parentElement.parentElement.clientHeight / 2;
445
+ var simulation = d3
446
+ .forceSimulation()
447
+ .force('collide', d3
448
+ .forceCollide()
449
+ .radius(d => {
450
+ return this.options.minCollision;
451
+ })
452
+ .iterations(2))
453
+ .force('charge', d3.forceManyBody())
454
+ .force('link', d3.forceLink().id(d => {
455
+ return d.id;
456
+ }))
457
+ .force('center', d3.forceCenter(x, y))
458
+ .on('tick', () => {
459
+ this.tick();
460
+ })
461
+ .on('end', () => {
462
+ if (this.options.zoomFit && !this.justLoaded) {
463
+ this.justLoaded = true;
464
+ this.zoomFit(2);
465
+ }
466
+ });
467
+ return simulation;
468
+ }
469
+ loadGraphData() {
470
+ this.nodes = [];
471
+ this.relationships = [];
472
+ this.updateWithGraphData(this.options.graphData);
473
+ }
474
+ loadGraphDataFromUrl(dataUrl) {
475
+ this.nodes = [];
476
+ this.relationships = [];
477
+ d3.json(dataUrl, (error, data) => {
478
+ if (error) {
479
+ throw error;
480
+ }
481
+ this.updateWithGraphData(data);
482
+ });
483
+ }
484
+ merge(target, source) {
485
+ Object.keys(source).forEach(property => {
486
+ target[property] = source[property];
487
+ });
488
+ }
489
+ graphDataToD3Data(data) {
490
+ var graph = {
491
+ nodes: [],
492
+ relationships: []
493
+ };
494
+ data.results.forEach(result => {
495
+ result.data.forEach(data => {
496
+ data.graph.nodes.forEach(node => {
497
+ if (!this.contains(graph.nodes, node.id)) {
498
+ graph.nodes.push(node);
499
+ }
500
+ });
501
+ data.graph.relationships.forEach(function (relationship) {
502
+ relationship.source = relationship.startNode;
503
+ relationship.target = relationship.endNode;
504
+ graph.relationships.push(relationship);
505
+ });
506
+ data.graph.relationships.sort(function (a, b) {
507
+ if (a.source > b.source) {
508
+ return 1;
509
+ }
510
+ else if (a.source < b.source) {
511
+ return -1;
512
+ }
513
+ else {
514
+ if (a.target > b.target) {
515
+ return 1;
516
+ }
517
+ if (a.target < b.target) {
518
+ return -1;
519
+ }
520
+ else {
521
+ return 0;
522
+ }
523
+ }
524
+ });
525
+ for (var i = 0; i < data.graph.relationships.length; i++) {
526
+ if (i !== 0 &&
527
+ data.graph.relationships[i].source === data.graph.relationships[i - 1].source &&
528
+ data.graph.relationships[i].target === data.graph.relationships[i - 1].target) {
529
+ data.graph.relationships[i].linknum = data.graph.relationships[i - 1].linknum + 1;
530
+ }
531
+ else {
532
+ data.graph.relationships[i].linknum = 1;
533
+ }
534
+ }
535
+ });
536
+ });
537
+ return graph;
538
+ }
539
+ randomD3Data(d, maxNodesToGenerate) {
540
+ var data = {
541
+ nodes: [],
542
+ relationships: []
543
+ }, i, label, node, numNodes = ((maxNodesToGenerate * Math.random()) << 0) + 1, relationship, s = this.size();
544
+ for (i = 0; i < numNodes; i++) {
545
+ label = this.randomLabel();
546
+ node = {
547
+ id: s.nodes + 1 + i,
548
+ labels: [label],
549
+ properties: {
550
+ random: label
551
+ },
552
+ x: d.x,
553
+ y: d.y
554
+ };
555
+ data.nodes[data.nodes.length] = node;
556
+ relationship = {
557
+ id: s.relationships + 1 + i,
558
+ type: label.toUpperCase(),
559
+ startNode: d.id,
560
+ endNode: s.nodes + 1 + i,
561
+ properties: {
562
+ from: Date.now()
563
+ },
564
+ source: d.id,
565
+ target: s.nodes + 1 + i,
566
+ linknum: s.relationships + 1 + i
567
+ };
568
+ data.relationships[data.relationships.length] = relationship;
569
+ }
570
+ return data;
571
+ }
572
+ randomLabel() {
573
+ var icons = Object.keys(this.options.iconMap);
574
+ return icons[(icons.length * Math.random()) << 0];
575
+ }
576
+ rotate(cx, cy, x, y, angle) {
577
+ var radians = (Math.PI / 180) * angle, cos = Math.cos(radians), sin = Math.sin(radians), nx = cos * (x - cx) + sin * (y - cy) + cx, ny = cos * (y - cy) - sin * (x - cx) + cy;
578
+ return { x: nx, y: ny };
579
+ }
580
+ rotatePoint(c, p, angle) {
581
+ return this.rotate(c.x, c.y, p.x, p.y, angle);
582
+ }
583
+ rotation(source, target) {
584
+ return (Math.atan2(target.y - source.y, target.x - source.x) * 180) / Math.PI;
585
+ }
586
+ size() {
587
+ return {
588
+ nodes: this.nodes.length,
589
+ relationships: this.relationships.length
590
+ };
591
+ }
592
+ stickNode(event, d) {
593
+ d.fx = event.x;
594
+ d.fy = event.y;
595
+ }
596
+ tick() {
597
+ this.tickNodes();
598
+ this.tickRelationships();
599
+ }
600
+ tickNodes() {
601
+ if (this.node) {
602
+ this.node.attr('transform', d => {
603
+ return 'translate(' + d.x + ', ' + d.y + ')';
604
+ });
605
+ }
606
+ }
607
+ tickRelationships() {
608
+ if (this.relationship) {
609
+ this.relationship.attr('transform', d => {
610
+ var angle = this.rotation(d.source, d.target);
611
+ return 'translate(' + d.source.x + ', ' + d.source.y + ') rotate(' + angle + ')';
612
+ });
613
+ this.tickRelationshipsTexts();
614
+ this.tickRelationshipsOutlines();
615
+ this.tickRelationshipsOverlays();
616
+ }
617
+ }
618
+ tickRelationshipsOutlines() {
619
+ const self = this;
620
+ this.relationship.each(function (relationship) {
621
+ var rel = d3.select(this);
622
+ var outline = rel.select('.outline'), text = rel.select('.text'), bbox = text.node().getBBox(), padding = 3;
623
+ outline.attr('d', d => {
624
+ var center = { x: 0, y: 0 }, angle = self.rotation(d.source, d.target), textBoundingBox = text.node().getBBox(), textPadding = 5, u = self.unitaryVector(d.source, d.target), textMargin = {
625
+ x: (d.target.x - d.source.x - (textBoundingBox.width + textPadding) * u.x) * 0.5,
626
+ y: (d.target.y - d.source.y - (textBoundingBox.width + textPadding) * u.y) * 0.5
627
+ }, n = self.unitaryNormalVector(d.source, d.target), rotatedPointA1 = self.rotatePoint(center, { x: 0 + (self.options.nodeRadius + 1) * u.x - n.x, y: 0 + (self.options.nodeRadius + 1) * u.y - n.y }, angle), rotatedPointB1 = self.rotatePoint(center, { x: textMargin.x - n.x, y: textMargin.y - n.y }, angle), rotatedPointC1 = self.rotatePoint(center, { x: textMargin.x, y: textMargin.y }, angle), rotatedPointD1 = self.rotatePoint(center, { x: 0 + (self.options.nodeRadius + 1) * u.x, y: 0 + (self.options.nodeRadius + 1) * u.y }, angle), rotatedPointA2 = self.rotatePoint(center, { x: d.target.x - d.source.x - textMargin.x - n.x, y: d.target.y - d.source.y - textMargin.y - n.y }, angle), rotatedPointB2 = self.rotatePoint(center, {
628
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x - n.x - u.x * self.options.arrowSize,
629
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y - n.y - u.y * self.options.arrowSize
630
+ }, angle), rotatedPointC2 = self.rotatePoint(center, {
631
+ x: d.target.x -
632
+ d.source.x -
633
+ (self.options.nodeRadius + 1) * u.x -
634
+ n.x +
635
+ (n.x - u.x) * self.options.arrowSize,
636
+ y: d.target.y -
637
+ d.source.y -
638
+ (self.options.nodeRadius + 1) * u.y -
639
+ n.y +
640
+ (n.y - u.y) * self.options.arrowSize
641
+ }, angle), rotatedPointD2 = self.rotatePoint(center, {
642
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x,
643
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y
644
+ }, angle), rotatedPointE2 = self.rotatePoint(center, {
645
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x + (-n.x - u.x) * self.options.arrowSize,
646
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y + (-n.y - u.y) * self.options.arrowSize
647
+ }, angle), rotatedPointF2 = self.rotatePoint(center, {
648
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x - u.x * self.options.arrowSize,
649
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y - u.y * self.options.arrowSize
650
+ }, angle), rotatedPointG2 = self.rotatePoint(center, { x: d.target.x - d.source.x - textMargin.x, y: d.target.y - d.source.y - textMargin.y }, angle);
651
+ return ('M ' +
652
+ rotatedPointA1.x +
653
+ ' ' +
654
+ rotatedPointA1.y +
655
+ ' L ' +
656
+ rotatedPointB1.x +
657
+ ' ' +
658
+ rotatedPointB1.y +
659
+ ' L ' +
660
+ rotatedPointC1.x +
661
+ ' ' +
662
+ rotatedPointC1.y +
663
+ ' L ' +
664
+ rotatedPointD1.x +
665
+ ' ' +
666
+ rotatedPointD1.y +
667
+ ' Z M ' +
668
+ rotatedPointA2.x +
669
+ ' ' +
670
+ rotatedPointA2.y +
671
+ ' L ' +
672
+ rotatedPointB2.x +
673
+ ' ' +
674
+ rotatedPointB2.y +
675
+ ' L ' +
676
+ rotatedPointC2.x +
677
+ ' ' +
678
+ rotatedPointC2.y +
679
+ ' L ' +
680
+ rotatedPointD2.x +
681
+ ' ' +
682
+ rotatedPointD2.y +
683
+ ' L ' +
684
+ rotatedPointE2.x +
685
+ ' ' +
686
+ rotatedPointE2.y +
687
+ ' L ' +
688
+ rotatedPointF2.x +
689
+ ' ' +
690
+ rotatedPointF2.y +
691
+ ' L ' +
692
+ rotatedPointG2.x +
693
+ ' ' +
694
+ rotatedPointG2.y +
695
+ ' Z');
696
+ });
697
+ });
698
+ }
699
+ tickRelationshipsOverlays() {
700
+ this.relationshipOverlay.attr('d', d => {
701
+ var center = { x: 0, y: 0 }, angle = this.rotation(d.source, d.target), n1 = this.unitaryNormalVector(d.source, d.target), n = this.unitaryNormalVector(d.source, d.target, 50), rotatedPointA = this.rotatePoint(center, { x: 0 - n.x, y: 0 - n.y }, angle), rotatedPointB = this.rotatePoint(center, { x: d.target.x - d.source.x - n.x, y: d.target.y - d.source.y - n.y }, angle), rotatedPointC = this.rotatePoint(center, { x: d.target.x - d.source.x + n.x - n1.x, y: d.target.y - d.source.y + n.y - n1.y }, angle), rotatedPointD = this.rotatePoint(center, { x: 0 + n.x - n1.x, y: 0 + n.y - n1.y }, angle);
702
+ return ('M ' +
703
+ rotatedPointA.x +
704
+ ' ' +
705
+ rotatedPointA.y +
706
+ ' L ' +
707
+ rotatedPointB.x +
708
+ ' ' +
709
+ rotatedPointB.y +
710
+ ' L ' +
711
+ rotatedPointC.x +
712
+ ' ' +
713
+ rotatedPointC.y +
714
+ ' L ' +
715
+ rotatedPointD.x +
716
+ ' ' +
717
+ rotatedPointD.y +
718
+ ' Z');
719
+ });
720
+ }
721
+ tickRelationshipsTexts() {
722
+ this.relationshipText.attr('transform', d => {
723
+ var angle = (this.rotation(d.source, d.target) + 360) % 360, mirror = angle > 90 && angle < 270, center = { x: 0, y: 0 }, n = this.unitaryNormalVector(d.source, d.target), nWeight = mirror ? 2 : -3, point = {
724
+ x: (d.target.x - d.source.x) * 0.5 + n.x * nWeight,
725
+ y: (d.target.y - d.source.y) * 0.5 + n.y * nWeight
726
+ }, rotatedPoint = this.rotatePoint(center, point, angle);
727
+ return 'translate(' + rotatedPoint.x + ', ' + rotatedPoint.y + ') rotate(' + (mirror ? 180 : 0) + ')';
728
+ });
729
+ }
730
+ toString(d) {
731
+ var s = d.labels ? d.labels[0] : d.type;
732
+ s += ' (<id>: ' + d.id;
733
+ Object.keys(d.properties).forEach(function (property) {
734
+ s += ', ' + property + ': ' + JSON.stringify(d.properties[property]);
735
+ });
736
+ s += ')';
737
+ return s;
738
+ }
739
+ unitaryNormalVector(source, target, newLength) {
740
+ var center = { x: 0, y: 0 }, vector = this.unitaryVector(source, target, newLength);
741
+ return this.rotatePoint(center, vector, 90);
742
+ }
743
+ unitaryVector(source, target, newLength) {
744
+ var length = Math.sqrt(Math.pow(target.x - source.x, 2) + Math.pow(target.y - source.y, 2)) / Math.sqrt(newLength || 1);
745
+ return {
746
+ x: (target.x - source.x) / length,
747
+ y: (target.y - source.y) / length
748
+ };
749
+ }
750
+ updateWithD3Data(d3Data) {
751
+ this.updateNodesAndRelationships(d3Data.nodes, d3Data.relationships);
752
+ }
753
+ updateWithGraphData(graphData) {
754
+ var d3Data = this.graphDataToD3Data(graphData);
755
+ this.updateWithD3Data(d3Data);
756
+ }
757
+ updateInfo(d) {
758
+ this.clearInfo();
759
+ if (d.labels) {
760
+ this.appendInfoElementClass('class', d.labels[0]);
761
+ }
762
+ else {
763
+ this.appendInfoElementRelationship('class', d.type);
764
+ }
765
+ this.appendInfoElementProperty('property', '&lt;id&gt;', d.id);
766
+ Object.keys(d.properties).forEach(property => {
767
+ this.appendInfoElementProperty('property', property, JSON.stringify(d.properties[property]));
768
+ });
769
+ }
770
+ updateNodes(n) {
771
+ Array.prototype.push.apply(this.nodes, n);
772
+ this.node = this.svgNodes.selectAll('.node').data(this.nodes, d => {
773
+ return d.id;
774
+ });
775
+ var nodeEnter = this.appendNodeToGraph();
776
+ this.node = nodeEnter.merge(this.node);
777
+ }
778
+ updateNodesAndRelationships(n, r) {
779
+ this.updateRelationships(r);
780
+ this.updateNodes(n);
781
+ this.simulation.nodes(this.nodes);
782
+ this.simulation.force('link').links(this.relationships);
783
+ }
784
+ updateRelationships(r) {
785
+ Array.prototype.push.apply(this.relationships, r);
786
+ this.relationship = this.svgRelationships.selectAll('.relationship').data(this.relationships, d => {
787
+ return d.id;
788
+ });
789
+ var relationshipEnter = this.appendRelationshipToGraph();
790
+ this.relationship = relationshipEnter.relationship.merge(this.relationship);
791
+ this.relationshipOutline = this.svg.selectAll('.relationship .outline');
792
+ this.relationshipOutline = relationshipEnter.outline.merge(this.relationshipOutline);
793
+ this.relationshipOverlay = this.svg.selectAll('.relationship .overlay');
794
+ this.relationshipOverlay = relationshipEnter.overlay.merge(this.relationshipOverlay);
795
+ this.relationshipText = this.svg.selectAll('.relationship .text');
796
+ this.relationshipText = relationshipEnter.text.merge(this.relationshipText);
797
+ }
798
+ zoomFit(transitionDuration) {
799
+ var bounds = this.svg.node().getBBox(), parent = this.svg.node().parentElement.parentElement, fullWidth = parent.clientWidth, fullHeight = parent.clientHeight, width = bounds.width, height = bounds.height, midX = bounds.x + width / 2, midY = bounds.y + height / 2;
800
+ if (width === 0 || height === 0) {
801
+ return; // nothing to fit
802
+ }
803
+ this.svgScale = 0.85 / Math.max(width / fullWidth, height / fullHeight);
804
+ this.svgTranslate = [fullWidth / 2 - this.svgScale * midX, fullHeight / 2 - this.svgScale * midY];
805
+ this.svg.attr('transform', 'translate(' + this.svgTranslate[0] + ', ' + this.svgTranslate[1] + ') scale(' + this.svgScale + ')');
806
+ }
807
+ }
808
+ //# sourceMappingURL=graph-viewer-old.js.map