lexgui 0.1.27 → 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.
- package/build/components/codeeditor.js +25 -10
- package/build/components/nodegraph.js +431 -233
- package/build/lexgui.css +331 -163
- package/build/lexgui.js +159 -51
- package/build/lexgui.module.js +161 -53
- package/changelog.md +8 -0
- package/demo.js +1 -1
- package/examples/code_editor.html +9 -12
- package/examples/index.html +2 -1
- package/examples/node_graph.html +4 -4
- package/examples/side_bar.html +80 -0
- package/package.json +1 -1
|
@@ -39,22 +39,29 @@ function doAsync( fn, ms ) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* @class
|
|
42
|
+
* @class GraphEditor
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
class
|
|
45
|
+
class GraphEditor {
|
|
46
46
|
|
|
47
47
|
static __instances = [];
|
|
48
48
|
|
|
49
|
-
|
|
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 =
|
|
56
|
-
static NODE_TITLE_RADIUS = [
|
|
57
|
-
static NODE_BODY_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
|
-
|
|
76
|
+
GraphEditor.__instances.push( this );
|
|
70
77
|
|
|
71
78
|
this.base_area = area;
|
|
72
|
-
this.area = new LX.Area( { className: "
|
|
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
|
-
|
|
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( '
|
|
92
|
-
this.root.addEventListener( '
|
|
93
|
-
this.root.addEventListener( '
|
|
94
|
-
this.root.addEventListener( '
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.
|
|
106
|
-
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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) <
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379
|
+
_processBackgroundClick( e ) {
|
|
194
380
|
|
|
195
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
392
|
+
this._updatePattern();
|
|
393
|
+
}
|
|
207
394
|
}
|
|
208
395
|
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
436
|
+
_frame() {
|
|
222
437
|
|
|
223
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
267
|
-
this.frames += 1;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
_drawBack() {
|
|
271
|
-
|
|
272
|
-
console.log( "_drawBack" );
|
|
478
|
+
svg.appendChild( pattern );
|
|
273
479
|
|
|
274
|
-
var
|
|
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
|
-
|
|
277
|
-
return;
|
|
487
|
+
svg.appendChild( rect );
|
|
278
488
|
|
|
279
|
-
|
|
489
|
+
this._background = svg;
|
|
280
490
|
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
288
|
-
this._pattern = ctx.createPattern(this._backImage, "repeat");
|
|
289
|
-
}
|
|
494
|
+
_updatePattern() {
|
|
290
495
|
|
|
291
|
-
|
|
496
|
+
if( !this._background )
|
|
497
|
+
return;
|
|
292
498
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
514
|
+
// Nodes
|
|
312
515
|
|
|
313
|
-
|
|
516
|
+
const w = this._domNodes.offsetWidth * 0.5;
|
|
517
|
+
const h = this._domNodes.offsetHeight * 0.5;
|
|
314
518
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
this._drawNode( node );
|
|
318
|
-
}
|
|
519
|
+
const dw = w - w * this._scale;
|
|
520
|
+
const dh = h - h * this._scale;
|
|
319
521
|
|
|
320
|
-
this.
|
|
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
|
-
|
|
528
|
+
_getPatternPosition( renderPosition ) {
|
|
324
529
|
|
|
325
|
-
|
|
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 *
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
461
|
-
|
|
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: "
|
|
652
|
+
type: "float"
|
|
466
653
|
},
|
|
467
654
|
{
|
|
468
655
|
name: "Offset",
|
|
469
|
-
type: "
|
|
656
|
+
type: "vec2"
|
|
470
657
|
}
|
|
471
658
|
],
|
|
472
659
|
outputs: [
|
|
473
660
|
{
|
|
474
661
|
name: "Speed",
|
|
475
|
-
type: "
|
|
662
|
+
type: "float"
|
|
476
663
|
},
|
|
477
664
|
{
|
|
478
665
|
name: "Offset",
|
|
479
|
-
type: "
|
|
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: "
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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 ??
|
|
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 *
|
|
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 {
|
|
732
|
+
export { GraphEditor, Graph, GraphNode };
|