grnsight 6.0.7 → 7.2.0
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.
- package/.eslintrc.yml +4 -4
- package/.github/workflows/node.js.yml +35 -0
- package/README.md +1 -1
- package/database/README.md +218 -97
- package/database/constants.py +42 -0
- package/database/filter_update.py +168 -0
- package/database/grnsettings-database/README.md +52 -0
- package/database/grnsettings-database/schema.sql +4 -0
- package/database/loader.py +30 -0
- package/database/loader_update.py +36 -0
- package/database/network-database/scripts/generate_network.py +15 -23
- package/database/network-database/scripts/generate_new_network_version.py +17 -24
- package/database/protein-protein-database/README.md +71 -0
- package/database/protein-protein-database/schema.sql +37 -0
- package/database/protein-protein-database/scripts/generate_protein_network.py +227 -0
- package/database/protein-protein-database/scripts/remove_duplicates.sh +4 -0
- package/database/utils.py +418 -0
- package/package.json +3 -2
- package/server/app.js +2 -0
- package/server/config/config.js +4 -4
- package/server/controllers/additional-sheet-parser.js +2 -1
- package/server/controllers/constants.js +5 -0
- package/server/controllers/custom-workbook-controller.js +4 -3
- package/server/controllers/demo-workbooks.js +1462 -6
- package/server/controllers/export-constants.js +3 -2
- package/server/controllers/exporters/sif.js +6 -1
- package/server/controllers/exporters/xlsx.js +8 -3
- package/server/controllers/expression-sheet-parser.js +0 -6
- package/server/controllers/grnsettings-database-controller.js +17 -0
- package/server/controllers/importers/sif.js +30 -11
- package/server/controllers/network-database-controller.js +2 -2
- package/server/controllers/network-sheet-parser.js +54 -12
- package/server/controllers/protein-database-controller.js +18 -0
- package/server/controllers/sif-constants.js +11 -4
- package/server/controllers/spreadsheet-controller.js +44 -1
- package/server/controllers/workbook-constants.js +21 -4
- package/server/dals/expression-dal.js +4 -4
- package/server/dals/grnsetting-dal.js +49 -0
- package/server/dals/network-dal.js +14 -15
- package/server/dals/protein-dal.js +106 -0
- package/test/additional-sheet-parser-tests.js +1 -1
- package/test/export-tests.js +136 -9
- package/test/import-sif-tests.js +67 -13
- package/test/test.js +1 -1
- package/test-files/additional-sheet-test-files/optimization-parameters-default.xlsx +0 -0
- package/test-files/demo-files/18_proteins_81_edges_PPI.xlsx +0 -0
- package/test-files/expression-data-test-sheets/expression_sheet_missing_data_ok_export_exact.xlsx +0 -0
- package/web-client/config/config.js +4 -4
- package/web-client/public/js/api/grnsight-api.js +18 -3
- package/web-client/public/js/constants.js +27 -12
- package/web-client/public/js/generateNetwork.js +170 -72
- package/web-client/public/js/graph.js +424 -161
- package/web-client/public/js/grnsight.js +25 -4
- package/web-client/public/js/grnstate.js +4 -1
- package/web-client/public/js/iframe-coordination.js +3 -3
- package/web-client/public/js/setup-handlers.js +76 -61
- package/web-client/public/js/setup-load-and-import-handlers.js +32 -7
- package/web-client/public/js/update-app.js +119 -28
- package/web-client/public/js/upload.js +142 -85
- package/web-client/public/js/warnings.js +25 -0
- package/web-client/public/lib/bootstrap.file-input/bootstrap.file-input.js +0 -1
- package/web-client/public/stylesheets/grnsight.styl +40 -16
- package/web-client/views/components/demo.pug +7 -5
- package/web-client/views/upload.pug +64 -50
- package/database/network-database/scripts/filter_genes.py +0 -76
- package/database/network-database/scripts/loader.py +0 -79
- package/database/network-database/scripts/loader_updates.py +0 -99
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import Grid from "d3-v4-grid";
|
|
2
2
|
import { grnState } from "./grnstate";
|
|
3
|
-
import { modifyChargeParameter, modifyLinkDistanceParameter, valueValidator } from "./update-app";
|
|
4
3
|
import {
|
|
5
|
-
|
|
4
|
+
modifyChargeParameter,
|
|
5
|
+
modifyLinkDistanceParameter,
|
|
6
|
+
valueValidator,
|
|
7
|
+
adjustGeneNameForExpression,
|
|
8
|
+
hasExpressionData,
|
|
9
|
+
} from "./update-app";
|
|
10
|
+
import {
|
|
6
11
|
VIEWPORT_FIT,
|
|
7
12
|
ZOOM_INPUT,
|
|
8
13
|
ZOOM_PERCENT,
|
|
@@ -11,6 +16,8 @@ import {
|
|
|
11
16
|
ZOOM_DISPLAY_MAXIMUM_VALUE,
|
|
12
17
|
ZOOM_DISPLAY_MIDDLE,
|
|
13
18
|
ZOOM_ADAPTIVE_MAX_SCALE,
|
|
19
|
+
NETWORK_GRN_MODE,
|
|
20
|
+
BOUNDARY_MARGIN
|
|
14
21
|
} from "./constants";
|
|
15
22
|
|
|
16
23
|
/* globals d3 */
|
|
@@ -33,6 +40,7 @@ import {
|
|
|
33
40
|
* Resize detection logic: to avoid "listener leaks," this is set up a single time here, with an assignable
|
|
34
41
|
* updateFunction being set when needed.
|
|
35
42
|
*/
|
|
43
|
+
|
|
36
44
|
let mutationCallback = null;
|
|
37
45
|
const resizeObserver = new MutationObserver((mutationsList, observer) => {
|
|
38
46
|
if (typeof(mutationCallback) === "function") {
|
|
@@ -50,6 +58,7 @@ export var updaters = {
|
|
|
50
58
|
export var drawGraph = function (workbook) {
|
|
51
59
|
/* eslint-enable no-unused-vars */
|
|
52
60
|
var $container = $(".grnsight-container");
|
|
61
|
+
var CURSOR_CLASSES = "cursorGrab cursorGrabbing";
|
|
53
62
|
d3.selectAll("svg").remove();
|
|
54
63
|
|
|
55
64
|
$container.removeClass(CURSOR_CLASSES).addClass("cursorGrab"); // allow graph dragging right away
|
|
@@ -61,8 +70,6 @@ export var drawGraph = function (workbook) {
|
|
|
61
70
|
|
|
62
71
|
var dashedLine = $("#dashedGrayLineButton").prop("checked");
|
|
63
72
|
|
|
64
|
-
var CURSOR_CLASSES = "cursorGrab cursorGrabbing";
|
|
65
|
-
|
|
66
73
|
$("#warningMessage").html(workbook.warnings.length !== 0 ? "Click here in order to view warnings." : "");
|
|
67
74
|
|
|
68
75
|
var getNodeWidth = function (node) {
|
|
@@ -159,35 +166,40 @@ export var drawGraph = function (workbook) {
|
|
|
159
166
|
|
|
160
167
|
var zoomDragPrevX = 0;
|
|
161
168
|
var zoomDragPrevY = 0;
|
|
169
|
+
let graphZoom = 0;
|
|
170
|
+
|
|
162
171
|
var zoomDragStarted = function () {
|
|
163
172
|
zoomDragPrevX = d3.event.x;
|
|
164
173
|
zoomDragPrevY = d3.event.y;
|
|
165
174
|
$container.removeClass(CURSOR_CLASSES).addClass("cursorGrabbing");
|
|
166
|
-
if (!adaptive) {
|
|
167
|
-
$container.removeClass(CURSOR_CLASSES);
|
|
168
|
-
}
|
|
169
175
|
};
|
|
170
176
|
|
|
171
177
|
var zoomDragged = function () {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
var scale = 1;
|
|
179
|
+
if (zoomContainer.attr("transform")) {
|
|
180
|
+
var string = zoomContainer.attr("transform");
|
|
181
|
+
scale = 1 / +(string.match(/scale\(([^\)]+)\)/)[1]);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (adaptive || (!adaptive &&
|
|
185
|
+
flexZoomInBounds(graphZoom) &&
|
|
186
|
+
viewportBoundsMoveDrag(graphZoom, d3.event.dx, d3.event.dy))
|
|
187
|
+
) {
|
|
188
|
+
zoom.translateBy(
|
|
189
|
+
zoomContainer,
|
|
190
|
+
scale * (d3.event.x - zoomDragPrevX),
|
|
191
|
+
scale * (d3.event.y - zoomDragPrevY)
|
|
192
|
+
);
|
|
181
193
|
}
|
|
194
|
+
zoomDragPrevX = d3.event.x;
|
|
195
|
+
zoomDragPrevY = d3.event.y;
|
|
182
196
|
};
|
|
183
197
|
|
|
184
198
|
var zoomDragEnded = function () {
|
|
185
199
|
$container.removeClass(CURSOR_CLASSES).addClass("cursorGrab");
|
|
186
|
-
if (!adaptive) {
|
|
187
|
-
$container.removeClass(CURSOR_CLASSES);
|
|
188
|
-
}
|
|
189
200
|
};
|
|
190
201
|
|
|
202
|
+
// zoomDrag and all functions that it calls handles cursor dragging
|
|
191
203
|
var zoomDrag = d3.drag()
|
|
192
204
|
.on("start", zoomDragStarted)
|
|
193
205
|
.on("drag", zoomDragged)
|
|
@@ -207,6 +219,21 @@ export var drawGraph = function (workbook) {
|
|
|
207
219
|
|
|
208
220
|
var boundingBoxContainer = zoomContainer.append("g"); // appended another g here...
|
|
209
221
|
|
|
222
|
+
// This rectangle catches all of the mousewheel and pan events, without letting
|
|
223
|
+
// them bubble up to the body.
|
|
224
|
+
var boundingBoxRect = boundingBoxContainer.append("rect")
|
|
225
|
+
.attr("width", width)
|
|
226
|
+
.attr("height", height)
|
|
227
|
+
.style("fill", "none")
|
|
228
|
+
.style("pointer-events", "all")
|
|
229
|
+
.attr("stroke", "none" )
|
|
230
|
+
.attr("id", "boundingBoxRect");
|
|
231
|
+
|
|
232
|
+
var flexibleContainerRect = boundingBoxContainer.append("rect")
|
|
233
|
+
.attr("class", "boundingBox")
|
|
234
|
+
.attr("fill", "none")
|
|
235
|
+
.attr("id", "flexibleContainerRect");
|
|
236
|
+
|
|
210
237
|
var zoom = d3.zoom()
|
|
211
238
|
.scaleExtent([MIN_SCALE, ZOOM_ADAPTIVE_MAX_SCALE])
|
|
212
239
|
.on("zoom", zoomed);
|
|
@@ -214,23 +241,14 @@ export var drawGraph = function (workbook) {
|
|
|
214
241
|
svg.style("pointer-events", "all").call(zoomDrag)
|
|
215
242
|
.style("font-family", "sans-serif");
|
|
216
243
|
|
|
217
|
-
|
|
244
|
+
// this allows zoomContainer to be zoomed, dragged
|
|
218
245
|
function zoomed () {
|
|
219
246
|
zoomContainer.attr("transform", d3.event.transform);
|
|
220
247
|
}
|
|
221
248
|
|
|
222
249
|
d3.select("svg").on("dblclick.zoom", null); // disables double click zooming
|
|
223
250
|
|
|
224
|
-
//
|
|
225
|
-
// them bubble up to the body.
|
|
226
|
-
boundingBoxContainer.append("rect")
|
|
227
|
-
.attr("width", width)
|
|
228
|
-
.attr("height", height)
|
|
229
|
-
.style("fill", "none")
|
|
230
|
-
.style("pointer-events", "all")
|
|
231
|
-
.attr("stroke", "none" )
|
|
232
|
-
.append("g");
|
|
233
|
-
|
|
251
|
+
// this controls the D-pad
|
|
234
252
|
d3.selectAll(".scrollBtn").on("click", null); // Remove event handlers, if there were any.
|
|
235
253
|
var arrowMovement = [ "Up", "Left", "Right", "Down" ];
|
|
236
254
|
arrowMovement.forEach(function (direction) {
|
|
@@ -240,14 +258,39 @@ export var drawGraph = function (workbook) {
|
|
|
240
258
|
});
|
|
241
259
|
d3.select(".center").on("click", center);
|
|
242
260
|
|
|
261
|
+
let xTranslation = 0;
|
|
262
|
+
let yTranslation = 0;
|
|
263
|
+
|
|
264
|
+
function updateZoomContainerInfo () {
|
|
265
|
+
// transform attribute of zoomContainer contains translation info about graph
|
|
266
|
+
if (zoomContainer.attr("transform")) {
|
|
267
|
+
xTranslation = Number(
|
|
268
|
+
zoomContainer
|
|
269
|
+
.attr("transform")
|
|
270
|
+
.split("(")[1]
|
|
271
|
+
.split(",")[0]
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
yTranslation = Number(
|
|
275
|
+
zoomContainer
|
|
276
|
+
.attr("transform")
|
|
277
|
+
.split("(")[1]
|
|
278
|
+
.split(",")[1]
|
|
279
|
+
.split(")")[0]
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// controls reading movement of zoomSlider and scaling graph to that zoomScale
|
|
243
285
|
const setGraphZoom = zoomScale => {
|
|
244
286
|
if (zoomScale < MIDDLE_SCALE) {
|
|
245
287
|
$container.removeClass(CURSOR_CLASSES).addClass("cursorGrab");
|
|
246
|
-
} else if (!adaptive && zoomScale >= MIDDLE_SCALE) {
|
|
247
|
-
$container.removeClass(CURSOR_CLASSES);
|
|
248
288
|
}
|
|
249
289
|
var container = zoomContainer;
|
|
250
|
-
|
|
290
|
+
if (adaptive || (!adaptive && flexZoomInBounds(graphZoom))) {
|
|
291
|
+
zoom.scaleTo(container, zoomScale);
|
|
292
|
+
graphZoom = zoomScale;
|
|
293
|
+
}
|
|
251
294
|
};
|
|
252
295
|
|
|
253
296
|
// See setupZoomElements below to see how these are initialized. They are declared here because
|
|
@@ -255,15 +298,34 @@ export var drawGraph = function (workbook) {
|
|
|
255
298
|
let sliderMidpoint;
|
|
256
299
|
let zoomScaleSliderLeft;
|
|
257
300
|
let zoomScaleSliderRight;
|
|
301
|
+
let prevGrnstateZoomVal;
|
|
302
|
+
let flexibleContainer = null;
|
|
258
303
|
|
|
259
304
|
const updateAppBasedOnZoomValue = () => {
|
|
305
|
+
let zoomDisplay;
|
|
260
306
|
|
|
261
|
-
|
|
262
|
-
|
|
307
|
+
// If the zoom value is out of bounds, reset it to the previous value.
|
|
308
|
+
if (adaptive) {
|
|
309
|
+
zoomDisplay = grnState.zoomValue;
|
|
310
|
+
} else if (
|
|
311
|
+
!adaptive &&
|
|
312
|
+
flexZoomInBounds(
|
|
313
|
+
(grnState.zoomValue <= ZOOM_DISPLAY_MIDDLE
|
|
314
|
+
? zoomScaleLeft
|
|
315
|
+
: zoomScaleRight)(grnState.zoomValue)
|
|
316
|
+
)
|
|
317
|
+
) {
|
|
318
|
+
zoomDisplay = grnState.zoomValue;
|
|
319
|
+
} else {
|
|
320
|
+
grnState.zoomValue = prevGrnstateZoomVal;
|
|
321
|
+
zoomDisplay = grnState.zoomValue;
|
|
263
322
|
}
|
|
264
323
|
|
|
265
|
-
const
|
|
266
|
-
|
|
324
|
+
const calcGraphZoom = (zoomDisplay <= ZOOM_DISPLAY_MIDDLE
|
|
325
|
+
? zoomScaleLeft
|
|
326
|
+
: zoomScaleRight)(zoomDisplay);
|
|
327
|
+
|
|
328
|
+
setGraphZoom(calcGraphZoom);
|
|
267
329
|
|
|
268
330
|
const finalDisplay = grnState.zoomValue;
|
|
269
331
|
$(ZOOM_PERCENT).text(`${finalDisplay}%`);
|
|
@@ -275,8 +337,21 @@ export var drawGraph = function (workbook) {
|
|
|
275
337
|
$(ZOOM_INPUT).val(finalDisplay);
|
|
276
338
|
}
|
|
277
339
|
|
|
278
|
-
|
|
279
|
-
|
|
340
|
+
// This controls movement of slider and is where the zoomSlider can be restricted
|
|
341
|
+
if (adaptive || (!adaptive && flexZoomInBounds(calcGraphZoom))) {
|
|
342
|
+
if (!adaptive) {
|
|
343
|
+
// Recenter graph when zooming to ensure that graph stays in viewport
|
|
344
|
+
center();
|
|
345
|
+
updateZoomContainerInfo();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
$(ZOOM_SLIDER).val(
|
|
349
|
+
(finalDisplay <= ZOOM_DISPLAY_MIDDLE
|
|
350
|
+
? zoomScaleSliderLeft
|
|
351
|
+
: zoomScaleSliderRight
|
|
352
|
+
).invert(finalDisplay)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
280
355
|
};
|
|
281
356
|
|
|
282
357
|
/**
|
|
@@ -317,10 +392,15 @@ export var drawGraph = function (workbook) {
|
|
|
317
392
|
}).blur(() => $(ZOOM_INPUT).val(grnState.zoomValue));
|
|
318
393
|
|
|
319
394
|
d3.select(ZOOM_SLIDER).on("input", function () {
|
|
320
|
-
const sliderValue =
|
|
395
|
+
const sliderValue = $(this).val();
|
|
396
|
+
prevGrnstateZoomVal = grnState.zoomValue;
|
|
397
|
+
|
|
321
398
|
grnState.zoomValue = Math.floor(
|
|
322
|
-
(sliderValue <= sliderMidpoint
|
|
399
|
+
(sliderValue <= sliderMidpoint
|
|
400
|
+
? zoomScaleSliderLeft
|
|
401
|
+
: zoomScaleSliderRight)(sliderValue)
|
|
323
402
|
);
|
|
403
|
+
|
|
324
404
|
updateAppBasedOnZoomValue();
|
|
325
405
|
}).on("mousedown", function () {
|
|
326
406
|
manualZoom = true;
|
|
@@ -359,20 +439,13 @@ export var drawGraph = function (workbook) {
|
|
|
359
439
|
var restrictGraphToViewport = function (fixed) {
|
|
360
440
|
if (!fixed) {
|
|
361
441
|
$("#restrict-graph-to-viewport span").removeClass("glyphicon-ok");
|
|
362
|
-
$(document).ready(function () {
|
|
363
|
-
$(".scale-and-scroll").show();
|
|
364
|
-
});
|
|
365
442
|
$("input[name=viewport]").removeProp("checked");
|
|
366
|
-
$container.addClass("cursorGrabbing");
|
|
367
443
|
adaptive = true;
|
|
368
|
-
|
|
444
|
+
flexibleContainer = null;
|
|
369
445
|
center();
|
|
370
|
-
} else
|
|
446
|
+
} else {
|
|
371
447
|
$("#restrict-graph-to-viewport span").addClass("glyphicon-ok");
|
|
372
448
|
$("input[name=viewport]").prop("checked", "checked");
|
|
373
|
-
$(document).ready(function () {
|
|
374
|
-
$(".scale-and-scroll").hide();
|
|
375
|
-
});
|
|
376
449
|
adaptive = false;
|
|
377
450
|
$container.removeClass(CURSOR_CLASSES);
|
|
378
451
|
if (grnState.zoomValue > ZOOM_DISPLAY_MIDDLE) {
|
|
@@ -382,9 +455,9 @@ export var drawGraph = function (workbook) {
|
|
|
382
455
|
}
|
|
383
456
|
width = $container.width();
|
|
384
457
|
height = $container.height();
|
|
385
|
-
d3.select("rect")
|
|
386
|
-
|
|
387
|
-
|
|
458
|
+
d3.select("rect")
|
|
459
|
+
.attr("width", width)
|
|
460
|
+
.attr("height", height);
|
|
388
461
|
$(".boundingBox").attr("width", width).attr("height", height);
|
|
389
462
|
center();
|
|
390
463
|
}
|
|
@@ -407,10 +480,17 @@ export var drawGraph = function (workbook) {
|
|
|
407
480
|
zoom.translateTo(zoomContainer, viewportWidth / 2, viewportHeight / 2);
|
|
408
481
|
}
|
|
409
482
|
|
|
483
|
+
// move: Moves graph with D-pad
|
|
410
484
|
function move (direction) {
|
|
411
|
-
var
|
|
412
|
-
var
|
|
413
|
-
|
|
485
|
+
var moveWidth = direction === "left" ? -50 : direction === "right" ? 50 : 0;
|
|
486
|
+
var moveHeight = direction === "up" ? -50 : direction === "down" ? 50 : 0;
|
|
487
|
+
if (adaptive) {
|
|
488
|
+
zoom.translateBy(zoomContainer, moveWidth, moveHeight);
|
|
489
|
+
} else if (!adaptive) {
|
|
490
|
+
if (viewportBoundsMoveDrag(graphZoom, moveWidth, moveHeight)) {
|
|
491
|
+
zoom.translateBy(zoomContainer, moveWidth, moveHeight);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
414
494
|
}
|
|
415
495
|
|
|
416
496
|
var defs = boundingBoxContainer.append("defs");
|
|
@@ -595,70 +675,72 @@ export var drawGraph = function (workbook) {
|
|
|
595
675
|
});
|
|
596
676
|
} else {
|
|
597
677
|
// Arrowheads
|
|
598
|
-
if (
|
|
599
|
-
d.strokeWidth
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
.
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
2: 2, 3: 10.5, 4: 11, 5: 9, 6: 9, 7: 10,
|
|
610
|
-
8: 9.8, 9: 9.1, 10: 10, 11: 9.5, 12: 9, 13: 8.3,
|
|
611
|
-
14: 8.3
|
|
612
|
-
} : {
|
|
613
|
-
2: 11.75, 3: 11, 4: 9.75, 5: 9.25, 6: 8.5, 7: 10,
|
|
614
|
-
8: 9.75, 9: 9.5, 10: 9, 11: 9.5, 12: 9.5, 13: 9.25,
|
|
615
|
-
14: 9
|
|
616
|
-
}
|
|
617
|
-
)[d.strokeWidth];
|
|
618
|
-
})
|
|
619
|
-
.attr("refY", function () {
|
|
620
|
-
return ((x1 === x2 && y1 === y2) ?
|
|
621
|
-
{
|
|
622
|
-
2: 6.7, 3: 5.45, 4: 5.3, 5: 5.5, 6: 5, 7: 5.4,
|
|
623
|
-
8: 5.65, 9: 6, 10: 5.7, 11: 5.5, 12: 5.9, 13: 6,
|
|
624
|
-
14: 6
|
|
625
|
-
} : {
|
|
626
|
-
2: 5, 3: 5, 4: 4.8, 5: 5, 6: 5, 7: 4.98,
|
|
627
|
-
8: 4.9, 9: 5.2, 10: 4.85, 11: 4.7, 12: 5.15,
|
|
628
|
-
13: 5, 14: 5.3
|
|
629
|
-
}
|
|
630
|
-
)[d.strokeWidth];
|
|
631
|
-
})
|
|
632
|
-
.attr("markerUnits", "userSpaceOnUse")
|
|
633
|
-
.attr("markerWidth", function () {
|
|
634
|
-
return 12 + ((d.strokeWidth < 7) ? d.strokeWidth * 2.25 : d.strokeWidth * 3);
|
|
635
|
-
})
|
|
636
|
-
.attr("markerHeight", function () {
|
|
637
|
-
return 5 + ((d.strokeWidth < 7) ? d.strokeWidth * 2.25 : d.strokeWidth * 3);
|
|
638
|
-
})
|
|
639
|
-
.attr("orient", function () {
|
|
640
|
-
return (x1 === x2 && y1 === y2) ?
|
|
678
|
+
if (grnState.mode === NETWORK_GRN_MODE) {
|
|
679
|
+
if (d.strokeWidth === 2) {
|
|
680
|
+
d.strokeWidth = 4;
|
|
681
|
+
}
|
|
682
|
+
defs.append("marker")
|
|
683
|
+
.attr("id", "arrowhead" + selfRef + "_StrokeWidth" + d.strokeWidth + minimum)
|
|
684
|
+
.attr("viewBox", "0 0 15 15")
|
|
685
|
+
.attr("preserveAspectRatio", "xMinYMin meet")
|
|
686
|
+
.attr("refX", function () {
|
|
687
|
+
// Individual offsets for each possible stroke width
|
|
688
|
+
return ((x1 === x2 && y1 === y2) ?
|
|
641
689
|
{
|
|
642
|
-
2:
|
|
643
|
-
8:
|
|
644
|
-
14:
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
690
|
+
2: 2, 3: 10.5, 4: 11, 5: 9, 6: 9, 7: 10,
|
|
691
|
+
8: 9.8, 9: 9.1, 10: 10, 11: 9.5, 12: 9, 13: 8.3,
|
|
692
|
+
14: 8.3
|
|
693
|
+
} : {
|
|
694
|
+
2: 11.75, 3: 11, 4: 9.75, 5: 9.25, 6: 8.5, 7: 10,
|
|
695
|
+
8: 9.75, 9: 9.5, 10: 9, 11: 9.5, 12: 9.5, 13: 9.25,
|
|
696
|
+
14: 9
|
|
697
|
+
}
|
|
698
|
+
)[d.strokeWidth];
|
|
699
|
+
})
|
|
700
|
+
.attr("refY", function () {
|
|
701
|
+
return ((x1 === x2 && y1 === y2) ?
|
|
702
|
+
{
|
|
703
|
+
2: 6.7, 3: 5.45, 4: 5.3, 5: 5.5, 6: 5, 7: 5.4,
|
|
704
|
+
8: 5.65, 9: 6, 10: 5.7, 11: 5.5, 12: 5.9, 13: 6,
|
|
705
|
+
14: 6
|
|
706
|
+
} : {
|
|
707
|
+
2: 5, 3: 5, 4: 4.8, 5: 5, 6: 5, 7: 4.98,
|
|
708
|
+
8: 4.9, 9: 5.2, 10: 4.85, 11: 4.7, 12: 5.15,
|
|
709
|
+
13: 5, 14: 5.3
|
|
710
|
+
}
|
|
711
|
+
)[d.strokeWidth];
|
|
712
|
+
})
|
|
713
|
+
.attr("markerUnits", "userSpaceOnUse")
|
|
714
|
+
.attr("markerWidth", function () {
|
|
715
|
+
return 12 + ((d.strokeWidth < 7) ? d.strokeWidth * 2.25 : d.strokeWidth * 3);
|
|
716
|
+
})
|
|
717
|
+
.attr("markerHeight", function () {
|
|
718
|
+
return 5 + ((d.strokeWidth < 7) ? d.strokeWidth * 2.25 : d.strokeWidth * 3);
|
|
719
|
+
})
|
|
720
|
+
.attr("orient", function () {
|
|
721
|
+
return (x1 === x2 && y1 === y2) ?
|
|
722
|
+
{
|
|
723
|
+
2: 270, 3: 270, 4: 268, 5: 264, 6: 268, 7: 252,
|
|
724
|
+
8: 248, 9: 243, 10: 240, 11: 240, 12: 235, 13: 233,
|
|
725
|
+
14: 232
|
|
726
|
+
}[d.strokeWidth] : "auto";
|
|
727
|
+
})
|
|
728
|
+
.append("path")
|
|
729
|
+
.attr("d", "M 0 0 L 14 5 L 0 10 Q 6 5 0 0")
|
|
730
|
+
.attr("style", function () {
|
|
731
|
+
if (unweighted || !grnState.colorOptimal) {
|
|
732
|
+
color = "black";
|
|
733
|
+
} else if ( normalize(d) <= grayThreshold) {
|
|
734
|
+
color = "gray";
|
|
735
|
+
} else {
|
|
736
|
+
color = d.stroke;
|
|
737
|
+
}
|
|
738
|
+
return "stroke: " + color + "; fill: " + color;
|
|
739
|
+
});
|
|
740
|
+
}
|
|
659
741
|
}
|
|
742
|
+
return "url(#" + d.type + selfRef + "_StrokeWidth" + d.strokeWidth + minimum + ")";
|
|
660
743
|
}
|
|
661
|
-
return "url(#" + d.type + selfRef + "_StrokeWidth" + d.strokeWidth + minimum + ")";
|
|
662
744
|
});
|
|
663
745
|
|
|
664
746
|
if (workbook.sheetType === "weighted") {
|
|
@@ -1008,13 +1090,21 @@ export var drawGraph = function (workbook) {
|
|
|
1008
1090
|
.append("g")
|
|
1009
1091
|
.selectAll(".coloring")
|
|
1010
1092
|
.data(function () {
|
|
1011
|
-
if (grnState.workbook.expression[dataset]
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1093
|
+
if (grnState.workbook.expression[dataset]) {
|
|
1094
|
+
const geneName = adjustGeneNameForExpression(p);
|
|
1095
|
+
if (
|
|
1096
|
+
grnState.workbook.expression[dataset].data[geneName]
|
|
1097
|
+
) {
|
|
1098
|
+
const result = getExpressionData(
|
|
1099
|
+
geneName,
|
|
1100
|
+
dataset,
|
|
1101
|
+
average
|
|
1102
|
+
);
|
|
1103
|
+
timePoints = result.timePoints;
|
|
1104
|
+
return result.data || [];
|
|
1105
|
+
}
|
|
1017
1106
|
}
|
|
1107
|
+
return [];
|
|
1018
1108
|
})
|
|
1019
1109
|
.attr("class", "coloring")
|
|
1020
1110
|
.enter().append("rect")
|
|
@@ -1032,6 +1122,9 @@ export var drawGraph = function (workbook) {
|
|
|
1032
1122
|
.attr("stroke-width", "0px")
|
|
1033
1123
|
.style("fill", function (d) {
|
|
1034
1124
|
d = d || 0; // missing values are changed to 0
|
|
1125
|
+
if (d === 0) {
|
|
1126
|
+
return "white";
|
|
1127
|
+
}
|
|
1035
1128
|
var scale = d3.scaleLinear()
|
|
1036
1129
|
.domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
|
|
1037
1130
|
.range([0, 1]);
|
|
@@ -1141,15 +1234,6 @@ export var drawGraph = function (workbook) {
|
|
|
1141
1234
|
}
|
|
1142
1235
|
};
|
|
1143
1236
|
|
|
1144
|
-
const hasExpressionData = sheets => {
|
|
1145
|
-
for (var property in sheets) {
|
|
1146
|
-
if (property.match(ENDS_IN_EXPRESSION_REGEXP)) {
|
|
1147
|
-
return true;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return false;
|
|
1151
|
-
};
|
|
1152
|
-
|
|
1153
1237
|
if (!$.isEmptyObject(workbook.expression) && hasExpressionData(workbook.expression) &&
|
|
1154
1238
|
grnState.nodeColoring.topDataset !== undefined) {
|
|
1155
1239
|
updaters.renderNodeColoring();
|
|
@@ -1297,6 +1381,120 @@ export var drawGraph = function (workbook) {
|
|
|
1297
1381
|
}
|
|
1298
1382
|
};
|
|
1299
1383
|
|
|
1384
|
+
function viewportBoundsMoveDrag (graphZoom, dx, dy) {
|
|
1385
|
+
updateZoomContainerInfo();
|
|
1386
|
+
flexibleContainer = calcFlexiBox();
|
|
1387
|
+
|
|
1388
|
+
if (
|
|
1389
|
+
flexibleContainer.x + flexibleContainer.width + dx >=
|
|
1390
|
+
-xTranslation / graphZoom +
|
|
1391
|
+
BOUNDARY_MARGIN / 2 +
|
|
1392
|
+
width / graphZoom -
|
|
1393
|
+
BOUNDARY_MARGIN
|
|
1394
|
+
) {
|
|
1395
|
+
return false;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (flexibleContainer.x + dx <= getLeftXBoundaryMargin()) {
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
if (
|
|
1403
|
+
flexibleContainer.y + flexibleContainer.height + dy >=
|
|
1404
|
+
-yTranslation / graphZoom +
|
|
1405
|
+
BOUNDARY_MARGIN / 2 +
|
|
1406
|
+
height / graphZoom -
|
|
1407
|
+
BOUNDARY_MARGIN
|
|
1408
|
+
) {
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (flexibleContainer.y + dy <= getTopYBoundaryMargin()) {
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
return true;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function calcFlexiBox () {
|
|
1420
|
+
const nodes = simulation.nodes();
|
|
1421
|
+
let nodeWidth = 0;
|
|
1422
|
+
if (nodes.length > 0) {
|
|
1423
|
+
nodeWidth = nodes[0].textWidth + 8;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
const xValuesNodes = nodes.map((node) => node.x);
|
|
1427
|
+
const yValuesNodes = nodes.map((node) => node.y);
|
|
1428
|
+
|
|
1429
|
+
let minX = Math.min(...xValuesNodes);
|
|
1430
|
+
let maxX = Math.max(...xValuesNodes) + nodeWidth;
|
|
1431
|
+
|
|
1432
|
+
let minY = Math.min(...yValuesNodes);
|
|
1433
|
+
let maxY = Math.max(...yValuesNodes) + nodeHeight;
|
|
1434
|
+
|
|
1435
|
+
// Handle left x and top y boundaries to not exceed graph BOUNDARY_MARGINs
|
|
1436
|
+
const BOUNDARY_MARGIN_X_L = getLeftXBoundaryMargin();
|
|
1437
|
+
const BOUNDARY_MARGIN_Y_T = getTopYBoundaryMargin();
|
|
1438
|
+
minX = minX < BOUNDARY_MARGIN_X_L ? BOUNDARY_MARGIN_X_L : minX;
|
|
1439
|
+
minY = minY < BOUNDARY_MARGIN_Y_T ? BOUNDARY_MARGIN_Y_T : minY;
|
|
1440
|
+
|
|
1441
|
+
maxX =
|
|
1442
|
+
maxX > -xTranslation / graphZoom + BOUNDARY_MARGIN / 2 + width / graphZoom - BOUNDARY_MARGIN
|
|
1443
|
+
? -xTranslation / graphZoom + BOUNDARY_MARGIN / 2 + width / graphZoom - BOUNDARY_MARGIN
|
|
1444
|
+
: maxX;
|
|
1445
|
+
|
|
1446
|
+
maxY =
|
|
1447
|
+
maxY > -yTranslation / graphZoom + BOUNDARY_MARGIN / 2 + height / graphZoom - BOUNDARY_MARGIN
|
|
1448
|
+
? -yTranslation / graphZoom + BOUNDARY_MARGIN / 2 + height / graphZoom - BOUNDARY_MARGIN
|
|
1449
|
+
: maxY;
|
|
1450
|
+
|
|
1451
|
+
let flexiBoxWidth = maxX - minX;
|
|
1452
|
+
if (maxX < 0 && minX < 0) {
|
|
1453
|
+
flexiBoxWidth = Math.abs(maxX) - Math.abs(minX);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
let flexiBoxHeight = maxY - minY;
|
|
1457
|
+
if (maxY < 0 && minY < 0) {
|
|
1458
|
+
flexiBoxHeight = Math.abs(maxY) - Math.abs(minY);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
boundingBoxRect
|
|
1462
|
+
.attr("x", -xTranslation / graphZoom + BOUNDARY_MARGIN / 2)
|
|
1463
|
+
.attr("width", width / graphZoom - BOUNDARY_MARGIN)
|
|
1464
|
+
.attr("y", -yTranslation / graphZoom + BOUNDARY_MARGIN / 2)
|
|
1465
|
+
.attr("height", height / graphZoom - BOUNDARY_MARGIN);
|
|
1466
|
+
|
|
1467
|
+
flexibleContainerRect
|
|
1468
|
+
.attr("x", minX)
|
|
1469
|
+
.attr("y", minY)
|
|
1470
|
+
.attr("width", flexiBoxWidth)
|
|
1471
|
+
.attr("height", flexiBoxHeight);
|
|
1472
|
+
return {x: minX, y: minY, maxX: maxX, maxY: maxY, width: flexiBoxWidth, height: flexiBoxHeight};
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// Checks if zoomValue is in bounds when zoom in and out
|
|
1476
|
+
function flexZoomInBounds (zoomValue) {
|
|
1477
|
+
if (flexibleContainer) {
|
|
1478
|
+
updateZoomContainerInfo();
|
|
1479
|
+
flexibleContainer = calcFlexiBox();
|
|
1480
|
+
if (flexibleContainer.width * zoomValue > width ) {
|
|
1481
|
+
return false;
|
|
1482
|
+
} else if (flexibleContainer.height * zoomValue > height) {
|
|
1483
|
+
return false;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
return true;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// Only calculate Left and Top boundary margins because calculate rightboundary and bottomboundary in tick
|
|
1490
|
+
function getLeftXBoundaryMargin () {
|
|
1491
|
+
return !adaptive && flexibleContainer ? -xTranslation / graphZoom + BOUNDARY_MARGIN / 2 : BOUNDARY_MARGIN;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function getTopYBoundaryMargin () {
|
|
1495
|
+
return !adaptive && flexibleContainer ? -yTranslation / graphZoom + BOUNDARY_MARGIN / 2 : BOUNDARY_MARGIN;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1300
1498
|
// Tick only runs while the graph physics are still running.
|
|
1301
1499
|
// (I.e. when the graph is completely relaxed, tick stops running.)
|
|
1302
1500
|
function tick () {
|
|
@@ -1310,38 +1508,53 @@ export var drawGraph = function (workbook) {
|
|
|
1310
1508
|
var getSelfReferringRadius = function (edge) {
|
|
1311
1509
|
return edge ? 17 + (getEdgeThickness(edge) / 2) : 0;
|
|
1312
1510
|
};
|
|
1313
|
-
|
|
1511
|
+
|
|
1314
1512
|
var SELF_REFERRING_Y_OFFSET = 6;
|
|
1315
1513
|
var MAX_WIDTH = 5000;
|
|
1316
1514
|
var MAX_HEIGHT = 5000;
|
|
1317
1515
|
var OFFSET_VALUE = 5;
|
|
1318
1516
|
|
|
1517
|
+
if (!adaptive) {
|
|
1518
|
+
flexibleContainer = calcFlexiBox();
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// this controls movement and position of nodes, clamps the nodes to boundary
|
|
1319
1522
|
try {
|
|
1320
1523
|
node.attr("x", function (d) {
|
|
1321
1524
|
var selfReferringEdge = getSelfReferringEdge(d);
|
|
1322
|
-
|
|
1323
1525
|
var selfReferringEdgeWidth = (selfReferringEdge ? getSelfReferringRadius(selfReferringEdge) +
|
|
1324
1526
|
selfReferringEdge.strokeWidth + 2 : 0);
|
|
1325
1527
|
var rightBoundary = width - (d.textWidth + OFFSET_VALUE) - BOUNDARY_MARGIN - selfReferringEdgeWidth;
|
|
1326
|
-
|
|
1528
|
+
if (!adaptive) {
|
|
1529
|
+
rightBoundary =
|
|
1530
|
+
-xTranslation / graphZoom +
|
|
1531
|
+
BOUNDARY_MARGIN / 2 +
|
|
1532
|
+
width / graphZoom -
|
|
1533
|
+
BOUNDARY_MARGIN -
|
|
1534
|
+
(d.textWidth + OFFSET_VALUE) -
|
|
1535
|
+
selfReferringEdgeWidth;
|
|
1536
|
+
}
|
|
1537
|
+
// currentXPos bounds the graph when toggle to !adaptive and moves each of the nodes to be in bounds
|
|
1538
|
+
var currentXPos = Math.max(getLeftXBoundaryMargin(), Math.min(rightBoundary, d.x));
|
|
1327
1539
|
if (adaptive && width < MAX_WIDTH &&
|
|
1328
|
-
|
|
1540
|
+
(currentXPos === getLeftXBoundaryMargin() ||
|
|
1541
|
+
currentXPos === rightBoundary)
|
|
1542
|
+
) {
|
|
1329
1543
|
if (!d3.select(this).classed("fixed")) {
|
|
1330
1544
|
width += OFFSET_VALUE;
|
|
1331
1545
|
boundingBoxContainer.attr("width", width);
|
|
1332
1546
|
|
|
1333
1547
|
link
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1548
|
+
.attr("x1", function (d) {
|
|
1549
|
+
return d.source.x;
|
|
1550
|
+
})
|
|
1551
|
+
.attr("x2", function (d) {
|
|
1552
|
+
return d.target.x;
|
|
1553
|
+
});
|
|
1340
1554
|
|
|
1341
|
-
node
|
|
1342
|
-
.
|
|
1343
|
-
|
|
1344
|
-
});
|
|
1555
|
+
node.attr("x", function (d) {
|
|
1556
|
+
return d.x;
|
|
1557
|
+
});
|
|
1345
1558
|
}
|
|
1346
1559
|
}
|
|
1347
1560
|
return d.x = currentXPos;
|
|
@@ -1350,24 +1563,34 @@ export var drawGraph = function (workbook) {
|
|
|
1350
1563
|
var selfReferringEdgeHeight = (selfReferringEdge ? getSelfReferringRadius(selfReferringEdge) +
|
|
1351
1564
|
selfReferringEdge.strokeWidth + SELF_REFERRING_Y_OFFSET + 0.5 : 0);
|
|
1352
1565
|
var bottomBoundary = height - nodeHeight - BOUNDARY_MARGIN - selfReferringEdgeHeight;
|
|
1353
|
-
|
|
1566
|
+
if (!adaptive) {
|
|
1567
|
+
bottomBoundary =
|
|
1568
|
+
-yTranslation / graphZoom +
|
|
1569
|
+
BOUNDARY_MARGIN / 2 +
|
|
1570
|
+
height / graphZoom -
|
|
1571
|
+
BOUNDARY_MARGIN -
|
|
1572
|
+
nodeHeight -
|
|
1573
|
+
selfReferringEdgeHeight;
|
|
1574
|
+
}
|
|
1575
|
+
// currentYPos bounds the graph when toggle to !adaptive and moves each of the nodes to be in bounds
|
|
1576
|
+
var currentYPos = Math.max(getTopYBoundaryMargin(), Math.min(bottomBoundary, d.y));
|
|
1577
|
+
|
|
1354
1578
|
if (adaptive && height < MAX_HEIGHT &&
|
|
1355
|
-
(currentYPos ===
|
|
1579
|
+
(currentYPos === getTopYBoundaryMargin() || currentYPos === bottomBoundary)) {
|
|
1356
1580
|
if (!d3.select(this).classed("fixed")) {
|
|
1357
1581
|
height += OFFSET_VALUE;
|
|
1358
1582
|
boundingBoxContainer.attr("height", height);
|
|
1359
1583
|
link
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1584
|
+
.attr("y1", function (d) {
|
|
1585
|
+
return d.source.y;
|
|
1586
|
+
})
|
|
1587
|
+
.attr("y2", function (d) {
|
|
1588
|
+
return d.target.y;
|
|
1589
|
+
});
|
|
1366
1590
|
|
|
1367
|
-
node
|
|
1368
|
-
.
|
|
1369
|
-
|
|
1370
|
-
});
|
|
1591
|
+
node.attr("y", function (d) {
|
|
1592
|
+
return d.y;
|
|
1593
|
+
});
|
|
1371
1594
|
}
|
|
1372
1595
|
}
|
|
1373
1596
|
return d.y = currentYPos;
|
|
@@ -1403,7 +1626,6 @@ export var drawGraph = function (workbook) {
|
|
|
1403
1626
|
var ADDITIONAL_SHIFT = 0.07;
|
|
1404
1627
|
var END_POINT_ADJUSTMENT = 1.2;
|
|
1405
1628
|
|
|
1406
|
-
|
|
1407
1629
|
// Self edge.
|
|
1408
1630
|
if (x1 === x2 && y1 === y2) {
|
|
1409
1631
|
// Move the position of the loop.
|
|
@@ -1499,8 +1721,49 @@ export var drawGraph = function (workbook) {
|
|
|
1499
1721
|
}
|
|
1500
1722
|
|
|
1501
1723
|
function dragged (d) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1724
|
+
if (!adaptive) {
|
|
1725
|
+
/* fx and fy stands for fixed x and y which is when node is fixed to a position or
|
|
1726
|
+
to prevent tick from adjusting position of node
|
|
1727
|
+
*/
|
|
1728
|
+
// prevents nodes from being dragged outside of right boundary
|
|
1729
|
+
if (
|
|
1730
|
+
d3.event.x + d.textWidth <=
|
|
1731
|
+
-xTranslation / graphZoom + BOUNDARY_MARGIN / 2 +
|
|
1732
|
+
width / graphZoom -
|
|
1733
|
+
BOUNDARY_MARGIN
|
|
1734
|
+
) {
|
|
1735
|
+
d.fx = d3.event.x;
|
|
1736
|
+
} else {
|
|
1737
|
+
d.fx =
|
|
1738
|
+
-xTranslation / graphZoom +
|
|
1739
|
+
BOUNDARY_MARGIN / 2 +
|
|
1740
|
+
width / graphZoom -
|
|
1741
|
+
BOUNDARY_MARGIN -
|
|
1742
|
+
d.textWidth;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// prevent nodes from being dragged out of bottom boundary
|
|
1746
|
+
if (
|
|
1747
|
+
d3.event.y + nodeHeight <=
|
|
1748
|
+
-yTranslation / graphZoom +
|
|
1749
|
+
BOUNDARY_MARGIN / 2 +
|
|
1750
|
+
height / graphZoom -
|
|
1751
|
+
BOUNDARY_MARGIN
|
|
1752
|
+
) {
|
|
1753
|
+
// fy stands for fixed y
|
|
1754
|
+
d.fy = d3.event.y;
|
|
1755
|
+
} else {
|
|
1756
|
+
d.fy =
|
|
1757
|
+
-yTranslation / graphZoom +
|
|
1758
|
+
BOUNDARY_MARGIN / 2 +
|
|
1759
|
+
height / graphZoom -
|
|
1760
|
+
BOUNDARY_MARGIN -
|
|
1761
|
+
nodeHeight;
|
|
1762
|
+
}
|
|
1763
|
+
} else {
|
|
1764
|
+
d.fx = d3.event.x;
|
|
1765
|
+
d.fy = d3.event.y;
|
|
1766
|
+
}
|
|
1504
1767
|
}
|
|
1505
1768
|
|
|
1506
1769
|
grnState.simulation = simulation;
|