@rws-framework/ai-tools 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.
Files changed (82) hide show
  1. package/.bin/add-v.sh +10 -0
  2. package/.bin/emerge.sh +11 -0
  3. package/.emerge-vis-output/rws-server/emerge-file_result_dependency_graph.graphml +2067 -0
  4. package/.emerge-vis-output/rws-server/emerge-filesystem_graph.graphml +1505 -0
  5. package/.emerge-vis-output/rws-server/emerge-statistics-and-metrics.json +1 -0
  6. package/.emerge-vis-output/rws-server/emerge-statistics-metrics.txt +1147 -0
  7. package/.emerge-vis-output/rws-server/html/emerge.html +501 -0
  8. package/.emerge-vis-output/rws-server/html/jsconfig.json +9 -0
  9. package/.emerge-vis-output/rws-server/html/resources/css/custom.css +211 -0
  10. package/.emerge-vis-output/rws-server/html/resources/js/emerge_common.js +45 -0
  11. package/.emerge-vis-output/rws-server/html/resources/js/emerge_data.js +13 -0
  12. package/.emerge-vis-output/rws-server/html/resources/js/emerge_git.js +1414 -0
  13. package/.emerge-vis-output/rws-server/html/resources/js/emerge_graph.js +539 -0
  14. package/.emerge-vis-output/rws-server/html/resources/js/emerge_heatmap.js +220 -0
  15. package/.emerge-vis-output/rws-server/html/resources/js/emerge_hull.js +180 -0
  16. package/.emerge-vis-output/rws-server/html/resources/js/emerge_main.js +1003 -0
  17. package/.emerge-vis-output/rws-server/html/resources/js/emerge_search.js +71 -0
  18. package/.emerge-vis-output/rws-server/html/resources/js/emerge_ui.js +199 -0
  19. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.css +4124 -0
  20. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.css.map +1 -0
  21. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.min.css +7 -0
  22. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.min.css.map +1 -0
  23. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.css +4123 -0
  24. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.css.map +1 -0
  25. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.min.css +7 -0
  26. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-grid.rtl.min.css.map +1 -0
  27. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.css +488 -0
  28. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.css.map +1 -0
  29. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.min.css +7 -0
  30. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.min.css.map +1 -0
  31. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.css +485 -0
  32. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.css.map +1 -0
  33. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.min.css +7 -0
  34. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-reboot.rtl.min.css.map +1 -0
  35. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.css +4266 -0
  36. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.css.map +1 -0
  37. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.min.css +7 -0
  38. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.min.css.map +1 -0
  39. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.css +4257 -0
  40. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.css.map +1 -0
  41. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.min.css +7 -0
  42. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap-utilities.rtl.min.css.map +1 -0
  43. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.css +10878 -0
  44. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.css.map +1 -0
  45. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.min.css +7 -0
  46. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.min.css.map +1 -0
  47. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.css +10842 -0
  48. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.css.map +1 -0
  49. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.min.css +7 -0
  50. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/css/bootstrap.rtl.min.css.map +1 -0
  51. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.js +7075 -0
  52. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.js.map +1 -0
  53. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.min.js +7 -0
  54. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.bundle.min.js.map +1 -0
  55. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.js +5202 -0
  56. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.js.map +1 -0
  57. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.min.js +7 -0
  58. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.esm.min.js.map +1 -0
  59. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.js +5249 -0
  60. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.js.map +1 -0
  61. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.min.js +7 -0
  62. package/.emerge-vis-output/rws-server/html/vendors/bootstrap/js/bootstrap.min.js.map +1 -0
  63. package/.emerge-vis-output/rws-server/html/vendors/d3/d3.v7.8.4.min.js +2 -0
  64. package/.emerge-vis-output/rws-server/html/vendors/d3/d3.v7.min.js +2 -0
  65. package/.emerge-vis-output/rws-server/html/vendors/dark-mode-switch/css/dark-mode.css +148 -0
  66. package/.emerge-vis-output/rws-server/html/vendors/dark-mode-switch/js/dark-mode-switch.min.js +1 -0
  67. package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/daterangepicker.css +410 -0
  68. package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/daterangepicker.min.js +8 -0
  69. package/.emerge-vis-output/rws-server/html/vendors/daterangepicker/moment.min.js +7 -0
  70. package/.emerge-vis-output/rws-server/html/vendors/hull/hull.js +373 -0
  71. package/.emerge-vis-output/rws-server/html/vendors/jquery/jquery-3.6.0.min.js +2 -0
  72. package/.emerge-vis-output/rws-server/html/vendors/popper/popper.min.js +6 -0
  73. package/.emerge-vis-output/rws-server/html/vendors/simpleheat/simpleheat.js +141 -0
  74. package/.eslintrc.json +53 -0
  75. package/README.md +862 -0
  76. package/package.json +49 -0
  77. package/src/index.ts +22 -0
  78. package/src/models/convo/ConvoLoader.ts +415 -0
  79. package/src/models/convo/VectorStore.ts +33 -0
  80. package/src/models/prompts/_prompt.ts +388 -0
  81. package/src/services/VectorStoreService.ts +15 -0
  82. package/tsconfig.json +24 -0
@@ -0,0 +1,220 @@
1
+ function initHeatmapSwitches() {
2
+ // normal heatmap
3
+ $("#switchActivateHeatmap").on('change', function() {
4
+ if ($(this).is(':checked')) {
5
+ heatmapActive = true
6
+ if ($("#switchMergeHeatmap").is(':checked')) {
7
+ $("#switchMergeHeatmap").prop('checked', false);
8
+ heatmapMerged = false
9
+ }
10
+
11
+ if ($("#switchChurnHeatmap").is(':checked')) {
12
+ $("#switchChurnHeatmap").prop('checked', false);
13
+ heatmapChurn = false
14
+ }
15
+
16
+ if ($("#switchHotspotHeatmap").is(':checked')) {
17
+ $("#switchHotspotHeatmap").prop('checked', false);
18
+ heatmapHotspot = false
19
+ }
20
+ } else {
21
+ heatmapActive = false
22
+ }
23
+
24
+ simulationUpdate();
25
+ });
26
+
27
+ // hybrid/merged heatmap
28
+ $("#switchMergeHeatmap").on('change', function() {
29
+ if ($(this).is(':checked')) {
30
+ heatmapMerged = true
31
+ if ($("#switchActivateHeatmap").is(':checked')) {
32
+ $("#switchActivateHeatmap").prop('checked', false);
33
+ heatmapActive = false
34
+ }
35
+
36
+ if ($("#switchChurnHeatmap").is(':checked')) {
37
+ $("#switchChurnHeatmap").prop('checked', false);
38
+ heatmapChurn = false
39
+ }
40
+
41
+ if ($("#switchHotspotHeatmap").is(':checked')) {
42
+ $("#switchHotspotHeatmap").prop('checked', false);
43
+ heatmapHotspot = false
44
+ }
45
+ } else {
46
+ heatmapMerged = false
47
+ }
48
+
49
+ simulationUpdate();
50
+ });
51
+
52
+ // churn heatmap
53
+ $("#switchChurnHeatmap").on('change', function() {
54
+ if ($(this).is(':checked')) {
55
+ heatmapChurn = true
56
+ if ($("#switchActivateHeatmap").is(':checked')) {
57
+ $("#switchActivateHeatmap").prop('checked', false);
58
+ heatmapActive = false
59
+ }
60
+
61
+ if ($("#switchMergeHeatmap").is(':checked')) {
62
+ $("#switchMergeHeatmap").prop('checked', false);
63
+ heatmapMerged = false
64
+ }
65
+
66
+ if ($("#switchHotspotHeatmap").is(':checked')) {
67
+ $("#switchHotspotHeatmap").prop('checked', false);
68
+ heatmapHotspot = false
69
+ }
70
+ } else {
71
+ heatmapChurn = false
72
+ }
73
+
74
+ simulationUpdate();
75
+ });
76
+
77
+ // hotspot heatmap
78
+ $("#switchHotspotHeatmap").on('change', function() {
79
+ if ($(this).is(':checked')) {
80
+ heatmapHotspot = true
81
+ if ($("#switchActivateHeatmap").is(':checked')) {
82
+ $("#switchActivateHeatmap").prop('checked', false);
83
+ heatmapActive = false
84
+ }
85
+
86
+ if ($("#switchMergeHeatmap").is(':checked')) {
87
+ $("#switchMergeHeatmap").prop('checked', false);
88
+ heatmapMerged = false
89
+ }
90
+
91
+ if ($("#switchChurnHeatmap").is(':checked')) {
92
+ $("#switchChurnHeatmap").prop('checked', false);
93
+ heatmapChurn = false
94
+ }
95
+ } else {
96
+ heatmapHotspot = false
97
+ }
98
+
99
+ simulationUpdate();
100
+ });
101
+
102
+ }
103
+
104
+ /**
105
+ * * MARK: - Drawing a heatmap for normal / hybrid (merged) mode
106
+ */
107
+
108
+ function calculateHeatmapScore(node) {
109
+
110
+ let totalScore = 0
111
+
112
+ if (normalHeatmapIsActive() || mergedHeatmapIsActive() ) {
113
+
114
+ let score = analysis_config['heatmap']['score']['base']
115
+ let slocScore = 0
116
+ let fanoutScore = 0
117
+
118
+ if (analysis_config['heatmap']['metrics']['active']['sloc'] == true) {
119
+ // add weighted sloc metric if present
120
+ if ('metric_sloc_in_entity' in node) {
121
+ slocScore = node.metric_sloc_in_entity * analysis_config['heatmap']['metrics']['weights']['sloc']
122
+ }
123
+ if ('metric_sloc_in_file' in node) {
124
+ slocScore = node.metric_sloc_in_file * analysis_config['heatmap']['metrics']['weights']['sloc']
125
+ }
126
+ }
127
+
128
+ if (analysis_config['heatmap']['metrics']['active']['fan_out'] == true) {
129
+ // add weighted fan-out metric is present
130
+ if ('metric_fan_out_dependency_graph' in node) {
131
+ fanoutScore = node.metric_fan_out_dependency_graph * analysis_config['heatmap']['metrics']['weights']['fan_out']
132
+ }
133
+ if ('metric_fan_out_inheritance_graph' in node) {
134
+ fanoutScore = node.metric_fan_out_inheritance_graph * analysis_config['heatmap']['metrics']['weights']['fan_out']
135
+ }
136
+ if ('metric_fan_out_complete_graph' in node) {
137
+ fanoutScore = node.metric_fan_out_complete_graph * analysis_config['heatmap']['metrics']['weights']['fan_out']
138
+ }
139
+ }
140
+
141
+ // limit the total score to the heatmap limit parameter, since the rendering seems to be buggy if this is exceeded
142
+ totalScore = score + slocScore + fanoutScore
143
+ if (totalScore > analysis_config['heatmap']['score']['limit']) {
144
+ totalScore = analysis_config['heatmap']['score']['limit']
145
+ }
146
+
147
+ } else if (churnHeatmapIsActive()) {
148
+ let score = analysis_config['churn_heatmap']['score']['base']
149
+ let churnScore = 0
150
+
151
+ if (node.hasOwnProperty('metric_git_code_churn')) {
152
+ if (analysis_config['churn_heatmap']['metrics']['active']['churn'] == true) {
153
+ churnScore = node.metric_git_code_churn * analysis_config['churn_heatmap']['metrics']['weights']['churn']
154
+ }
155
+
156
+ totalScore = score + churnScore
157
+ if (totalScore > analysis_config['churn_heatmap']['score']['limit']) {
158
+ totalScore = analysis_config['churn_heatmap']['score']['limit']
159
+ }
160
+ }
161
+
162
+ } else if (hotspotHeatmapIsActive()) {
163
+ // approach: let's define a hotspot as a product of the following factors
164
+ // 1. code churn over a given time period
165
+ // 2. whitespace complexity over a given time period
166
+ // 3. SLOC over a given time period
167
+ // 4. connectivity based on dependencies inside the network (i.e. fan-in / fan-out)
168
+
169
+ let score = analysis_config['hotspot_heatmap']['score']['base']
170
+ let churnScore = 0
171
+ let ws_complexity_score = 0
172
+ let connectivity_score = 0
173
+ let sloc_score = 0
174
+
175
+ if (node.hasOwnProperty('metric_git_code_churn') && node.hasOwnProperty('metric_git_ws_complexity') ) {
176
+ if (analysis_config['hotspot_heatmap']['metrics']['active']['churn'] == true && analysis_config['hotspot_heatmap']['metrics']['active']['ws_complexity'] == true) {
177
+
178
+ churnScore = node.metric_git_code_churn * analysis_config['hotspot_heatmap']['metrics']['weights']['churn'] * 2.0
179
+ ws_complexity_score = node.metric_git_code_churn * analysis_config['hotspot_heatmap']['metrics']['weights']['ws_complexity'] * 0.75
180
+
181
+ if ('metric_fan_out_dependency_graph' in node) {
182
+ connectivity_score = node.metric_fan_out_dependency_graph * 1.5
183
+ }
184
+
185
+ totalScore = (churnScore + ws_complexity_score + connectivity_score )
186
+ if (totalScore > analysis_config['hotspot_heatmap']['score']['limit'] * 1.25 ) {
187
+ totalScore = analysis_config['hotspot_heatmap']['score']['limit'] * 1.25
188
+ }
189
+ }
190
+ }
191
+
192
+ }
193
+
194
+ return totalScore
195
+ }
196
+
197
+ function drawHeatMap(context) {
198
+
199
+ if (normalHeatmapIsActive() || mergedHeatmapIsActive()) {
200
+ heat.max(analysis_config['heatmap']['score']['limit']);
201
+ }
202
+
203
+ if (churnHeatmapIsActive()) {
204
+ heat.max(analysis_config['churn_heatmap']['score']['limit']);
205
+ }
206
+
207
+ if (hotspotHeatmapIsActive()) {
208
+ heat.max(analysis_config['hotspot_heatmap']['score']['limit']);
209
+ }
210
+
211
+ heat.clear();
212
+ currentGraph.nodes.forEach(function(node, i) {
213
+ heat.add([node.x, node.y, calculateHeatmapScore(node)])
214
+ })
215
+
216
+ heat.draw()
217
+
218
+ // reset alpha from heatmap rendering
219
+ context.globalAlpha = 1;
220
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * * MARK: - Rendering concave hulls of clusters.
3
+ */
4
+ function setupGraphClustersById() {
5
+ clusterMap = {}
6
+ currentGraph.nodes.forEach(function(node, i) {
7
+ let nodeClusterId = 0
8
+
9
+ if (node.hasOwnProperty('metric_file_result_dependency_graph_louvain_modularity_in_file')) {
10
+ nodeClusterId = node.metric_file_result_dependency_graph_louvain_modularity_in_file
11
+ } else if (node.hasOwnProperty('metric_entity_result_dependency_graph_louvain_modularity_in_entity')) {
12
+ nodeClusterId = node.metric_entity_result_dependency_graph_louvain_modularity_in_entity
13
+ } else if (node.hasOwnProperty('metric_entity_result_inheritance_graph_louvain_modularity_in_entity')) {
14
+ nodeClusterId = node.metric_entity_result_inheritance_graph_louvain_modularity_in_entity
15
+ } else if (node.hasOwnProperty('metric_entity_result_complete_graph_louvain_modularity_in_entity')) {
16
+ nodeClusterId = node.metric_entity_result_complete_graph_louvain_modularity_in_entity
17
+ }
18
+
19
+ nodeClusterId = nodeClusterId.toString()
20
+
21
+ if (nodeClusterId in clusterMap) {
22
+ clusterMap[nodeClusterId].push(node)
23
+ } else {
24
+ clusterMap[nodeClusterId] = []
25
+ clusterMap[nodeClusterId].push(node)
26
+ }
27
+ })
28
+
29
+ // console.log(clusterMap)
30
+ }
31
+
32
+ function onMouseOverHullMenuNode(clusterId) {
33
+ if (!selectedClusterHullIds.includes(clusterId)) {
34
+ addHighlightToSVGCircle(clusterId)
35
+ }
36
+ hoveredClusterHullId = clusterId
37
+ simulationUpdate()
38
+ }
39
+
40
+ function onMouseOutHullMenuNode(clusterId) {
41
+ if (!selectedClusterHullIds.includes(clusterId)) {
42
+ removeHighlightFromSVGCircle(clusterId)
43
+ }
44
+ hoveredClusterHullId = undefined
45
+ simulationUpdate()
46
+ }
47
+
48
+ function addHighlightToSVGCircle(clusterId) {
49
+ let hullNodeSVG = $("#clusterHullNodeSVGCircle-" + clusterId)
50
+ hullNodeSVG.attr('stroke', 'yellow')
51
+ hullNodeSVG.attr('stroke-width', '2')
52
+ }
53
+
54
+ function removeHighlightFromSVGCircle(clusterId) {
55
+ let hullNodeSVG = $("#clusterHullNodeSVGCircle-" + clusterId)
56
+ hullNodeSVG.attr('stroke', 'black')
57
+ hullNodeSVG.attr('stroke-width', '1')
58
+ }
59
+
60
+ function onClickHullMenuNode(clusterId) {
61
+ if (selectedClusterHullIds.includes(clusterId)) {
62
+ removeItemAll(selectedClusterHullIds, clusterId)
63
+ removeHighlightFromSVGCircle(clusterId)
64
+ } else {
65
+ selectedClusterHullIds.push(clusterId)
66
+ addHighlightToSVGCircle(clusterId)
67
+ }
68
+ simulationUpdate()
69
+ }
70
+
71
+ function createClusterHullMenu() {
72
+ // check if there is at least one cluster
73
+ if ("0" in clusterMap) {
74
+ // build menu
75
+ let clusterMenuHtml = "<div id=\"clusterHullContainer\"> \
76
+ <div class=\"row\">"
77
+
78
+ clusterMenuHtml += "<div class=\"svg-column\">"
79
+
80
+ // insert SVG circles
81
+ let iteration = 0
82
+ Object.keys(clusterMap).forEach(function(key) {
83
+
84
+ if (iteration < maxClusterHulls) {
85
+ let firstNode = clusterMap[key][0]
86
+ let color = nodeColorByModularity(firstNode)
87
+
88
+ let svgElement = "<svg onmouseover=\"onMouseOverHullMenuNode(" + key + ")\" onmouseout=\"onMouseOutHullMenuNode(" + key + ")\" onclick=\"onClickHullMenuNode(" + key + ")\" height=\"16px\" width=\"16px\" viewBox=\"0 0 18 18\"><circle id=\"clusterHullNodeSVGCircle-" + key + "\" cx=\"5\" cy=\"10\" r=\"4\" \
89
+ stroke=\"black\" stroke-width=\"1\" fill=\""
90
+ svgElement += color
91
+ svgElement += "\" /></svg>"
92
+ clusterMenuHtml += svgElement
93
+ }
94
+ iteration += 1
95
+ });
96
+
97
+ // finish menu and append to div
98
+ clusterMenuHtml += "</div></div></div>"
99
+ d3.select("#clusterHullMenu").html(clusterMenuHtml)
100
+
101
+ // add tooltips to cluster hull nodes
102
+ iteration = 0
103
+ Object.keys(clusterMap).forEach(function(clusterId) {
104
+ if (iteration < maxClusterHulls) {
105
+ let clusterToolTipDescription = "<u>Cluster metrics</u><br>"
106
+ let clusterMetrics = clusterMetricsMap[clusterId]
107
+
108
+ // TODO: check why clusterMetrics can be undefined
109
+ if (clusterMetrics !== undefined) {
110
+ // add all cluster metrics that we can find
111
+ Object.keys(clusterMetrics).forEach(function(clusterMetric) {
112
+ let metricPrettyName = clusterMetric.replace(/_/gi, " ").replace(/metric/gi, "")
113
+ clusterToolTipDescription += metricPrettyName + ": " + "<b>" + clusterMetrics[clusterMetric] + "</b>" + "<br>"
114
+ })
115
+
116
+ $('#' + 'clusterHullNodeSVGCircle-' + clusterId).attr('data-bs-toggle', 'tooltip')
117
+ $('#' + 'clusterHullNodeSVGCircle-' + clusterId).attr('data-bs-title', clusterToolTipDescription)
118
+ $('#' + 'clusterHullNodeSVGCircle-' + clusterId).attr('data-bs-html', 'true')
119
+ $('#' + 'clusterHullNodeSVGCircle-' + clusterId).attr('data-bs-placement', 'bottom')
120
+
121
+ }
122
+ }
123
+ })
124
+ }
125
+
126
+ const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
127
+ const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
128
+ }
129
+
130
+ function getPointArrayForClusterId(id) {
131
+ let pointArray = []
132
+ let clusterId = id.toString()
133
+
134
+ if (clusterId in clusterMap) {
135
+ clusterMap[clusterId].forEach(function(node, i) {
136
+ pointArray.push([node.x, node.y])
137
+ })
138
+ }
139
+ return pointArray
140
+ }
141
+
142
+ // calculate hull
143
+ function getHullFromPointArray(pointArray) {
144
+ let hullArray = hull(pointArray, 60)
145
+ return hullArray
146
+ }
147
+
148
+ // draw a single cluster hull as a polygon
149
+ function drawHull(context, clusterId) {
150
+ let pointArray = getPointArrayForClusterId(clusterId)
151
+ let hullArray = getHullFromPointArray(pointArray)
152
+ let firstNodeInCluster = clusterMap[clusterId][0]
153
+ context.fillStyle = nodeColorByModularity(firstNodeInCluster, 0.2)
154
+
155
+ context.beginPath();
156
+
157
+ let firstPoint = hullArray[0]
158
+ context.moveTo(firstPoint[0], firstPoint[1]);
159
+
160
+ hullArray.forEach(function(arrayElement, i) {
161
+ context.lineTo(arrayElement[0], arrayElement[1]);
162
+ })
163
+
164
+ context.closePath();
165
+ context.fill();
166
+ }
167
+
168
+ // draw all required cluster hulls (selected/hovered nodes from the menu)
169
+ function drawHulls(context) {
170
+
171
+ // draw a hull if someone hovers ofer a hull menu cluster node
172
+ if (hoveredClusterHullId !== undefined) {
173
+ drawHull(context, hoveredClusterHullId)
174
+ }
175
+
176
+ // draw every hull was was selected by a mouse click before
177
+ selectedClusterHullIds.forEach(clusterId => {
178
+ drawHull(context, clusterId)
179
+ })
180
+ }