lexgui 0.1.29 → 0.1.31
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/build/components/codeeditor.js +11 -22
- package/build/components/nodegraph.js +927 -364
- package/build/lexgui.css +74 -40
- package/build/lexgui.js +136 -34
- package/build/lexgui.module.js +134 -32
- package/changelog.md +28 -0
- package/examples/code_editor.html +10 -13
- package/examples/index.html +1 -1
- package/examples/previews/code_editor.png +0 -0
- package/examples/previews/dialogs.png +0 -0
- package/examples/previews/node_graph.png +0 -0
- package/package.json +1 -1
|
@@ -4,43 +4,7 @@ if(!LX) {
|
|
|
4
4
|
throw("lexgui.js missing!");
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
LX.components.push( '
|
|
8
|
-
|
|
9
|
-
function flushCss(element) {
|
|
10
|
-
// By reading the offsetHeight property, we are forcing
|
|
11
|
-
// the browser to flush the pending CSS changes (which it
|
|
12
|
-
// does to ensure the value obtained is accurate).
|
|
13
|
-
element.offsetHeight;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function swapElements (obj, a, b) {
|
|
17
|
-
[obj[a], obj[b]] = [obj[b], obj[a]];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function swapArrayElements (array, id0, id1) {
|
|
21
|
-
[array[id0], array[id1]] = [array[id1], array[id0]];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function sliceChar(str, idx) {
|
|
25
|
-
return str.substr(0, idx) + str.substr(idx + 1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function firstNonspaceIndex(str) {
|
|
29
|
-
return str.search(/\S|$/);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function deleteElement( el ) {
|
|
33
|
-
if( el ) el.remove();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let ASYNC_ENABLED = true;
|
|
37
|
-
|
|
38
|
-
function doAsync( fn, ms ) {
|
|
39
|
-
if( ASYNC_ENABLED )
|
|
40
|
-
setTimeout( fn, ms ?? 0 );
|
|
41
|
-
else
|
|
42
|
-
fn();
|
|
43
|
-
}
|
|
7
|
+
LX.components.push( 'GraphEditor' );
|
|
44
8
|
|
|
45
9
|
class BoundingBox {
|
|
46
10
|
|
|
@@ -50,7 +14,7 @@ class BoundingBox {
|
|
|
50
14
|
this.size = s ?? new LX.vec2( 0, 0 );
|
|
51
15
|
}
|
|
52
16
|
|
|
53
|
-
merge
|
|
17
|
+
merge( bb ) {
|
|
54
18
|
|
|
55
19
|
console.assert( bb.constructor == BoundingBox );
|
|
56
20
|
|
|
@@ -67,7 +31,7 @@ class BoundingBox {
|
|
|
67
31
|
this.size = merge_max.sub( merge_min );
|
|
68
32
|
}
|
|
69
33
|
|
|
70
|
-
inside
|
|
34
|
+
inside( bb, full = true ) {
|
|
71
35
|
|
|
72
36
|
const min_0 = this.origin;
|
|
73
37
|
const max_0 = this.origin.add( this.size );
|
|
@@ -75,8 +39,16 @@ class BoundingBox {
|
|
|
75
39
|
const min_1 = bb.origin;
|
|
76
40
|
const max_1 = bb.origin.add( bb.size );
|
|
77
41
|
|
|
78
|
-
|
|
79
|
-
|
|
42
|
+
if( full )
|
|
43
|
+
{
|
|
44
|
+
return min_1.x >= min_0.x && max_1.x <= max_0.x
|
|
45
|
+
&& min_1.y >= min_0.y && max_1.y <= max_0.y;
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
{
|
|
49
|
+
return max_1.x >= min_0.x && min_1.x <= max_0.x
|
|
50
|
+
&& max_1.y >= min_0.y && min_1.y <= max_0.y;
|
|
51
|
+
}
|
|
80
52
|
}
|
|
81
53
|
};
|
|
82
54
|
|
|
@@ -97,6 +69,10 @@ class GraphEditor {
|
|
|
97
69
|
static EVENT_MOUSEWHEEL = 1;
|
|
98
70
|
|
|
99
71
|
static LAST_GROUP_ID = 0;
|
|
72
|
+
static LAST_FUNCTION_ID = 0;
|
|
73
|
+
|
|
74
|
+
static STOPPED = 0;
|
|
75
|
+
static RUNNING = 1;
|
|
100
76
|
|
|
101
77
|
// Node Drawing
|
|
102
78
|
|
|
@@ -114,10 +90,10 @@ class GraphEditor {
|
|
|
114
90
|
|
|
115
91
|
GraphEditor.__instances.push( this );
|
|
116
92
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
m.add( "
|
|
93
|
+
const useSidebar = options.sidebar ?? true;
|
|
94
|
+
|
|
95
|
+
this._sidebar = area.addSidebar( m => {
|
|
96
|
+
m.add( "Create", { icon: "fa fa-add", bottom: true, callback: (e) => this._onSidebarCreate( e ) } );
|
|
121
97
|
});
|
|
122
98
|
|
|
123
99
|
this.base_area = area;
|
|
@@ -127,12 +103,20 @@ class GraphEditor {
|
|
|
127
103
|
|
|
128
104
|
this.root = this.area.root;
|
|
129
105
|
this.root.tabIndex = -1;
|
|
106
|
+
|
|
130
107
|
area.attach( this.root );
|
|
131
108
|
|
|
109
|
+
this._graphContainer = area.sections[ 1 ].root;
|
|
110
|
+
this._sidebarDom = area.sections[ 0 ].root;
|
|
111
|
+
this._sidebarActive = useSidebar;
|
|
112
|
+
|
|
113
|
+
// Set sidebar state depending on options..
|
|
114
|
+
this._toggleSideBar( useSidebar );
|
|
115
|
+
|
|
132
116
|
// Bind resize
|
|
133
117
|
|
|
134
118
|
area.onresize = ( bb ) => {
|
|
135
|
-
|
|
119
|
+
|
|
136
120
|
};
|
|
137
121
|
|
|
138
122
|
area.addOverlayButtons( [
|
|
@@ -141,27 +125,20 @@ class GraphEditor {
|
|
|
141
125
|
icon: "fa fa-table-columns",
|
|
142
126
|
callback: () => this._toggleSideBar(),
|
|
143
127
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// {
|
|
159
|
-
// name: "Rotate",
|
|
160
|
-
// icon: "fa-solid fa-rotate-right",
|
|
161
|
-
// callback: (value, event) => console.log(value),
|
|
162
|
-
// selectable: true
|
|
163
|
-
// }
|
|
164
|
-
// ],
|
|
128
|
+
[
|
|
129
|
+
{
|
|
130
|
+
name: "Start Graph",
|
|
131
|
+
icon: "fa fa-play",
|
|
132
|
+
callback: (value, event) => this.start(),
|
|
133
|
+
selectable: true
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "Stop Graph",
|
|
137
|
+
icon: "fa-solid fa-stop",
|
|
138
|
+
callback: (value, event) => this.stop(),
|
|
139
|
+
selectable: true
|
|
140
|
+
}
|
|
141
|
+
],
|
|
165
142
|
[
|
|
166
143
|
{
|
|
167
144
|
name: "Enable Snapping",
|
|
@@ -184,9 +161,14 @@ class GraphEditor {
|
|
|
184
161
|
{
|
|
185
162
|
name: "Export",
|
|
186
163
|
icon: "fa fa-diagram-project",
|
|
187
|
-
callback: (value, event) => this.
|
|
164
|
+
callback: (value, event) => this.currentGraph.export()
|
|
188
165
|
}
|
|
189
|
-
]
|
|
166
|
+
],
|
|
167
|
+
{
|
|
168
|
+
name: "",
|
|
169
|
+
class: "graph-title",
|
|
170
|
+
callback: (value, event) => this._showRenameGraphDialog()
|
|
171
|
+
}
|
|
190
172
|
], { float: "htc" } );
|
|
191
173
|
|
|
192
174
|
this.root.addEventListener( 'keydown', this._processKeyDown.bind( this ), true );
|
|
@@ -202,7 +184,7 @@ class GraphEditor {
|
|
|
202
184
|
this.root.addEventListener( 'focusout', this._processFocus.bind( this, false ) );
|
|
203
185
|
|
|
204
186
|
this.propertiesDialog = new LX.PocketDialog( "Properties", null, {
|
|
205
|
-
size: [ "
|
|
187
|
+
size: [ "350px", null ],
|
|
206
188
|
position: [ "8px", "8px" ],
|
|
207
189
|
float: "left",
|
|
208
190
|
class: 'lexgraphpropdialog'
|
|
@@ -236,18 +218,17 @@ class GraphEditor {
|
|
|
236
218
|
|
|
237
219
|
this.keys = { };
|
|
238
220
|
|
|
239
|
-
this._sidebar = area.sections[ 0 ].root;
|
|
240
|
-
this._sidebarActive = false;
|
|
241
|
-
|
|
242
221
|
this._snapToGrid = false;
|
|
243
222
|
this._snapValue = 1.0;
|
|
244
223
|
|
|
245
|
-
|
|
224
|
+
// Graphs, Nodes and connections
|
|
246
225
|
|
|
247
|
-
|
|
226
|
+
this.currentGraph = null;
|
|
248
227
|
|
|
249
|
-
this.
|
|
250
|
-
this.
|
|
228
|
+
this.graphs = { };
|
|
229
|
+
this.nodes = { };
|
|
230
|
+
this.groups = { };
|
|
231
|
+
this.variables = { };
|
|
251
232
|
|
|
252
233
|
this.selectedNodes = [ ];
|
|
253
234
|
|
|
@@ -273,7 +254,6 @@ class GraphEditor {
|
|
|
273
254
|
// Back pattern
|
|
274
255
|
|
|
275
256
|
const f = 15.0;
|
|
276
|
-
this._patternPosition = new LX.vec2( 0, 0 );
|
|
277
257
|
this._patternSize = new LX.vec2( f );
|
|
278
258
|
this._circlePatternSize = f * 0.04;
|
|
279
259
|
this._circlePatternColor = '#71717a9c';
|
|
@@ -372,6 +352,11 @@ class GraphEditor {
|
|
|
372
352
|
node.position = new LX.vec2( 0, 0 );
|
|
373
353
|
node.color = null;
|
|
374
354
|
|
|
355
|
+
if( baseClass.name == 'NodeFunction' )
|
|
356
|
+
{
|
|
357
|
+
node.gid = baseClass.gid;
|
|
358
|
+
}
|
|
359
|
+
|
|
375
360
|
// Extra options
|
|
376
361
|
if ( options ) {
|
|
377
362
|
for (var i in options) {
|
|
@@ -393,24 +378,41 @@ class GraphEditor {
|
|
|
393
378
|
|
|
394
379
|
setGraph( graph ) {
|
|
395
380
|
|
|
381
|
+
// Nothing to do, already there...
|
|
382
|
+
if( this.currentGraph && graph.id == this.currentGraph.id )
|
|
383
|
+
return;
|
|
384
|
+
|
|
396
385
|
this.clear();
|
|
397
386
|
|
|
398
|
-
|
|
387
|
+
graph.id = graph.id ?? graph.constructor.name + '-' + LX.UTILS.uidGenerator();
|
|
388
|
+
|
|
389
|
+
this.graphs[ graph.id ] = graph;
|
|
399
390
|
|
|
400
|
-
if( !
|
|
391
|
+
if( !graph.nodes )
|
|
401
392
|
{
|
|
402
393
|
console.warn( 'Graph does not contain any node!' );
|
|
403
394
|
return;
|
|
404
395
|
}
|
|
405
396
|
|
|
406
|
-
|
|
397
|
+
this.currentGraph = graph;
|
|
398
|
+
|
|
399
|
+
this._updatePattern();
|
|
400
|
+
|
|
401
|
+
for( let node of graph.nodes )
|
|
407
402
|
{
|
|
408
403
|
this._createNodeDOM( node );
|
|
409
404
|
}
|
|
410
405
|
|
|
411
|
-
for( let
|
|
406
|
+
for( let group of graph.groups )
|
|
407
|
+
{
|
|
408
|
+
const groupDom = this._createGroup( group );
|
|
409
|
+
groupDom.querySelector( '.lexgraphgrouptitle' ).value = group.name;
|
|
410
|
+
this._domNodes.prepend( groupDom );
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
for( let linkId in graph.links )
|
|
412
414
|
{
|
|
413
|
-
const links =
|
|
415
|
+
const links = graph.links[ linkId ];
|
|
414
416
|
|
|
415
417
|
for( let link of links )
|
|
416
418
|
{
|
|
@@ -418,24 +420,21 @@ class GraphEditor {
|
|
|
418
420
|
}
|
|
419
421
|
}
|
|
420
422
|
|
|
421
|
-
|
|
422
|
-
this.
|
|
423
|
-
//
|
|
423
|
+
this._updateGraphName( graph.name );
|
|
424
|
+
this._togglePropertiesDialog( false );
|
|
424
425
|
}
|
|
425
426
|
|
|
426
427
|
/**
|
|
427
428
|
* @method loadGraph
|
|
428
|
-
* @param {
|
|
429
|
+
* @param {String} url
|
|
430
|
+
* @param {Function} callback Function to call once the graph is loaded
|
|
429
431
|
*/
|
|
430
432
|
|
|
431
433
|
loadGraph( url, callback ) {
|
|
432
434
|
|
|
433
435
|
const onComplete = ( json ) => {
|
|
434
436
|
|
|
435
|
-
|
|
436
|
-
graph.configure( json );
|
|
437
|
-
|
|
438
|
-
this.setGraph( graph );
|
|
437
|
+
let graph = ( json.type == 'Graph' ) ? this.addGraph( json ) : this.addGraphFunction( json );
|
|
439
438
|
|
|
440
439
|
if( callback )
|
|
441
440
|
callback( graph );
|
|
@@ -446,6 +445,86 @@ class GraphEditor {
|
|
|
446
445
|
LX.requestJSON( url, onComplete, onError );
|
|
447
446
|
}
|
|
448
447
|
|
|
448
|
+
/**
|
|
449
|
+
* @method addGraph
|
|
450
|
+
* @param {Object} o Options to configure the graph
|
|
451
|
+
*/
|
|
452
|
+
|
|
453
|
+
addGraph( o ) {
|
|
454
|
+
|
|
455
|
+
let graph = new Graph();
|
|
456
|
+
graph.editor = this;
|
|
457
|
+
|
|
458
|
+
if( o )
|
|
459
|
+
{
|
|
460
|
+
// Load functions first if any..
|
|
461
|
+
|
|
462
|
+
for( let fn of o.functions ?? [] )
|
|
463
|
+
{
|
|
464
|
+
this.addGraphFunction( fn );
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
graph.configure( o );
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
this.setGraph( graph );
|
|
471
|
+
|
|
472
|
+
this._sidebar.add( graph.name, { icon: "fa fa-diagram-project", className: graph.id, callback: (e) => { this.setGraph( graph ) } } );
|
|
473
|
+
|
|
474
|
+
this._sidebar.select( graph.name );
|
|
475
|
+
|
|
476
|
+
return graph;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @method addGraphFunction
|
|
481
|
+
* @param {Object} o Options to configure the graph
|
|
482
|
+
*/
|
|
483
|
+
|
|
484
|
+
addGraphFunction( o ) {
|
|
485
|
+
|
|
486
|
+
let func = new GraphFunction();
|
|
487
|
+
func.editor = this;
|
|
488
|
+
|
|
489
|
+
if( o )
|
|
490
|
+
{
|
|
491
|
+
// Load other inner functions first if any..
|
|
492
|
+
|
|
493
|
+
for( let fn of o.functions ?? [] )
|
|
494
|
+
{
|
|
495
|
+
this.addGraphFunction( fn );
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
func.configure( o );
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
this.setGraph( func );
|
|
502
|
+
|
|
503
|
+
// Add a new node to use this function..
|
|
504
|
+
|
|
505
|
+
class NodeFunction extends GraphNode
|
|
506
|
+
{
|
|
507
|
+
onCreate() {
|
|
508
|
+
this.addInput( null, "float" );
|
|
509
|
+
this.addOutput( null, "any" );
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
onExecute() {
|
|
513
|
+
const func = NodeFunction.func;
|
|
514
|
+
const value = func.getOutputData( this.getInput( 0 ) );
|
|
515
|
+
this.setOutput( 0, value );
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
NodeFunction.func = func;
|
|
520
|
+
NodeFunction.gid = func.id;
|
|
521
|
+
GraphEditor.registerCustomNode( "function/" + func.name, NodeFunction );
|
|
522
|
+
|
|
523
|
+
this._sidebar.add( func.name, { icon: "fa fa-florin-sign", className: func.id, callback: (e) => { this.setGraph( func ) } } );
|
|
524
|
+
|
|
525
|
+
this._sidebar.select( func.name );
|
|
526
|
+
}
|
|
527
|
+
|
|
449
528
|
/**
|
|
450
529
|
* @method clear
|
|
451
530
|
*/
|
|
@@ -470,10 +549,10 @@ class GraphEditor {
|
|
|
470
549
|
|
|
471
550
|
propagateEventToAllNodes( eventName, params ) {
|
|
472
551
|
|
|
473
|
-
if( !this.
|
|
552
|
+
if( !this.currentGraph )
|
|
474
553
|
return;
|
|
475
554
|
|
|
476
|
-
for ( let node of this.
|
|
555
|
+
for ( let node of this.currentGraph.nodes )
|
|
477
556
|
{
|
|
478
557
|
if( !node[ eventName ] )
|
|
479
558
|
continue;
|
|
@@ -511,6 +590,7 @@ class GraphEditor {
|
|
|
511
590
|
_createNodeDOM( node ) {
|
|
512
591
|
|
|
513
592
|
node.editor = this;
|
|
593
|
+
node.graphID = this.currentGraph.id;
|
|
514
594
|
|
|
515
595
|
var nodeContainer = document.createElement( 'div' );
|
|
516
596
|
nodeContainer.classList.add( 'lexgraphnode' );
|
|
@@ -527,6 +607,12 @@ class GraphEditor {
|
|
|
527
607
|
const category = node.constructor.category;
|
|
528
608
|
nodeContainer.classList.add( category );
|
|
529
609
|
}
|
|
610
|
+
else
|
|
611
|
+
{
|
|
612
|
+
const pos = node.type.lastIndexOf( "/" );
|
|
613
|
+
const category = node.type.substring( 0, pos );
|
|
614
|
+
nodeContainer.classList.add( category );
|
|
615
|
+
}
|
|
530
616
|
|
|
531
617
|
// Update with manual color
|
|
532
618
|
|
|
@@ -582,14 +668,16 @@ class GraphEditor {
|
|
|
582
668
|
LX.addContextMenu(null, e, m => {
|
|
583
669
|
|
|
584
670
|
m.add( "Copy", () => {
|
|
585
|
-
|
|
586
|
-
|
|
671
|
+
this._clipboardData = {
|
|
672
|
+
id: node.id,
|
|
673
|
+
gid: this.currentGraph.id
|
|
674
|
+
};
|
|
587
675
|
} );
|
|
588
676
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
} );
|
|
677
|
+
// TODO
|
|
678
|
+
// m.add( "Paste", () => {
|
|
679
|
+
|
|
680
|
+
// } );
|
|
593
681
|
|
|
594
682
|
m.add( "" );
|
|
595
683
|
|
|
@@ -604,6 +692,13 @@ class GraphEditor {
|
|
|
604
692
|
// Only for left click..
|
|
605
693
|
if( e.button != LX.MOUSE_LEFT_CLICK )
|
|
606
694
|
return;
|
|
695
|
+
|
|
696
|
+
// Open graph function..
|
|
697
|
+
if( node.constructor.func )
|
|
698
|
+
{
|
|
699
|
+
this._sidebar.select( node.constructor.func.name )
|
|
700
|
+
}
|
|
701
|
+
|
|
607
702
|
} );
|
|
608
703
|
|
|
609
704
|
// Title header
|
|
@@ -613,7 +708,7 @@ class GraphEditor {
|
|
|
613
708
|
nodeContainer.appendChild( nodeHeader );
|
|
614
709
|
|
|
615
710
|
// Properties
|
|
616
|
-
// if( node.properties.length
|
|
711
|
+
// if( node.properties.length )
|
|
617
712
|
// {
|
|
618
713
|
// var nodeProperties = document.createElement( 'div' );
|
|
619
714
|
// nodeProperties.classList.add( 'lexgraphnodeproperties' );
|
|
@@ -656,7 +751,7 @@ class GraphEditor {
|
|
|
656
751
|
{
|
|
657
752
|
var nodeInputs = null;
|
|
658
753
|
|
|
659
|
-
if(
|
|
754
|
+
if( hasInputs )
|
|
660
755
|
{
|
|
661
756
|
nodeInputs = document.createElement( 'div' );
|
|
662
757
|
nodeInputs.classList.add( 'lexgraphnodeinputs' );
|
|
@@ -678,6 +773,7 @@ class GraphEditor {
|
|
|
678
773
|
|
|
679
774
|
var type = document.createElement( 'span' );
|
|
680
775
|
type.className = 'io__type input ' + i.type;
|
|
776
|
+
type.dataset[ 'type' ] = i.type;
|
|
681
777
|
type.innerHTML = '<span>' + i.type[ 0 ].toUpperCase() + '</span>';
|
|
682
778
|
input.appendChild( type );
|
|
683
779
|
|
|
@@ -702,7 +798,7 @@ class GraphEditor {
|
|
|
702
798
|
{
|
|
703
799
|
var nodeOutputs = null;
|
|
704
800
|
|
|
705
|
-
if(
|
|
801
|
+
if( hasOutputs )
|
|
706
802
|
{
|
|
707
803
|
nodeOutputs = document.createElement( 'div' );
|
|
708
804
|
nodeOutputs.classList.add( 'lexgraphnodeoutputs' );
|
|
@@ -731,6 +827,7 @@ class GraphEditor {
|
|
|
731
827
|
|
|
732
828
|
var type = document.createElement( 'span' );
|
|
733
829
|
type.className = 'io__type output ' + o.type;
|
|
830
|
+
type.dataset[ 'type' ] = o.type;
|
|
734
831
|
type.innerHTML = '<span>' + o.type[ 0 ].toUpperCase() + '</span>';
|
|
735
832
|
output.appendChild( type );
|
|
736
833
|
|
|
@@ -750,6 +847,141 @@ class GraphEditor {
|
|
|
750
847
|
onDragStart: this._onDragNode.bind( this )
|
|
751
848
|
} );
|
|
752
849
|
|
|
850
|
+
this._addNodeIOEvents( nodeContainer );
|
|
851
|
+
|
|
852
|
+
const id = node.id ?? node.title.toLowerCase().replaceAll( /\s/g, '-' ) + '-' + LX.UTILS.uidGenerator();
|
|
853
|
+
this.nodes[ id ] = { data: node, dom: nodeContainer };
|
|
854
|
+
|
|
855
|
+
node.id = id;
|
|
856
|
+
nodeContainer.dataset[ 'id' ] = id;
|
|
857
|
+
|
|
858
|
+
this._domNodes.appendChild( nodeContainer );
|
|
859
|
+
|
|
860
|
+
// Only 1 main per graph!
|
|
861
|
+
if( node.title == 'Main' )
|
|
862
|
+
{
|
|
863
|
+
this.main = id;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
node.size = new LX.vec2( nodeContainer.offsetWidth, nodeContainer.offsetHeight );
|
|
867
|
+
|
|
868
|
+
node.resizeObserver = new ResizeObserver( entries => {
|
|
869
|
+
|
|
870
|
+
for( const entry of entries ) {
|
|
871
|
+
const bb = entry.contentRect;
|
|
872
|
+
if( !bb.width || !bb.height )
|
|
873
|
+
continue;
|
|
874
|
+
node.size = new LX.vec2( nodeContainer.offsetWidth, nodeContainer.offsetHeight );
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
node.resizeObserver.observe( nodeContainer );
|
|
879
|
+
|
|
880
|
+
return nodeContainer;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
_updateNodeDOMIOs( dom, node ) {
|
|
884
|
+
|
|
885
|
+
// Inputs and outputs
|
|
886
|
+
var nodeIO = dom.querySelector( '.lexgraphnodeios' );
|
|
887
|
+
|
|
888
|
+
const hasInputs = node.inputs && node.inputs.length;
|
|
889
|
+
const hasOutputs = node.outputs && node.outputs.length;
|
|
890
|
+
|
|
891
|
+
// Inputs
|
|
892
|
+
{
|
|
893
|
+
var nodeInputs = null;
|
|
894
|
+
|
|
895
|
+
if( hasInputs )
|
|
896
|
+
{
|
|
897
|
+
nodeInputs = nodeIO.querySelector( '.lexgraphnodeinputs' );
|
|
898
|
+
nodeInputs.innerHTML = "";
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
for( let i of node.inputs )
|
|
902
|
+
{
|
|
903
|
+
if( !i.type )
|
|
904
|
+
{
|
|
905
|
+
console.warn( `Missing type for node [${ node.title }], skipping...` );
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
var input = document.createElement( 'div' );
|
|
910
|
+
input.className = 'lexgraphnodeio ioinput';
|
|
911
|
+
input.dataset[ 'index' ] = nodeInputs.childElementCount;
|
|
912
|
+
|
|
913
|
+
var type = document.createElement( 'span' );
|
|
914
|
+
type.className = 'io__type input ' + i.type;
|
|
915
|
+
type.innerHTML = '<span>' + i.type[ 0 ].toUpperCase() + '</span>';
|
|
916
|
+
input.appendChild( type );
|
|
917
|
+
|
|
918
|
+
var typeDesc = document.createElement( 'span' );
|
|
919
|
+
typeDesc.className = 'io__typedesc input ' + i.type;
|
|
920
|
+
typeDesc.innerHTML = i.type;
|
|
921
|
+
input.appendChild( typeDesc );
|
|
922
|
+
|
|
923
|
+
if( i.name )
|
|
924
|
+
{
|
|
925
|
+
var name = document.createElement( 'span' );
|
|
926
|
+
name.classList.add( 'io__name' );
|
|
927
|
+
name.innerText = i.name;
|
|
928
|
+
input.appendChild( name );
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
nodeInputs.appendChild( input );
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// Outputs
|
|
936
|
+
{
|
|
937
|
+
var nodeOutputs = null;
|
|
938
|
+
|
|
939
|
+
if( hasOutputs )
|
|
940
|
+
{
|
|
941
|
+
nodeOutputs = nodeIO.querySelector( '.lexgraphnodeoutputs' );
|
|
942
|
+
nodeOutputs.innerHTML = "";
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
for( let o of node.outputs )
|
|
946
|
+
{
|
|
947
|
+
if( !o.type )
|
|
948
|
+
{
|
|
949
|
+
console.warn( `Missing type for node [${ node.title }], skipping...` );
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
var output = document.createElement( 'div' );
|
|
953
|
+
output.className = 'lexgraphnodeio iooutput';
|
|
954
|
+
output.dataset[ 'index' ] = nodeOutputs.childElementCount;
|
|
955
|
+
|
|
956
|
+
if( o.name )
|
|
957
|
+
{
|
|
958
|
+
var name = document.createElement( 'span' );
|
|
959
|
+
name.classList.add( 'io__name' );
|
|
960
|
+
name.innerText = o.name;
|
|
961
|
+
output.appendChild( name );
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
var type = document.createElement( 'span' );
|
|
965
|
+
type.className = 'io__type output ' + o.type;
|
|
966
|
+
type.innerHTML = '<span>' + o.type[ 0 ].toUpperCase() + '</span>';
|
|
967
|
+
output.appendChild( type );
|
|
968
|
+
|
|
969
|
+
var typeDesc = document.createElement( 'span' );
|
|
970
|
+
typeDesc.className = 'io__typedesc output ' + o.type;
|
|
971
|
+
typeDesc.innerHTML = o.type;
|
|
972
|
+
output.appendChild( typeDesc );
|
|
973
|
+
|
|
974
|
+
nodeOutputs.appendChild( output );
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
this._addNodeIOEvents( dom );
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
_addNodeIOEvents( nodeContainer ) {
|
|
982
|
+
|
|
983
|
+
const nodeIO = nodeContainer.querySelector( '.lexgraphnodeios' );
|
|
984
|
+
|
|
753
985
|
// Manage links
|
|
754
986
|
|
|
755
987
|
nodeIO.querySelectorAll( '.lexgraphnodeio' ).forEach( el => {
|
|
@@ -775,8 +1007,11 @@ class GraphEditor {
|
|
|
775
1007
|
|
|
776
1008
|
el.addEventListener( 'mouseup', e => {
|
|
777
1009
|
|
|
1010
|
+
e.stopPropagation();
|
|
1011
|
+
e.stopImmediatePropagation();
|
|
1012
|
+
|
|
778
1013
|
// Single click..
|
|
779
|
-
if( ( LX.getTime() - this.lastMouseDown ) <
|
|
1014
|
+
if( ( LX.getTime() - this.lastMouseDown ) < 200 ) {
|
|
780
1015
|
delete this._generatingLink;
|
|
781
1016
|
return;
|
|
782
1017
|
}
|
|
@@ -787,14 +1022,11 @@ class GraphEditor {
|
|
|
787
1022
|
if( !this._onLink( e ) )
|
|
788
1023
|
{
|
|
789
1024
|
// Delete entire SVG if not a successful connection..
|
|
790
|
-
deleteElement( this._generatingLink.path ? this._generatingLink.path.parentElement : null );
|
|
1025
|
+
LX.UTILS.deleteElement( this._generatingLink.path ? this._generatingLink.path.parentElement : null );
|
|
791
1026
|
}
|
|
792
1027
|
|
|
793
1028
|
delete this._generatingLink;
|
|
794
1029
|
}
|
|
795
|
-
|
|
796
|
-
e.stopPropagation();
|
|
797
|
-
e.stopImmediatePropagation();
|
|
798
1030
|
} );
|
|
799
1031
|
|
|
800
1032
|
el.addEventListener( 'click', e => {
|
|
@@ -808,36 +1040,29 @@ class GraphEditor {
|
|
|
808
1040
|
} );
|
|
809
1041
|
|
|
810
1042
|
} );
|
|
1043
|
+
}
|
|
811
1044
|
|
|
812
|
-
|
|
813
|
-
this.nodes[ id ] = { data: node, dom: nodeContainer };
|
|
1045
|
+
_getAllDOMNodes( includeGroups, exclude ) {
|
|
814
1046
|
|
|
815
|
-
|
|
816
|
-
nodeContainer.dataset[ 'id' ] = id;
|
|
1047
|
+
var elements = null;
|
|
817
1048
|
|
|
818
|
-
|
|
1049
|
+
if( includeGroups )
|
|
1050
|
+
elements = Array.from( this._domNodes.childNodes );
|
|
1051
|
+
else
|
|
1052
|
+
elements = Array.from( this._domNodes.childNodes ).filter( v => v.classList.contains( 'lexgraphnode' ) );
|
|
819
1053
|
|
|
820
|
-
|
|
821
|
-
if( node.title == 'Main' )
|
|
1054
|
+
if( exclude )
|
|
822
1055
|
{
|
|
823
|
-
|
|
1056
|
+
elements = elements.filter( v => v != exclude );
|
|
824
1057
|
}
|
|
825
1058
|
|
|
826
|
-
return
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
_getAllDOMNodes( includeGroups ) {
|
|
830
|
-
|
|
831
|
-
if( includeGroups )
|
|
832
|
-
return this._domNodes.childNodes;
|
|
833
|
-
|
|
834
|
-
return Array.from( this._domNodes.childNodes ).filter( v => v.classList.contains( 'lexgraphnode' ) );
|
|
1059
|
+
return elements;
|
|
835
1060
|
}
|
|
836
1061
|
|
|
837
1062
|
_onMoveNodes( target ) {
|
|
838
1063
|
|
|
839
1064
|
let dT = this._snapToGrid ? this._snappedDeltaMousePosition : this._deltaMousePosition;
|
|
840
|
-
dT.div( this.
|
|
1065
|
+
dT.div( this.currentGraph.scale, dT);
|
|
841
1066
|
|
|
842
1067
|
for( let nodeId of this.selectedNodes )
|
|
843
1068
|
{
|
|
@@ -867,17 +1092,20 @@ class GraphEditor {
|
|
|
867
1092
|
return;
|
|
868
1093
|
|
|
869
1094
|
let dT = this._snapToGrid ? this._snappedDeltaMousePosition : this._deltaMousePosition;
|
|
870
|
-
dT.div( this.
|
|
1095
|
+
dT.div( this.currentGraph.scale, dT);
|
|
871
1096
|
|
|
872
1097
|
this._translateNode( target, dT );
|
|
873
1098
|
|
|
874
1099
|
for( let nodeId of groupNodeIds )
|
|
875
1100
|
{
|
|
876
|
-
const
|
|
1101
|
+
const isGroup = nodeId.constructor !== String;
|
|
877
1102
|
|
|
878
|
-
this.
|
|
1103
|
+
const el = isGroup ? nodeId : this._getNodeDOMElement( nodeId );
|
|
879
1104
|
|
|
880
|
-
this.
|
|
1105
|
+
this._translateNode( el, dT, !isGroup );
|
|
1106
|
+
|
|
1107
|
+
if( !isGroup )
|
|
1108
|
+
this._updateNodeLinks( nodeId );
|
|
881
1109
|
}
|
|
882
1110
|
}
|
|
883
1111
|
|
|
@@ -889,7 +1117,7 @@ class GraphEditor {
|
|
|
889
1117
|
|
|
890
1118
|
const groupNodeIds = [ ];
|
|
891
1119
|
|
|
892
|
-
for( let dom of this._getAllDOMNodes() )
|
|
1120
|
+
for( let dom of this._getAllDOMNodes( true, target ) )
|
|
893
1121
|
{
|
|
894
1122
|
const x = parseFloat( dom.style.left );
|
|
895
1123
|
const y = parseFloat( dom.style.top );
|
|
@@ -898,7 +1126,7 @@ class GraphEditor {
|
|
|
898
1126
|
if( !group_bb.inside( node_bb ) )
|
|
899
1127
|
continue;
|
|
900
1128
|
|
|
901
|
-
groupNodeIds.push( dom.dataset[ 'id' ] );
|
|
1129
|
+
groupNodeIds.push( dom.dataset[ 'id' ] ?? dom );
|
|
902
1130
|
}
|
|
903
1131
|
|
|
904
1132
|
target.nodes = groupNodeIds;
|
|
@@ -966,6 +1194,16 @@ class GraphEditor {
|
|
|
966
1194
|
case 'select':
|
|
967
1195
|
panel.addDropdown( p.name, p.options, p.value, (v) => { p.value = v } );
|
|
968
1196
|
break;
|
|
1197
|
+
case 'array':
|
|
1198
|
+
panel.addArray( p.name, p.value, (v) => {
|
|
1199
|
+
p.value = v;
|
|
1200
|
+
if( node.type == "function/Input" )
|
|
1201
|
+
{
|
|
1202
|
+
node.setOutputs( v );
|
|
1203
|
+
this._updateNodeDOMIOs( dom, node );
|
|
1204
|
+
}
|
|
1205
|
+
}, { innerValues: p.options } );
|
|
1206
|
+
break;
|
|
969
1207
|
}
|
|
970
1208
|
}
|
|
971
1209
|
|
|
@@ -984,7 +1222,7 @@ class GraphEditor {
|
|
|
984
1222
|
this._togglePropertiesDialog( false );
|
|
985
1223
|
}
|
|
986
1224
|
|
|
987
|
-
_translateNode( dom, deltaTranslation ) {
|
|
1225
|
+
_translateNode( dom, deltaTranslation, updateBasePosition = true ) {
|
|
988
1226
|
|
|
989
1227
|
const translation = deltaTranslation.add( new LX.vec2( parseFloat( dom.style.left ), parseFloat( dom.style.top ) ) );
|
|
990
1228
|
|
|
@@ -998,27 +1236,44 @@ class GraphEditor {
|
|
|
998
1236
|
|
|
999
1237
|
dom.style.left = ( translation.x ) + "px";
|
|
1000
1238
|
dom.style.top = ( translation.y ) + "px";
|
|
1239
|
+
|
|
1240
|
+
// Update base node position..
|
|
1241
|
+
if( updateBasePosition && dom.dataset[ 'id' ] )
|
|
1242
|
+
{
|
|
1243
|
+
let baseNode = this.nodes[ dom.dataset[ 'id' ] ];
|
|
1244
|
+
baseNode.data.position = translation;
|
|
1245
|
+
}
|
|
1001
1246
|
}
|
|
1002
1247
|
|
|
1003
1248
|
_deleteNode( nodeId ) {
|
|
1004
1249
|
|
|
1005
|
-
const
|
|
1250
|
+
const nodeInfo = this.nodes[ nodeId ];
|
|
1251
|
+
const node = nodeInfo.data;
|
|
1252
|
+
const el = nodeInfo.dom;
|
|
1006
1253
|
|
|
1007
1254
|
console.assert( el );
|
|
1008
1255
|
|
|
1009
|
-
if(
|
|
1256
|
+
if( node.constructor.blockDelete )
|
|
1010
1257
|
{
|
|
1011
|
-
console.warn( `Can't delete
|
|
1258
|
+
console.warn( `Can't delete node!` );
|
|
1012
1259
|
return;
|
|
1013
1260
|
}
|
|
1014
1261
|
|
|
1015
|
-
deleteElement( el );
|
|
1262
|
+
LX.UTILS.deleteElement( el );
|
|
1263
|
+
|
|
1264
|
+
// Delete from the editor
|
|
1016
1265
|
|
|
1017
1266
|
delete this.nodes[ nodeId ];
|
|
1018
1267
|
|
|
1268
|
+
// Delete from the graph data
|
|
1269
|
+
|
|
1270
|
+
const idx = this.currentGraph.nodes.findIndex( v => v.id === nodeId );
|
|
1271
|
+
console.assert( idx >= 0 );
|
|
1272
|
+
this.currentGraph.nodes.splice( idx, 1 );
|
|
1273
|
+
|
|
1019
1274
|
// Delete connected links..
|
|
1020
1275
|
|
|
1021
|
-
for( let key in this.
|
|
1276
|
+
for( let key in this.currentGraph.links )
|
|
1022
1277
|
{
|
|
1023
1278
|
if( !key.includes( nodeId ) )
|
|
1024
1279
|
continue;
|
|
@@ -1028,13 +1283,13 @@ class GraphEditor {
|
|
|
1028
1283
|
|
|
1029
1284
|
// Remove the connection from the other before deleting..
|
|
1030
1285
|
|
|
1031
|
-
const numLinks = this.
|
|
1286
|
+
const numLinks = this.currentGraph.links[ key ].length;
|
|
1032
1287
|
|
|
1033
1288
|
for( var i = 0; i < numLinks; ++i )
|
|
1034
1289
|
{
|
|
1035
|
-
var link = this.
|
|
1290
|
+
var link = this.currentGraph.links[ key ][ i ];
|
|
1036
1291
|
|
|
1037
|
-
deleteElement( link.path.parentElement );
|
|
1292
|
+
LX.UTILS.deleteElement( link.path.parentElement );
|
|
1038
1293
|
|
|
1039
1294
|
const targetNodeId = targetIsInput ? link.inputNode : link.outputNode;
|
|
1040
1295
|
|
|
@@ -1057,19 +1312,60 @@ class GraphEditor {
|
|
|
1057
1312
|
{
|
|
1058
1313
|
var active = false;
|
|
1059
1314
|
for( var links of io.links )
|
|
1060
|
-
|
|
1061
|
-
|
|
1315
|
+
{
|
|
1316
|
+
if( !links )
|
|
1317
|
+
continue;
|
|
1318
|
+
for( var j of links ) {
|
|
1062
1319
|
active |= ( !!j );
|
|
1063
1320
|
}
|
|
1321
|
+
}
|
|
1064
1322
|
if( !active )
|
|
1065
1323
|
delete io.dataset[ 'active' ];
|
|
1066
1324
|
}
|
|
1067
1325
|
}
|
|
1068
1326
|
|
|
1069
|
-
delete this.
|
|
1327
|
+
delete this.currentGraph.links[ key ];
|
|
1070
1328
|
}
|
|
1071
1329
|
}
|
|
1072
1330
|
|
|
1331
|
+
_deleteGroup( groupId ) {
|
|
1332
|
+
|
|
1333
|
+
const dom = this.groups[ groupId ];
|
|
1334
|
+
LX.UTILS.deleteElement( dom );
|
|
1335
|
+
|
|
1336
|
+
// Delete from the editor
|
|
1337
|
+
|
|
1338
|
+
delete this.groups[ groupId ];
|
|
1339
|
+
|
|
1340
|
+
// Delete from the graph data
|
|
1341
|
+
|
|
1342
|
+
const idx = this.currentGraph.groups.findIndex( v => v.id === groupId );
|
|
1343
|
+
console.assert( idx >= 0 );
|
|
1344
|
+
this.currentGraph.groups.splice( idx, 1 );
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
_cloneNode( nodeId, graphId, position ) {
|
|
1348
|
+
|
|
1349
|
+
const graph = this.graphs[ graphId ?? this.currentGraph.id ];
|
|
1350
|
+
|
|
1351
|
+
const nodeData = graph.getNodeById( nodeId );
|
|
1352
|
+
|
|
1353
|
+
if( !nodeData )
|
|
1354
|
+
return;
|
|
1355
|
+
|
|
1356
|
+
const el = this._getNodeDOMElement( nodeId );
|
|
1357
|
+
const newNode = GraphEditor.addNode( nodeData.type );
|
|
1358
|
+
newNode.properties = LX.deepCopy( nodeData.properties );
|
|
1359
|
+
|
|
1360
|
+
const newDom = this._createNodeDOM( newNode );
|
|
1361
|
+
|
|
1362
|
+
this._translateNode( newDom, position ?? this._getNodePosition( el ) );
|
|
1363
|
+
|
|
1364
|
+
this._selectNode( newDom, true );
|
|
1365
|
+
|
|
1366
|
+
this.currentGraph.nodes.push( newNode );
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1073
1369
|
_cloneNodes() {
|
|
1074
1370
|
|
|
1075
1371
|
// Clone all selected nodes
|
|
@@ -1079,20 +1375,7 @@ class GraphEditor {
|
|
|
1079
1375
|
|
|
1080
1376
|
for( let nodeId of selectedIds )
|
|
1081
1377
|
{
|
|
1082
|
-
|
|
1083
|
-
if( !nodeInfo )
|
|
1084
|
-
return;
|
|
1085
|
-
|
|
1086
|
-
const el = this._getNodeDOMElement( nodeId );
|
|
1087
|
-
const data = nodeInfo.data;
|
|
1088
|
-
const newNode = GraphEditor.addNode( data.type );
|
|
1089
|
-
const newDom = this._createNodeDOM( newNode );
|
|
1090
|
-
|
|
1091
|
-
this._translateNode( newDom, this._getNodePosition( el ) );
|
|
1092
|
-
|
|
1093
|
-
this._selectNode( newDom, true );
|
|
1094
|
-
|
|
1095
|
-
this.graph.nodes.push( newNode );
|
|
1378
|
+
this._cloneNode( nodeId );
|
|
1096
1379
|
}
|
|
1097
1380
|
}
|
|
1098
1381
|
|
|
@@ -1110,7 +1393,7 @@ class GraphEditor {
|
|
|
1110
1393
|
_getLinks( nodeSrcId, nodeDstId ) {
|
|
1111
1394
|
|
|
1112
1395
|
const str = nodeSrcId + '@' + nodeDstId;
|
|
1113
|
-
return this.
|
|
1396
|
+
return this.currentGraph.links[ str ];
|
|
1114
1397
|
}
|
|
1115
1398
|
|
|
1116
1399
|
_deleteLinks( nodeId, io ) {
|
|
@@ -1132,7 +1415,7 @@ class GraphEditor {
|
|
|
1132
1415
|
var links = this._getLinks( targetId, nodeId );
|
|
1133
1416
|
|
|
1134
1417
|
var linkIdx = links.findIndex( i => ( i.inputIdx == srcIndex && i.outputIdx == targetIndex ) );
|
|
1135
|
-
deleteElement( links[ linkIdx ].path.parentElement );
|
|
1418
|
+
LX.UTILS.deleteElement( links[ linkIdx ].path.parentElement );
|
|
1136
1419
|
links.splice( linkIdx, 1 );
|
|
1137
1420
|
|
|
1138
1421
|
// Input has no longer any connected link
|
|
@@ -1182,7 +1465,7 @@ class GraphEditor {
|
|
|
1182
1465
|
var links = this._getLinks( nodeId, targetId );
|
|
1183
1466
|
|
|
1184
1467
|
var linkIdx = links.findIndex( i => ( i.inputIdx == targetIndex && i.outputIdx == srcIndex ) );
|
|
1185
|
-
deleteElement( links[ linkIdx ].path.parentElement );
|
|
1468
|
+
LX.UTILS.deleteElement( links[ linkIdx ].path.parentElement );
|
|
1186
1469
|
links.splice( linkIdx, 1 );
|
|
1187
1470
|
|
|
1188
1471
|
// Remove a connection from the output connections
|
|
@@ -1274,7 +1557,7 @@ class GraphEditor {
|
|
|
1274
1557
|
|
|
1275
1558
|
if( this._snapToGrid )
|
|
1276
1559
|
{
|
|
1277
|
-
const snapSize = this._patternSize.x * this._snapValue * this.
|
|
1560
|
+
const snapSize = this._patternSize.x * this._snapValue * this.currentGraph.scale;
|
|
1278
1561
|
snapPosition.x = Math.floor( snapPosition.x / snapSize ) * snapSize;
|
|
1279
1562
|
snapPosition.y = Math.floor( snapPosition.y / snapSize ) * snapSize;
|
|
1280
1563
|
this._snappedDeltaMousePosition = snapPosition.sub( this._lastSnappedMousePosition );
|
|
@@ -1291,7 +1574,8 @@ class GraphEditor {
|
|
|
1291
1574
|
|
|
1292
1575
|
else if( e.type == 'mouseup' )
|
|
1293
1576
|
{
|
|
1294
|
-
if( ( LX.getTime() - this.lastMouseDown ) <
|
|
1577
|
+
if( ( LX.getTime() - this.lastMouseDown ) < 200 ) {
|
|
1578
|
+
|
|
1295
1579
|
this._processClick( e );
|
|
1296
1580
|
}
|
|
1297
1581
|
|
|
@@ -1323,7 +1607,7 @@ class GraphEditor {
|
|
|
1323
1607
|
|
|
1324
1608
|
e.preventDefault();
|
|
1325
1609
|
|
|
1326
|
-
if( ( LX.getTime() - this.lastMouseDown ) <
|
|
1610
|
+
if( ( LX.getTime() - this.lastMouseDown ) < 300 ) {
|
|
1327
1611
|
this._processContextMenu( e );
|
|
1328
1612
|
}
|
|
1329
1613
|
}
|
|
@@ -1374,8 +1658,14 @@ class GraphEditor {
|
|
|
1374
1658
|
// It the event reaches this, the link isn't valid..
|
|
1375
1659
|
if( this._generatingLink )
|
|
1376
1660
|
{
|
|
1377
|
-
|
|
1661
|
+
const linkInfo = Object.assign( { }, this._generatingLink );
|
|
1662
|
+
|
|
1663
|
+
// Delete old link
|
|
1664
|
+
LX.UTILS.deleteElement( this._generatingLink.path ? this._generatingLink.path.parentElement : null );
|
|
1378
1665
|
delete this._generatingLink;
|
|
1666
|
+
|
|
1667
|
+
// Open contextmenu to auto-connect something..
|
|
1668
|
+
this._processContextMenu( e, linkInfo );
|
|
1379
1669
|
}
|
|
1380
1670
|
|
|
1381
1671
|
else if( this._boxSelecting )
|
|
@@ -1385,7 +1675,7 @@ class GraphEditor {
|
|
|
1385
1675
|
|
|
1386
1676
|
this._selectNodesInBox( this._boxSelecting, this._mousePosition, e.altKey );
|
|
1387
1677
|
|
|
1388
|
-
deleteElement( this._currentBoxSelectionSVG );
|
|
1678
|
+
LX.UTILS.deleteElement( this._currentBoxSelectionSVG );
|
|
1389
1679
|
|
|
1390
1680
|
delete this._currentBoxSelectionSVG;
|
|
1391
1681
|
delete this._boxSelecting;
|
|
@@ -1399,7 +1689,7 @@ class GraphEditor {
|
|
|
1399
1689
|
|
|
1400
1690
|
if( rightPressed )
|
|
1401
1691
|
{
|
|
1402
|
-
this.
|
|
1692
|
+
this.currentGraph.translation.add( this._deltaMousePosition.div( this.currentGraph.scale ), this.currentGraph.translation );
|
|
1403
1693
|
|
|
1404
1694
|
this._updatePattern();
|
|
1405
1695
|
|
|
@@ -1435,10 +1725,10 @@ class GraphEditor {
|
|
|
1435
1725
|
|
|
1436
1726
|
const delta = e.deltaY;
|
|
1437
1727
|
|
|
1438
|
-
if( delta > 0.0 ) this.
|
|
1439
|
-
else this.
|
|
1728
|
+
if( delta > 0.0 ) this.currentGraph.scale *= 0.9;
|
|
1729
|
+
else this.currentGraph.scale *= ( 1.0 / 0.9 );
|
|
1440
1730
|
|
|
1441
|
-
this.
|
|
1731
|
+
this.currentGraph.scale = LX.UTILS.clamp( this.currentGraph.scale, GraphEditor.MIN_SCALE, GraphEditor.MAX_SCALE );
|
|
1442
1732
|
|
|
1443
1733
|
// Compute zoom center in pattern space using new scale
|
|
1444
1734
|
// and get delta..
|
|
@@ -1447,17 +1737,46 @@ class GraphEditor {
|
|
|
1447
1737
|
|
|
1448
1738
|
const deltaCenter = newCenter.sub( center );
|
|
1449
1739
|
|
|
1450
|
-
this.
|
|
1740
|
+
this.currentGraph.translation.add( deltaCenter, this.currentGraph.translation );
|
|
1451
1741
|
|
|
1452
|
-
this._updatePattern(
|
|
1742
|
+
this._updatePattern();
|
|
1453
1743
|
}
|
|
1454
1744
|
|
|
1455
|
-
_processContextMenu( e ) {
|
|
1745
|
+
_processContextMenu( e, autoConnect ) {
|
|
1456
1746
|
|
|
1457
|
-
LX.addContextMenu(
|
|
1458
|
-
|
|
1747
|
+
LX.addContextMenu( null, e, m => {
|
|
1748
|
+
|
|
1749
|
+
var eventPosition = null;
|
|
1750
|
+
|
|
1751
|
+
if( e )
|
|
1752
|
+
{
|
|
1753
|
+
const rect = this.root.getBoundingClientRect();
|
|
1754
|
+
|
|
1755
|
+
const localPosition = new LX.vec2( e.clientX - rect.x, e.clientY - rect.y );
|
|
1756
|
+
|
|
1757
|
+
eventPosition = this._getPatternPosition( localPosition );
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
if( this._clipboardData )
|
|
1761
|
+
{
|
|
1762
|
+
m.add( "Paste", () => {
|
|
1763
|
+
|
|
1764
|
+
const nodeId = this._clipboardData.id;
|
|
1765
|
+
const graphId = this._clipboardData.gid;
|
|
1766
|
+
|
|
1767
|
+
this._cloneNode( nodeId, graphId, eventPosition );
|
|
1768
|
+
|
|
1769
|
+
} );
|
|
1770
|
+
m.add( "" );
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1459
1773
|
for( let type in GraphEditor.NODE_TYPES )
|
|
1460
1774
|
{
|
|
1775
|
+
const baseClass = GraphEditor.NODE_TYPES[ type ];
|
|
1776
|
+
|
|
1777
|
+
if( baseClass.blockAdd )
|
|
1778
|
+
continue;
|
|
1779
|
+
|
|
1461
1780
|
m.add( type, () => {
|
|
1462
1781
|
|
|
1463
1782
|
const newNode = GraphEditor.addNode( type );
|
|
@@ -1469,18 +1788,38 @@ class GraphEditor {
|
|
|
1469
1788
|
dom.mustSnap = true;
|
|
1470
1789
|
}
|
|
1471
1790
|
|
|
1472
|
-
if(
|
|
1791
|
+
if( eventPosition )
|
|
1473
1792
|
{
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
let position = new LX.vec2( e.clientX - rect.x, e.clientY - rect.y );
|
|
1477
|
-
|
|
1478
|
-
position = this._getPatternPosition( position );
|
|
1479
|
-
|
|
1480
|
-
this._translateNode( dom, position );
|
|
1793
|
+
this._translateNode( dom, eventPosition );
|
|
1481
1794
|
}
|
|
1482
1795
|
|
|
1483
|
-
this.
|
|
1796
|
+
this.currentGraph.nodes.push( newNode );
|
|
1797
|
+
|
|
1798
|
+
if( autoConnect && newNode.inputs.length )
|
|
1799
|
+
{
|
|
1800
|
+
const srcId = autoConnect.domEl.dataset[ 'id' ];
|
|
1801
|
+
const srcType = autoConnect.io.childNodes[ autoConnect.index ].dataset[ 'type' ];
|
|
1802
|
+
const srcIsInput = autoConnect.ioType == GraphEditor.NODE_IO_INPUT;
|
|
1803
|
+
|
|
1804
|
+
const newLink = {
|
|
1805
|
+
inputNode: srcIsInput ? srcId : newNode.id,
|
|
1806
|
+
inputIdx: srcIsInput ? autoConnect.index : 0,
|
|
1807
|
+
inputType: srcIsInput ? srcType : newNode.inputs[ 0 ].type,
|
|
1808
|
+
outputNode: srcIsInput ? newNode.id : srcId,
|
|
1809
|
+
outputIdx: srcIsInput ? 0 : autoConnect.index,
|
|
1810
|
+
outputType: srcIsInput ? newNode.inputs[ 0 ].type : srcType,
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// Store link
|
|
1814
|
+
|
|
1815
|
+
const pathId = newLink.outputNode + '@' + newLink.inputNode;
|
|
1816
|
+
|
|
1817
|
+
if( !this.currentGraph.links[ pathId ] ) this.currentGraph.links[ pathId ] = [];
|
|
1818
|
+
|
|
1819
|
+
this.currentGraph.links[ pathId ].push( newLink );
|
|
1820
|
+
|
|
1821
|
+
this._createLink( newLink );
|
|
1822
|
+
}
|
|
1484
1823
|
|
|
1485
1824
|
} );
|
|
1486
1825
|
}
|
|
@@ -1494,101 +1833,40 @@ class GraphEditor {
|
|
|
1494
1833
|
start() {
|
|
1495
1834
|
|
|
1496
1835
|
this.mustStop = false;
|
|
1836
|
+
this.state = GraphEditor.RUNNING;
|
|
1497
1837
|
|
|
1498
1838
|
this.propagateEventToAllNodes( 'onStart' );
|
|
1499
1839
|
|
|
1500
1840
|
requestAnimationFrame( this._frame.bind(this) );
|
|
1501
1841
|
}
|
|
1502
1842
|
|
|
1503
|
-
/**
|
|
1504
|
-
* @method stop
|
|
1505
|
-
*/
|
|
1506
|
-
|
|
1507
|
-
stop() {
|
|
1508
|
-
|
|
1509
|
-
this.mustStop = true;
|
|
1510
|
-
this.state = GraphEditor.STOPPED;
|
|
1511
|
-
|
|
1512
|
-
this.propagateEventToAllNodes( 'onStop' );
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
/**
|
|
1516
|
-
* @method _frame
|
|
1517
|
-
*/
|
|
1518
|
-
|
|
1519
|
-
_frame() {
|
|
1520
|
-
|
|
1521
|
-
if( this.mustStop )
|
|
1522
|
-
{
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
requestAnimationFrame( this._frame.bind(this) );
|
|
1527
|
-
|
|
1528
|
-
this._runStep();
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
/**
|
|
1532
|
-
* @method _runStep
|
|
1533
|
-
*/
|
|
1534
|
-
|
|
1535
|
-
_runStep() {
|
|
1536
|
-
|
|
1537
|
-
const main = this.main;
|
|
1538
|
-
|
|
1539
|
-
if( !main )
|
|
1540
|
-
return;
|
|
1541
|
-
|
|
1542
|
-
const visitedNodes = { };
|
|
1543
|
-
|
|
1544
|
-
this._executionNodes = [ ];
|
|
1545
|
-
|
|
1546
|
-
// Reser variables each step?
|
|
1547
|
-
this.variables = { };
|
|
1548
|
-
|
|
1549
|
-
const mainId = this.main.dataset[ 'id' ];
|
|
1550
|
-
|
|
1551
|
-
const addNode = ( id ) => {
|
|
1552
|
-
|
|
1553
|
-
if( visitedNodes[ id ] )
|
|
1554
|
-
return;
|
|
1555
|
-
|
|
1556
|
-
visitedNodes[ id ] = true;
|
|
1557
|
-
|
|
1558
|
-
for( let linkId in this.graph.links )
|
|
1559
|
-
{
|
|
1560
|
-
const idx = linkId.indexOf( '@' + id );
|
|
1561
|
-
|
|
1562
|
-
if( idx < 0 )
|
|
1563
|
-
continue;
|
|
1564
|
-
|
|
1565
|
-
const preNodeId = linkId.substring( 0, idx );
|
|
1843
|
+
/**
|
|
1844
|
+
* @method stop
|
|
1845
|
+
*/
|
|
1566
1846
|
|
|
1567
|
-
|
|
1847
|
+
stop() {
|
|
1568
1848
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
};
|
|
1849
|
+
this.mustStop = true;
|
|
1850
|
+
this.state = GraphEditor.STOPPED;
|
|
1572
1851
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1852
|
+
this.propagateEventToAllNodes( 'onStop' );
|
|
1853
|
+
}
|
|
1575
1854
|
|
|
1576
|
-
|
|
1855
|
+
/**
|
|
1856
|
+
* @method _frame
|
|
1857
|
+
*/
|
|
1577
1858
|
|
|
1578
|
-
|
|
1859
|
+
_frame() {
|
|
1579
1860
|
|
|
1580
|
-
|
|
1861
|
+
if( this.mustStop )
|
|
1581
1862
|
{
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
if( node.data.onBeforeStep )
|
|
1585
|
-
node.data.onBeforeStep();
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1586
1865
|
|
|
1587
|
-
|
|
1866
|
+
requestAnimationFrame( this._frame.bind(this) );
|
|
1588
1867
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
}
|
|
1868
|
+
// Only run here main graph!
|
|
1869
|
+
this.currentGraph._runStep( this.main );
|
|
1592
1870
|
}
|
|
1593
1871
|
|
|
1594
1872
|
_generatePattern() {
|
|
@@ -1597,8 +1875,8 @@ class GraphEditor {
|
|
|
1597
1875
|
{
|
|
1598
1876
|
var pattern = document.createElementNS( 'http://www.w3.org/2000/svg', 'pattern' );
|
|
1599
1877
|
pattern.setAttribute( 'id', 'pattern-0' );
|
|
1600
|
-
pattern.setAttribute( 'x',
|
|
1601
|
-
pattern.setAttribute( 'y',
|
|
1878
|
+
pattern.setAttribute( 'x', 0.0 );
|
|
1879
|
+
pattern.setAttribute( 'y', 0.0 );
|
|
1602
1880
|
pattern.setAttribute( 'width', this._patternSize.x )
|
|
1603
1881
|
pattern.setAttribute( 'height', this._patternSize.y );
|
|
1604
1882
|
pattern.setAttribute( 'patternUnits', 'userSpaceOnUse' );
|
|
@@ -1638,9 +1916,9 @@ class GraphEditor {
|
|
|
1638
1916
|
if( !this._background )
|
|
1639
1917
|
return;
|
|
1640
1918
|
|
|
1641
|
-
const patternSize = this._patternSize.mul( this.
|
|
1642
|
-
const circlePatternSize = this._circlePatternSize * this.
|
|
1643
|
-
const patternPosition = this.
|
|
1919
|
+
const patternSize = this._patternSize.mul( this.currentGraph.scale );
|
|
1920
|
+
const circlePatternSize = this._circlePatternSize * this.currentGraph.scale;
|
|
1921
|
+
const patternPosition = this.currentGraph.translation.mul( this.currentGraph.scale );
|
|
1644
1922
|
|
|
1645
1923
|
let pattern = this._background.querySelector( 'pattern' );
|
|
1646
1924
|
pattern.setAttribute( 'x', patternPosition.x );
|
|
@@ -1658,24 +1936,34 @@ class GraphEditor {
|
|
|
1658
1936
|
const w = this._domNodes.offsetWidth * 0.5;
|
|
1659
1937
|
const h = this._domNodes.offsetHeight * 0.5;
|
|
1660
1938
|
|
|
1661
|
-
const dw = w - w * this.
|
|
1662
|
-
const dh = h - h * this.
|
|
1939
|
+
const dw = w - w * this.currentGraph.scale;
|
|
1940
|
+
const dh = h - h * this.currentGraph.scale;
|
|
1663
1941
|
|
|
1664
1942
|
this._domNodes.style.transform = `
|
|
1665
1943
|
translate(` + ( patternPosition.x - dw ) + `px, ` + ( patternPosition.y - dh ) + `px)
|
|
1666
|
-
scale(` + this.
|
|
1944
|
+
scale(` + this.currentGraph.scale + `)
|
|
1667
1945
|
`;
|
|
1668
1946
|
this._domLinks.style.transform = this._domNodes.style.transform;
|
|
1947
|
+
|
|
1948
|
+
// Hide nodes outside the viewport
|
|
1949
|
+
|
|
1950
|
+
const nodesOutsideViewport = this._getNonVisibleNodes();
|
|
1951
|
+
|
|
1952
|
+
for( let node of nodesOutsideViewport )
|
|
1953
|
+
{
|
|
1954
|
+
let dom = this._getNodeDOMElement( node.id );
|
|
1955
|
+
dom.classList.toggle( 'hiddenOpacity', true );
|
|
1956
|
+
}
|
|
1669
1957
|
}
|
|
1670
1958
|
|
|
1671
1959
|
_getPatternPosition( renderPosition ) {
|
|
1672
1960
|
|
|
1673
|
-
return renderPosition.div( this.
|
|
1961
|
+
return renderPosition.div( this.currentGraph.scale ).sub( this.currentGraph.translation );
|
|
1674
1962
|
}
|
|
1675
1963
|
|
|
1676
1964
|
_getRenderPosition( patternPosition ) {
|
|
1677
1965
|
|
|
1678
|
-
return patternPosition.add( this.
|
|
1966
|
+
return patternPosition.add( this.currentGraph.translation ).mul( this.currentGraph.scale );
|
|
1679
1967
|
}
|
|
1680
1968
|
|
|
1681
1969
|
_onLink( e ) {
|
|
@@ -1738,11 +2026,6 @@ class GraphEditor {
|
|
|
1738
2026
|
this._deleteLinks( src_nodeId, linkData.io );
|
|
1739
2027
|
}
|
|
1740
2028
|
|
|
1741
|
-
// Mark as active
|
|
1742
|
-
|
|
1743
|
-
linkData.io.dataset[ 'active' ] = true;
|
|
1744
|
-
e.target.parentElement.dataset[ 'active' ] = true;
|
|
1745
|
-
|
|
1746
2029
|
// Store the end io..
|
|
1747
2030
|
|
|
1748
2031
|
var srcDom = linkData.io;
|
|
@@ -1763,9 +2046,9 @@ class GraphEditor {
|
|
|
1763
2046
|
|
|
1764
2047
|
const pathId = ( srcIsInput ? dst_nodeId : src_nodeId ) + '@' + ( srcIsInput ? src_nodeId : dst_nodeId );
|
|
1765
2048
|
|
|
1766
|
-
if( !this.
|
|
2049
|
+
if( !this.currentGraph.links[ pathId ] ) this.currentGraph.links[ pathId ] = [];
|
|
1767
2050
|
|
|
1768
|
-
this.
|
|
2051
|
+
this.currentGraph.links[ pathId ].push( {
|
|
1769
2052
|
path: path,
|
|
1770
2053
|
inputNode: srcIsInput ? src_nodeId : dst_nodeId,
|
|
1771
2054
|
inputIdx: srcIsInput ? src_ioIndex : dst_ioIndex,
|
|
@@ -1777,6 +2060,11 @@ class GraphEditor {
|
|
|
1777
2060
|
|
|
1778
2061
|
path.dataset[ 'id' ] = pathId;
|
|
1779
2062
|
|
|
2063
|
+
// Mark as active links...
|
|
2064
|
+
|
|
2065
|
+
linkData.io.dataset[ 'active' ] = true;
|
|
2066
|
+
e.target.parentElement.dataset[ 'active' ] = true;
|
|
2067
|
+
|
|
1780
2068
|
// Successful link..
|
|
1781
2069
|
return true;
|
|
1782
2070
|
}
|
|
@@ -1814,6 +2102,7 @@ class GraphEditor {
|
|
|
1814
2102
|
|
|
1815
2103
|
let startPos = new LX.vec2( startRect.x - offsetX, startRect.y - offsetY );
|
|
1816
2104
|
let endPos = null;
|
|
2105
|
+
let endioEl = null;
|
|
1817
2106
|
|
|
1818
2107
|
if( e )
|
|
1819
2108
|
{
|
|
@@ -1822,7 +2111,6 @@ class GraphEditor {
|
|
|
1822
2111
|
// Add node position, since I can't get the correct position directly from the event..
|
|
1823
2112
|
if( e.target.hasClass( [ 'lexgraphnode', 'lexgraphgroup' ] ) )
|
|
1824
2113
|
{
|
|
1825
|
-
console.log( this._getNodePosition( e.target ) );
|
|
1826
2114
|
endPos.add( this._getNodePosition( e.target ), endPos );
|
|
1827
2115
|
endPos.add( new LX.vec2( 3, 3 ), endPos );
|
|
1828
2116
|
}
|
|
@@ -1840,7 +2128,8 @@ class GraphEditor {
|
|
|
1840
2128
|
}
|
|
1841
2129
|
else
|
|
1842
2130
|
{
|
|
1843
|
-
|
|
2131
|
+
endioEl = endIO.querySelector( '.io__type' );
|
|
2132
|
+
const ioRect = endioEl.getBoundingClientRect();
|
|
1844
2133
|
endPos = new LX.vec2( ioRect.x - offsetX, ioRect.y - offsetY );
|
|
1845
2134
|
}
|
|
1846
2135
|
|
|
@@ -1851,7 +2140,11 @@ class GraphEditor {
|
|
|
1851
2140
|
startPos = tmp;
|
|
1852
2141
|
}
|
|
1853
2142
|
|
|
1854
|
-
|
|
2143
|
+
let color = getComputedStyle( ioEl ).backgroundColor;
|
|
2144
|
+
|
|
2145
|
+
if( type == GraphEditor.NODE_IO_OUTPUT && endioEl )
|
|
2146
|
+
color = getComputedStyle( endioEl ).backgroundColor;
|
|
2147
|
+
|
|
1855
2148
|
this._createLinkPath( path, startPos, endPos, color, !!e );
|
|
1856
2149
|
|
|
1857
2150
|
return path;
|
|
@@ -1907,11 +2200,14 @@ class GraphEditor {
|
|
|
1907
2200
|
io1.links = [ ];
|
|
1908
2201
|
io1.links[ link.outputIdx ] = io1.links[ link.outputIdx ] ?? [ ];
|
|
1909
2202
|
io1.links[ link.outputIdx ].push( link.outputNode );
|
|
2203
|
+
|
|
2204
|
+
io0.dataset[ 'active' ] = true;
|
|
2205
|
+
io1.dataset[ 'active' ] = true;
|
|
1910
2206
|
}
|
|
1911
2207
|
|
|
1912
2208
|
_createLinkPath( path, startPos, endPos, color, exactEnd ) {
|
|
1913
2209
|
|
|
1914
|
-
const dist = 6 * this.
|
|
2210
|
+
const dist = 6 * this.currentGraph.scale;
|
|
1915
2211
|
startPos.add( new LX.vec2( dist, dist ), startPos );
|
|
1916
2212
|
|
|
1917
2213
|
if( !exactEnd )
|
|
@@ -2062,16 +2358,44 @@ class GraphEditor {
|
|
|
2062
2358
|
"/>`;
|
|
2063
2359
|
}
|
|
2064
2360
|
|
|
2065
|
-
|
|
2066
|
-
|
|
2361
|
+
_getNonVisibleNodes() {
|
|
2362
|
+
|
|
2363
|
+
const nonVisibleNodes = [ ];
|
|
2067
2364
|
|
|
2068
|
-
if( !this.
|
|
2365
|
+
if( !this.currentGraph )
|
|
2069
2366
|
{
|
|
2070
2367
|
console.warn( "No graph set" );
|
|
2071
2368
|
return [];
|
|
2072
2369
|
}
|
|
2073
2370
|
|
|
2074
|
-
|
|
2371
|
+
const graph_bb = new BoundingBox( new LX.vec2( 0, 0 ), new LX.vec2( this.root.offsetWidth, this.root.offsetHeight ) );
|
|
2372
|
+
|
|
2373
|
+
for( let node of this.currentGraph.nodes )
|
|
2374
|
+
{
|
|
2375
|
+
let pos = this._getRenderPosition( node.position );
|
|
2376
|
+
|
|
2377
|
+
let dom = this._getNodeDOMElement( node.id );
|
|
2378
|
+
|
|
2379
|
+
if( !dom )
|
|
2380
|
+
continue;
|
|
2381
|
+
|
|
2382
|
+
const node_bb = new BoundingBox( pos, node.size.mul( this.currentGraph.scale ) );
|
|
2383
|
+
|
|
2384
|
+
if( graph_bb.inside( node_bb, false ) )
|
|
2385
|
+
{
|
|
2386
|
+
// Show if node in viewport..
|
|
2387
|
+
dom.classList.toggle( 'hiddenOpacity', false );
|
|
2388
|
+
|
|
2389
|
+
// And hide content if scale is very small..
|
|
2390
|
+
dom.childNodes[ 1 ].classList.toggle( 'hiddenOpacity', this.currentGraph.scale < 0.5 );
|
|
2391
|
+
|
|
2392
|
+
continue;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
nonVisibleNodes.push( node );
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
return nonVisibleNodes;
|
|
2075
2399
|
}
|
|
2076
2400
|
|
|
2077
2401
|
_selectNodesInBox( lt, rb, remove ) {
|
|
@@ -2145,11 +2469,8 @@ class GraphEditor {
|
|
|
2145
2469
|
|
|
2146
2470
|
for( let nodeId of nodeIds )
|
|
2147
2471
|
{
|
|
2148
|
-
const
|
|
2149
|
-
|
|
2150
|
-
const x = parseFloat( dom.style.left );
|
|
2151
|
-
const y = parseFloat( dom.style.top );
|
|
2152
|
-
const node_bb = new BoundingBox( new LX.vec2( x, y ), new LX.vec2( dom.offsetWidth - 6, dom.offsetHeight - 6 ) );
|
|
2472
|
+
const node = this.nodes[ nodeId ].data;
|
|
2473
|
+
const node_bb = new BoundingBox( node.position, node.size );
|
|
2153
2474
|
|
|
2154
2475
|
if( group_bb )
|
|
2155
2476
|
{
|
|
@@ -2180,11 +2501,13 @@ class GraphEditor {
|
|
|
2180
2501
|
* @returns JSON data from the serialized graph
|
|
2181
2502
|
*/
|
|
2182
2503
|
|
|
2183
|
-
_createGroup() {
|
|
2504
|
+
_createGroup( bb ) {
|
|
2184
2505
|
|
|
2185
|
-
const group_bb = this._getBoundingFromNodes( this.selectedNodes );
|
|
2506
|
+
const group_bb = bb ?? this._getBoundingFromNodes( this.selectedNodes );
|
|
2507
|
+
const group_id = bb ? bb.id : "group-" + LX.UTILS.uidGenerator();
|
|
2186
2508
|
|
|
2187
2509
|
let groupDOM = document.createElement( 'div' );
|
|
2510
|
+
groupDOM.id = group_id;
|
|
2188
2511
|
groupDOM.classList.add( 'lexgraphgroup' );
|
|
2189
2512
|
groupDOM.style.left = group_bb.origin.x + "px";
|
|
2190
2513
|
groupDOM.style.top = group_bb.origin.y + "px";
|
|
@@ -2196,6 +2519,8 @@ class GraphEditor {
|
|
|
2196
2519
|
|
|
2197
2520
|
groupResizer.addEventListener( 'mousedown', inner_mousedown );
|
|
2198
2521
|
|
|
2522
|
+
this.groups[ group_id ] = groupDOM;
|
|
2523
|
+
|
|
2199
2524
|
var that = this;
|
|
2200
2525
|
var lastPos = [0,0];
|
|
2201
2526
|
|
|
@@ -2215,7 +2540,7 @@ class GraphEditor {
|
|
|
2215
2540
|
function inner_mousemove( e )
|
|
2216
2541
|
{
|
|
2217
2542
|
let dt = new LX.vec2( lastPos[0] - e.x, lastPos[1] - e.y );
|
|
2218
|
-
dt.div( that.
|
|
2543
|
+
dt.div( that.currentGraph.scale, dt);
|
|
2219
2544
|
|
|
2220
2545
|
groupDOM.style.width = ( parseFloat( groupDOM.style.width ) - dt.x ) + "px";
|
|
2221
2546
|
groupDOM.style.height = ( parseFloat( groupDOM.style.height ) - dt.y ) + "px";
|
|
@@ -2273,6 +2598,19 @@ class GraphEditor {
|
|
|
2273
2598
|
groupTitle.focus();
|
|
2274
2599
|
} );
|
|
2275
2600
|
|
|
2601
|
+
groupDOM.addEventListener( 'contextmenu', e => {
|
|
2602
|
+
|
|
2603
|
+
e.preventDefault();
|
|
2604
|
+
e.stopPropagation();
|
|
2605
|
+
e.stopImmediatePropagation();
|
|
2606
|
+
|
|
2607
|
+
LX.addContextMenu(null, e, m => {
|
|
2608
|
+
m.add( "Delete", () => {
|
|
2609
|
+
this._deleteGroup( group_id );
|
|
2610
|
+
} );
|
|
2611
|
+
});
|
|
2612
|
+
} );
|
|
2613
|
+
|
|
2276
2614
|
groupDOM.appendChild( groupResizer );
|
|
2277
2615
|
groupDOM.appendChild( groupTitle );
|
|
2278
2616
|
|
|
@@ -2286,6 +2624,8 @@ class GraphEditor {
|
|
|
2286
2624
|
} );
|
|
2287
2625
|
|
|
2288
2626
|
GraphEditor.LAST_GROUP_ID++;
|
|
2627
|
+
|
|
2628
|
+
return groupDOM;
|
|
2289
2629
|
}
|
|
2290
2630
|
|
|
2291
2631
|
_addUndoStep( deleteRedo = true ) {
|
|
@@ -2370,78 +2710,104 @@ class GraphEditor {
|
|
|
2370
2710
|
}
|
|
2371
2711
|
}
|
|
2372
2712
|
|
|
2373
|
-
_toggleSideBar() {
|
|
2713
|
+
_toggleSideBar( force ) {
|
|
2374
2714
|
|
|
2375
|
-
this.
|
|
2715
|
+
this._sidebarActive = force ?? !this._sidebarActive;
|
|
2716
|
+
this._sidebarDom.classList.toggle( 'hidden', !this._sidebarActive );
|
|
2717
|
+
this._graphContainer.style.width = this._sidebarActive ? "calc( 100% - 64px )" : "100%";
|
|
2376
2718
|
}
|
|
2377
|
-
}
|
|
2378
2719
|
|
|
2379
|
-
|
|
2720
|
+
_onSidebarCreate( e ) {
|
|
2721
|
+
|
|
2722
|
+
LX.addContextMenu(null, e, m => {
|
|
2723
|
+
m.add( "Graph", () => this.addGraph() );
|
|
2724
|
+
m.add( "Function", () => this.addGraphFunction() );
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2380
2727
|
|
|
2728
|
+
_showRenameGraphDialog() {
|
|
2381
2729
|
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2730
|
+
const dialog = new LX.Dialog( this.currentGraph.constructor.name, p => {
|
|
2731
|
+
p.addText( "Name", this.currentGraph.name, v => {
|
|
2732
|
+
this._updateGraphName( v );
|
|
2733
|
+
dialog.close();
|
|
2734
|
+
} );
|
|
2735
|
+
}, { modal: true, size: [ "350px", null ] } );
|
|
2736
|
+
}
|
|
2385
2737
|
|
|
2386
|
-
|
|
2738
|
+
_updateGraphName( name ) {
|
|
2387
2739
|
|
|
2388
|
-
|
|
2389
|
-
* @param {*} options
|
|
2390
|
-
*
|
|
2391
|
-
*/
|
|
2740
|
+
const newNameKey = name.replace( /\s/g, '' ).replaceAll( '.', '' );
|
|
2392
2741
|
|
|
2393
|
-
|
|
2742
|
+
// Change graph name button
|
|
2743
|
+
const nameDom = LX.root.querySelector( '.graph-title button' );
|
|
2744
|
+
console.assert( nameDom );
|
|
2745
|
+
nameDom.innerText = name;
|
|
2394
2746
|
|
|
2395
|
-
|
|
2747
|
+
// Change name in sidebar
|
|
2748
|
+
const graphNameKey = this.currentGraph.name.replace( /\s/g, '' ).replaceAll( '.', '' );
|
|
2749
|
+
const sidebarItem = this._sidebar.items.find( v => v.name === graphNameKey );
|
|
2750
|
+
if( sidebarItem )
|
|
2751
|
+
{
|
|
2752
|
+
sidebarItem.name = newNameKey;
|
|
2753
|
+
sidebarItem.domEl.id = newNameKey;
|
|
2754
|
+
sidebarItem.domEl.querySelector(".lexsidebarentrydesc").innerText = name;
|
|
2755
|
+
}
|
|
2396
2756
|
|
|
2397
|
-
//
|
|
2757
|
+
// Change registered nodes function
|
|
2758
|
+
const oldType = 'function/' + this.currentGraph.name;
|
|
2759
|
+
const nodeClass = GraphEditor.NODE_TYPES[ oldType ];
|
|
2398
2760
|
|
|
2399
|
-
|
|
2761
|
+
if( nodeClass )
|
|
2762
|
+
{
|
|
2763
|
+
delete GraphEditor.NODE_TYPES[ oldType ];
|
|
2764
|
+
|
|
2765
|
+
nodeClass.title = name;
|
|
2766
|
+
|
|
2767
|
+
GraphEditor.registerCustomNode( "function/" + name, nodeClass );
|
|
2768
|
+
}
|
|
2400
2769
|
|
|
2401
|
-
this.
|
|
2770
|
+
this.currentGraph.name = name;
|
|
2771
|
+
}
|
|
2402
2772
|
|
|
2403
|
-
|
|
2404
|
-
// mainNode.position = new LX.vec2( 650, 400 );
|
|
2773
|
+
_addGlobalActions() {
|
|
2405
2774
|
|
|
2406
|
-
|
|
2407
|
-
|
|
2775
|
+
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2408
2778
|
|
|
2409
|
-
|
|
2410
|
-
// floatNode.position = new LX.vec2( 200, 200 );
|
|
2779
|
+
LX.GraphEditor = GraphEditor;
|
|
2411
2780
|
|
|
2412
|
-
// const stringNode = GraphEditor.addNode( 'inputs/String' );
|
|
2413
|
-
// stringNode.position = new LX.vec2( 400, 50 );
|
|
2414
2781
|
|
|
2415
|
-
|
|
2416
|
-
|
|
2782
|
+
/**
|
|
2783
|
+
* @class Graph
|
|
2784
|
+
*/
|
|
2417
2785
|
|
|
2418
|
-
|
|
2419
|
-
// // multNode.position = new LX.vec2( 200, 400 );
|
|
2786
|
+
class Graph {
|
|
2420
2787
|
|
|
2421
|
-
|
|
2422
|
-
|
|
2788
|
+
/**
|
|
2789
|
+
* @param {*} options
|
|
2790
|
+
*
|
|
2791
|
+
*/
|
|
2423
2792
|
|
|
2424
|
-
|
|
2425
|
-
// orNode.position = new LX.vec2( 435, 435 );
|
|
2793
|
+
constructor( name, options = {} ) {
|
|
2426
2794
|
|
|
2427
|
-
|
|
2428
|
-
|
|
2795
|
+
this.name = name ?? "Unnamed Graph";
|
|
2796
|
+
this.type = 'Graph';
|
|
2429
2797
|
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
// keydownNode,
|
|
2437
|
-
// orNode,
|
|
2438
|
-
// equalNode,
|
|
2439
|
-
// // multNode
|
|
2440
|
-
// ];
|
|
2798
|
+
this.nodes = [ ];
|
|
2799
|
+
this.groups = [ ];
|
|
2800
|
+
this.links = { };
|
|
2801
|
+
|
|
2802
|
+
this.scale = 1.0;
|
|
2803
|
+
this.translation = new LX.vec2( 0, 0 );
|
|
2441
2804
|
}
|
|
2442
2805
|
|
|
2443
2806
|
configure( o ) {
|
|
2444
2807
|
|
|
2808
|
+
this.id = o.id;
|
|
2809
|
+
this.name = o.name;
|
|
2810
|
+
|
|
2445
2811
|
this.nodes.length = 0;
|
|
2446
2812
|
|
|
2447
2813
|
for( let node of o.nodes )
|
|
@@ -2453,13 +2819,12 @@ class Graph {
|
|
|
2453
2819
|
newNode.color = node.color;
|
|
2454
2820
|
newNode.position = new LX.vec2( node.position.x, node.position.y );
|
|
2455
2821
|
newNode.type = node.type;
|
|
2456
|
-
|
|
2457
|
-
// newNode.outputs = node.outputs;
|
|
2458
|
-
// newNode.properties = node.properties;
|
|
2822
|
+
newNode.properties = node.properties;
|
|
2459
2823
|
|
|
2460
2824
|
this.nodes.push( newNode );
|
|
2461
2825
|
}
|
|
2462
2826
|
|
|
2827
|
+
this.groups = o.groups;
|
|
2463
2828
|
this.links = o.links;
|
|
2464
2829
|
|
|
2465
2830
|
// editor options?
|
|
@@ -2468,20 +2833,111 @@ class Graph {
|
|
|
2468
2833
|
}
|
|
2469
2834
|
|
|
2470
2835
|
/**
|
|
2471
|
-
* @method
|
|
2836
|
+
* @method getNodeById
|
|
2837
|
+
*/
|
|
2838
|
+
|
|
2839
|
+
getNodeById( id ) {
|
|
2840
|
+
|
|
2841
|
+
for( let node of this.nodes )
|
|
2842
|
+
{
|
|
2843
|
+
if( node.id == id ) return node;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
/**
|
|
2848
|
+
* @method _runStep
|
|
2849
|
+
*/
|
|
2850
|
+
|
|
2851
|
+
_runStep( mainId ) {
|
|
2852
|
+
|
|
2853
|
+
if( !mainId )
|
|
2854
|
+
return;
|
|
2855
|
+
|
|
2856
|
+
const nodes = this.nodes.reduce( ( ac, a ) => ( {...ac, [ a.id ] : a } ), {} );
|
|
2857
|
+
|
|
2858
|
+
// Not main graph..
|
|
2859
|
+
if( !nodes[ mainId ] )
|
|
2860
|
+
return;
|
|
2861
|
+
|
|
2862
|
+
const visitedNodes = { };
|
|
2863
|
+
|
|
2864
|
+
this._executionNodes = [ ];
|
|
2865
|
+
|
|
2866
|
+
// Reser variables each step?
|
|
2867
|
+
this.variables = { };
|
|
2868
|
+
|
|
2869
|
+
const addNode = ( id ) => {
|
|
2870
|
+
|
|
2871
|
+
if( visitedNodes[ id ] )
|
|
2872
|
+
return;
|
|
2873
|
+
|
|
2874
|
+
visitedNodes[ id ] = true;
|
|
2875
|
+
|
|
2876
|
+
for( let linkId in this.links )
|
|
2877
|
+
{
|
|
2878
|
+
const idx = linkId.indexOf( '@' + id );
|
|
2879
|
+
|
|
2880
|
+
if( idx < 0 )
|
|
2881
|
+
continue;
|
|
2882
|
+
|
|
2883
|
+
const preNodeId = linkId.substring( 0, idx );
|
|
2884
|
+
|
|
2885
|
+
this._executionNodes.push( preNodeId );
|
|
2886
|
+
|
|
2887
|
+
addNode( preNodeId );
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
|
|
2891
|
+
// TODO: Search "no output" nodes and add to the executable list (same as main)..
|
|
2892
|
+
// ...
|
|
2893
|
+
|
|
2894
|
+
this._executionNodes.push( mainId );
|
|
2895
|
+
|
|
2896
|
+
addNode( mainId );
|
|
2897
|
+
|
|
2898
|
+
for( var i = this._executionNodes.length - 1; i >= 0; --i )
|
|
2899
|
+
{
|
|
2900
|
+
const node = nodes[ this._executionNodes[ i ] ];
|
|
2901
|
+
|
|
2902
|
+
if( node.onBeforeStep )
|
|
2903
|
+
node.onBeforeStep();
|
|
2904
|
+
|
|
2905
|
+
node.execute();
|
|
2906
|
+
|
|
2907
|
+
if( node.onBeforeStep )
|
|
2908
|
+
node.onAfterStep();
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
/**
|
|
2913
|
+
* @method serialize
|
|
2472
2914
|
* @param {Boolean} prettify
|
|
2473
2915
|
* @returns JSON data from the serialized graph
|
|
2474
2916
|
*/
|
|
2475
2917
|
|
|
2476
|
-
|
|
2918
|
+
serialize( prettify = true ) {
|
|
2477
2919
|
|
|
2478
2920
|
var o = { };
|
|
2479
|
-
|
|
2480
|
-
o.
|
|
2921
|
+
|
|
2922
|
+
o.id = this.id;
|
|
2923
|
+
o.name = this.name;
|
|
2924
|
+
o.type = this.type;
|
|
2925
|
+
|
|
2926
|
+
o.nodes = [ ];
|
|
2927
|
+
o.groups = [ ];
|
|
2928
|
+
o.functions = [ ];
|
|
2929
|
+
o.links = { };
|
|
2481
2930
|
|
|
2482
2931
|
for( let node of this.nodes )
|
|
2483
2932
|
{
|
|
2484
2933
|
o.nodes.push( node.serialize() );
|
|
2934
|
+
|
|
2935
|
+
const fnOrigin = this.editor.graphs[ node.gid ];
|
|
2936
|
+
|
|
2937
|
+
if( fnOrigin )
|
|
2938
|
+
{
|
|
2939
|
+
o.functions.push( JSON.parse( fnOrigin.serialize() ) );
|
|
2940
|
+
}
|
|
2485
2941
|
}
|
|
2486
2942
|
|
|
2487
2943
|
for( let linkId in this.links )
|
|
@@ -2491,6 +2947,15 @@ class Graph {
|
|
|
2491
2947
|
o.links[ linkId ] = ioLinks;
|
|
2492
2948
|
}
|
|
2493
2949
|
|
|
2950
|
+
for( let group of this.groups )
|
|
2951
|
+
{
|
|
2952
|
+
const groupDom = this.editor.groups[ group.id ];
|
|
2953
|
+
const group_bb = this.editor._getBoundingFromGroup( groupDom );
|
|
2954
|
+
group_bb.id = group.id;
|
|
2955
|
+
group_bb.name = group.name
|
|
2956
|
+
o.groups.push( group_bb );
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2494
2959
|
// editor options?
|
|
2495
2960
|
|
|
2496
2961
|
// zoom/translation ??
|
|
@@ -2505,10 +2970,19 @@ class Graph {
|
|
|
2505
2970
|
console.error( `Can't export GraphNode [${ this.title }] of type [${ this.type }].` );
|
|
2506
2971
|
}
|
|
2507
2972
|
|
|
2508
|
-
LX.downloadFile( this.name + ".json", o );
|
|
2509
|
-
|
|
2510
2973
|
return o;
|
|
2511
2974
|
}
|
|
2975
|
+
|
|
2976
|
+
/**
|
|
2977
|
+
* @method export
|
|
2978
|
+
*/
|
|
2979
|
+
|
|
2980
|
+
export() {
|
|
2981
|
+
|
|
2982
|
+
const o = this.serialize();
|
|
2983
|
+
|
|
2984
|
+
LX.downloadFile( this.name + ".json", o );
|
|
2985
|
+
}
|
|
2512
2986
|
}
|
|
2513
2987
|
|
|
2514
2988
|
LX.Graph = Graph;
|
|
@@ -2562,16 +3036,18 @@ class GraphNode {
|
|
|
2562
3036
|
if( !this.inputs || !this.inputs.length || !this.inputs[ index ] )
|
|
2563
3037
|
return;
|
|
2564
3038
|
|
|
3039
|
+
const graph = this.editor.graphs[ this.graphID ];
|
|
3040
|
+
|
|
2565
3041
|
// Get data from link
|
|
2566
3042
|
|
|
2567
|
-
for( let linkId in
|
|
3043
|
+
for( let linkId in graph.links )
|
|
2568
3044
|
{
|
|
2569
3045
|
const idx = linkId.indexOf( '@' + this.id );
|
|
2570
3046
|
|
|
2571
3047
|
if( idx < 0 )
|
|
2572
3048
|
continue;
|
|
2573
3049
|
|
|
2574
|
-
const nodeLinks =
|
|
3050
|
+
const nodeLinks = graph.links[ linkId ];
|
|
2575
3051
|
|
|
2576
3052
|
for ( var link of nodeLinks )
|
|
2577
3053
|
{
|
|
@@ -2589,23 +3065,27 @@ class GraphNode {
|
|
|
2589
3065
|
if( !this.outputs || !this.outputs.length || !this.outputs[ index ] )
|
|
2590
3066
|
return;
|
|
2591
3067
|
|
|
3068
|
+
const graph = this.editor.graphs[ this.graphID ];
|
|
3069
|
+
|
|
2592
3070
|
// Set data in link
|
|
2593
3071
|
|
|
2594
|
-
for( let linkId in
|
|
3072
|
+
for( let linkId in graph.links )
|
|
2595
3073
|
{
|
|
2596
3074
|
const idx = linkId.indexOf( this.id + '@' );
|
|
2597
3075
|
|
|
2598
3076
|
if( idx < 0 )
|
|
2599
3077
|
continue;
|
|
2600
3078
|
|
|
2601
|
-
const nodeLinks =
|
|
3079
|
+
const nodeLinks = graph.links[ linkId ];
|
|
2602
3080
|
|
|
2603
3081
|
for ( var link of nodeLinks )
|
|
2604
3082
|
{
|
|
2605
3083
|
if( link.outputIdx != index )
|
|
2606
3084
|
continue;
|
|
2607
3085
|
|
|
2608
|
-
|
|
3086
|
+
let innerData = data;
|
|
3087
|
+
|
|
3088
|
+
if( innerData != undefined && link.inputType != link.outputType && link.inputType != "any" && link.outputType != "any" )
|
|
2609
3089
|
{
|
|
2610
3090
|
// In case of supported casting, use function to cast..
|
|
2611
3091
|
|
|
@@ -2613,19 +3093,14 @@ class GraphNode {
|
|
|
2613
3093
|
|
|
2614
3094
|
// Use function if it's possible to cast!
|
|
2615
3095
|
|
|
2616
|
-
|
|
3096
|
+
innerData = fn ? fn( LX.deepCopy( innerData ) ) : null;
|
|
2617
3097
|
}
|
|
2618
3098
|
|
|
2619
|
-
link.data =
|
|
3099
|
+
link.data = innerData;
|
|
2620
3100
|
}
|
|
2621
3101
|
}
|
|
2622
3102
|
}
|
|
2623
3103
|
|
|
2624
|
-
getOutput( index ) {
|
|
2625
|
-
|
|
2626
|
-
return this.outputs[ index ].value;
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
3104
|
serialize() {
|
|
2630
3105
|
|
|
2631
3106
|
var o = { };
|
|
@@ -2645,6 +3120,45 @@ class GraphNode {
|
|
|
2645
3120
|
|
|
2646
3121
|
LX.GraphNode = GraphNode;
|
|
2647
3122
|
|
|
3123
|
+
/**
|
|
3124
|
+
* @class GraphFunction
|
|
3125
|
+
*/
|
|
3126
|
+
|
|
3127
|
+
class GraphFunction extends Graph {
|
|
3128
|
+
|
|
3129
|
+
constructor( name, options = { } ) {
|
|
3130
|
+
|
|
3131
|
+
super();
|
|
3132
|
+
|
|
3133
|
+
this.name = name ?? ( "GraphFunction" + GraphEditor.LAST_FUNCTION_ID );
|
|
3134
|
+
this.type = 'GraphFunction';
|
|
3135
|
+
|
|
3136
|
+
GraphEditor.LAST_FUNCTION_ID++
|
|
3137
|
+
|
|
3138
|
+
const nodeInput = GraphEditor.addNode( "function/Input" );
|
|
3139
|
+
nodeInput.position = new LX.vec2( 150, 250 );
|
|
3140
|
+
|
|
3141
|
+
const nodeOutput = GraphEditor.addNode( "function/Output" );
|
|
3142
|
+
nodeOutput.position = new LX.vec2( 650, 350 );
|
|
3143
|
+
|
|
3144
|
+
this.nodes.push( nodeInput, nodeOutput );
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
getOutputData( inputValue ) {
|
|
3148
|
+
|
|
3149
|
+
const inputNode = this.nodes[ 0 ];
|
|
3150
|
+
inputNode.setOutput( 0, inputValue );
|
|
3151
|
+
|
|
3152
|
+
const outputNode = this.nodes[ 1 ];
|
|
3153
|
+
|
|
3154
|
+
this._runStep( outputNode.id );
|
|
3155
|
+
|
|
3156
|
+
return outputNode.getInput( 0 );
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
LX.GraphFunction = GraphFunction;
|
|
3161
|
+
|
|
2648
3162
|
/*
|
|
2649
3163
|
************ PREDEFINED NODES ************
|
|
2650
3164
|
|
|
@@ -2656,6 +3170,54 @@ LX.GraphNode = GraphNode;
|
|
|
2656
3170
|
- onExecute: Callback for node execution
|
|
2657
3171
|
*/
|
|
2658
3172
|
|
|
3173
|
+
/*
|
|
3174
|
+
Function nodes
|
|
3175
|
+
*/
|
|
3176
|
+
|
|
3177
|
+
class NodeFuncInput extends GraphNode
|
|
3178
|
+
{
|
|
3179
|
+
onCreate() {
|
|
3180
|
+
this.addOutput( null, "float" );
|
|
3181
|
+
this.addProperty( "Outputs", "array", [ "float" ], [ 'float', 'int', 'bool', 'vec2', 'vec3', 'vec4', 'mat44' ] );
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
onExecute() {
|
|
3185
|
+
// var a = this.getInput( 0 ) ?? this.properties[ 0 ].value;
|
|
3186
|
+
// var b = this.getInput( 1 ) ?? this.properties[ 1 ].value;
|
|
3187
|
+
// this.setOutput( 0, a + b );
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
setOutputs( v ) {
|
|
3191
|
+
|
|
3192
|
+
this.outputs.length = 0;
|
|
3193
|
+
|
|
3194
|
+
for( var i of v )
|
|
3195
|
+
{
|
|
3196
|
+
this.outputs.push( { name: null, type: i } );
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
NodeFuncInput.blockDelete = true;
|
|
3202
|
+
NodeFuncInput.blockAdd = true;
|
|
3203
|
+
GraphEditor.registerCustomNode( "function/Input", NodeFuncInput );
|
|
3204
|
+
|
|
3205
|
+
class NodeFuncOutput extends GraphNode
|
|
3206
|
+
{
|
|
3207
|
+
onCreate() {
|
|
3208
|
+
this.addInput( null, "any" );
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
onExecute() {
|
|
3212
|
+
// var a = this.getInput( 0 ) ?? this.properties[ 0 ].value;
|
|
3213
|
+
// var b = this.getInput( 1 ) ?? this.properties[ 1 ].value;
|
|
3214
|
+
// this.setOutput( 0, a + b );
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
NodeFuncOutput.blockDelete = true;
|
|
3219
|
+
NodeFuncOutput.blockAdd = true;
|
|
3220
|
+
GraphEditor.registerCustomNode( "function/Output", NodeFuncOutput );
|
|
2659
3221
|
|
|
2660
3222
|
/*
|
|
2661
3223
|
Math nodes
|
|
@@ -3047,7 +3609,7 @@ class NodeSetVariable extends GraphNode
|
|
|
3047
3609
|
var varValue = this.getInput( 1 );
|
|
3048
3610
|
if( varValue == undefined )
|
|
3049
3611
|
return;
|
|
3050
|
-
this.
|
|
3612
|
+
this.editor.setVariable( varName, varValue );
|
|
3051
3613
|
this.setOutput( 0, varValue );
|
|
3052
3614
|
}
|
|
3053
3615
|
}
|
|
@@ -3112,6 +3674,7 @@ class NodeMain extends GraphNode
|
|
|
3112
3674
|
};
|
|
3113
3675
|
}
|
|
3114
3676
|
|
|
3677
|
+
NodeMain.blockDelete = true;
|
|
3115
3678
|
GraphEditor.registerCustomNode( "system/Main", NodeMain );
|
|
3116
3679
|
|
|
3117
3680
|
export { GraphEditor, Graph, GraphNode };
|