sunrize 1.5.13 → 1.6.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.
Files changed (41) hide show
  1. package/package.json +5 -5
  2. package/src/Application/Application.js +2 -7
  3. package/src/Application/Dashboard.js +10 -10
  4. package/src/Application/DataStorage.js +177 -177
  5. package/src/Application/Document.js +72 -127
  6. package/src/Application/Interface.js +4 -0
  7. package/src/Application/Tabs.js +3 -3
  8. package/src/Controls/Dialog.js +3 -1
  9. package/src/Editors/BrowserFrame.js +171 -0
  10. package/src/Editors/OutlineEditor.js +149 -123
  11. package/src/Editors/OutlineRouteGraph.js +493 -493
  12. package/src/Editors/OutlineView.js +102 -64
  13. package/src/Editors/SceneProperties.js +137 -136
  14. package/src/Editors/ScriptEditor.js +1 -1
  15. package/src/Tools/Core/X3DNodeTool.js +66 -12
  16. package/src/Tools/Grids/AngleGridTool.js +0 -5
  17. package/src/Tools/Grids/AngleGridTool.x3d +1 -0
  18. package/src/Tools/Grids/AxonometricGrid.x3d +5 -5
  19. package/src/Tools/Grids/AxonometricGridTool.js +0 -5
  20. package/src/Tools/Grids/AxonometricGridTool.x3d +1 -0
  21. package/src/Tools/Grids/GridTool.js +0 -5
  22. package/src/Tools/Grids/GridTool.x3d +1 -0
  23. package/src/Tools/Grids/X3DGridNodeTool.js +131 -84
  24. package/src/Tools/Grouping/X3DTransformNodeTool.js +19 -21
  25. package/src/Tools/Grouping/X3DTransformNodeTool.x3d +20 -15
  26. package/src/Tools/Layering/X3DActiveLayerNodeTool.js +16 -21
  27. package/src/Tools/Lighting/DirectionalLightTool.js +32 -1
  28. package/src/Tools/Lighting/X3DLightNodeTool.x3d +4 -21
  29. package/src/Tools/Shaders/TextureShader.x3d +16 -3
  30. package/src/Tools/SnapTool/{SnapSourceTool.js → SnapSource.js} +2 -7
  31. package/src/Tools/SnapTool/SnapTarget.js +650 -0
  32. package/src/Tools/SnapTool/SnapTool.x3d +28 -27
  33. package/src/Tools/SnapTool/X3DSnapNodeTool.js +16 -15
  34. package/src/Tools/Sound/SoundTool.x3d +4 -21
  35. package/src/Tools/TextureProjection/X3DTextureProjectorNodeTool.x3d +19 -26
  36. package/src/Undo/Editor.js +185 -46
  37. package/src/assets/Info.plist +56 -56
  38. package/src/assets/themes/default-template.css +1 -0
  39. package/src/assets/themes/default.css +1 -0
  40. package/src/Editors/BrowserSize.js +0 -101
  41. package/src/Tools/SnapTool/SnapTargetTool.js +0 -20
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
 
3
3
  const
4
- $ = require ("jquery"),
4
+ $ = require ("jquery"),
5
5
  X3D = require ("../X3D"),
6
- OutlineView = require ("./OutlineView");
6
+ OutlineView = require ("./OutlineView");
7
7
 
8
8
  const
9
- routeColor = "#000000",
10
- routeSelectedColor = "rgb(255, 69, 58)";
9
+ routeColor = "#000000",
10
+ routeSelectedColor = "rgb(255, 69, 58)";
11
11
 
12
12
  module .exports = class OutlineRouteGraph extends OutlineView
13
13
  {
@@ -15,500 +15,500 @@ module .exports = class OutlineRouteGraph extends OutlineView
15
15
  {
16
16
  super (element);
17
17
 
18
- this .selectedRoutes = new Set ();
18
+ this .selectedRoutes = new Set ();
19
19
  }
20
20
 
21
- selectRoutes (type, event)
22
- {
23
- // Select routes.
21
+ selectRoutes (type, event)
22
+ {
23
+ // Select routes.
24
+
25
+ const
26
+ element = $(event .currentTarget) .closest (".field", this .sceneGraph),
27
+ field = this .getField (element);
28
+
29
+ event .preventDefault ();
30
+ event .stopImmediatePropagation ();
31
+
32
+ this .selectedRoutes .clear ();
33
+
34
+ switch (type)
35
+ {
36
+ case "input":
37
+ {
38
+ for (const route of field .getInputRoutes ())
39
+ {
40
+ this .selectedRoutes .add (route);
41
+ this .expandTo (route .getSourceNode (), true);
42
+ }
43
+
44
+ break;
45
+ }
46
+ case "output":
47
+ {
48
+ for (const route of field .getOutputRoutes ())
49
+ {
50
+ this .selectedRoutes .add (route);
51
+ this .expandTo (route .getDestinationNode (), true);
52
+ }
53
+
54
+ break;
55
+ }
56
+ }
57
+
58
+ this .requestUpdateRouteGraph ();
59
+ }
60
+
61
+ selectSingleRoute (type, event)
62
+ {
63
+ // Select single route.
64
+
65
+ const
66
+ element = $(event .currentTarget) .closest (".route", this .sceneGraph),
67
+ field = this .getField (element);
68
+
69
+ event .preventDefault ();
70
+ event .stopImmediatePropagation ();
71
+
72
+ this .selectedRoutes .clear ();
73
+
74
+ switch (type)
75
+ {
76
+ case "input":
77
+ {
78
+ const route = this .getRoute (element, field .getInputRoutes ());
79
+
80
+ this .selectedRoutes .add (route);
81
+ this .expandTo (route .getSourceNode (), true);
82
+ break;
83
+ }
84
+ case "output":
85
+ {
86
+ const route = this .getRoute (element, field .getOutputRoutes ());
87
+
88
+ this .selectedRoutes .add (route);
89
+ this .expandTo (route .getDestinationNode (), true);
90
+ break;
91
+ }
92
+ }
93
+
94
+ this .requestUpdateRouteGraph ();
95
+ }
24
96
 
25
- const
26
- element = $(event .currentTarget) .closest (".field", this .sceneGraph),
27
- field = this .getField (element);
97
+ #updateRouteGraphId = undefined;
28
98
 
29
- event .preventDefault ();
30
- event .stopImmediatePropagation ();
31
-
32
- this .selectedRoutes .clear ();
99
+ requestUpdateRouteGraph ()
100
+ {
101
+ clearTimeout (this .#updateRouteGraphId);
102
+
103
+ this .#updateRouteGraphId = setTimeout (() => this .updateRouteGraph ());
104
+ }
105
+
106
+ updateRouteGraph ()
107
+ {
108
+ const canvases = this .sceneGraph .find (".route-curves");
33
109
 
34
- switch (type)
35
- {
36
- case "input":
37
- {
38
- for (const route of field .getInputRoutes ())
39
- {
40
- this .selectedRoutes .add (route);
41
- this .expandTo (route .getSourceNode (), true);
42
- }
43
-
44
- break;
45
- }
46
- case "output":
47
- {
48
- for (const route of field .getOutputRoutes ())
49
- {
50
- this .selectedRoutes .add (route);
51
- this .expandTo (route .getDestinationNode (), true);
52
- }
110
+ // Determine visible routes, those routes have a value of 2.
53
111
 
54
- break;
55
- }
56
- }
112
+ const fields = new Map ();
57
113
 
58
- this .requestUpdateRouteGraph ();
59
- }
60
-
61
- selectSingleRoute (type, event)
62
- {
63
- // Select single route.
64
-
65
- const
66
- element = $(event .currentTarget) .closest (".route", this .sceneGraph),
67
- field = this .getField (element);
68
-
69
- event .preventDefault ();
70
- event .stopImmediatePropagation ();
71
-
72
- this .selectedRoutes .clear ();
73
-
74
- switch (type)
75
- {
76
- case "input":
77
- {
78
- const route = this .getRoute (element, field .getInputRoutes ());
79
-
80
- this .selectedRoutes .add (route);
81
- this .expandTo (route .getSourceNode (), true);
82
- break;
83
- }
84
- case "output":
85
- {
86
- const route = this .getRoute (element, field .getOutputRoutes ());
87
-
88
- this .selectedRoutes .add (route);
89
- this .expandTo (route .getDestinationNode (), true);
90
- break;
91
- }
92
- }
93
-
94
- this .requestUpdateRouteGraph ();
95
- }
96
-
97
- #updateRouteGraphId = undefined;
98
-
99
- requestUpdateRouteGraph ()
100
- {
101
- clearTimeout (this .#updateRouteGraphId);
102
-
103
- this .#updateRouteGraphId = setTimeout (() => this .updateRouteGraph ());
104
- }
105
-
106
- updateRouteGraph ()
107
- {
108
- const canvases = this .sceneGraph .find (".route-curves");
109
-
110
- // Determine visible routes, those routes have a value of 2.
111
-
112
- const fields = new Map ();
113
-
114
- for (const element of this .sceneGraph .find (".field"))
115
- {
116
- const field = this .getField ($(element));
117
-
118
- for (const route of field .getInputRoutes ())
119
- fields .set (route, (fields .get (route) ?? 0) + 1);
120
-
121
- for (const route of field .getOutputRoutes ())
122
- fields .set (route, (fields .get (route) ?? 0) + 1);
123
- }
124
-
125
- this .sceneGraph
126
- .find (".field-routes")
127
- .each ((i, canvas2d) => this .updateFieldRoutes ($(canvas2d), fields));
128
-
129
- this .sceneGraph
130
- .find (".single-route")
131
- .each ((i, canvas2d) => this .updateSingleRoute ($(canvas2d), fields));
132
-
133
- this .updateRouteCurves (canvases, fields);
134
- }
135
-
136
- updateFieldRoutes (canvas, fields)
137
- {
138
- // Draw horizontal lines.
139
-
140
- const
141
- element = canvas .closest (".field", this .sceneGraph),
142
- field = this .getField (element),
143
- parent = canvas .parent (),
144
- context = canvas .get (0) .getContext ("2d");
145
-
146
- canvas .height (Math .ceil (parent .height ()));
147
-
148
- canvas .prop ("width", canvas .width ());
149
- canvas .prop ("height", canvas .height ());
150
-
151
- context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
152
-
153
- switch (field .getAccessType ())
154
- {
155
- case X3D .X3DConstants .initializeOnly:
156
- {
157
- break;
158
- }
159
- case X3D .X3DConstants .inputOnly:
160
- {
161
- if (this .hasInputRoutes (field, fields))
162
- {
163
- context .strokeStyle = this .haveSelectedRoute (field .getInputRoutes ()) ? routeSelectedColor : routeColor;
164
-
165
- context .beginPath ();
166
- context .moveTo (26, 3.5);
167
- context .lineTo (canvas .prop ("width") + 1, 3.5);
168
- context .stroke ();
169
- }
170
-
171
- break
172
- }
173
- case X3D .X3DConstants .outputOnly:
174
- {
175
- if (this .hasOutputRoutes (field, fields))
176
- {
177
- context .strokeStyle = this .haveSelectedRoute (field .getOutputRoutes ()) ? routeSelectedColor : routeColor;
178
-
179
- context .beginPath ();
180
- context .moveTo (26, 8.5);
181
- context .lineTo (canvas .prop ("width") + 1, 8.5);
182
- context .stroke ();
183
- }
184
-
185
- break
186
- }
187
- case X3D .X3DConstants .inputOutput:
188
- {
189
- if (this .hasInputRoutes (field, fields))
190
- {
191
- context .strokeStyle = this .haveSelectedRoute (field .getInputRoutes ()) ? routeSelectedColor : routeColor;
192
-
193
- context .beginPath ();
194
- context .moveTo (40, 3.5);
195
- context .lineTo (canvas .prop ("width") + 1, 3.5);
196
- context .stroke ();
197
- }
198
-
199
- if (this .hasOutputRoutes (field, fields))
200
- {
201
- context .strokeStyle = this .haveSelectedRoute (field .getOutputRoutes ()) ? routeSelectedColor : routeColor;
202
-
203
- context .beginPath ();
204
- context .moveTo (field .getInputRoutes () .size ? 54 : 40, 8.5);
205
- context .lineTo (canvas .prop ("width") + 1, 8.5);
206
- context .stroke ();
207
- }
208
-
209
- break;
210
- }
211
- }
212
- }
213
-
214
- updateSingleRoute (canvas, fields)
215
- {
216
- // Draw horizontal line.
217
-
218
- const
219
- element = canvas .closest (".route", this .sceneGraph),
220
- type = element .attr ("route-type"),
221
- parent = canvas .parent (),
222
- context = canvas .get (0) .getContext ("2d");
223
-
224
- canvas .height (Math .ceil (parent .height ()));
225
-
226
- canvas .prop ("width", canvas .width ());
227
- canvas .prop ("height", canvas .height ());
228
-
229
- context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
230
-
231
- switch (type)
232
- {
233
- case "input":
234
- {
235
- const
236
- field = this .getField (element),
237
- route = this .getRoute (element, field .getInputRoutes ());
238
-
239
- if (!route)
240
- break;
241
-
242
- if (fields .get (route) !== 2)
243
- break;
244
-
245
- context .strokeStyle = this .selectedRoutes .has (route) ? routeSelectedColor : routeColor;
246
-
247
- context .beginPath ();
248
- context .moveTo (26, 3.5);
249
- context .lineTo (canvas .prop ("width") + 1, 3.5);
250
- context .stroke ();
251
- break;
252
- }
253
- case "output":
254
- {
255
- const
256
- field = this .getField (element),
257
- route = this .getRoute (element, field .getOutputRoutes ());
258
-
259
- if (!route)
260
- break;
261
-
262
- if (fields .get (route) !== 2)
263
- break;
264
-
265
- context .strokeStyle = this .selectedRoutes .has (route) ? routeSelectedColor : routeColor;
266
-
267
- context .beginPath ();
268
- context .moveTo (26, 8.5);
269
- context .lineTo (canvas .prop ("width") + 1, 8.5);
270
- context .stroke ();
271
- break;
272
- }
273
- }
274
- }
275
-
276
- updateRouteCurves (canvases, fields)
277
- {
278
- // Scale canvases
279
-
280
- for (let i = 0, length = canvases .length - 1; i < length; ++ i)
281
- {
282
- const
283
- canvas1 = $(canvases [i]),
284
- canvas2 = $(canvases [i + 1]),
285
- rect1 = canvas1 .get (0) .getBoundingClientRect (),
286
- rect2 = canvas2 .get (0) .getBoundingClientRect ();
287
-
288
- canvas1 .height (Math .ceil (rect2 .y - rect1 .y));
289
-
290
- canvas1 .prop ("width", canvas1 .width ());
291
- canvas1 .prop ("height", canvas1 .height ());
292
- }
293
-
294
- // Draw arcs or vertical lines.
295
-
296
- const routes = new Set ();
297
-
298
- for (const canvas2d of canvases)
299
- {
300
- const
301
- canvas = $(canvas2d),
302
- element = canvas .closest ("li", this .sceneGraph),
303
- context = canvas .get (0) .getContext ("2d");
304
-
305
- // Clear canvases.
306
-
307
- context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
308
-
309
- const selectedRoutes = new Set (routes);
310
-
311
- if ((element .hasClass ("field") && !element .data ("full-expanded")) || element .hasClass ("route"))
312
- {
313
- const
314
- field = this .getField (element),
315
- routeId = element .attr ("route-id") !== undefined ? parseInt (element .attr ("route-id")) : undefined;
316
-
317
- let
318
- numInputRoutesDown = 0,
319
- numInputRoutesUp = 0,
320
- numOutputRoutesDown = 0,
321
- numOutputRoutesUp = 0,
322
- numSelectedInputRoutesUp = 0,
323
- numSelectedInputRoutesDown = 0,
324
- numSelectedOutputRoutesUp = 0,
325
- numSelectedOutputRoutesDown = 0;
326
-
327
- for (const route of field .getInputRoutes ())
328
- {
329
- if (routeId !== undefined && route .getId () !== routeId)
330
- continue;
331
-
332
- if (fields .get (route) !== 2)
333
- continue;
334
-
335
- if (routes .has (route))
336
- {
337
- numInputRoutesUp += 1;
338
- numSelectedInputRoutesUp += this .selectedRoutes .has (route);
339
-
340
- routes .delete (route);
341
- selectedRoutes .delete (route);
342
- }
343
- else
344
- {
345
- numInputRoutesDown += 1;
346
- numSelectedInputRoutesDown += this .selectedRoutes .has (route);
347
-
348
- routes .add (route);
349
- }
350
- }
351
-
352
- for (const route of field .getOutputRoutes ())
353
- {
354
- if (routeId !== undefined && route .getId () !== routeId)
355
- continue;
356
-
357
- if (fields .get (route) !== 2)
358
- continue;
359
-
360
- if (routes .has (route))
361
- {
362
- numOutputRoutesUp += 1;
363
- numSelectedOutputRoutesUp += this .selectedRoutes .has (route);
364
-
365
- routes .delete (route);
366
- selectedRoutes .delete (route);
367
- }
368
- else
369
- {
370
- numOutputRoutesDown += 1;
371
- numSelectedOutputRoutesDown += this .selectedRoutes .has (route);
372
-
373
- routes .add (route);
374
- }
375
- }
376
-
377
- // Determine vertical selected lines.
378
-
379
- const
380
- hasVerticalSelectedRoutes = this .haveSelectedRoute (selectedRoutes),
381
- draw = (state, selected) => (state === "normal" && !selected) || (state === "selected" && selected);
382
-
383
- for (const state of ["normal", "selected"])
384
- {
385
- // Draw curve up.
386
-
387
- if (numInputRoutesUp)
388
- {
389
- // Input curve up.
390
-
391
- if (draw (state, numSelectedInputRoutesUp))
392
- {
393
- context .strokeStyle = numSelectedInputRoutesUp ? routeSelectedColor : routeColor;
394
-
395
- context .beginPath ();
396
- context .arc (0, 0, 9.5, 1/2 * Math .PI, 2 * Math .PI, true);
397
- context .lineTo (9.5, 0);
398
- context .stroke ();
399
- }
400
- }
401
-
402
- if (numOutputRoutesUp)
403
- {
404
- // Output curve up.
405
-
406
- if (draw (state, numSelectedOutputRoutesUp))
407
- {
408
- context .strokeStyle = numSelectedOutputRoutesUp ? routeSelectedColor : routeColor;
409
-
410
- context .beginPath ();
411
- context .arc (0, 5, 9.5, 1/2 * Math .PI, 2 * Math .PI, true);
412
- context .lineTo (9.5, 0);
413
- context .stroke ();
414
- }
415
- }
416
-
417
- // Draw curve down.
418
-
419
- if (numInputRoutesDown)
420
- {
421
- // Input curve down.
422
-
423
- if (draw (state, numSelectedInputRoutesDown))
424
- {
425
- context .strokeStyle = numSelectedInputRoutesDown ? routeSelectedColor : routeColor;
426
-
427
- context .beginPath ();
428
- context .arc (0, 19, 9.5, 3/2 * Math .PI, 2 * Math .PI);
429
- context .lineTo (9.5, canvas .prop ("height") + 1);
430
- context .stroke ();
431
- }
432
- }
433
-
434
- if (numOutputRoutesDown)
435
- {
436
- // Output curve down.
437
-
438
- if (draw (state, numSelectedOutputRoutesDown))
439
- {
440
- context .strokeStyle = numSelectedOutputRoutesDown ? routeSelectedColor : routeColor;
441
-
442
- context .beginPath ();
443
- context .arc (0, 24, 9.5, 3/2 * Math .PI, 2 * Math .PI);
444
- context .lineTo (9.5, canvas .prop ("height") + 1);
445
- context .stroke ();
446
- }
447
- }
448
-
449
- // Draw vertical line.
450
-
451
- if (routes .size - (numInputRoutesDown + numOutputRoutesDown) > 0)
452
- {
453
- if (draw (state, hasVerticalSelectedRoutes))
454
- {
455
- context .strokeStyle = hasVerticalSelectedRoutes ? routeSelectedColor : routeColor;
456
-
457
- context .beginPath ();
458
- context .moveTo (9.5, 0);
459
- context .lineTo (9.5, canvas .prop ("height") + 1);
460
- context .stroke ();
461
- }
462
- }
463
- }
464
- }
465
- else
466
- {
467
- // Draw vertical line.
468
-
469
- if (routes .size)
470
- {
471
- context .strokeStyle = this .haveSelectedRoute (routes) ? routeSelectedColor : routeColor;
472
-
473
- context .beginPath ();
474
- context .moveTo (9.5, 0);
475
- context .lineTo (9.5, canvas .prop ("height") + 1);
476
- context .stroke ();
477
- }
478
- }
479
- }
480
- }
481
-
482
- hasInputRoutes (field, fields)
483
- {
484
- for (const route of field .getInputRoutes ())
485
- {
486
- if (fields .get (route) === 2)
487
- return true;
488
- }
489
-
490
- return false;
491
- }
492
-
493
- hasOutputRoutes (field, fields)
494
- {
495
- for (const route of field .getOutputRoutes ())
496
- {
497
- if (fields .get (route) === 2)
498
- return true;
499
- }
500
-
501
- return false;
502
- }
503
-
504
- haveSelectedRoute (routes)
505
- {
506
- for (const route of routes)
507
- {
508
- if (this .selectedRoutes .has (route))
509
- return true;
510
- }
511
-
512
- return false;
513
- }
114
+ for (const element of this .sceneGraph .find (".field"))
115
+ {
116
+ const field = this .getField ($(element));
117
+
118
+ for (const route of field .getInputRoutes ())
119
+ fields .set (route, (fields .get (route) ?? 0) + 1);
120
+
121
+ for (const route of field .getOutputRoutes ())
122
+ fields .set (route, (fields .get (route) ?? 0) + 1);
123
+ }
124
+
125
+ this .sceneGraph
126
+ .find (".field-routes")
127
+ .each ((i, canvas2d) => this .updateFieldRoutes ($(canvas2d), fields));
128
+
129
+ this .sceneGraph
130
+ .find (".single-route")
131
+ .each ((i, canvas2d) => this .updateSingleRoute ($(canvas2d), fields));
132
+
133
+ this .updateRouteCurves (canvases, fields);
134
+ }
135
+
136
+ updateFieldRoutes (canvas, fields)
137
+ {
138
+ // Draw horizontal lines.
139
+
140
+ const
141
+ element = canvas .closest (".field", this .sceneGraph),
142
+ field = this .getField (element),
143
+ parent = canvas .parent (),
144
+ context = canvas .get (0) .getContext ("2d");
145
+
146
+ canvas .height (Math .ceil (parent .height ()));
147
+
148
+ canvas .prop ("width", canvas .width ());
149
+ canvas .prop ("height", canvas .height ());
150
+
151
+ context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
152
+
153
+ switch (field .getAccessType ())
154
+ {
155
+ case X3D .X3DConstants .initializeOnly:
156
+ {
157
+ break;
158
+ }
159
+ case X3D .X3DConstants .inputOnly:
160
+ {
161
+ if (this .hasInputRoutes (field, fields))
162
+ {
163
+ context .strokeStyle = this .haveSelectedRoute (field .getInputRoutes ()) ? routeSelectedColor : routeColor;
164
+
165
+ context .beginPath ();
166
+ context .moveTo (26, 3.5);
167
+ context .lineTo (canvas .prop ("width") + 1, 3.5);
168
+ context .stroke ();
169
+ }
170
+
171
+ break
172
+ }
173
+ case X3D .X3DConstants .outputOnly:
174
+ {
175
+ if (this .hasOutputRoutes (field, fields))
176
+ {
177
+ context .strokeStyle = this .haveSelectedRoute (field .getOutputRoutes ()) ? routeSelectedColor : routeColor;
178
+
179
+ context .beginPath ();
180
+ context .moveTo (26, 8.5);
181
+ context .lineTo (canvas .prop ("width") + 1, 8.5);
182
+ context .stroke ();
183
+ }
184
+
185
+ break
186
+ }
187
+ case X3D .X3DConstants .inputOutput:
188
+ {
189
+ if (this .hasInputRoutes (field, fields))
190
+ {
191
+ context .strokeStyle = this .haveSelectedRoute (field .getInputRoutes ()) ? routeSelectedColor : routeColor;
192
+
193
+ context .beginPath ();
194
+ context .moveTo (40, 3.5);
195
+ context .lineTo (canvas .prop ("width") + 1, 3.5);
196
+ context .stroke ();
197
+ }
198
+
199
+ if (this .hasOutputRoutes (field, fields))
200
+ {
201
+ context .strokeStyle = this .haveSelectedRoute (field .getOutputRoutes ()) ? routeSelectedColor : routeColor;
202
+
203
+ context .beginPath ();
204
+ context .moveTo (field .getInputRoutes () .size ? 54 : 40, 8.5);
205
+ context .lineTo (canvas .prop ("width") + 1, 8.5);
206
+ context .stroke ();
207
+ }
208
+
209
+ break;
210
+ }
211
+ }
212
+ }
213
+
214
+ updateSingleRoute (canvas, fields)
215
+ {
216
+ // Draw horizontal line.
217
+
218
+ const
219
+ element = canvas .closest (".route", this .sceneGraph),
220
+ type = element .attr ("route-type"),
221
+ parent = canvas .parent (),
222
+ context = canvas .get (0) .getContext ("2d");
223
+
224
+ canvas .height (Math .ceil (parent .height ()));
225
+
226
+ canvas .prop ("width", canvas .width ());
227
+ canvas .prop ("height", canvas .height ());
228
+
229
+ context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
230
+
231
+ switch (type)
232
+ {
233
+ case "input":
234
+ {
235
+ const
236
+ field = this .getField (element),
237
+ route = this .getRoute (element, field .getInputRoutes ());
238
+
239
+ if (!route)
240
+ break;
241
+
242
+ if (fields .get (route) !== 2)
243
+ break;
244
+
245
+ context .strokeStyle = this .selectedRoutes .has (route) ? routeSelectedColor : routeColor;
246
+
247
+ context .beginPath ();
248
+ context .moveTo (26, 3.5);
249
+ context .lineTo (canvas .prop ("width") + 1, 3.5);
250
+ context .stroke ();
251
+ break;
252
+ }
253
+ case "output":
254
+ {
255
+ const
256
+ field = this .getField (element),
257
+ route = this .getRoute (element, field .getOutputRoutes ());
258
+
259
+ if (!route)
260
+ break;
261
+
262
+ if (fields .get (route) !== 2)
263
+ break;
264
+
265
+ context .strokeStyle = this .selectedRoutes .has (route) ? routeSelectedColor : routeColor;
266
+
267
+ context .beginPath ();
268
+ context .moveTo (26, 8.5);
269
+ context .lineTo (canvas .prop ("width") + 1, 8.5);
270
+ context .stroke ();
271
+ break;
272
+ }
273
+ }
274
+ }
275
+
276
+ updateRouteCurves (canvases, fields)
277
+ {
278
+ // Scale canvases
279
+
280
+ for (let i = 0, length = canvases .length - 1; i < length; ++ i)
281
+ {
282
+ const
283
+ canvas1 = $(canvases [i]),
284
+ canvas2 = $(canvases [i + 1]),
285
+ rect1 = canvas1 .get (0) .getBoundingClientRect (),
286
+ rect2 = canvas2 .get (0) .getBoundingClientRect ();
287
+
288
+ canvas1 .height (Math .ceil (rect2 .y - rect1 .y));
289
+
290
+ canvas1 .prop ("width", canvas1 .width ());
291
+ canvas1 .prop ("height", canvas1 .height ());
292
+ }
293
+
294
+ // Draw arcs or vertical lines.
295
+
296
+ const routes = new Set ();
297
+
298
+ for (const canvas2d of canvases)
299
+ {
300
+ const
301
+ canvas = $(canvas2d),
302
+ element = canvas .closest ("li", this .sceneGraph),
303
+ context = canvas .get (0) .getContext ("2d");
304
+
305
+ // Clear canvases.
306
+
307
+ context .clearRect (0, 0, canvas .prop ("width"), canvas .prop ("height"));
308
+
309
+ const selectedRoutes = new Set (routes);
310
+
311
+ if ((element .hasClass ("field") && !element .data ("full-expanded")) || element .hasClass ("route"))
312
+ {
313
+ const
314
+ field = this .getField (element),
315
+ routeId = element .attr ("route-id") !== undefined ? parseInt (element .attr ("route-id")) : undefined;
316
+
317
+ let
318
+ numInputRoutesDown = 0,
319
+ numInputRoutesUp = 0,
320
+ numOutputRoutesDown = 0,
321
+ numOutputRoutesUp = 0,
322
+ numSelectedInputRoutesUp = 0,
323
+ numSelectedInputRoutesDown = 0,
324
+ numSelectedOutputRoutesUp = 0,
325
+ numSelectedOutputRoutesDown = 0;
326
+
327
+ for (const route of field .getInputRoutes ())
328
+ {
329
+ if (routeId !== undefined && route .getId () !== routeId)
330
+ continue;
331
+
332
+ if (fields .get (route) !== 2)
333
+ continue;
334
+
335
+ if (routes .has (route))
336
+ {
337
+ numInputRoutesUp += 1;
338
+ numSelectedInputRoutesUp += this .selectedRoutes .has (route);
339
+
340
+ routes .delete (route);
341
+ selectedRoutes .delete (route);
342
+ }
343
+ else
344
+ {
345
+ numInputRoutesDown += 1;
346
+ numSelectedInputRoutesDown += this .selectedRoutes .has (route);
347
+
348
+ routes .add (route);
349
+ }
350
+ }
351
+
352
+ for (const route of field .getOutputRoutes ())
353
+ {
354
+ if (routeId !== undefined && route .getId () !== routeId)
355
+ continue;
356
+
357
+ if (fields .get (route) !== 2)
358
+ continue;
359
+
360
+ if (routes .has (route))
361
+ {
362
+ numOutputRoutesUp += 1;
363
+ numSelectedOutputRoutesUp += this .selectedRoutes .has (route);
364
+
365
+ routes .delete (route);
366
+ selectedRoutes .delete (route);
367
+ }
368
+ else
369
+ {
370
+ numOutputRoutesDown += 1;
371
+ numSelectedOutputRoutesDown += this .selectedRoutes .has (route);
372
+
373
+ routes .add (route);
374
+ }
375
+ }
376
+
377
+ // Determine vertical selected lines.
378
+
379
+ const
380
+ hasVerticalSelectedRoutes = this .haveSelectedRoute (selectedRoutes),
381
+ draw = (state, selected) => (state === "normal" && !selected) || (state === "selected" && selected);
382
+
383
+ for (const state of ["normal", "selected"])
384
+ {
385
+ // Draw curve up.
386
+
387
+ if (numInputRoutesUp)
388
+ {
389
+ // Input curve up.
390
+
391
+ if (draw (state, numSelectedInputRoutesUp))
392
+ {
393
+ context .strokeStyle = numSelectedInputRoutesUp ? routeSelectedColor : routeColor;
394
+
395
+ context .beginPath ();
396
+ context .arc (0, 0, 9.5, 1/2 * Math .PI, 2 * Math .PI, true);
397
+ context .lineTo (9.5, 0);
398
+ context .stroke ();
399
+ }
400
+ }
401
+
402
+ if (numOutputRoutesUp)
403
+ {
404
+ // Output curve up.
405
+
406
+ if (draw (state, numSelectedOutputRoutesUp))
407
+ {
408
+ context .strokeStyle = numSelectedOutputRoutesUp ? routeSelectedColor : routeColor;
409
+
410
+ context .beginPath ();
411
+ context .arc (0, 5, 9.5, 1/2 * Math .PI, 2 * Math .PI, true);
412
+ context .lineTo (9.5, 0);
413
+ context .stroke ();
414
+ }
415
+ }
416
+
417
+ // Draw curve down.
418
+
419
+ if (numInputRoutesDown)
420
+ {
421
+ // Input curve down.
422
+
423
+ if (draw (state, numSelectedInputRoutesDown))
424
+ {
425
+ context .strokeStyle = numSelectedInputRoutesDown ? routeSelectedColor : routeColor;
426
+
427
+ context .beginPath ();
428
+ context .arc (0, 19, 9.5, 3/2 * Math .PI, 2 * Math .PI);
429
+ context .lineTo (9.5, canvas .prop ("height") + 1);
430
+ context .stroke ();
431
+ }
432
+ }
433
+
434
+ if (numOutputRoutesDown)
435
+ {
436
+ // Output curve down.
437
+
438
+ if (draw (state, numSelectedOutputRoutesDown))
439
+ {
440
+ context .strokeStyle = numSelectedOutputRoutesDown ? routeSelectedColor : routeColor;
441
+
442
+ context .beginPath ();
443
+ context .arc (0, 24, 9.5, 3/2 * Math .PI, 2 * Math .PI);
444
+ context .lineTo (9.5, canvas .prop ("height") + 1);
445
+ context .stroke ();
446
+ }
447
+ }
448
+
449
+ // Draw vertical line.
450
+
451
+ if (routes .size - (numInputRoutesDown + numOutputRoutesDown) > 0)
452
+ {
453
+ if (draw (state, hasVerticalSelectedRoutes))
454
+ {
455
+ context .strokeStyle = hasVerticalSelectedRoutes ? routeSelectedColor : routeColor;
456
+
457
+ context .beginPath ();
458
+ context .moveTo (9.5, 0);
459
+ context .lineTo (9.5, canvas .prop ("height") + 1);
460
+ context .stroke ();
461
+ }
462
+ }
463
+ }
464
+ }
465
+ else
466
+ {
467
+ // Draw vertical line.
468
+
469
+ if (routes .size)
470
+ {
471
+ context .strokeStyle = this .haveSelectedRoute (routes) ? routeSelectedColor : routeColor;
472
+
473
+ context .beginPath ();
474
+ context .moveTo (9.5, 0);
475
+ context .lineTo (9.5, canvas .prop ("height") + 1);
476
+ context .stroke ();
477
+ }
478
+ }
479
+ }
480
+ }
481
+
482
+ hasInputRoutes (field, fields)
483
+ {
484
+ for (const route of field .getInputRoutes ())
485
+ {
486
+ if (fields .get (route) === 2)
487
+ return true;
488
+ }
489
+
490
+ return false;
491
+ }
492
+
493
+ hasOutputRoutes (field, fields)
494
+ {
495
+ for (const route of field .getOutputRoutes ())
496
+ {
497
+ if (fields .get (route) === 2)
498
+ return true;
499
+ }
500
+
501
+ return false;
502
+ }
503
+
504
+ haveSelectedRoute (routes)
505
+ {
506
+ for (const route of routes)
507
+ {
508
+ if (this .selectedRoutes .has (route))
509
+ return true;
510
+ }
511
+
512
+ return false;
513
+ }
514
514
  };