lexgui 0.1.26 → 0.1.28

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.
@@ -39,22 +39,29 @@ function doAsync( fn, ms ) {
39
39
  }
40
40
 
41
41
  /**
42
- * @class GraphCanvas
42
+ * @class GraphEditor
43
43
  */
44
44
 
45
- class GraphCanvas {
45
+ class GraphEditor {
46
46
 
47
47
  static __instances = [];
48
48
 
49
- static BACK_IMAGE_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAplJREFUeF7tm8FqwzAQRG2DDynEvaT//5fNKXFRCqZQkjLSyEzxC/Qmb5eZN9pYasfL5WMd+MQoMBZDxnEc5nl+2tS6rkP5mabpZeNlzf1+f6wrNZ99jrau6HC73f7UpazbDDmfFwsl1+vncDq9WWqVIkerhyGN6LiBwRAM0RRwE5hej4RofPxa7TYYQzBEU8BNYHo9EqLxsc+WVX7Lsrw3tvb9eDqB6f2RkEYM3QZjCIZoCrgJTK9HQjQ+9hnq5WSWw8U6Z9yJIyF1PmxPYUiYgBiCIZoCbmKOVu8xQ4rkr65wNUtY3aIAQ71FvQ5HRRiCIZoCh5whvBhqkPxc7QaGLaveiy7XDRiCIZoC7i0hvR4J0fjgtDedaHd/25s6d+p1UeliCF9768zo8UcdzJB6L/ja26hdFwHZshpdcQvorseWFWYwhmCIpoB7S0ivR0I0PnhTTyfa3R936o0JcT/OltWoaJeEcHRS7wqG1GvHm3qjdv9CQBLS6LJbQHc9hnqYwRiCIZoC7i0hvR4J0fjY5+ik/Bbu1OuccSeOhNT5sD2FIWECYgiGaAq4iTlaPWaIxts+37I47a13xZ1gElLvRZfDTwzBEE0B95aQXo87dY2P7qvZsholdicOQzBEU8BNYHo9EqLxwYthOtHu/kgICdEUcBOYXo+EaHwwQ9KJdvfH/6mTEE0BN4Hp9ZghGh/MkHSi3f2REBKiKeAmML0eCdH4YIakE+3uj4SQEE0BN4Hp9bhT1/jovpotq1Fid+IwBEM0BdwEptcjIRofvIekE+3uj4SQEE0BN4Hp9UiIxgczJJ1od38khIRoCrgJTK9HQjQ+mCHpRLv7IyEkRFPATWB6PRKi8cEMSSfa3R8JISGaAm4C0+t9AfQNJzgm7/oIAAAAAElFTkSuQmCC";
49
+ // Canvas
50
+
51
+ static MIN_SCALE = 0.25;
52
+ static MAX_SCALE = 4.0;
53
+
54
+ static EVENT_MOUSEMOVE = 0;
55
+ static EVENT_MOUSEWHEEL = 1;
50
56
 
51
57
  // Node Drawing
58
+
52
59
  static NODE_TITLE_HEIGHT = 24;
53
60
  static NODE_ROW_HEIGHT = 16;
54
61
 
55
- static NODE_SHAPE_RADIUS = 4;
56
- static NODE_TITLE_RADIUS = [GraphCanvas.NODE_SHAPE_RADIUS, GraphCanvas.NODE_SHAPE_RADIUS, 0, 0];
57
- static NODE_BODY_RADIUS = [GraphCanvas.NODE_SHAPE_RADIUS, GraphCanvas.NODE_SHAPE_RADIUS, GraphCanvas.NODE_SHAPE_RADIUS, GraphCanvas.NODE_SHAPE_RADIUS];
62
+ static NODE_SHAPE_RADIUS = 12;
63
+ static NODE_TITLE_RADIUS = [ GraphEditor.NODE_SHAPE_RADIUS, GraphEditor.NODE_SHAPE_RADIUS, 0, 0 ];
64
+ static NODE_BODY_RADIUS = [ GraphEditor.NODE_SHAPE_RADIUS, GraphEditor.NODE_SHAPE_RADIUS, GraphEditor.NODE_SHAPE_RADIUS, GraphEditor.NODE_SHAPE_RADIUS ];
58
65
 
59
66
  static DEFAULT_NODE_TITLE_COLOR = "#4a59b0";
60
67
  static DEFAULT_NODE_BODY_COLOR = "#111";
@@ -66,68 +73,233 @@ class GraphCanvas {
66
73
 
67
74
  constructor( area, options = {} ) {
68
75
 
69
- GraphCanvas.__instances.push( this );
76
+ GraphEditor.__instances.push( this );
70
77
 
71
78
  this.base_area = area;
72
- this.area = new LX.Area( { className: "lexGraph" } );
79
+ this.area = new LX.Area( { className: "lexgraph" } );
73
80
 
74
- area.root.classList.add('grapharea');
81
+ area.root.classList.add( 'grapharea' );
75
82
 
76
83
  this.root = this.area.root;
84
+ this.root.tabIndex = -1;
77
85
  area.attach( this.root );
78
86
 
79
87
  // Bind resize
88
+
80
89
  area.onresize = ( bb ) => {
81
- this.dom.width = bb.width;
82
- this.dom.height = bb.height;
83
- this._backDirty = true;
84
- this._frontDirty = true;
90
+
85
91
  };
86
92
 
87
- this.root.addEventListener( 'keydown', this._processKey.bind(this), true);
88
- this.root.addEventListener( 'mousedown', this._processMouse.bind(this) );
89
- this.root.addEventListener( 'mouseup', this._processMouse.bind(this) );
90
- this.root.addEventListener( 'mousemove', this._processMouse.bind(this) );
91
- this.root.addEventListener( 'click', this._processMouse.bind(this) );
92
- this.root.addEventListener( 'contextmenu', this._processMouse.bind(this) );
93
- this.root.addEventListener( 'focus', this._processFocus.bind(this, true) );
94
- this.root.addEventListener( 'focusout', this._processFocus.bind(this, false) );
95
-
96
- // State
97
-
98
- this.drawAllFrames = false;
99
- this.isFocused = false;
100
- this._backDirty = true;
101
- this._frontDirty = true;
102
-
103
- // Canvas
104
-
105
- this.dom = document.createElement('canvas');
106
- this.dom.width = area.size[0];
107
- this.dom.height = area.size[1];
108
- this.dom.tabIndex = -1;
109
- this.area.attach( this.dom );
110
-
111
- this.frames = 0;
112
- this.fps = 0;
113
- this._lastDrawTime = 0;
114
- this._drawTime = 0;
115
-
116
- this.font = new FontFace("Ubuntu", "url(../data/Ubuntu-Bold.ttf)");
117
- this.font.load().then(
118
- ( font ) => {
119
- document.fonts.add( font );
120
- requestAnimationFrame( this.frame.bind(this) );
121
- },
122
- (err) => {
123
- console.error(err);
124
- },
125
- );
93
+ this.root.addEventListener( 'keydown', this._processKey.bind( this ), true );
94
+ this.root.addEventListener( 'mousedown', this._processMouse.bind( this ) );
95
+ this.root.addEventListener( 'mouseup', this._processMouse.bind( this ) );
96
+ this.root.addEventListener( 'mousemove', this._processMouse.bind( this ) );
97
+ this.root.addEventListener( 'mousewheel', this._processMouse.bind(this) );
98
+ this.root.addEventListener( 'click', this._processMouse.bind( this ) );
99
+ this.root.addEventListener( 'contextmenu', this._processMouse.bind( this ) );
100
+ this.root.addEventListener( 'focus', this._processFocus.bind( this, true) );
101
+ this.root.addEventListener( 'focusout', this._processFocus.bind( this, false ) );
102
+
103
+ this._lastMousePosition = new LX.vec2( 0, 0 );
104
+
105
+ // Back pattern
106
+
107
+ const f = 15.0;
108
+ this._patternPosition = new LX.vec2( 0, 0 );
109
+ this._patternSize = new LX.vec2( f );
110
+ this._circlePatternSize = f * 0.04;
111
+ this._circlePatternColor = '#71717a9c';
112
+
113
+ this._generatePattern();
114
+
115
+ // Renderer state
116
+
117
+ this._scale = 1.0;
118
+
119
+ // Node container
120
+
121
+ this._domNodes = document.createElement( 'div' );
122
+ this._domNodes.classList.add( 'lexgraphnodes' );
123
+ this.root.appendChild( this._domNodes );
124
+
125
+ // requestAnimationFrame( this._frame.bind(this) );
126
126
  }
127
127
 
128
128
  static getInstances()
129
129
  {
130
- return GraphCanvas.__instances;
130
+ return GraphEditor.__instances;
131
+ }
132
+
133
+ /**
134
+ * @method setGraph
135
+ * @param {Graph} graph:
136
+ */
137
+
138
+ setGraph( graph ) {
139
+
140
+ this.graph = graph;
141
+
142
+ if( !this.graph.nodes )
143
+ {
144
+ console.warn( 'Graph does not contain any node!' );
145
+ return ;
146
+ }
147
+
148
+ for( let node of this.graph.nodes )
149
+ {
150
+ this._createNode( node );
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @method clear
156
+ */
157
+
158
+ clear() {
159
+
160
+ this._domNodes.innerHTML = "";
161
+ }
162
+
163
+ /**
164
+ * @method unSelectAll
165
+ */
166
+
167
+ unSelectAll( forceOrder = true ) {
168
+
169
+ this._domNodes.querySelectorAll( '.lexgraphnode' ).forEach( v => {
170
+ v.classList.remove( 'selected' );
171
+ // if( forceOrder )
172
+ // v.style.zIndex = "0";
173
+ } );
174
+ }
175
+
176
+ _createNode( node ) {
177
+
178
+ var nodeContainer = document.createElement( 'div' );
179
+ nodeContainer.classList.add( 'lexgraphnode' );
180
+
181
+ nodeContainer.style.left = node.position.x + "px";
182
+ nodeContainer.style.top = node.position.y + "px";
183
+
184
+ if( node.color )
185
+ {
186
+ nodeContainer.style.backgroundColor = node.color;
187
+ }
188
+
189
+ // nodeContainer.addEventListener( 'click', e => {
190
+
191
+ // // TODO: check multiple selection
192
+ // this.unSelectAll();
193
+
194
+ // nodeContainer.classList.toggle( 'selected' );
195
+ // nodeContainer.style.zIndex = "1";
196
+
197
+ // // Refresh node render order
198
+ // this._domNodes.appendChild( nodeContainer );
199
+ // } );
200
+
201
+ // Title header
202
+ var nodeHeader = document.createElement( 'div' );
203
+ nodeHeader.classList.add( 'lexgraphnodeheader' );
204
+ nodeHeader.innerText = node.name;
205
+ nodeContainer.appendChild( nodeHeader );
206
+
207
+ // Inputs and outputs
208
+ var nodeIO = document.createElement( 'div' );
209
+ nodeIO.classList.add( 'lexgraphnodeios' );
210
+ nodeContainer.appendChild( nodeIO );
211
+
212
+ const hasInputs = node.inputs && node.inputs.length;
213
+ const hasOutputs = node.outputs && node.outputs.length;
214
+
215
+ // Inputs
216
+ {
217
+ var nodeInputs = null;
218
+
219
+ if( node.inputs && node.inputs.length )
220
+ {
221
+ nodeInputs = document.createElement( 'div' );
222
+ nodeInputs.classList.add( 'lexgraphnodeinputs' );
223
+ nodeInputs.style.width = hasOutputs ? "50%" : "100%";
224
+ nodeIO.appendChild( nodeInputs );
225
+ }
226
+
227
+ for( let i of node.inputs )
228
+ {
229
+ if( !i.type )
230
+ {
231
+ console.warn( `Missing type for node [${ node.name }], skipping...` );
232
+ continue;
233
+ }
234
+
235
+ var input = document.createElement( 'div' );
236
+ input.classList.add( 'lexgraphnodeio' );
237
+
238
+ var type = document.createElement( 'span' );
239
+ type.className = 'io__type input ' + i.type;
240
+ type.innerHTML = "<span>" + i.type[ 0 ].toUpperCase() + "</span>";
241
+ input.appendChild( type );
242
+
243
+ if( i.name )
244
+ {
245
+ var name = document.createElement( 'span' );
246
+ name.classList.add( 'io__name' );
247
+ name.innerText = i.name;
248
+ input.appendChild( name );
249
+ }
250
+
251
+ nodeInputs.appendChild( input );
252
+ }
253
+ }
254
+
255
+ // Outputs
256
+ {
257
+ var nodeOutputs = null;
258
+
259
+ if( node.outputs && node.outputs.length )
260
+ {
261
+ nodeOutputs = document.createElement( 'div' );
262
+ nodeOutputs.classList.add( 'lexgraphnodeoutputs' );
263
+ nodeOutputs.style.width = hasInputs ? "50%" : "100%";
264
+ nodeIO.appendChild( nodeOutputs );
265
+ }
266
+
267
+ for( let o of node.outputs )
268
+ {
269
+ if( !o.type )
270
+ {
271
+ console.warn( `Missing type for node [${ node.name }], skipping...` );
272
+ }
273
+
274
+ var output = document.createElement( 'div' );
275
+ output.className = 'lexgraphnodeio output';
276
+
277
+ if( o.name )
278
+ {
279
+ var name = document.createElement( 'span' );
280
+ name.classList.add( 'io__name' );
281
+ name.innerText = o.name;
282
+ output.appendChild( name );
283
+ }
284
+
285
+ var type = document.createElement( 'span' );
286
+ type.className = 'io__type output ' + o.type;
287
+ type.innerHTML = "<span>" + o.type[ 0 ].toUpperCase() + "</span>";
288
+ output.appendChild( type );
289
+
290
+ nodeOutputs.appendChild( output );
291
+ }
292
+ }
293
+
294
+ // Drag listener
295
+
296
+ LX.makeDraggable( nodeContainer, { onMove: e => {
297
+ const dP = this._deltaMousePosition.div( this._scale );
298
+ nodeContainer.style.left = (parseFloat(nodeContainer.style.left) + dP.x) + 'px';
299
+ nodeContainer.style.top = (parseFloat(nodeContainer.style.top) + dP.y) + 'px';
300
+ } } );
301
+
302
+ this._domNodes.appendChild( nodeContainer );
131
303
  }
132
304
 
133
305
  _processFocus( active ) {
@@ -135,13 +307,18 @@ class GraphCanvas {
135
307
  this.isFocused = active;
136
308
  }
137
309
 
138
- _processKey(e) {
310
+ _processKey( e ) {
139
311
 
140
312
  var key = e.key ?? e.detail.key;
141
313
  console.log( key );
142
314
  }
143
315
 
144
- _processMouse(e) {
316
+ _processMouse( e ) {
317
+
318
+ const rect = this.root.getBoundingClientRect();
319
+
320
+ this._mousePosition = new LX.vec2( e.clientX - rect.x , e.clientY - rect.y );
321
+ this._deltaMousePosition = this._mousePosition.sub( this._lastMousePosition );
145
322
 
146
323
  if( e.type == 'mousedown' )
147
324
  {
@@ -150,14 +327,14 @@ class GraphCanvas {
150
327
 
151
328
  else if( e.type == 'mouseup' )
152
329
  {
153
- if( (LX.getTime() - this.lastMouseDown) < 300 ) {
154
- this._processClick(e);
330
+ if( (LX.getTime() - this.lastMouseDown) < 120 ) {
331
+ this._processClick( e );
155
332
  }
156
333
  }
157
334
 
158
335
  else if( e.type == 'mousemove' )
159
336
  {
160
-
337
+ this._processMouseMove( e );
161
338
  }
162
339
 
163
340
  else if ( e.type == 'click' ) // trip
@@ -171,57 +348,94 @@ class GraphCanvas {
171
348
  }
172
349
  }
173
350
 
351
+ else if ( e.type == 'mousewheel' ) {
352
+ e.preventDefault();
353
+ this._processWheel( e );
354
+ }
355
+
174
356
  else if ( e.type == 'contextmenu' ) {
175
- e.preventDefault()
176
- this._processContextMenu( e );
357
+
358
+ e.preventDefault();
359
+
360
+ if( (LX.getTime() - this.lastMouseDown) < 120 ) {
361
+ this._processContextMenu( e );
362
+ }
177
363
  }
364
+
365
+ this._lastMousePosition = this._mousePosition;
178
366
  }
179
367
 
180
368
  _processClick( e ) {
181
369
 
182
-
183
- }
370
+ if( e.target.classList.contains( 'lexgraphnodes' ) )
371
+ {
372
+ this._processBackgroundClick( e );
373
+ return;
374
+ }
184
375
 
185
- _processContextMenu( e ) {
186
-
187
- LX.addContextMenu( "Test", e, m => {
188
- m.add( "option 1", () => { } );
189
- m.add( "option 2", () => { } );
190
- });
376
+ console.log(e.target);
191
377
  }
192
378
 
193
- _forceDraw() {
379
+ _processBackgroundClick( e ) {
194
380
 
195
- this._backDirty = true;
196
- this._frontDirty = true;
381
+ this.unSelectAll( false );
197
382
  }
198
383
 
199
- /**
200
- * @method setGraph
201
- * @param {Graph} graph:
202
- */
384
+ _processMouseMove( e ) {
203
385
 
204
- setGraph( graph ) {
386
+ const rightPressed = ( e.which == 3 );
387
+
388
+ if( rightPressed )
389
+ {
390
+ this._patternPosition.add( this._deltaMousePosition.div( this._scale ), this._patternPosition );
205
391
 
206
- this.graph = graph;
392
+ this._updatePattern();
393
+ }
207
394
  }
208
395
 
209
- /**
210
- * @method clear
211
- */
396
+ _processWheel( e ) {
397
+
398
+ // Compute zoom center in pattern space using current scale
399
+
400
+ const rect = this.root.getBoundingClientRect();
401
+ const zoomCenter = this._mousePosition ?? new LX.vec2( rect.width * 0.5, rect.height * 0.5 );
402
+
403
+ const center = this._getPatternPosition( zoomCenter );
404
+
405
+ const delta = e.deltaY;
406
+
407
+ if( delta > 0.0 ) this._scale *= 0.9;
408
+ else this._scale *= ( 1.0 / 0.9 );
409
+
410
+ this._scale = LX.UTILS.clamp( this._scale, GraphEditor.MIN_SCALE, GraphEditor.MAX_SCALE );
411
+
412
+ // Compute zoom center in pattern space using new scale
413
+ // and get delta..
212
414
 
213
- clear( ) {
415
+ const newCenter = this._getPatternPosition( zoomCenter );
214
416
 
417
+ const deltaCenter = newCenter.sub( center );
418
+
419
+ this._patternPosition = this._patternPosition.add( deltaCenter );
420
+
421
+ this._updatePattern( GraphEditor.EVENT_MOUSEWHEEL );
422
+ }
423
+
424
+ _processContextMenu( e ) {
425
+
426
+ LX.addContextMenu( "Test", e, m => {
427
+ m.add( "option 1", () => { } );
428
+ m.add( "option 2", () => { } );
429
+ });
215
430
  }
216
431
 
217
432
  /**
218
433
  * @method frame
219
434
  */
220
435
 
221
- frame() {
436
+ _frame() {
222
437
 
223
- // this.update();
224
- this.draw();
438
+ this._update();
225
439
 
226
440
  requestAnimationFrame( this.frame.bind(this) );
227
441
  }
@@ -230,102 +444,90 @@ class GraphCanvas {
230
444
  * @method update
231
445
  */
232
446
 
233
- update() {
447
+ _update() {
234
448
 
235
449
  console.log("Update");
236
450
  }
237
451
 
238
- /**
239
- * @method draw
240
- */
241
-
242
- draw() {
452
+ _generatePattern() {
243
453
 
244
- if (!this.dom || !this.dom.width || !this.dom.height)
245
- return;
246
-
247
- // Count Fps
248
- var now = LX.getTime();
249
- this._drawTime = (now - this._lastDrawTime) * 0.001;
250
- this._lastDrawTime = now;
251
-
252
- // if (this.graph) {
253
- // this.ds.computeVisibleArea(this.viewport);
254
- // }
255
-
256
- const forceDraw = this.drawAllFrames || (this._backDirty || this._frontDirty);
257
-
258
- if ( forceDraw )
454
+ // Generate pattern
259
455
  {
260
- if( this._backDirty )
261
- this._drawBack();
262
- if ( this._frontDirty )
263
- this._drawFront();
456
+ var pattern = document.createElementNS( 'http://www.w3.org/2000/svg', 'pattern' );
457
+ pattern.setAttribute( 'id', 'pattern-0' );
458
+ pattern.setAttribute( 'x', this._patternPosition.x );
459
+ pattern.setAttribute( 'y', this._patternPosition.y );
460
+ pattern.setAttribute( 'width', this._patternSize.x )
461
+ pattern.setAttribute( 'height', this._patternSize.y );
462
+ pattern.setAttribute( 'patternUnits', 'userSpaceOnUse' );
463
+
464
+ var circle = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' );
465
+ circle.setAttribute( 'cx', this._circlePatternSize );
466
+ circle.setAttribute( 'cy', this._circlePatternSize );
467
+ circle.setAttribute( 'r', this._circlePatternSize );
468
+ circle.setAttribute( 'fill', this._circlePatternColor );
469
+
470
+ pattern.appendChild( circle );
264
471
  }
472
+
473
+ var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
474
+ svg.classList.add( "background-svg" );
475
+ svg.style.width = "100%";
476
+ svg.style.height = "100%";
265
477
 
266
- this.fps = this._drawTime ? (1.0 / this._drawTime) : 0;
267
- this.frames += 1;
268
- }
269
-
270
- _drawBack() {
271
-
272
- console.log( "_drawBack" );
478
+ svg.appendChild( pattern );
273
479
 
274
- var ctx = this.dom.getContext("2d");
480
+ var rect = document.createElementNS( 'http://www.w3.org/2000/svg', 'rect' );
481
+ rect.setAttribute( 'x', '0' );
482
+ rect.setAttribute( 'y', '0' );
483
+ rect.setAttribute( 'width', '100%' );
484
+ rect.setAttribute( 'height', '100%' );
485
+ rect.setAttribute( 'fill', 'url(#pattern-0)' );
275
486
 
276
- if ( !GraphCanvas.BACK_IMAGE_SRC )
277
- return;
487
+ svg.appendChild( rect );
278
488
 
279
- ctx.imageSmoothingEnabled = false;
489
+ this._background = svg;
280
490
 
281
- if ( !this._backImage ) {
282
- this._backImage = new Image();
283
- this._backImage.src = GraphCanvas.BACK_IMAGE_SRC;
284
- this._backImage.onload = this._forceDraw.bind(this);
285
- }
491
+ this.root.appendChild( this._background );
492
+ }
286
493
 
287
- if ( !this._pattern && this._backImage.width > 0) {
288
- this._pattern = ctx.createPattern(this._backImage, "repeat");
289
- }
494
+ _updatePattern() {
290
495
 
291
- // Draw background
496
+ if( !this._background )
497
+ return;
292
498
 
293
- if (this._pattern) {
294
- ctx.fillStyle = this._pattern;
295
- ctx.fillRect(0, 0, this.dom.width, this.dom.height);
296
- ctx.fillStyle = "transparent";
297
- }
499
+ const patternSize = this._patternSize.mul( this._scale );
500
+ const circlePatternSize = this._circlePatternSize * this._scale;
501
+ const patternPosition = this._patternPosition.mul( this._scale );
298
502
 
299
- ctx.globalAlpha = 1.0;
300
- ctx.imageSmoothingEnabled = true;
301
-
302
- // Draw node connections
303
-
304
- this._drawConnections();
305
-
306
- this._backDirty = false;
307
- }
503
+ let pattern = this._background.querySelector( 'pattern' );
504
+ pattern.setAttribute( 'x', patternPosition.x );
505
+ pattern.setAttribute( 'y', patternPosition.y );
506
+ pattern.setAttribute( 'width', patternSize.x )
507
+ pattern.setAttribute( 'height', patternSize.y );
308
508
 
309
- _drawFront() {
509
+ var circle = this._background.querySelector( 'circle' );
510
+ circle.setAttribute( 'cx', circlePatternSize );
511
+ circle.setAttribute( 'cy', circlePatternSize );
512
+ circle.setAttribute( 'r', circlePatternSize );
310
513
 
311
- console.log( "_drawFront" );
514
+ // Nodes
312
515
 
313
- let nodes = this._getVisibleNodes();
516
+ const w = this._domNodes.offsetWidth * 0.5;
517
+ const h = this._domNodes.offsetHeight * 0.5;
314
518
 
315
- for( let node of nodes )
316
- {
317
- this._drawNode( node );
318
- }
519
+ const dw = w - w * this._scale;
520
+ const dh = h - h * this._scale;
319
521
 
320
- this._frontDirty = false;
522
+ this._domNodes.style.transform = `
523
+ translate(` + ( patternPosition.x - dw ) + `px, ` + ( patternPosition.y - dh ) + `px)
524
+ scale(` + this._scale + `)
525
+ `;
321
526
  }
322
527
 
323
- _resetCanvasShadows( ctx ) {
528
+ _getPatternPosition( renderPosition ) {
324
529
 
325
- ctx.shadowOffsetX = 0;
326
- ctx.shadowOffsetY = 0;
327
- ctx.shadowBlur = 0;
328
- ctx.shadowColor = "rgba(0,0,0,0)";
530
+ return renderPosition.div( this._scale ).sub( this._patternPosition );
329
531
  }
330
532
 
331
533
  _computeNodeSize( node ) {
@@ -336,7 +538,7 @@ class GraphCanvas {
336
538
  let sX = 32 + textMetrics.width * 1.475;
337
539
 
338
540
  const rows = Math.max(1, Math.max(node.inputs.length, node.outputs.length));
339
- let sY = rows * GraphCanvas.NODE_ROW_HEIGHT + GraphCanvas.NODE_TITLE_HEIGHT;
541
+ let sY = rows * GraphEditor.NODE_ROW_HEIGHT + GraphEditor.NODE_TITLE_HEIGHT;
340
542
 
341
543
  return [sX, sY];
342
544
  }
@@ -345,20 +547,20 @@ class GraphCanvas {
345
547
 
346
548
  console.log( "_drawConnections" );
347
549
 
348
- const ctx = this.dom.getContext("2d");
550
+ // const ctx = this.dom.getContext("2d");
349
551
 
350
- let nodes = this._getVisibleNodes();
552
+ // let nodes = this._getVisibleNodes();
351
553
 
352
- let start = { x: 50, y: 20 };
353
- let cp1 = { x: 230, y: 30 };
354
- let cp2 = { x: 150, y: 80 };
355
- let end = { x: 250, y: 100 };
554
+ // let start = { x: 50, y: 20 };
555
+ // let cp1 = { x: 230, y: 30 };
556
+ // let cp2 = { x: 150, y: 80 };
557
+ // let end = { x: 250, y: 100 };
356
558
 
357
- // Cubic Bézier curve
358
- ctx.beginPath();
359
- ctx.moveTo(start.x, start.y);
360
- ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
361
- ctx.stroke();
559
+ // // Cubic Bézier curve
560
+ // ctx.beginPath();
561
+ // ctx.moveTo(start.x, start.y);
562
+ // ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
563
+ // ctx.stroke();
362
564
 
363
565
  // for( let node of nodes )
364
566
  // {
@@ -374,55 +576,7 @@ class GraphCanvas {
374
576
  // }
375
577
  }
376
578
 
377
- _drawNode( node ) {
378
-
379
- console.log( node.name );
380
-
381
- // Process some attributes
382
- node.size = node.size ?? this._computeNodeSize( node );
383
- node.color = node.color ?? GraphCanvas.DEFAULT_NODE_BODY_COLOR;
384
- node.titleColor = node.titleColor ?? GraphCanvas.DEFAULT_NODE_TITLE_COLOR;
385
-
386
- let [pX, pY] = node.position;
387
- let [sX, sY] = node.size;
388
-
389
- const ctx = this.dom.getContext("2d");
390
- const offsetY = GraphCanvas.NODE_TITLE_HEIGHT;
391
-
392
- // Body
393
-
394
- ctx.shadowBlur = 8;
395
- ctx.shadowColor = "#000";
396
-
397
- ctx.beginPath();
398
- ctx.fillStyle = node.color;
399
- ctx.roundRect( pX, pY, sX, sY, GraphCanvas.NODE_BODY_RADIUS );
400
- ctx.fill();
401
-
402
- this._resetCanvasShadows( ctx );
403
-
404
- // Draw border
405
- ctx.beginPath();
406
- ctx.strokeStyle = "#555";
407
- ctx.roundRect( pX, pY, sX, sY, GraphCanvas.NODE_BODY_RADIUS );
408
- ctx.stroke();
409
-
410
- // Title
411
-
412
- ctx.beginPath();
413
- var titleGrd = ctx.createLinearGradient(pX, pY, pX + sX, pY + offsetY);
414
- titleGrd.addColorStop(0, node.color);
415
- titleGrd.addColorStop(1, node.titleColor);
416
-
417
- ctx.fillStyle = titleGrd;
418
- ctx.roundRect( pX + 1, pY, sX - 2, offsetY, GraphCanvas.NODE_TITLE_RADIUS );
419
- ctx.fill();
420
-
421
- ctx.font = "14px Ubuntu";
422
- ctx.fillStyle = "#ddd";
423
- ctx.fillText( node.name, pX + 16, pY + offsetY * 0.75);
424
- }
425
-
579
+ // TODO: Return the ones in the viewport
426
580
  _getVisibleNodes() {
427
581
 
428
582
  if( !this.graph )
@@ -431,13 +585,12 @@ class GraphCanvas {
431
585
  return [];
432
586
  }
433
587
 
434
- // TODO: Return the ones in the viewport
435
588
  return this.graph.nodes;
436
589
  }
437
590
 
438
591
  }
439
592
 
440
- LX.GraphCanvas = GraphCanvas;
593
+ LX.GraphEditor = GraphEditor;
441
594
 
442
595
  /**
443
596
  * @class Graph
@@ -457,26 +610,60 @@ class Graph {
457
610
  this.nodes = [
458
611
  new GraphNode({
459
612
  name: "Node 1",
460
- xsize: [120, 100],
461
- position: [200, 200],
613
+ position: new LX.vec2( 200, 200 ),
614
+ inputs: [
615
+ {
616
+ name: "Speed",
617
+ type: "float"
618
+ },
619
+ {
620
+ name: "Offset",
621
+ type: "vec2"
622
+ }
623
+ ]
624
+ }),
625
+ new GraphNode({
626
+ name: "Node 2",
627
+ size: new LX.vec2( 120, 100 ),
628
+ position: new LX.vec2( 500, 350 ),
629
+ color: "#f7884c",
630
+ inputs: [],
631
+ outputs: [
632
+ {
633
+ name: "Speed",
634
+ type: "float"
635
+ },
636
+ {
637
+ name: "Offset",
638
+ type: "vec2"
639
+ },
640
+ {
641
+ name: "Loop",
642
+ type: "bool"
643
+ }
644
+ ]
645
+ }),
646
+ new GraphNode({
647
+ name: "Node 3",
648
+ position: new LX.vec2( 200, 400 ),
462
649
  inputs: [
463
650
  {
464
651
  name: "Speed",
465
- type: "number"
652
+ type: "float"
466
653
  },
467
654
  {
468
655
  name: "Offset",
469
- type: "number"
656
+ type: "vec2"
470
657
  }
471
658
  ],
472
659
  outputs: [
473
660
  {
474
661
  name: "Speed",
475
- type: "number"
662
+ type: "float"
476
663
  },
477
664
  {
478
665
  name: "Offset",
479
- type: "number"
666
+ type: "vec3"
480
667
  },
481
668
  {
482
669
  name: "Loop",
@@ -485,11 +672,21 @@ class Graph {
485
672
  ]
486
673
  }),
487
674
  new GraphNode({
488
- name: "Node 2",
489
- size: [120, 100],
490
- position: [500, 350],
491
- inputs: [],
492
- outputs: []
675
+ name: "Add",
676
+ position: new LX.vec2( 300, 300 ),
677
+ inputs: [
678
+ {
679
+ type: "float"
680
+ },
681
+ {
682
+ type: "float"
683
+ }
684
+ ],
685
+ outputs: [
686
+ {
687
+ type: "float"
688
+ }
689
+ ]
493
690
  })
494
691
  ];
495
692
  }
@@ -512,7 +709,8 @@ class GraphNode {
512
709
 
513
710
  this.name = options.name ?? "Unnamed";
514
711
  this.size = options.size;
515
- this.position = options.position ?? [0, 0];
712
+ this.position = options.position ?? new LX.vec2( 0, 0 );
713
+ this.color = options.color;
516
714
 
517
715
  this.inputs = options.inputs ?? [];
518
716
  this.outputs = options.outputs ?? [];
@@ -523,7 +721,7 @@ class GraphNode {
523
721
  let sX = 16 + this.name.length * 10;
524
722
 
525
723
  const rows = Math.max(1, Math.max(this.inputs.length, this.outputs.length));
526
- let sY = rows * GraphCanvas.NODE_ROW_HEIGHT + GraphCanvas.NODE_TITLE_HEIGHT;
724
+ let sY = rows * GraphEditor.NODE_ROW_HEIGHT + GraphEditor.NODE_TITLE_HEIGHT;
527
725
 
528
726
  return [sX, sY];
529
727
  }
@@ -531,4 +729,4 @@ class GraphNode {
531
729
 
532
730
  LX.GraphNode = GraphNode;
533
731
 
534
- export { GraphCanvas, Graph, GraphNode };
732
+ export { GraphEditor, Graph, GraphNode };