lexgui 0.1.30 → 0.1.32
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/nodegraph.js +187 -88
- package/build/components/timeline.js +768 -442
- package/build/components/videoeditor.js +539 -0
- package/build/lexgui.css +95 -11
- package/build/lexgui.js +287 -248
- package/build/lexgui.module.js +262 -223
- package/changelog.md +20 -0
- package/examples/index.html +2 -1
- package/examples/node_graph.html +0 -1
- package/examples/timeline.html +279 -0
- package/examples/video_editor.html +35 -0
- package/package.json +1 -1
|
@@ -352,6 +352,11 @@ class GraphEditor {
|
|
|
352
352
|
node.position = new LX.vec2( 0, 0 );
|
|
353
353
|
node.color = null;
|
|
354
354
|
|
|
355
|
+
if( baseClass.name == 'NodeFunction' )
|
|
356
|
+
{
|
|
357
|
+
node.gid = baseClass.gid;
|
|
358
|
+
}
|
|
359
|
+
|
|
355
360
|
// Extra options
|
|
356
361
|
if ( options ) {
|
|
357
362
|
for (var i in options) {
|
|
@@ -450,7 +455,17 @@ class GraphEditor {
|
|
|
450
455
|
let graph = new Graph();
|
|
451
456
|
graph.editor = this;
|
|
452
457
|
|
|
453
|
-
if( o )
|
|
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
|
+
}
|
|
454
469
|
|
|
455
470
|
this.setGraph( graph );
|
|
456
471
|
|
|
@@ -471,7 +486,17 @@ class GraphEditor {
|
|
|
471
486
|
let func = new GraphFunction();
|
|
472
487
|
func.editor = this;
|
|
473
488
|
|
|
474
|
-
if( o )
|
|
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
|
+
}
|
|
475
500
|
|
|
476
501
|
this.setGraph( func );
|
|
477
502
|
|
|
@@ -492,6 +517,7 @@ class GraphEditor {
|
|
|
492
517
|
}
|
|
493
518
|
|
|
494
519
|
NodeFunction.func = func;
|
|
520
|
+
NodeFunction.gid = func.id;
|
|
495
521
|
GraphEditor.registerCustomNode( "function/" + func.name, NodeFunction );
|
|
496
522
|
|
|
497
523
|
this._sidebar.add( func.name, { icon: "fa fa-florin-sign", className: func.id, callback: (e) => { this.setGraph( func ) } } );
|
|
@@ -642,13 +668,16 @@ class GraphEditor {
|
|
|
642
668
|
LX.addContextMenu(null, e, m => {
|
|
643
669
|
|
|
644
670
|
m.add( "Copy", () => {
|
|
645
|
-
this._clipboardData =
|
|
671
|
+
this._clipboardData = {
|
|
672
|
+
id: node.id,
|
|
673
|
+
gid: this.currentGraph.id
|
|
674
|
+
};
|
|
646
675
|
} );
|
|
647
676
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
} );
|
|
677
|
+
// TODO
|
|
678
|
+
// m.add( "Paste", () => {
|
|
679
|
+
|
|
680
|
+
// } );
|
|
652
681
|
|
|
653
682
|
m.add( "" );
|
|
654
683
|
|
|
@@ -1315,6 +1344,28 @@ class GraphEditor {
|
|
|
1315
1344
|
this.currentGraph.groups.splice( idx, 1 );
|
|
1316
1345
|
}
|
|
1317
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
|
+
|
|
1318
1369
|
_cloneNodes() {
|
|
1319
1370
|
|
|
1320
1371
|
// Clone all selected nodes
|
|
@@ -1324,20 +1375,7 @@ class GraphEditor {
|
|
|
1324
1375
|
|
|
1325
1376
|
for( let nodeId of selectedIds )
|
|
1326
1377
|
{
|
|
1327
|
-
|
|
1328
|
-
if( !nodeInfo )
|
|
1329
|
-
return;
|
|
1330
|
-
|
|
1331
|
-
const el = this._getNodeDOMElement( nodeId );
|
|
1332
|
-
const data = nodeInfo.data;
|
|
1333
|
-
const newNode = GraphEditor.addNode( data.type );
|
|
1334
|
-
const newDom = this._createNodeDOM( newNode );
|
|
1335
|
-
|
|
1336
|
-
this._translateNode( newDom, this._getNodePosition( el ) );
|
|
1337
|
-
|
|
1338
|
-
this._selectNode( newDom, true );
|
|
1339
|
-
|
|
1340
|
-
this.currentGraph.nodes.push( newNode );
|
|
1378
|
+
this._cloneNode( nodeId );
|
|
1341
1379
|
}
|
|
1342
1380
|
}
|
|
1343
1381
|
|
|
@@ -1706,80 +1744,86 @@ class GraphEditor {
|
|
|
1706
1744
|
|
|
1707
1745
|
_processContextMenu( e, autoConnect ) {
|
|
1708
1746
|
|
|
1709
|
-
|
|
1710
|
-
{
|
|
1711
|
-
LX.addContextMenu(null, e, m => {
|
|
1712
|
-
m.add( "Paste", () => {
|
|
1713
|
-
// TODO: paste node data
|
|
1714
|
-
// ...
|
|
1715
|
-
} );
|
|
1716
|
-
});
|
|
1717
|
-
}
|
|
1718
|
-
else
|
|
1719
|
-
{
|
|
1720
|
-
LX.addContextMenu( "ADD NODE", e, m => {
|
|
1721
|
-
|
|
1722
|
-
for( let type in GraphEditor.NODE_TYPES )
|
|
1723
|
-
{
|
|
1724
|
-
const baseClass = GraphEditor.NODE_TYPES[ type ];
|
|
1747
|
+
LX.addContextMenu( null, e, m => {
|
|
1725
1748
|
|
|
1726
|
-
|
|
1727
|
-
continue;
|
|
1749
|
+
var eventPosition = null;
|
|
1728
1750
|
|
|
1729
|
-
|
|
1751
|
+
if( e )
|
|
1752
|
+
{
|
|
1753
|
+
const rect = this.root.getBoundingClientRect();
|
|
1730
1754
|
|
|
1731
|
-
|
|
1755
|
+
const localPosition = new LX.vec2( e.clientX - rect.x, e.clientY - rect.y );
|
|
1732
1756
|
|
|
1733
|
-
|
|
1757
|
+
eventPosition = this._getPatternPosition( localPosition );
|
|
1758
|
+
}
|
|
1734
1759
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
}
|
|
1760
|
+
if( this._clipboardData )
|
|
1761
|
+
{
|
|
1762
|
+
m.add( "Paste", () => {
|
|
1739
1763
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
const rect = this.root.getBoundingClientRect();
|
|
1764
|
+
const nodeId = this._clipboardData.id;
|
|
1765
|
+
const graphId = this._clipboardData.gid;
|
|
1743
1766
|
|
|
1744
|
-
|
|
1767
|
+
this._cloneNode( nodeId, graphId, eventPosition );
|
|
1745
1768
|
|
|
1746
|
-
|
|
1769
|
+
} );
|
|
1770
|
+
m.add( "" );
|
|
1771
|
+
}
|
|
1747
1772
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1773
|
+
for( let type in GraphEditor.NODE_TYPES )
|
|
1774
|
+
{
|
|
1775
|
+
const baseClass = GraphEditor.NODE_TYPES[ type ];
|
|
1750
1776
|
|
|
1751
|
-
|
|
1777
|
+
if( baseClass.blockAdd )
|
|
1778
|
+
continue;
|
|
1752
1779
|
|
|
1753
|
-
|
|
1754
|
-
{
|
|
1755
|
-
const srcId = autoConnect.domEl.dataset[ 'id' ];
|
|
1756
|
-
const srcType = autoConnect.io.childNodes[ autoConnect.index ].dataset[ 'type' ];
|
|
1757
|
-
const srcIsInput = autoConnect.ioType == GraphEditor.NODE_IO_INPUT;
|
|
1780
|
+
m.add( type, () => {
|
|
1758
1781
|
|
|
1759
|
-
|
|
1760
|
-
inputNode: srcIsInput ? srcId : newNode.id,
|
|
1761
|
-
inputIdx: srcIsInput ? autoConnect.index : 0,
|
|
1762
|
-
inputType: srcIsInput ? srcType : newNode.inputs[ 0 ].type,
|
|
1763
|
-
outputNode: srcIsInput ? newNode.id : srcId,
|
|
1764
|
-
outputIdx: srcIsInput ? 0 : autoConnect.index,
|
|
1765
|
-
outputType: srcIsInput ? newNode.inputs[ 0 ].type : srcType,
|
|
1766
|
-
}
|
|
1782
|
+
const newNode = GraphEditor.addNode( type );
|
|
1767
1783
|
|
|
1768
|
-
|
|
1784
|
+
const dom = this._createNodeDOM( newNode );
|
|
1769
1785
|
|
|
1770
|
-
|
|
1786
|
+
if( this._snapToGrid )
|
|
1787
|
+
{
|
|
1788
|
+
dom.mustSnap = true;
|
|
1789
|
+
}
|
|
1771
1790
|
|
|
1772
|
-
|
|
1791
|
+
if( eventPosition )
|
|
1792
|
+
{
|
|
1793
|
+
this._translateNode( dom, eventPosition );
|
|
1794
|
+
}
|
|
1773
1795
|
|
|
1774
|
-
|
|
1796
|
+
this.currentGraph.nodes.push( newNode );
|
|
1775
1797
|
|
|
1776
|
-
|
|
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,
|
|
1777
1811
|
}
|
|
1778
1812
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
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
|
+
}
|
|
1823
|
+
|
|
1824
|
+
} );
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1783
1827
|
}
|
|
1784
1828
|
|
|
1785
1829
|
/**
|
|
@@ -2683,21 +2727,47 @@ class GraphEditor {
|
|
|
2683
2727
|
|
|
2684
2728
|
_showRenameGraphDialog() {
|
|
2685
2729
|
|
|
2686
|
-
new LX.Dialog( this.currentGraph.constructor.name, p => {
|
|
2687
|
-
p.addText( "Name", this.currentGraph.name, v =>
|
|
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
|
+
} );
|
|
2688
2735
|
}, { modal: true, size: [ "350px", null ] } );
|
|
2689
2736
|
}
|
|
2690
2737
|
|
|
2691
2738
|
_updateGraphName( name ) {
|
|
2692
2739
|
|
|
2693
|
-
|
|
2740
|
+
const newNameKey = name.replace( /\s/g, '' ).replaceAll( '.', '' );
|
|
2694
2741
|
|
|
2742
|
+
// Change graph name button
|
|
2695
2743
|
const nameDom = LX.root.querySelector( '.graph-title button' );
|
|
2696
2744
|
console.assert( nameDom );
|
|
2697
2745
|
nameDom.innerText = name;
|
|
2698
2746
|
|
|
2699
|
-
//
|
|
2700
|
-
|
|
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
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// Change registered nodes function
|
|
2758
|
+
const oldType = 'function/' + this.currentGraph.name;
|
|
2759
|
+
const nodeClass = GraphEditor.NODE_TYPES[ oldType ];
|
|
2760
|
+
|
|
2761
|
+
if( nodeClass )
|
|
2762
|
+
{
|
|
2763
|
+
delete GraphEditor.NODE_TYPES[ oldType ];
|
|
2764
|
+
|
|
2765
|
+
nodeClass.title = name;
|
|
2766
|
+
|
|
2767
|
+
GraphEditor.registerCustomNode( "function/" + name, nodeClass );
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
this.currentGraph.name = name;
|
|
2701
2771
|
}
|
|
2702
2772
|
|
|
2703
2773
|
_addGlobalActions() {
|
|
@@ -2762,6 +2832,18 @@ class Graph {
|
|
|
2762
2832
|
// zoom/translation ??
|
|
2763
2833
|
}
|
|
2764
2834
|
|
|
2835
|
+
/**
|
|
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
|
+
|
|
2765
2847
|
/**
|
|
2766
2848
|
* @method _runStep
|
|
2767
2849
|
*/
|
|
@@ -2828,12 +2910,12 @@ class Graph {
|
|
|
2828
2910
|
}
|
|
2829
2911
|
|
|
2830
2912
|
/**
|
|
2831
|
-
* @method
|
|
2913
|
+
* @method serialize
|
|
2832
2914
|
* @param {Boolean} prettify
|
|
2833
2915
|
* @returns JSON data from the serialized graph
|
|
2834
2916
|
*/
|
|
2835
2917
|
|
|
2836
|
-
|
|
2918
|
+
serialize( prettify = true ) {
|
|
2837
2919
|
|
|
2838
2920
|
var o = { };
|
|
2839
2921
|
|
|
@@ -2841,13 +2923,21 @@ class Graph {
|
|
|
2841
2923
|
o.name = this.name;
|
|
2842
2924
|
o.type = this.type;
|
|
2843
2925
|
|
|
2844
|
-
o.nodes
|
|
2845
|
-
o.groups
|
|
2846
|
-
o.
|
|
2926
|
+
o.nodes = [ ];
|
|
2927
|
+
o.groups = [ ];
|
|
2928
|
+
o.functions = [ ];
|
|
2929
|
+
o.links = { };
|
|
2847
2930
|
|
|
2848
2931
|
for( let node of this.nodes )
|
|
2849
2932
|
{
|
|
2850
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
|
+
}
|
|
2851
2941
|
}
|
|
2852
2942
|
|
|
2853
2943
|
for( let linkId in this.links )
|
|
@@ -2880,10 +2970,19 @@ class Graph {
|
|
|
2880
2970
|
console.error( `Can't export GraphNode [${ this.title }] of type [${ this.type }].` );
|
|
2881
2971
|
}
|
|
2882
2972
|
|
|
2883
|
-
LX.downloadFile( this.name + ".json", o );
|
|
2884
|
-
|
|
2885
2973
|
return o;
|
|
2886
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
|
+
}
|
|
2887
2986
|
}
|
|
2888
2987
|
|
|
2889
2988
|
LX.Graph = Graph;
|