diff --git a/build/litegraph.js b/build/litegraph.js index 9b2aa6c39..43cc6f3b9 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -85,7 +85,7 @@ var LiteGraph = global.LiteGraph = { debug: false, catch_exceptions: true, throw_errors: true, - allow_scripts: false, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits registered_node_types: {}, //nodetypes by string node_types_by_file_extension: {}, //used for droping files in the canvas Nodes: {}, //node types by classname @@ -167,8 +167,9 @@ var LiteGraph = global.LiteGraph = { * @param {Function} func * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable */ - wrapFunctionAsNode: function( name, func, param_types, return_type ) + wrapFunctionAsNode: function( name, func, param_types, return_type, properties ) { var params = Array(func.length); var code = ""; @@ -176,6 +177,8 @@ var LiteGraph = global.LiteGraph = { for(var i = 0; i < names.length; ++i) code += "this.addInput('"+names[i]+"',"+(param_types && param_types[i] ? "'" + param_types[i] + "'" : "0") + ");\n"; code += "this.addOutput('out',"+( return_type ? "'" + return_type + "'" : 0 )+");\n"; + if(properties) + code += "this.properties = " + JSON.stringify(properties) + ";\n"; var classobj = Function(code); classobj.title = name.split("/").pop(); classobj.desc = "Generated from " + func.name; @@ -518,8 +521,8 @@ LGraph.prototype.clear = function() this.catch_errors = true; //subgraph_data - this.global_inputs = {}; - this.global_outputs = {}; + this.inputs = {}; + this.outputs = {}; //notify canvas to redraw this.change(); @@ -1254,16 +1257,20 @@ LGraph.prototype.getGroupOnPos = function(x,y) * @param {String} type * @param {*} value [optional] */ -LGraph.prototype.addGlobalInput = function(name, type, value) +LGraph.prototype.addInput = function(name, type, value) { - this.global_inputs[name] = { name: name, type: type, value: value }; + var input = this.inputs[ name ]; + if( input ) //already exist + return; + + this.inputs[ name ] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalInputAdded) - this.onGlobalInputAdded(name, type); + if(this.onInputAdded) + this.onInputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** @@ -1272,32 +1279,23 @@ LGraph.prototype.addGlobalInput = function(name, type, value) * @param {String} name * @param {*} data */ -LGraph.prototype.setGlobalInputData = function(name, data) +LGraph.prototype.setInputData = function(name, data) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return; input.value = data; } -/** -* Assign a data to the global graph input (same as setGlobalInputData) -* @method setInputData -* @param {String} name -* @param {*} data -*/ -LGraph.prototype.setInputData = LGraph.prototype.setGlobalInputData; - - /** * Returns the current value of a global graph input -* @method getGlobalInputData +* @method getInputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalInputData = function(name) +LGraph.prototype.getInputData = function(name) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return null; return input.value; @@ -1305,105 +1303,105 @@ LGraph.prototype.getGlobalInputData = function(name) /** * Changes the name of a global graph input -* @method renameGlobalInput +* @method renameInput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalInput = function(old_name, name) +LGraph.prototype.renameInput = function(old_name, name) { if(name == old_name) return; - if(!this.global_inputs[old_name]) + if(!this.inputs[old_name]) return false; - if(this.global_inputs[name]) + if(this.inputs[name]) { console.error("there is already one input with that name"); return false; } - this.global_inputs[name] = this.global_inputs[old_name]; - delete this.global_inputs[old_name]; + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; this._version++; - if(this.onGlobalInputRenamed) - this.onGlobalInputRenamed(old_name, name); + if(this.onInputRenamed) + this.onInputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph input -* @method changeGlobalInputType +* @method changeInputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalInputType = function(name, type) +LGraph.prototype.changeInputType = function(name, type) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - if(this.global_inputs[name].type && this.global_inputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.inputs[name].type && this.inputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_inputs[name].type = type; + this.inputs[name].type = type; this._version++; - if(this.onGlobalInputTypeChanged) - this.onGlobalInputTypeChanged(name, type); + if(this.onInputTypeChanged) + this.onInputTypeChanged(name, type); } /** * Removes a global graph input -* @method removeGlobalInput +* @method removeInput * @param {String} name * @param {String} type */ -LGraph.prototype.removeGlobalInput = function(name) +LGraph.prototype.removeInput = function(name) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - delete this.global_inputs[name]; + delete this.inputs[name]; this._version++; - if(this.onGlobalInputRemoved) - this.onGlobalInputRemoved(name); + if(this.onInputRemoved) + this.onInputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } /** * Creates a global graph output -* @method addGlobalOutput +* @method addOutput * @param {String} name * @param {String} type * @param {*} value */ -LGraph.prototype.addGlobalOutput = function(name, type, value) +LGraph.prototype.addOutput = function(name, type, value) { - this.global_outputs[name] = { name: name, type: type, value: value }; + this.outputs[name] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalOutputAdded) - this.onGlobalOutputAdded(name, type); + if(this.onOutputAdded) + this.onOutputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Assign a data to the global output -* @method setGlobalOutputData +* @method setOutputData * @param {String} name * @param {String} value */ -LGraph.prototype.setGlobalOutputData = function(name, value) +LGraph.prototype.setOutputData = function(name, value) { - var output = this.global_outputs[ name ]; + var output = this.outputs[ name ]; if (!output) return; output.value = value; @@ -1411,92 +1409,83 @@ LGraph.prototype.setGlobalOutputData = function(name, value) /** * Returns the current value of a global graph output -* @method getGlobalOutputData +* @method getOutputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalOutputData = function(name) +LGraph.prototype.getOutputData = function(name) { - var output = this.global_outputs[name]; + var output = this.outputs[name]; if (!output) return null; return output.value; } -/** -* Returns the current value of a global graph output (sames as getGlobalOutputData) -* @method getOutputData -* @param {String} name -* @return {*} the data -*/ -LGraph.prototype.getOutputData = LGraph.prototype.getGlobalOutputData; - - /** * Renames a global graph output -* @method renameGlobalOutput +* @method renameOutput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalOutput = function(old_name, name) +LGraph.prototype.renameOutput = function(old_name, name) { - if(!this.global_outputs[old_name]) + if(!this.outputs[old_name]) return false; - if(this.global_outputs[name]) + if(this.outputs[name]) { console.error("there is already one output with that name"); return false; } - this.global_outputs[name] = this.global_outputs[old_name]; - delete this.global_outputs[old_name]; + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; this._version++; - if(this.onGlobalOutputRenamed) - this.onGlobalOutputRenamed(old_name, name); + if(this.onOutputRenamed) + this.onOutputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph output -* @method changeGlobalOutputType +* @method changeOutputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalOutputType = function(name, type) +LGraph.prototype.changeOutputType = function(name, type) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - if(this.global_outputs[name].type && this.global_outputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.outputs[name].type && this.outputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_outputs[name].type = type; + this.outputs[name].type = type; this._version++; - if(this.onGlobalOutputTypeChanged) - this.onGlobalOutputTypeChanged(name, type); + if(this.onOutputTypeChanged) + this.onOutputTypeChanged(name, type); } /** * Removes a global graph output -* @method removeGlobalOutput +* @method removeOutput * @param {String} name */ -LGraph.prototype.removeGlobalOutput = function(name) +LGraph.prototype.removeOutput = function(name) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - delete this.global_outputs[name]; + delete this.outputs[name]; this._version++; - if(this.onGlobalOutputRemoved) - this.onGlobalOutputRemoved(name); + if(this.onOutputRemoved) + this.onOutputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } @@ -1809,7 +1798,7 @@ LiteGraph.LLink = LLink; + collapsed: if it is collapsed supported callbacks: - + onAdded: when added to graph + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + onRemoved: when removed from graph + onStart: when the graph starts playing + onStop: when the graph stops playing @@ -1828,6 +1817,7 @@ LiteGraph.LLink = LLink; + onDblClick: double clicked in the node + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + onSelected + onDeselected @@ -7430,6 +7420,8 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) var input_html = ""; title = title || ""; + var modified = false; + var dialog = document.createElement("div"); dialog.className = "graphdialog rounded"; dialog.innerHTML = " "; @@ -7444,7 +7436,8 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) dialog.style.transform = "scale("+this.ds.scale+")"; dialog.addEventListener("mouseleave",function(e){ - dialog.close(); + if(!modified) + dialog.close(); }); if(that.prompt_box) @@ -7462,6 +7455,7 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) var input = dialog.querySelector("input"); input.addEventListener("keydown", function(e){ + modified = true; if(e.keyCode == 27) //ESC dialog.close(); else if(e.keyCode == 13) @@ -8240,7 +8234,7 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) var dialog = that.createDialog( "Name" , options ); var input = dialog.querySelector("input"); if(input && slot_info){ - input.value = slot_info.label; + input.value = slot_info.label || ""; } dialog.querySelector("button").addEventListener("click",function(e){ if(input.value) @@ -8846,20 +8840,23 @@ function Subgraph() { var that = this; this.size = [140,80]; + this.properties = { enabled: true }; + this.addInput("enabled","boolean"); //create inner graph this.subgraph = new LGraph(); this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = true; - this.subgraph.onGlobalInputAdded = this.onSubgraphNewGlobalInput.bind(this); - this.subgraph.onGlobalInputRenamed = this.onSubgraphRenamedGlobalInput.bind(this); - this.subgraph.onGlobalInputTypeChanged = this.onSubgraphTypeChangeGlobalInput.bind(this); - - this.subgraph.onGlobalOutputAdded = this.onSubgraphNewGlobalOutput.bind(this); - this.subgraph.onGlobalOutputRenamed = this.onSubgraphRenamedGlobalOutput.bind(this); - this.subgraph.onGlobalOutputTypeChanged = this.onSubgraphTypeChangeGlobalOutput.bind(this); + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } Subgraph.title = "Subgraph"; @@ -8889,7 +8886,6 @@ Subgraph.prototype.onDblClick = function(e,pos,graphcanvas) setTimeout(function(){ graphcanvas.openSubgraph( that.subgraph ); },10 ); } - Subgraph.prototype.onMouseDown = function(e,pos,graphcanvas) { if( !this.flags.collapsed && pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) @@ -8899,13 +8895,43 @@ Subgraph.prototype.onMouseDown = function(e,pos,graphcanvas) } } -Subgraph.prototype.onSubgraphNewGlobalInput = function(name, type) +Subgraph.prototype.onExecute = function() { - //add input to the node - this.addInput(name, type); + if( !this.getInputOrProperty("enabled") ) + return; + + //send inputs to subgraph global inputs + if(this.inputs) + for(var i = 0; i < this.inputs.length; i++) + { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData( input.name, value ); + } + + //execute + this.subgraph.runStep(); + + //send subgraph global outputs to outputs + if(this.outputs) + for(var i = 0; i < this.outputs.length; i++) + { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData( output.name ); + this.setOutputData(i, value); + } } -Subgraph.prototype.onSubgraphRenamedGlobalInput = function(oldname, name) +//**** INPUTS *********************************** +Subgraph.prototype.onSubgraphNewInput = function(name, type) +{ + //add input to the node + var slot = this.findInputSlot(name); + if(slot == -1) + this.addInput(name, type); +} + +Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { var slot = this.findInputSlot( oldname ); if(slot == -1) @@ -8914,7 +8940,7 @@ Subgraph.prototype.onSubgraphRenamedGlobalInput = function(oldname, name) info.name = name; } -Subgraph.prototype.onSubgraphTypeChangeGlobalInput = function(name, type) +Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { var slot = this.findInputSlot( name ); if(slot == -1) @@ -8923,15 +8949,23 @@ Subgraph.prototype.onSubgraphTypeChangeGlobalInput = function(name, type) info.type = type; } - -Subgraph.prototype.onSubgraphNewGlobalOutput = function(name, type) +Subgraph.prototype.onSubgraphRemovedInput = function(name) { - //add output to the node - this.addOutput(name, type); + var slot = this.findInputSlot( name ); + if(slot == -1) + return; + this.removeInput(slot); } +//**** OUTPUTS *********************************** +Subgraph.prototype.onSubgraphNewOutput = function(name, type) +{ + var slot = this.findOutputSlot(name); + if(slot == -1) + this.addOutput(name, type); +} -Subgraph.prototype.onSubgraphRenamedGlobalOutput = function(oldname, name) +Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { var slot = this.findOutputSlot( oldname ); if(slot == -1) @@ -8940,7 +8974,7 @@ Subgraph.prototype.onSubgraphRenamedGlobalOutput = function(oldname, name) info.name = name; } -Subgraph.prototype.onSubgraphTypeChangeGlobalOutput = function(name, type) +Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { var slot = this.findOutputSlot( name ); if(slot == -1) @@ -8949,6 +8983,14 @@ Subgraph.prototype.onSubgraphTypeChangeGlobalOutput = function(name, type) info.type = type; } +Subgraph.prototype.onSubgraphRemovedOutput = function(name) +{ + var slot = this.findInputSlot( name ); + if(slot == -1) + return; + this.removeOutput(slot); +} +// ***************************************************** Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { @@ -8965,42 +9007,13 @@ Subgraph.prototype.onResize = function(size) size[1] += 20; } -Subgraph.prototype.onExecute = function() -{ - //send inputs to subgraph global inputs - if(this.inputs) - for(var i = 0; i < this.inputs.length; i++) - { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setGlobalInputData( input.name, value ); - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if(this.outputs) - for(var i = 0; i < this.outputs.length; i++) - { - var output = this.outputs[i]; - var value = this.subgraph.getGlobalOutputData( output.name ); - this.setOutputData(i, value); - } -} - -Subgraph.prototype.configure = function(o) -{ - LGraphNode.prototype.configure.call(this, o); - //this.subgraph.configure(o.graph); -} - Subgraph.prototype.serialize = function() { var data = LGraphNode.prototype.serialize.call(this); data.subgraph = this.subgraph.serialize(); return data; } +//no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() Subgraph.prototype.clone = function() { @@ -9013,38 +9026,31 @@ Subgraph.prototype.clone = function() return node; } - LiteGraph.registerNodeType("graph/subgraph", Subgraph ); //Input for a subgraph -function GlobalInput() +function GraphInput() { + this.addOutput("",""); - //random name to avoid problems with other outputs when added - var input_name = "input_" + (Math.random()*1000).toFixed(); - - this.addOutput(input_name, null ); - - this.properties = { name: input_name, type: null }; - + this.name_in_graph = ""; + this.properties = {}; var that = this; Object.defineProperty( this.properties, "name", { get: function() { - return input_name; + return that.name_in_graph; }, set: function(v) { - if(v == "") + if( v == "" || v == that.name_in_graph || v == "enabled" ) return; - - var info = that.getOutputInfo(0); - if(info.name == v) - return; - info.name = v; - if(that.graph) - that.graph.renameGlobalInput(input_name, v); - input_name = v; + if(that.name_in_graph) //already added + that.graph.renameInput( that.name_in_graph, v ); + else + that.graph.addInput( v, that.properties.type ); + that.name_widget.value = v; + that.name_in_graph = v; }, enumerable: true }); @@ -9052,103 +9058,132 @@ function GlobalInput() Object.defineProperty( this.properties, "type", { get: function() { return that.outputs[0].type; }, set: function(v) { - that.outputs[0].type = v; - if(that.graph) - that.graph.changeGlobalInputType(input_name, that.outputs[0].type); + that.outputs[0].type = v; + if(that.name_in_graph) //already added + that.graph.changeInputType( that.name_in_graph, that.outputs[0].type); + that.type_widget.value = v; }, enumerable: true }); + + this.name_widget = this.addWidget("text","Name", this.properties.name, function(v){ + if(!v) return; + that.properties.name = v; + }); + this.type_widget = this.addWidget("text","Type", this.properties.type, function(v){ + v = v || ""; + that.properties.type = v; + }); + + this.widgets_up = true; + this.size = [180,60]; } -GlobalInput.title = "Input"; -GlobalInput.desc = "Input of the graph"; +GraphInput.title = "Input"; +GraphInput.desc = "Input of the graph"; -//When added to graph tell the graph this is a new global input -GlobalInput.prototype.onAdded = function() +GraphInput.prototype.getTitle = function() { - this.graph.addGlobalInput( this.properties.name, this.properties.type ); + if(this.flags.collapsed) + return this.properties.name; + return this.title; } -GlobalInput.prototype.onExecute = function() +GraphInput.prototype.onExecute = function() { var name = this.properties.name; //read from global input - var data = this.graph.global_inputs[name]; - if(!data) return; + var data = this.graph.inputs[name]; + if(!data) + return; //put through output this.setOutputData(0,data.value); } -LiteGraph.registerNodeType("graph/input", GlobalInput); +GraphInput.prototype.onRemoved = function() +{ + if(this.name_in_graph) + this.graph.removeInput( this.name_in_graph ); +} +LiteGraph.registerNodeType("graph/input", GraphInput); //Output for a subgraph -function GlobalOutput() +function GraphOutput() { - //random name to avoid problems with other outputs when added - var output_name = "output_" + (Math.random()*1000).toFixed(); - - this.addInput(output_name, null); - - this._value = null; - - this.properties = {name: output_name, type: null }; + this.addInput("",""); + this.name_in_graph = ""; + this.properties = {}; var that = this; - Object.defineProperty(this.properties, "name", { + Object.defineProperty( this.properties, "name", { get: function() { - return output_name; + return that.name_in_graph; }, set: function(v) { - if(v == "") + if( v == "" || v == that.name_in_graph ) return; - - var info = that.getInputInfo(0); - if(info.name == v) - return; - info.name = v; - if(that.graph) - that.graph.renameGlobalOutput(output_name, v); - output_name = v; + if(that.name_in_graph) //already added + that.graph.renameOutput( that.name_in_graph, v ); + else + that.graph.addOutput( v, that.properties.type ); + that.name_widget.value = v; + that.name_in_graph = v; }, enumerable: true }); - Object.defineProperty(this.properties, "type", { + Object.defineProperty( this.properties, "type", { get: function() { return that.inputs[0].type; }, set: function(v) { that.inputs[0].type = v; - if(that.graph) - that.graph.changeGlobalInputType( output_name, that.inputs[0].type ); + if(that.name_in_graph) //already added + that.graph.changeOutputType( that.name_in_graph, that.inputs[0].type); + that.type_widget.value = v || ""; }, enumerable: true }); + + this.name_widget = this.addWidget("text","Name", this.properties.name, function(v){ + if(!v) return; + that.properties.name = v; + }); + this.type_widget = this.addWidget("text","Type", this.properties.type, function(v){ + v = v || ""; + that.properties.type = v; + }); + + this.widgets_up = true; + this.size = [180,60]; } -GlobalOutput.title = "Output"; -GlobalOutput.desc = "Output of the graph"; +GraphOutput.title = "Output"; +GraphOutput.desc = "Output of the graph"; -GlobalOutput.prototype.onAdded = function() -{ - var name = this.graph.addGlobalOutput( this.properties.name, this.properties.type ); -} - -GlobalOutput.prototype.getValue = function() -{ - return this._value; -} - -GlobalOutput.prototype.onExecute = function() +GraphOutput.prototype.onExecute = function() { this._value = this.getInputData(0); - this.graph.setGlobalOutputData( this.properties.name, this._value ); + this.graph.setOutputData( this.properties.name, this._value ); } -LiteGraph.registerNodeType("graph/output", GlobalOutput); +GraphOutput.prototype.onRemoved = function() +{ + if(this.name_in_graph) + this.graph.removeOutput( this.name_in_graph ); +} + +GraphOutput.prototype.getTitle = function() +{ + if(this.flags.collapsed) + return this.properties.name; + return this.title; +} + +LiteGraph.registerNodeType("graph/output", GraphOutput); @@ -9832,7 +9867,7 @@ var LiteGraph = global.LiteGraph; this.addOutput( "v", "boolean" ); this.addOutput( "e", LiteGraph.EVENT ); this.properties = { font: "", value: false }; - this.size = [124,64]; + this.size = [160,44]; } WidgetToggle.title = "Toggle"; @@ -9846,17 +9881,19 @@ var LiteGraph = global.LiteGraph; var size = this.size[1] * 0.5; var margin = 0.25; var h = this.size[1] * 0.8; + ctx.font = this.properties.font || ((size * 0.8).toFixed(0) + "px Arial"); + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size) ) * 0.5; ctx.fillStyle = "#AAA"; - ctx.fillRect(10, h - size,size,size); + ctx.fillRect(x, h - size,size,size); ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect(10+size*margin,h - size + size*margin,size*(1-margin*2),size*(1-margin*2)); + ctx.fillRect(x + size*margin,h - size + size*margin,size*(1-margin*2),size*(1-margin*2) ); ctx.textAlign = "left"; - ctx.font = this.properties.font || ((size * 0.8).toFixed(0) + "px Arial"); ctx.fillStyle = "#AAA"; - ctx.fillText( this.title, size + 20, h * 0.85 ); + ctx.fillText( this.title, size * 1.2 + x, h * 0.85 ); ctx.textAlign = "left"; } @@ -11028,7 +11065,7 @@ function MathFloor() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; } MathFloor.title = "Floor"; @@ -11049,7 +11086,7 @@ function MathFrac() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; } MathFrac.title = "Frac"; @@ -11071,7 +11108,7 @@ function MathSmoothStep() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; this.properties = { A: 0, B: 1 }; } @@ -11102,7 +11139,7 @@ function MathScale() { this.addInput("in","number",{label:""}); this.addOutput("out","number",{label:""}); - this.size = [60,20]; + this.size = [80,30]; this.addProperty( "factor", 1 ); } @@ -11124,7 +11161,7 @@ function MathAverageFilter() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; this.addProperty( "samples", 10 ); this._values = new Float32Array(10); this._current = 0; @@ -11176,7 +11213,7 @@ function MathTendTo() this.addInput("in","number"); this.addOutput("out","number"); this.addProperty( "factor", 0.1 ); - this.size = [60,20]; + this.size = [80,30]; this._value = null; } @@ -11215,7 +11252,7 @@ MathOperation.values = ["+","-","*","/","%","^"]; MathOperation.title = "Operation"; MathOperation.desc = "Easy math operators"; MathOperation["@OP"] = { type:"enum", title: "operation", values: MathOperation.values }; -MathOperation.size = [100,50]; +MathOperation.size = [100,60]; MathOperation.prototype.getTitle = function() { @@ -11344,7 +11381,7 @@ function MathCondition() this.addProperty( "B", 1 ); this.addProperty( "OP", ">", "string", { values: MathCondition.values } ); - this.size = [60,40]; + this.size = [80,60]; } MathCondition.values = [">","<","==","!=","<=",">="]; @@ -12356,7 +12393,14 @@ if(global.glMatrix) LiteGraph.wrapFunctionAsNode("string/split",toUpperCase, ["String","String"],"Array"); + function toFixed(a) + { + if(a != null && a.constructor === Number) + return a.toFixed(this.properties.precision); + return a; + } + LiteGraph.wrapFunctionAsNode("string/toFixed", toFixed, ["Number"], "String", { precision: 0 } ); })(this); (function(global){ diff --git a/build/litegraph.min.js b/build/litegraph.min.js index e64ef877c..1aecb24cf 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,319 +1,321 @@ -(function(u){function d(a){c.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function l(a,b,e,r,h,c){this.id=a;this.type=b;this.origin_id=e;this.origin_slot=r;this.target_id=h;this.target_slot=c;this._data=null;this._pos=new Float32Array(2)}function q(a){this._ctor(a)}function g(a){this._ctor(a)}function s(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(u){function d(a){c.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function k(a,b,e,t,l,c){this.id=a;this.type=b;this.origin_id=e;this.origin_slot=t;this.target_id=l;this.target_slot=c;this._data=null;this._pos=new Float32Array(2)}function q(a){this._ctor(a)}function g(a){this._ctor(a)}function r(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function m(a,b,e){e=e||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new s;this.zoom_modify_alpha=!0;this.title_text_font=""+c.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+c.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=c.NODE_TITLE_COLOR;this.default_link_color=c.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.render_only_selected= +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new r;this.zoom_modify_alpha=!0;this.title_text_font=""+c.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+c.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=c.NODE_TITLE_COLOR;this.default_link_color=c.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.render_only_selected= this.clear_background=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots=!0;this.render_execution_order= !1;this.render_title_colored=!0;this.links_render_mode=c.SPLINE_LINK;this.canvas_mouse=[0,0];this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();e.skip_render||this.startRendering();this.autoresize= -e.autoresize}function w(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function B(a,b,e,r,h,c){return ea&&rb?!0:!1}function z(a,b){var e=a[0]+a[2],r=a[1]+a[3],h=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>h||ek.width-d.width-10&&(c=k.width-d.width-10);f>k.height-d.height-10&&(f=k.height-d.height-10)}h.style.left=c+"px";h.style.top=f+"px";b.scale&&(h.style.transform="scale("+b.scale+")")}var c=u.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10, +e.autoresize}function w(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function B(a,b,e,t,l,c){return ea&&tb?!0:!1}function z(a,b){var e=a[0]+a[2],t=a[1]+a[3],l=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>l||eh.width-d.width-10&&(c=h.width-d.width-10);f>h.height-d.height-10&&(f=h.height-d.height-10)}l.style.left=c+"px";l.style.top=f+"px";b.scale&&(l.style.transform="scale("+b.scale+")")}var c=u.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3, DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{}, -Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;c.debug&&console.log("Node registered: "+a);a.split("/");var e=b.name,r=a.lastIndexOf("/");b.category=a.substr(0,r);b.title||(b.title=e);if(b.prototype)for(var h in q.prototype)b.prototype[h]||(b.prototype[h]=q.prototype[h]);Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape= -c.BOX_SHAPE;break;case "round":this._shape=c.ROUND_SHAPE;break;case "circle":this._shape=c.CIRCLE_SHAPE;break;case "card":this._shape=c.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0});this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[e]=b);b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end");if(b.supported_extensions)for(h in b.supported_extensions)this.node_types_by_file_extension[b.supported_extensions[h].toLowerCase()]= -b},wrapFunctionAsNode:function(a,b,e,r){for(var h=Array(b.length),f="",k=c.getParameterNames(b),d=0;df&&(f=h.size[0]),k+=h.size[1]+a;b+=f+a}this.setDirtyCanvas(!0,!0)};d.prototype.getTime=function(){return this.globaltime};d.prototype.getFixedTime=function(){return this.fixedtime};d.prototype.getElapsedTime=function(){return this.elapsed_time};d.prototype.sendEventToAllNodes=function(a,b,e){e=e||c.ALWAYS;var r=this._nodes_in_order?this._nodes_in_order:this._nodes; -if(r)for(var h=0,f=r.length;h=c.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};q.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var e in a.properties){if(this.properties[e]= -a.properties[e],this.onPropertyChanged)this.onPropertyChanged(e,a.properties[e])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=c.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(e=0;e=this.outputs.length)){var e=this.outputs[a];if(e&&(e._data=b,this.outputs[a].links))for(e=0;e=this.outputs.length)){var e=this.outputs[a];if(e&&(e.type=b,this.outputs[a].links))for(e=0;e=this.inputs.length||null==this.inputs[a].link)){var e=this.graph.links[this.inputs[a].link];if(!e)return null;if(!b)return e.data;var c=this.graph.getNodeById(e.origin_id);if(!c)return e.data;if(c.updateOutputData)c.updateOutputData(e.origin_slot);else if(c.onExecute)c.onExecute();return e.data}};q.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id); -return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};q.prototype.getInputDataByName=function(a,b){var e=this.findInputSlot(a);return-1==e?null:this.getInputData(e,b)};q.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&& -null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};q.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,e=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};q.prototype.getOutputInfo= -function(a){return this.outputs?a=this.outputs.length)return null; -a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],e=0;ea&&this.pos[1]-h-eb)return!0;return!1};q.prototype.getSlotInPosition=function(a,b){var e=new Float32Array(2);if(this.inputs)for(var c=0,h=this.inputs.length;c=this.outputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); -if(!b)throw"target node is null";if(b==this)return null;if(e.constructor===String){if(e=b.findInputSlot(e),-1==e)return c.debug&&console.log("Connect: Error, no slot of name "+e),null}else{if(e===c.EVENT)return null;if(!b.inputs||e>=b.inputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[e].link&&b.disconnectInput(e);var r=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(e,r.type,r))return null;var h=b.inputs[e],f=null;if(c.isValidConnection(r.type, -h.type)){f=new l(this.graph.last_link_id++,h.type,this.id,a,b.id,e);this.graph.links[f.id]=f;null==r.links&&(r.links=[]);r.links.push(f.id);b.inputs[e].link=f.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(c.OUTPUT,a,!0,f,r);if(b.onConnectionsChange)b.onConnectionsChange(c.INPUT,e,!0,f,h);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(c.INPUT,b,e,this,a),this.graph.onNodeConnectionChange(c.OUTPUT,this,a,b,e))}this.setDirtyCanvas(!1, -!0);this.graph.connectionChange(this,f);return f};q.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return c.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),!1;var e=this.outputs[a];if(!e||!e.links||0==e.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found"; -for(var r=0,h=e.links.length;r=this.inputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var e=this.inputs[a].link;this.inputs[a].link=null;var r=this.graph.links[e];if(r){var h=this.graph.getNodeById(r.origin_id);if(!h)return!1;var f=h.outputs[r.origin_slot];if(!f||!f.links||0==f.links.length)return!1;for(var k=0,d=f.links.length;kb&&this.inputs[b].pos)return e[0]=this.pos[0]+this.inputs[b].pos[0],e[1]=this.pos[1]+this.inputs[b].pos[1],e;if(!a&&r>b&&this.outputs[b].pos)return e[0]=this.pos[0]+this.outputs[b].pos[0], -e[1]=this.pos[1]+this.outputs[b].pos[1],e;if(this.horizontal)return e[0]=this.pos[0]+this.size[0]/r*(b+0.5),e[1]=a?this.pos[1]-c.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],e;e[0]=a?this.pos[0]+h:this.pos[0]+this.size[0]+1-h;e[1]=this.pos[1]+(b+0.7)*c.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return e};q.prototype.alignToGrid=function(){this.pos[0]=c.CANVAS_GRID_SIZE*Math.round(this.pos[0]/c.CANVAS_GRID_SIZE);this.pos[1]=c.CANVAS_GRID_SIZE*Math.round(this.pos[1]/c.CANVAS_GRID_SIZE)};q.prototype.trace= -function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>q.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};q.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};q.prototype.loadImage=function(a){var b=new Image;b.src=c.node_images_path+a;b.ready=!1;var e=this;b.onload=function(){this.ready=!0;e.setDirtyCanvas(!0)};return b};q.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b= -this.graph.list_of_graphcanvas,e=0;ea.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0}); -Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};g.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};g.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};g.prototype.move= -function(a,b,e){this._pos[0]+=a;this._pos[1]+=b;if(!e)for(e=0;ethis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var e=this.element.getBoundingClientRect(); -if(e){b=b||[0.5*e.width,0.5*e.height];e=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var c=this.convertCanvasToOffset(b),e=[c[0]-e[0],c[1]-e[1]];this.offset[0]+=e[0];this.offset[1]+=e[1];if(this.onredraw)this.onredraw(this)}}};s.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};s.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};u.LGraphCanvas=c.LGraphCanvas=m;m.link_type_colors={"-1":c.EVENT_LINK_COLOR,number:"#AAA", -node:"#DCA"};m.gradients={};m.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]); -if(this.onClear)this.onClear()};m.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};m.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};m.prototype.closeSubgraph=function(){if(this._graph_stack&& -0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};m.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas"; -a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesnt support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL()); -this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};m.prototype._doNothing=function(a){a.preventDefault();return!1};m.prototype._doReturnTrue=function(a){a.preventDefault();return!0};m.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback= -this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel", -this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};m.prototype.unbindEvents=function(){if(this._events_binded){var a= -this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter", -this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};m.getFileExtension=function(a){var b=a.indexOf("?");-1!= -b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};m.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};m.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas= -!0)};m.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};m.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};m.prototype.stopRendering=function(){this.is_rendering=!1};m.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a); -var b=this.getCanvasWindow();m.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var e=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),f=!1,h=300>c.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();c.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1== -a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,f=!0);var k=!1;if(e&&this.allow_interaction&&!f){this.live_mode||e.flags.pinned||this.bringToFront(e);if(!this.connecting_node&&!e.flags.collapsed&&!this.live_mode)if(!f&&!1!==e.resizable&&B(a.canvasX,a.canvasY,e.pos[0]+e.size[0]-5,e.pos[1]+e.size[1]-5,10,10))this.resizing_node=e,this.canvas.style.cursor= -"se-resize",f=!0;else{if(e.outputs)for(var d=0,n=e.outputs.length;dk[0]+4||a.canvasYk[1]+4)){this.showLinkMenu(e,a);break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&(a.ctrlKey&&(this.dragging_rectangle=null),10>w([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing= -!0:this.selected_group.recomputeInsideNodes());h&&this.showSearchBox(a);k=!0}!f&&k&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&this.processContextMenu(e,a);this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=c.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation(); -if(this.onMouseDown)this.onMouseDown(a);return!1}}};m.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){m.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],e=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas= -!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(e[0]/this.ds.scale,e[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= -e[0]/this.ds.scale,this.ds.offset[1]+=e[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction){this.connecting_node&&(this.dirty_canvas=!0);for(var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,h=this.graph._nodes.length;bthis.dragging_rectangle[3]?this.dragging_rectangle[1]-h:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=k;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=h;h=[];for(k=0;ka.click_time&& -B(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-c.NODE_TITLE_HEIGHT,c.NODE_TITLE_HEIGHT,c.NODE_TITLE_HEIGHT)&&f.collapse(),this.dirty_bgcanvas=this.dirty_canvas=!0,this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]),this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]),this.graph.config.align_to_grid&&this.node_dragged.alignToGrid(),this.node_dragged=null;else{f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas= -!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change(); -a.stopPropagation();a.preventDefault();return!1}};m.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var e=this.ds.scale;0b&&(e*=1/1.1);this.ds.changeScale(e,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};m.prototype.isOverNodeBox=function(a,b,e){var f=c.NODE_TITLE_HEIGHT;return B(b,e,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};m.prototype.isOverNodeInput=function(a, -b,e,c){if(a.inputs)for(var h=0,f=a.inputs.length;hf&&(f=l.size[0]),h+=l.size[1]+a;b+=f+a}this.setDirtyCanvas(!0,!0)};d.prototype.getTime=function(){return this.globaltime};d.prototype.getFixedTime=function(){return this.fixedtime};d.prototype.getElapsedTime=function(){return this.elapsed_time}; +d.prototype.sendEventToAllNodes=function(a,b,e){e=e||c.ALWAYS;var t=this._nodes_in_order?this._nodes_in_order:this._nodes;if(t)for(var l=0,f=t.length;l=c.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags= +{}};q.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var e in a.properties){if(this.properties[e]=a.properties[e],this.onPropertyChanged)this.onPropertyChanged(e,a.properties[e])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=c.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(e=0;e=this.outputs.length)){var e=this.outputs[a];if(e&&(e._data=b,this.outputs[a].links))for(e=0;e=this.outputs.length)){var e=this.outputs[a];if(e&&(e.type= +b,this.outputs[a].links))for(e=0;e=this.inputs.length||null==this.inputs[a].link)){var e=this.graph.links[this.inputs[a].link];if(!e)return null;if(!b)return e.data;var c=this.graph.getNodeById(e.origin_id);if(!c)return e.data;if(c.updateOutputData)c.updateOutputData(e.origin_slot);else if(c.onExecute)c.onExecute();return e.data}};q.prototype.getInputDataType= +function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};q.prototype.getInputDataByName=function(a,b){var e=this.findInputSlot(a);return-1==e?null:this.getInputData(e,b)};q.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};q.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,e=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};q.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],e=0;ea&&this.pos[1]-l-eb)return!0;return!1};q.prototype.getSlotInPosition=function(a,b){var e=new Float32Array(2);if(this.inputs)for(var c=0,l=this.inputs.length;c< +l;++c){var f=this.inputs[c];this.getConnectionPos(!0,c,e);if(B(a,b,e[0]-10,e[1]-5,20,10))return{input:f,slot:c,link_pos:e,locked:f.locked}}if(this.outputs)for(c=0,l=this.outputs.length;c=this.outputs.length)return c.debug&& +console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(e.constructor===String){if(e=b.findInputSlot(e),-1==e)return c.debug&&console.log("Connect: Error, no slot of name "+e),null}else{if(e===c.EVENT)return null;if(!b.inputs||e>=b.inputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[e].link&&b.disconnectInput(e);var t=this.outputs[a]; +if(b.onConnectInput&&!1===b.onConnectInput(e,t.type,t))return null;var l=b.inputs[e],f=null;if(c.isValidConnection(t.type,l.type)){f=new k(this.graph.last_link_id++,l.type,this.id,a,b.id,e);this.graph.links[f.id]=f;null==t.links&&(t.links=[]);t.links.push(f.id);b.inputs[e].link=f.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(c.OUTPUT,a,!0,f,t);if(b.onConnectionsChange)b.onConnectionsChange(c.INPUT,e,!0,f,l);this.graph&&this.graph.onNodeConnectionChange&& +(this.graph.onNodeConnectionChange(c.INPUT,b,e,this,a),this.graph.onNodeConnectionChange(c.OUTPUT,this,a,b,e))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,f);return f};q.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return c.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),!1;var e=this.outputs[a];if(!e|| +!e.links||0==e.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,l=e.links.length;f=this.inputs.length)return c.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var e=this.inputs[a].link;this.inputs[a].link=null;var f=this.graph.links[e];if(f){var l=this.graph.getNodeById(f.origin_id);if(!l)return!1;var h=l.outputs[f.origin_slot];if(!h||!h.links||0==h.links.length)return!1;for(var d=0,m=h.links.length;d< +m;d++)if(h.links[d]==e){h.links.splice(d,1);break}delete this.graph.links[e];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(c.INPUT,a,!1,f,b);if(l.onConnectionsChange)l.onConnectionsChange(c.OUTPUT,d,!1,f,h);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(c.OUTPUT,l,d),this.graph.onNodeConnectionChange(c.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};q.prototype.getConnectionPos=function(a, +b,e){e=e||new Float32Array(2);var f=0;a&&this.inputs&&(f=this.inputs.length);!a&&this.outputs&&(f=this.outputs.length);var l=0.5*c.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||c.NODE_COLLAPSED_WIDTH,this.horizontal?(e[0]=this.pos[0]+0.5*b,e[1]=a?this.pos[1]-c.NODE_TITLE_HEIGHT:this.pos[1]):(e[0]=a?this.pos[0]:this.pos[0]+b,e[1]=this.pos[1]-0.5*c.NODE_TITLE_HEIGHT),e;if(a&&-1==b)return e[0]=this.pos[0]+0.5*c.NODE_TITLE_HEIGHT,e[1]=this.pos[1]+0.5*c.NODE_TITLE_HEIGHT,e;if(a&& +f>b&&this.inputs[b].pos)return e[0]=this.pos[0]+this.inputs[b].pos[0],e[1]=this.pos[1]+this.inputs[b].pos[1],e;if(!a&&f>b&&this.outputs[b].pos)return e[0]=this.pos[0]+this.outputs[b].pos[0],e[1]=this.pos[1]+this.outputs[b].pos[1],e;if(this.horizontal)return e[0]=this.pos[0]+this.size[0]/f*(b+0.5),e[1]=a?this.pos[1]-c.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],e;e[0]=a?this.pos[0]+l:this.pos[0]+this.size[0]+1-l;e[1]=this.pos[1]+(b+0.7)*c.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return e}; +q.prototype.alignToGrid=function(){this.pos[0]=c.CANVAS_GRID_SIZE*Math.round(this.pos[0]/c.CANVAS_GRID_SIZE);this.pos[1]=c.CANVAS_GRID_SIZE*Math.round(this.pos[1]/c.CANVAS_GRID_SIZE)};q.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>q.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};q.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};q.prototype.loadImage=function(a){var b=new Image; +b.src=c.node_images_path+a;b.ready=!1;var e=this;b.onload=function(){this.ready=!0;e.setDirtyCanvas(!0)};return b};q.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,e=0;ea.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};g.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; +this.font=a.font};g.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};g.prototype.move=function(a,b,e){this._pos[0]+=a;this._pos[1]+=b;if(!e)for(e=0;ethis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var e=this.element.getBoundingClientRect();if(e){b=b||[0.5*e.width,0.5*e.height];e=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var c=this.convertCanvasToOffset(b),e=[c[0]-e[0],c[1]-e[1]];this.offset[0]+=e[0];this.offset[1]+=e[1];if(this.onredraw)this.onredraw(this)}}}; +r.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};r.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};u.LGraphCanvas=c.LGraphCanvas=m;m.link_type_colors={"-1":c.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};m.gradients={};m.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= +this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};m.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};m.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; +if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};m.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};m.prototype.setCanvas= +function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+ +a.localName;throw"This browser doesnt support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};m.prototype._doNothing=function(a){a.preventDefault();return!1};m.prototype._doReturnTrue=function(a){a.preventDefault();return!0};m.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded"); +else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart", +this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop", +this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};m.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup", +this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback= +this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};m.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};m.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas); +this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};m.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};m.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};m.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering|| +(this.is_rendering=!0,a.call(this))};m.prototype.stopRendering=function(){this.is_rendering=!1};m.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();m.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var e=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5), +f=!1,l=300>c.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();c.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,f=!0);var h=!1;if(e&&this.allow_interaction&&!f){this.live_mode||e.flags.pinned||this.bringToFront(e);if(!this.connecting_node&& +!e.flags.collapsed&&!this.live_mode)if(!f&&!1!==e.resizable&&B(a.canvasX,a.canvasY,e.pos[0]+e.size[0]-5,e.pos[1]+e.size[1]-5,10,10))this.resizing_node=e,this.canvas.style.cursor="se-resize",f=!0;else{if(e.outputs)for(var d=0,n=e.outputs.length;dh[0]+4||a.canvasYh[1]+4)){this.showLinkMenu(e,a);break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&(a.ctrlKey&&(this.dragging_rectangle=null),10>w([a.canvasX,a.canvasY], +[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());l&&this.showSearchBox(a);h=!0}!f&&h&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&this.processContextMenu(e,a);this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=c.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement|| +"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};m.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){m.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],e=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY; +a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(e[0]/ +this.ds.scale,e[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=e[0]/this.ds.scale,this.ds.offset[1]+=e[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction){this.connecting_node&&(this.dirty_canvas=!0);for(var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,l=this.graph._nodes.length;bthis.dragging_rectangle[3]?this.dragging_rectangle[1]-l:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0]; +this.dragging_rectangle[1]=d;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=l;l=[];for(d=0;da.click_time&&B(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-c.NODE_TITLE_HEIGHT,c.NODE_TITLE_HEIGHT,c.NODE_TITLE_HEIGHT)&&f.collapse(),this.dirty_bgcanvas=this.dirty_canvas=!0,this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]),this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]),this.graph.config.align_to_grid&&this.node_dragged.alignToGrid(),this.node_dragged=null;else{f=this.graph.getNodeOnPos(a.canvasX, +a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas= +!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};m.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var e=this.ds.scale;0b&&(e*=1/1.1);this.ds.changeScale(e,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};m.prototype.isOverNodeBox=function(a,b,e){var f=c.NODE_TITLE_HEIGHT;return B(b, +e,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};m.prototype.isOverNodeInput=function(a,b,e,c){if(a.inputs)for(var f=0,d=a.inputs.length;fe-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};m.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),e=0;e> ";b.fillText(c+e.getTitle(),0.5*a.width,40);b.restore()}e=!1;this.onRenderBackground&&(e=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save(); -this.ds.toCanvasContext(b);if(this.background_image&&0.5> ";b.fillText(c+e.getTitle(),0.5*a.width,40);b.restore()}e=!1;this.onRenderBackground&&(e=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save(); +this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale,t=a._shape||a.constructor.shape||c.ROUND_SHAPE,l=a.constructor.title_mode,p=!0;l==c.TRANSPARENT_TITLE?p=!1:l==c.AUTOHIDE_TITLE&&n&&(p=!0);f[0]=0;f[1]=p?-h:0;f[2]=e[0]+1;f[3]=p?e[1]+h:e[1];n=b.globalAlpha;b.beginPath();t==c.BOX_SHAPE||g?b.fillRect(f[0],f[1],f[2],f[3]):t==c.ROUND_SHAPE||t==c.CARD_SHAPE? -b.roundRect(f[0],f[1],f[2],f[3],this.round_radius,t==c.CARD_SHAPE?0:this.round_radius):t==c.CIRCLE_SHAPE&&b.arc(0.5*e[0],0.5*e[1],0.5*e[0],0,2*Math.PI);b.fill();b.shadowColor="transparent";b.fillStyle="rgba(0,0,0,0.2)";b.fillRect(0,-1,f[2],2);b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||l==c.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,h,e,this.ds.scale,k);else if(l!=c.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p= -a.constructor.title_color||k;a.flags.collapsed&&(b.shadowColor=c.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var C=m.gradients[p];C||(C=m.gradients[p]=b.createLinearGradient(0,0,400,0),C.addColorStop(0,p),C.addColorStop(1,"#000"));b.fillStyle=C}else b.fillStyle=p;b.beginPath();t==c.BOX_SHAPE||g?b.rect(0,-h,e[0]+1,h):t!=c.ROUND_SHAPE&&t!=c.CARD_SHAPE||b.roundRect(0,-h,e[0]+1,h,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b, -h,e,this.ds.scale);else t==c.ROUND_SHAPE||t==c.CIRCLE_SHAPE||t==c.CARD_SHAPE?(g&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*h,-0.5*h,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||c.NODE_DEFAULT_BOXCOLOR,b.beginPath(),b.arc(0.5*h,-0.5*h,5,0,2*Math.PI),b.fill()):(g&&(b.fillStyle="black",b.fillRect(0.5*(h-10)-1,-0.5*(h+10)-1,12,12)),b.fillStyle=a.boxcolor||c.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(h-10),-0.5*(h+10),10,10));b.globalAlpha=n;if(a.onDrawTitleText)a.onDrawTitleText(b,h,e,this.ds.scale, -this.title_text_font,d);!g&&(b.font=this.title_text_font,g=a.getTitle())&&(b.fillStyle=d?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="center",n=b.measureText(g),b.fillText(g,h+0.5*n.width,c.NODE_TITLE_TEXT_Y-h),b.textAlign="left"):(b.textAlign="left",b.fillText(g,h,c.NODE_TITLE_TEXT_Y-h)));if(a.onDrawTitle)a.onDrawTitle(b)}if(d){if(a.onBounding)a.onBounding(f);l==c.TRANSPARENT_TITLE&&(f[1]-=h,f[3]+=h);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();t== -c.BOX_SHAPE?b.rect(-6+f[0],-6+f[1],12+f[2],12+f[3]):t==c.ROUND_SHAPE||t==c.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+f[0],-6+f[1],12+f[2],12+f[3],2*this.round_radius):t==c.CARD_SHAPE?b.roundRect(-6+f[0],-6+f[1],12+f[2],12+f[3],2*this.round_radius,2):t==c.CIRCLE_SHAPE&&b.arc(0.5*e[0],0.5*e[1],0.5*e[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=k;b.globalAlpha=1}};var A=new Float32Array(4),k=new Float32Array(4),t=new Float32Array(2),v=new Float32Array(2);m.prototype.drawConnections= -function(a){var b=c.getTime(),e=this.visible_area;A[0]=e[0]-20;A[1]=e[1]-20;A[2]=e[2]+40;A[3]=e[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var e=this.graph._nodes,f=0,h=e.length;fk[2]&&(k[0]+=k[2],k[2]=Math.abs(k[2]));0>k[3]&&(k[1]+=k[3],k[3]=Math.abs(k[3]));if(z(k,A)){var D=g.outputs[l],l=d.inputs[n];if(D&&l&&(g=D.dir||(g.horizontal?c.DOWN:c.RIGHT),l=l.dir||(d.horizontal?c.UP:c.LEFT),this.renderLink(a,p,C,m,!1,0,null,g,l),m&&m._last_time&&1E3>b-m._last_time)){var D=2-0.002*(b-m._last_time),E=a.globalAlpha;a.globalAlpha=E*D;this.renderLink(a,p,C,m,!0, -D,"white",g,l);a.globalAlpha=E}}}}}}a.globalAlpha=1};m.prototype.renderLink=function(a,b,e,f,h,k,d,n,g,t){f&&this.visible_links.push(f);!d&&f&&(d=f.color||m.link_type_colors[f.type]);d||(d=this.default_link_color);null!=f&&this.highlighted_links[f.id]&&(d="#FFF");n=n||c.RIGHT;g=g||c.LEFT;var p=w(b,e);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(C[0],C[1]),a.rotate(D),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(t[0],t[1]),a.rotate(E),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(h[0],h[1],5,0,2*Math.PI),a.fill());if(k)for(a.fillStyle=d,C=0;5>C;++C)k=(0.001*c.getTime()+0.2*C)%1,h=this.computeConnectionPoint(b,e,k,n,g),a.beginPath(),a.arc(h[0], -h[1],5,0,2*Math.PI),a.fill()};m.prototype.computeConnectionPoint=function(a,b,e,f,h){f=f||c.RIGHT;h=h||c.LEFT;var k=w(a,b),d=[a[0],a[1]],n=[b[0],b[1]];switch(f){case c.LEFT:d[0]+=-0.25*k;break;case c.RIGHT:d[0]+=0.25*k;break;case c.UP:d[1]+=-0.25*k;break;case c.DOWN:d[1]+=0.25*k}switch(h){case c.LEFT:n[0]+=-0.25*k;break;case c.RIGHT:n[0]+=0.25*k;break;case c.UP:n[1]+=-0.25*k;break;case c.DOWN:n[1]+=0.25*k}f=(1-e)*(1-e)*(1-e);h=3*(1-e)*(1-e)*e;k=3*(1-e)*e*e;e*=e*e;return[f*a[0]+h*d[0]+k*n[0]+e*b[0], -f*a[1]+h*d[1]+k*n[1]+e*b[1]]};m.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,e=0;ep.last_y&&dp.options.max&&(p.value=p.options.max);else if("mousedown"==e.type)if((f=p.options.values)&&f.constructor===Function&&(f=p.options.values(p,a)),h=40>h?-1:h>k-40?1:0,"number"==p.type)p.value+=0.1*h*(p.options.step||1),null!=p.options.min&&p.valuep.options.max&&(p.value=p.options.max);else if(h)e=f.indexOf(p.value)+h,e>=f.length&&(e=0),0>e&&(e=f.length-1),p.value=f[e];else{new c.ContextMenu(f,{scale:Math.max(1,this.ds.scale),event:e,className:"dark",callback:C.bind(p)},g);var C=function(a,b,e){this.value=a;n.dirty_canvas=!0;return!1}}p.callback&&setTimeout(function(){this.callback(this.value,n,a,b)}.bind(p),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==e.type&&(p.value=!p.value,p.callback&&setTimeout(function(){p.callback(p.value, -n,a,b)},20));break;case "string":case "text":"mousedown"==e.type&&this.prompt("Value",p.value,function(b){this.value=b;p.callback&&p.callback(b,n,a)}.bind(p),e);break;default:p.mouse&&p.mouse(ctx,e,[h,d],a)}return p}}return null};m.prototype.drawGroups=function(a,b){if(this.graph){var e=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fthis.ds.scale,s=a._shape||a.constructor.shape||c.ROUND_SHAPE,k=a.constructor.title_mode,p=!0;k==c.TRANSPARENT_TITLE?p=!1:k==c.AUTOHIDE_TITLE&&n&&(p=!0);f[0]=0;f[1]=p?-l:0;f[2]=e[0]+1;f[3]=p?e[1]+l:e[1];n=b.globalAlpha;b.beginPath();s==c.BOX_SHAPE||g?b.fillRect(f[0],f[1],f[2],f[3]):s==c.ROUND_SHAPE||s==c.CARD_SHAPE? +b.roundRect(f[0],f[1],f[2],f[3],this.round_radius,s==c.CARD_SHAPE?0:this.round_radius):s==c.CIRCLE_SHAPE&&b.arc(0.5*e[0],0.5*e[1],0.5*e[0],0,2*Math.PI);b.fill();b.shadowColor="transparent";b.fillStyle="rgba(0,0,0,0.2)";b.fillRect(0,-1,f[2],2);b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||k==c.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,l,e,this.ds.scale,d);else if(k!=c.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p= +a.constructor.title_color||d;a.flags.collapsed&&(b.shadowColor=c.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var C=m.gradients[p];C||(C=m.gradients[p]=b.createLinearGradient(0,0,400,0),C.addColorStop(0,p),C.addColorStop(1,"#000"));b.fillStyle=C}else b.fillStyle=p;b.beginPath();s==c.BOX_SHAPE||g?b.rect(0,-l,e[0]+1,l):s!=c.ROUND_SHAPE&&s!=c.CARD_SHAPE||b.roundRect(0,-l,e[0]+1,l,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b, +l,e,this.ds.scale);else s==c.ROUND_SHAPE||s==c.CIRCLE_SHAPE||s==c.CARD_SHAPE?(g&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*l,-0.5*l,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||c.NODE_DEFAULT_BOXCOLOR,b.beginPath(),b.arc(0.5*l,-0.5*l,5,0,2*Math.PI),b.fill()):(g&&(b.fillStyle="black",b.fillRect(0.5*(l-10)-1,-0.5*(l+10)-1,12,12)),b.fillStyle=a.boxcolor||c.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(l-10),-0.5*(l+10),10,10));b.globalAlpha=n;if(a.onDrawTitleText)a.onDrawTitleText(b,l,e,this.ds.scale, +this.title_text_font,h);!g&&(b.font=this.title_text_font,g=a.getTitle())&&(b.fillStyle=h?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="center",n=b.measureText(g),b.fillText(g,l+0.5*n.width,c.NODE_TITLE_TEXT_Y-l),b.textAlign="left"):(b.textAlign="left",b.fillText(g,l,c.NODE_TITLE_TEXT_Y-l)));if(a.onDrawTitle)a.onDrawTitle(b)}if(h){if(a.onBounding)a.onBounding(f);k==c.TRANSPARENT_TITLE&&(f[1]-=l,f[3]+=l);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();s== +c.BOX_SHAPE?b.rect(-6+f[0],-6+f[1],12+f[2],12+f[3]):s==c.ROUND_SHAPE||s==c.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+f[0],-6+f[1],12+f[2],12+f[3],2*this.round_radius):s==c.CARD_SHAPE?b.roundRect(-6+f[0],-6+f[1],12+f[2],12+f[3],2*this.round_radius,2):s==c.CIRCLE_SHAPE&&b.arc(0.5*e[0],0.5*e[1],0.5*e[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=d;b.globalAlpha=1}};var A=new Float32Array(4),h=new Float32Array(4),s=new Float32Array(2),v=new Float32Array(2);m.prototype.drawConnections= +function(a){var b=c.getTime(),e=this.visible_area;A[0]=e[0]-20;A[1]=e[1]-20;A[2]=e[2]+40;A[3]=e[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var e=this.graph._nodes,f=0,l=e.length;fh[2]&&(h[0]+=h[2],h[2]=Math.abs(h[2]));0>h[3]&&(h[1]+=h[3],h[3]=Math.abs(h[3]));if(z(h,A)){var D=g.outputs[k],k=d.inputs[m];if(D&&k&&(g=D.dir||(g.horizontal?c.DOWN:c.RIGHT),k=k.dir||(d.horizontal?c.UP:c.LEFT),this.renderLink(a,p,C,n,!1,0,null,g,k),n&&n._last_time&&1E3>b-n._last_time)){var D=2-0.002*(b-n._last_time),E=a.globalAlpha;a.globalAlpha=E*D;this.renderLink(a,p,C,n,!0, +D,"white",g,k);a.globalAlpha=E}}}}}}a.globalAlpha=1};m.prototype.renderLink=function(a,b,e,f,l,d,h,n,g,s){f&&this.visible_links.push(f);!h&&f&&(h=f.color||m.link_type_colors[f.type]);h||(h=this.default_link_color);null!=f&&this.highlighted_links[f.id]&&(h="#FFF");n=n||c.RIGHT;g=g||c.LEFT;var p=w(b,e);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(C[0],C[1]),a.rotate(D),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(s[0],s[1]),a.rotate(E),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(l[0],l[1],5,0,2*Math.PI),a.fill());if(d)for(a.fillStyle=h,C=0;5>C;++C)d=(0.001*c.getTime()+0.2*C)%1,l=this.computeConnectionPoint(b,e,d,n,g),a.beginPath(),a.arc(l[0], +l[1],5,0,2*Math.PI),a.fill()};m.prototype.computeConnectionPoint=function(a,b,e,f,l){f=f||c.RIGHT;l=l||c.LEFT;var d=w(a,b),h=[a[0],a[1]],n=[b[0],b[1]];switch(f){case c.LEFT:h[0]+=-0.25*d;break;case c.RIGHT:h[0]+=0.25*d;break;case c.UP:h[1]+=-0.25*d;break;case c.DOWN:h[1]+=0.25*d}switch(l){case c.LEFT:n[0]+=-0.25*d;break;case c.RIGHT:n[0]+=0.25*d;break;case c.UP:n[1]+=-0.25*d;break;case c.DOWN:n[1]+=0.25*d}f=(1-e)*(1-e)*(1-e);l=3*(1-e)*(1-e)*e;d=3*(1-e)*e*e;e*=e*e;return[f*a[0]+l*h[0]+d*n[0]+e*b[0], +f*a[1]+l*h[1]+d*n[1]+e*b[1]]};m.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,e=0;ep.last_y&&hp.options.max&&(p.value=p.options.max);else if("mousedown"==e.type)if((f=p.options.values)&&f.constructor===Function&&(f=p.options.values(p,a)),l=40>l?-1:l>d-40?1:0,"number"==p.type)p.value+=0.1*l*(p.options.step||1),null!=p.options.min&&p.valuep.options.max&&(p.value=p.options.max);else if(l)e=f.indexOf(p.value)+l,e>=f.length&&(e=0),0>e&&(e=f.length-1),p.value=f[e];else{new c.ContextMenu(f,{scale:Math.max(1,this.ds.scale),event:e,className:"dark",callback:C.bind(p)},g);var C=function(a,b,e){this.value=a;n.dirty_canvas=!0;return!1}}p.callback&&setTimeout(function(){this.callback(this.value,n,a,b)}.bind(p),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==e.type&&(p.value=!p.value,p.callback&&setTimeout(function(){p.callback(p.value, +n,a,b)},20));break;case "string":case "text":"mousedown"==e.type&&this.prompt("Value",p.value,function(b){this.value=b;p.callback&&p.callback(b,n,a)}.bind(p),e);break;default:p.mouse&&p.mouse(ctx,e,[l,h],a)}return p}}return null};m.prototype.drawGroups=function(a,b){if(this.graph){var e=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fe&&0.01>b.editor_alpha&&(clearInterval(c),1>e&&(b.live_mode=!0));1"+g+""+a+"",value:g});if(n.length)return new c.ContextMenu(n,{event:e,callback:d,parentMenu:f,allow_html:!0,node:h},b),!1}};m.decodeHTML=function(a){var b=document.createElement("div"); -b.innerText=a;return b.innerHTML};m.onResizeNode=function(a,b,e,c,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};m.prototype.showLinkMenu=function(a,b){var e=this;new c.ContextMenu(["Delete"],{event:b,callback:function(b){switch(b){case "Delete":e.graph.removeLink(a.id)}}});return!1};m.onShowPropertyEditor=function(a,b,e,c,f){function d(){var b=g.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[k]=b;n.parentNode&&n.parentNode.removeChild(n);f.setDirtyCanvas(!0,!0)}var k= -a.property||"title";b=f[k];var n=document.createElement("div");n.className="graphdialog";n.innerHTML="";n.querySelector(".name").innerText=k;var g=n.querySelector("input");g&&(g.value=b,g.addEventListener("blur",function(a){this.focus()}),g.addEventListener("keydown",function(a){13==a.keyCode&&(d(),a.preventDefault(),a.stopPropagation())}));b=m.active_canvas.canvas;e=b.getBoundingClientRect();var t=c=-20;e&&(c-= -e.left,t-=e.top);event?(n.style.left=event.clientX+c+"px",n.style.top=event.clientY+t+"px"):(n.style.left=0.5*b.width+c+"px",n.style.top=0.5*b.height+t+"px");n.querySelector("button").addEventListener("click",d);b.parentNode.appendChild(n)};m.prototype.prompt=function(a,b,e,c){var f=this;a=a||"";var d=document.createElement("div");d.className="graphdialog rounded";d.innerHTML=" ";d.close=function(){f.prompt_box= -null;d.parentNode&&d.parentNode.removeChild(d)};1m.search_limit))break}if(Array.prototype.filter)for(k=Object.keys(c.registered_node_types).filter(function(a){return-1!==a.toLowerCase().indexOf(e)}),d=0;dm.search_limit);d++); -else for(d in c.registered_node_types)if(-1!=d.indexOf(e)&&(a(d),-1!==m.search_limit&&p++>m.search_limit))break}}var h=this,d=document.createElement("div");d.className="litegraph litesearchbox graphdialog rounded";d.innerHTML="Search
";d.close=function(){h.search_box=null;document.body.focus();setTimeout(function(){h.canvas.focus()},20);d.parentNode&&d.parentNode.removeChild(d)};var k=null;1";else if("enum"==d&&k.values){g=""}else if("boolean"==d)g="";else{console.warn("unknown type: "+d);return}var p=this.createDialog(""+b+""+g+"",e);if("enum"==d&&k.values){var C=p.querySelector("select");C.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==d)(C=p.querySelector("input"))&&C.addEventListener("click",function(a){f(!!C.checked)});else if(C=p.querySelector("input"))C.addEventListener("blur",function(a){this.focus()}),C.value=void 0!==a.properties[b]?a.properties[b]: -"",C.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())});p.querySelector("button").addEventListener("click",c)}};m.prototype.createDialog=function(a,b){b=b||{};var e=document.createElement("div");e.className="graphdialog";e.innerHTML=a;var c=this.canvas.getBoundingClientRect(),f=-20,d=-20;c&&(f-=c.left,d-=c.top);b.position?(f+=b.position[0],d+=b.position[1]):b.event?(f+=b.event.clientX,d+=b.event.clientY):(f+=0.5*this.canvas.width,d+=0.5*this.canvas.height); -e.style.left=f+"px";e.style.top=d+"px";this.canvas.parentNode.appendChild(e);e.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return e};m.onMenuNodeCollapse=function(a,b,e,c,f){f.collapse()};m.onMenuNodePin=function(a,b,e,c,f){f.pin()};m.onMenuNodeMode=function(a,b,e,f,d){new c.ContextMenu(["Always","On Event","On Trigger","Never"],{event:e,callback:function(a){if(d)switch(a){case "On Event":d.mode=c.ON_EVENT;break;case "On Trigger":d.mode=c.ON_TRIGGER;break;case "Never":d.mode= -c.NEVER;break;default:d.mode=c.ALWAYS}},parentMenu:f,node:d});return!1};m.onMenuNodeColors=function(a,b,e,f,d){if(!d)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var k in m.node_colors)a=m.node_colors[k],a={value:k,content:""+k+""},b.push(a);new c.ContextMenu(b,{event:e,callback:function(a){d&& -((a=a.value?m.node_colors[a.value]:null)?d.constructor===c.LGraphGroup?d.color=a.groupcolor:(d.color=a.color,d.bgcolor=a.bgcolor):(delete d.color,delete d.bgcolor),d.setDirtyCanvas(!0,!0))},parentMenu:f,node:d});return!1};m.onMenuNodeShapes=function(a,b,e,f,d){if(!d)throw"no node passed";new c.ContextMenu(c.VALID_SHAPES,{event:e,callback:function(a){d&&(d.shape=a,d.setDirtyCanvas(!0))},parentMenu:f,node:d});return!1};m.onMenuNodeRemove=function(a,b,e,c,f){if(!f)throw"no node passed";!1!==f.removable&& -(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};m.onMenuNodeClone=function(a,b,e,c,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};m.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233", -bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};m.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:m.onMenuAdd},{content:"Add Group",callback:m.onGroupAdd}],this._graph_stack&&0Name",c),k=d.querySelector("input");k&&p&&(k.value=p.label);d.querySelector("button").addEventListener("click",function(a){k.value&&(p&&(p.label=k.value),e.setDirty(!0));d.close()})}},extra:a},n=null;a&&(n=a.getSlotInPosition(b.canvasX,b.canvasY),m.active_node=a);n?(d=[],n&&n.output&&n.output.links&&n.output.links.length&&d.push({content:"Disconnect Links",slot:n}),d.push(n.locked?"Cannot remove":{content:"Remove Slot", -slot:n}),d.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:n}),k.title=(n.input?n.input.type:n.output.type)||"*",n.input&&n.input.type==c.ACTION&&(k.title="Action"),n.output&&n.output.type==c.EVENT&&(k.title="Event")):a?d=this.getNodeMenuOptions(a):(d=this.getCanvasMenuOptions(),(n=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&d.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:n,options:this.getGroupMenuOptions(n)}}));d&&new c.ContextMenu(d,k,f)};this.CanvasRenderingContext2D&& -(CanvasRenderingContext2D.prototype.roundRect=function(a,b,e,c,f,d){void 0===f&&(f=5);void 0===d&&(d=f);this.moveTo(a+f,b);this.lineTo(a+e-f,b);this.quadraticCurveTo(a+e,b,a+e,b+f);this.lineTo(a+e,b+c-d);this.quadraticCurveTo(a+e,b+c,a+e-d,b+c);this.lineTo(a+d,b+c);this.quadraticCurveTo(a,b+c,a,b+c-d);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});c.compareObjects=function(a,b){for(var e in a)if(a[e]!=b[e])return!1;return!0};c.distance=w;c.colorToString=function(a){return"rgba("+Math.round(255* -a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};c.isInsideRectangle=B;c.growBounding=function(a,b,e){ba[2]&&(a[2]=b);ea[3]&&(a[3]=e)};c.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};c.overlapBounding=z;c.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),e=0,c,f,d=0;6>d;d+=2)c="0123456789ABCDEF".indexOf(a.charAt(d)), -f="0123456789ABCDEF".indexOf(a.charAt(d+1)),b[e]=16*c+f,e++;return b};c.num2hex=function(a){for(var b="#",e,c,f=0;3>f;f++)e=a[f]/16,c=a[f]%16,b+="0123456789ABCDEF".charAt(e)+"0123456789ABCDEF".charAt(c);return b};y.prototype.addItem=function(a,b,e){function c(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,c=!0;d.current_submenu&&d.current_submenu.close(a);if(e.callback){var k=e.callback.call(this,b,e,a,d,e.node);!0===k&&(c=!1)}if(b&&(b.callback&&!e.ignore_item_callbacks&& -!0!==b.disabled&&(k=b.callback.call(this,b,e,a,d,e.extra),!0===k&&(c=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new d.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:d,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:e.autoopen});c=!1}c&&!d.lock&&d.close()}var d=this;e=e||{};var k=document.createElement("div");k.className="litemenu-entry submenu";var n=!1;if(null===b)k.classList.add("separator"); -else{k.innerHTML=b&&b.title?b.title:a;if(k.value=b)b.disabled&&(n=!0,k.classList.add("disabled")),(b.submenu||b.has_submenu)&&k.classList.add("has_submenu");"function"==typeof b?(k.dataset.value=a,k.onclick_callback=b):k.dataset.value=b;b.className&&(k.className+=" "+b.className)}this.root.appendChild(k);n||k.addEventListener("click",f);e.autoopen&&k.addEventListener("mouseenter",c);return k};y.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&& -!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!y.isCursorOverElement(a,this.parentMenu.root)&&y.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};y.trigger=function(a,b,e,c){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,e);f.srcElement=c;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f); -return f};y.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};y.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};y.isCursorOverElement=function(a,b){var e=a.clientX,c=a.clientY,f=b.getBoundingClientRect();return f?c>f.top&&cf.left&&ea?b:ethis.size[0]-n.NODE_TITLE_HEIGHT&&0>d[1]){var g=this;setTimeout(function(){k.openSubgraph(g.subgraph)},10)}};l.prototype.onSubgraphNewGlobalInput=function(c,d){this.addInput(c,d)};l.prototype.onSubgraphRenamedGlobalInput=function(c,d){var k=this.findInputSlot(c);-1!=k&&(this.getInputInfo(k).name=d)};l.prototype.onSubgraphTypeChangeGlobalInput=function(c,d){var k=this.findInputSlot(c);-1!=k&&(this.getInputInfo(k).type=d)};l.prototype.onSubgraphNewGlobalOutput=function(c,d){this.addOutput(c,d)}; -l.prototype.onSubgraphRenamedGlobalOutput=function(c,d){var k=this.findOutputSlot(c);-1!=k&&(this.getOutputInfo(k).name=d)};l.prototype.onSubgraphTypeChangeGlobalOutput=function(c,d){var k=this.findOutputSlot(c);-1!=k&&(this.getOutputInfo(k).type=d)};l.prototype.getExtraMenuOptions=function(c){var d=this;return[{content:"Open",callback:function(){c.openSubgraph(d.subgraph)}}]};l.prototype.onResize=function(c){c[1]+=20};l.prototype.onExecute=function(){if(this.inputs)for(var c=0;c=m?this.trigger(null,g):this._pending.push([m,g])};s.prototype.onExecute=function(){var d=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var g=0;g"+g+""+a+"",value:g});if(n.length)return new c.ContextMenu(n,{event:e,callback:d,parentMenu:f,allow_html:!0,node:l},b),!1}};m.decodeHTML=function(a){var b=document.createElement("div"); +b.innerText=a;return b.innerHTML};m.onResizeNode=function(a,b,e,c,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};m.prototype.showLinkMenu=function(a,b){var e=this;new c.ContextMenu(["Delete"],{event:b,callback:function(b){switch(b){case "Delete":e.graph.removeLink(a.id)}}});return!1};m.onShowPropertyEditor=function(a,b,e,c,f){function d(){var b=g.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[h]=b;n.parentNode&&n.parentNode.removeChild(n);f.setDirtyCanvas(!0,!0)}var h= +a.property||"title";b=f[h];var n=document.createElement("div");n.className="graphdialog";n.innerHTML="";n.querySelector(".name").innerText=h;var g=n.querySelector("input");g&&(g.value=b,g.addEventListener("blur",function(a){this.focus()}),g.addEventListener("keydown",function(a){13==a.keyCode&&(d(),a.preventDefault(),a.stopPropagation())}));b=m.active_canvas.canvas;e=b.getBoundingClientRect();var s=c=-20;e&&(c-= +e.left,s-=e.top);event?(n.style.left=event.clientX+c+"px",n.style.top=event.clientY+s+"px"):(n.style.left=0.5*b.width+c+"px",n.style.top=0.5*b.height+s+"px");n.querySelector("button").addEventListener("click",d);b.parentNode.appendChild(n)};m.prototype.prompt=function(a,b,e,c){var f=this;a=a||"";var d=!1,h=document.createElement("div");h.className="graphdialog rounded";h.innerHTML=" ";h.close= +function(){f.prompt_box=null;h.parentNode&&h.parentNode.removeChild(h)};1m.search_limit))break}if(Array.prototype.filter)for(n=Object.keys(c.registered_node_types).filter(function(a){return-1!==a.toLowerCase().indexOf(e)}),d=0;dm.search_limit);d++);else for(d in c.registered_node_types)if(-1!=d.indexOf(e)&&(a(d),-1!==m.search_limit&&p++>m.search_limit))break}}var h=this,d=document.createElement("div");d.className="litegraph litesearchbox graphdialog rounded";d.innerHTML="Search
";d.close=function(){h.search_box=null;document.body.focus();setTimeout(function(){h.canvas.focus()},20);d.parentNode&& +d.parentNode.removeChild(d)};var n=null;1";else if("enum"==d&&h.values){g=""}else if("boolean"== +d)g="";else{console.warn("unknown type: "+d);return}var p=this.createDialog(""+b+""+g+"",e);if("enum"==d&&h.values){var C=p.querySelector("select");C.addEventListener("change",function(a){c(a.target.value)})}else if("boolean"==d)(C=p.querySelector("input"))&&C.addEventListener("click",function(a){c(!!C.checked)});else if(C=p.querySelector("input"))C.addEventListener("blur", +function(a){this.focus()}),C.value=void 0!==a.properties[b]?a.properties[b]:"",C.addEventListener("keydown",function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())});p.querySelector("button").addEventListener("click",f)}};m.prototype.createDialog=function(a,b){b=b||{};var e=document.createElement("div");e.className="graphdialog";e.innerHTML=a;var f=this.canvas.getBoundingClientRect(),c=-20,d=-20;f&&(c-=f.left,d-=f.top);b.position?(c+=b.position[0],d+=b.position[1]):b.event?(c+=b.event.clientX, +d+=b.event.clientY):(c+=0.5*this.canvas.width,d+=0.5*this.canvas.height);e.style.left=c+"px";e.style.top=d+"px";this.canvas.parentNode.appendChild(e);e.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return e};m.onMenuNodeCollapse=function(a,b,e,c,f){f.collapse()};m.onMenuNodePin=function(a,b,e,c,f){f.pin()};m.onMenuNodeMode=function(a,b,e,f,d){new c.ContextMenu(["Always","On Event","On Trigger","Never"],{event:e,callback:function(a){if(d)switch(a){case "On Event":d.mode=c.ON_EVENT; +break;case "On Trigger":d.mode=c.ON_TRIGGER;break;case "Never":d.mode=c.NEVER;break;default:d.mode=c.ALWAYS}},parentMenu:f,node:d});return!1};m.onMenuNodeColors=function(a,b,e,f,d){if(!d)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var h in m.node_colors)a=m.node_colors[h],a={value:h,content:""+h+""},b.push(a);new c.ContextMenu(b,{event:e,callback:function(a){d&&((a=a.value?m.node_colors[a.value]:null)?d.constructor===c.LGraphGroup?d.color=a.groupcolor:(d.color=a.color,d.bgcolor=a.bgcolor):(delete d.color,delete d.bgcolor),d.setDirtyCanvas(!0,!0))},parentMenu:f,node:d});return!1};m.onMenuNodeShapes=function(a,b,e,f,d){if(!d)throw"no node passed";new c.ContextMenu(c.VALID_SHAPES,{event:e,callback:function(a){d&&(d.shape=a,d.setDirtyCanvas(!0))},parentMenu:f,node:d});return!1}; +m.onMenuNodeRemove=function(a,b,e,f,c){if(!c)throw"no node passed";!1!==c.removable&&(c.graph.remove(c),c.setDirtyCanvas(!0,!0))};m.onMenuNodeClone=function(a,b,e,f,c){!1!=c.clonable&&(a=c.clone())&&(a.pos=[c.pos[0]+5,c.pos[1]+5],c.graph.add(a),c.setDirtyCanvas(!0,!0))};m.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, +pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};m.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:m.onMenuAdd},{content:"Add Group",callback:m.onGroupAdd}],this._graph_stack&& +0Name",c),h=d.querySelector("input");h&&p&&(h.value=p.label||"");d.querySelector("button").addEventListener("click",function(a){h.value&&(p&&(p.label=h.value),e.setDirty(!0));d.close()})}},extra:a},n=null;a&&(n=a.getSlotInPosition(b.canvasX,b.canvasY),m.active_node=a);n?(d=[],n&& +n.output&&n.output.links&&n.output.links.length&&d.push({content:"Disconnect Links",slot:n}),d.push(n.locked?"Cannot remove":{content:"Remove Slot",slot:n}),d.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:n}),h.title=(n.input?n.input.type:n.output.type)||"*",n.input&&n.input.type==c.ACTION&&(h.title="Action"),n.output&&n.output.type==c.EVENT&&(h.title="Event")):a?d=this.getNodeMenuOptions(a):(d=this.getCanvasMenuOptions(),(n=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&d.push(null, +{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:n,options:this.getGroupMenuOptions(n)}}));d&&new c.ContextMenu(d,h,f)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,e,c,f,d){void 0===f&&(f=5);void 0===d&&(d=f);this.moveTo(a+f,b);this.lineTo(a+e-f,b);this.quadraticCurveTo(a+e,b,a+e,b+f);this.lineTo(a+e,b+c-d);this.quadraticCurveTo(a+e,b+c,a+e-d,b+c);this.lineTo(a+d,b+c);this.quadraticCurveTo(a,b+c,a,b+c-d);this.lineTo(a,b+f);this.quadraticCurveTo(a, +b,a+f,b)});c.compareObjects=function(a,b){for(var e in a)if(a[e]!=b[e])return!1;return!0};c.distance=w;c.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};c.isInsideRectangle=B;c.growBounding=function(a,b,e){ba[2]&&(a[2]=b);ea[3]&&(a[3]=e)};c.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]? +!1:!0};c.overlapBounding=z;c.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),e=0,c,f,d=0;6>d;d+=2)c="0123456789ABCDEF".indexOf(a.charAt(d)),f="0123456789ABCDEF".indexOf(a.charAt(d+1)),b[e]=16*c+f,e++;return b};c.num2hex=function(a){for(var b="#",e,c,f=0;3>f;f++)e=a[f]/16,c=a[f]%16,b+="0123456789ABCDEF".charAt(e)+"0123456789ABCDEF".charAt(c);return b};y.prototype.addItem=function(a,b,e){function c(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b= +this.value,c=!0;d.current_submenu&&d.current_submenu.close(a);if(e.callback){var h=e.callback.call(this,b,e,a,d,e.node);!0===h&&(c=!1)}if(b&&(b.callback&&!e.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,e,a,d,e.extra),!0===h&&(c=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new d.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:d,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra, +autoopen:e.autoopen});c=!1}c&&!d.lock&&d.close()}var d=this;e=e||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var n=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(n=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+b.className)}this.root.appendChild(h);n||h.addEventListener("click", +f);e.autoopen&&h.addEventListener("mouseenter",c);return h};y.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!y.isCursorOverElement(a,this.parentMenu.root)&&y.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};y.trigger= +function(a,b,e,c){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,e);f.srcElement=c;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};y.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};y.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};y.isCursorOverElement=function(a,b){var e=a.clientX,c=a.clientY,f=b.getBoundingClientRect(); +return f?c>f.top&&cf.left&&ea?b:ethis.size[0]-n.NODE_TITLE_HEIGHT&&0>d[1]){var g=this;setTimeout(function(){h.openSubgraph(g.subgraph)},10)}};k.prototype.onExecute=function(){if(this.getInputOrProperty("enabled")){if(this.inputs)for(var c= +0;c=m?this.trigger(null,g):this._pending.push([m,g])};r.prototype.onExecute=function(){var d=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var g=0;gd[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};q.prototype.onMouseMove=function(c){if(this.mouse_captured){var d=this.old_y-c.canvasY;c.shiftKey&&(d*=10);if(c.metaKey||c.altKey)d*=0.1;this.old_y=c.canvasY;c=this._remainder+d/q.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+(c|0)*this.properties.step,this.properties.min,this.properties.max); -this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};q.prototype.onMouseUp=function(c,d){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(d[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};y.registerNodeType("widget/number",q);g.title="Knob";g.desc="Circular controller";g.size=[80,100];g.prototype.onDrawForeground= -function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));var d=0.5*this.size[0],n=0.5*this.size[1],f=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(d,n);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,f,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0, -0,f-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore();c.fillStyle="black";c.beginPath();c.arc(d,n,0.75*f,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var g=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(d+Math.cos(g)*f*0.65,n+Math.sin(g)*f*0.65,0.05*f,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*f)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision), -d,n+0.15*f)}};g.prototype.onExecute=function(){this.setOutputData(0,this.properties.value);this.boxcolor=y.colorToString([this.value,this.value,this.value])};g.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||y.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0}; -g.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var d=this.value,d=d-0.01*(c[1]-this.oldmouse[1]);1d&&(d=0);this.value=d;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};g.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};g.prototype.onPropertyChanged=function(c,d){if("min"==c||"max"==c||"value"==c)return this.properties[c]= -parseFloat(d),!0};y.registerNodeType("widget/knob",g);s.title="Internal Slider";s.prototype.onPropertyChanged=function(c,d){"value"==c&&(this.slider.value=d)};s.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};y.registerNodeType("widget/internal_slider",s);m.title="H.Slider";m.desc="Linear slider controller";m.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha= -1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2,2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};m.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=y.colorToString([this.value,this.value,this.value])};m.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1; -this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};m.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var d=this.value,d=d+(c[0]-this.oldmouse[0])/this.size[0];1d&&(d=0);this.value=d;this.oldmouse=c;this.setDirtyCanvas(!0)}};m.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};m.prototype.onMouseLeave=function(c){};y.registerNodeType("widget/hslider",m);w.title="Progress";w.desc= -"Shows data in linear progress";w.prototype.onExecute=function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var d=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),d=Math.min(1,d),d=Math.max(0,d);c.fillRect(2,2,(this.size[0]-4)*d,this.size[1]-4)};y.registerNodeType("widget/progress",w);B.title="Text";B.desc="Shows the input value";B.widgets=[{name:"resize", -text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];B.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var d=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var n=this.properties.fontsize;c.textAlign=this.properties.align;c.font=n.toString()+"px "+this.properties.font; -this.str="number"==typeof d?d.toFixed(this.properties.decimals):d;if("string"==typeof this.str){var d=this.str.split("\\n"),f;for(f in d)c.fillText(d[f],"left"==this.properties.align?15:this.size[0]-15,-0.15*n+n*(parseInt(f)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};B.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};B.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+ -"px "+this.properties.font;var d=0,n;for(n in c){var f=this.last_ctx.measureText(c[n]).width;dg?l.xbox.axes.lx:0,this._left_axis[1]=Math.abs(l.xbox.axes.ly)>g?l.xbox.axes.ly:0,this._right_axis[0]=Math.abs(l.xbox.axes.rx)>g?l.xbox.axes.rx:0,this._right_axis[1]=Math.abs(l.xbox.axes.ry)>g?l.xbox.axes.ry:0,this._triggers[0]=Math.abs(l.xbox.axes.ltrigger)>g?l.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(l.xbox.axes.rtrigger)>g?l.xbox.axes.rtrigger:0);if(this.outputs)for(g=0;gl;l++)if(g[l]){l=g[l];g=this.xbox_mapping;g||(g=this.xbox_mapping={axes:[],buttons:{},hat:"",hatmap:d.CENTER});g.axes.lx=l.axes[0];g.axes.ly=l.axes[1];g.axes.rx=l.axes[2];g.axes.ry=l.axes[3];g.axes.ltrigger=l.buttons[6].value; -g.axes.rtrigger=l.buttons[7].value;g.hat="";g.hatmap=d.CENTER;for(var s=0;sd[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};q.prototype.onMouseMove=function(c){if(this.mouse_captured){var d=this.old_y-c.canvasY;c.shiftKey&&(d*=10);if(c.metaKey||c.altKey)d*=0.1;this.old_y=c.canvasY;c=this._remainder+d/q.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};q.prototype.onMouseUp=function(c,d){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(d[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};y.registerNodeType("widget/number",q);g.title= +"Knob";g.desc="Circular controller";g.size=[80,100];g.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));var d=0.5*this.size[0],n=0.5*this.size[1],f=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(d,n);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,f,0,1.5*Math.PI);c.fill();c.strokeStyle="black"; +c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,f-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore();c.fillStyle="black";c.beginPath();c.arc(d,n,0.75*f,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var g=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(d+Math.cos(g)*f*0.65,n+Math.sin(g)*f*0.65,0.05*f,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA"; +c.font=Math.floor(0.5*f)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),d,n+0.15*f)}};g.prototype.onExecute=function(){this.setOutputData(0,this.properties.value);this.boxcolor=y.colorToString([this.value,this.value,this.value])};g.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||y.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+ +this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};g.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var d=this.value,d=d-0.01*(c[1]-this.oldmouse[1]);1d&&(d=0);this.value=d;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};g.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse= +null,this.captureInput(!1))};g.prototype.onPropertyChanged=function(c,d){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(d),!0};y.registerNodeType("widget/knob",g);r.title="Internal Slider";r.prototype.onPropertyChanged=function(c,d){"value"==c&&(this.slider.value=d)};r.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};y.registerNodeType("widget/internal_slider",r);m.title="H.Slider";m.desc="Linear slider controller";m.prototype.onDrawForeground=function(c){-1== +this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2,2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};m.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor= +y.colorToString([this.value,this.value,this.value])};m.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};m.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var d=this.value,d=d+(c[0]-this.oldmouse[0])/this.size[0];1d&&(d=0);this.value=d;this.oldmouse=c;this.setDirtyCanvas(!0)}};m.prototype.onMouseUp=function(c){this.oldmouse=null; +this.captureInput(!1)};m.prototype.onMouseLeave=function(c){};y.registerNodeType("widget/hslider",m);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute=function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var d=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),d=Math.min(1,d),d=Math.max(0,d);c.fillRect(2,2,(this.size[0]-4)* +d,this.size[1]-4)};y.registerNodeType("widget/progress",w);B.title="Text";B.desc="Shows the input value";B.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];B.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var d=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize): +c.shadowColor="transparent";var n=this.properties.fontsize;c.textAlign=this.properties.align;c.font=n.toString()+"px "+this.properties.font;this.str="number"==typeof d?d.toFixed(this.properties.decimals):d;if("string"==typeof this.str){var d=this.str.split("\\n"),f;for(f in d)c.fillText(d[f],"left"==this.properties.align?15:this.size[0]-15,-0.15*n+n*(parseInt(f)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};B.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&& +(this.properties.value=c)};B.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var d=0,n;for(n in c){var f=this.last_ctx.measureText(c[n]).width;dg?k.xbox.axes.lx:0,this._left_axis[1]=Math.abs(k.xbox.axes.ly)>g?k.xbox.axes.ly:0,this._right_axis[0]=Math.abs(k.xbox.axes.rx)>g?k.xbox.axes.rx:0,this._right_axis[1]=Math.abs(k.xbox.axes.ry)>g?k.xbox.axes.ry:0,this._triggers[0]=Math.abs(k.xbox.axes.ltrigger)>g?k.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(k.xbox.axes.rtrigger)>g?k.xbox.axes.rtrigger:0);if(this.outputs)for(g=0;gk;k++)if(g[k]){k=g[k];g=this.xbox_mapping;g||(g=this.xbox_mapping={axes:[],buttons:{},hat:"",hatmap:d.CENTER});g.axes.lx=k.axes[0];g.axes.ly=k.axes[1];g.axes.rx=k.axes[2];g.axes.ry=k.axes[3];g.axes.ltrigger=k.buttons[6].value; +g.axes.rtrigger=k.buttons[7].value;g.hat="";g.hatmap=d.CENTER;for(var r=0;r","string",{values:a.values});this.size=[60,40]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function e(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function r(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number"); -this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,e){e.properties.formula=a});this.addWidget("toggle","allow",p.allow_scripts,function(a){p.allow_scripts=a});this._func=null}function h(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function G(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function J(){this.addInput("vec3", +20];this.addProperty("min",0);this.addProperty("max",1)}function z(){this.properties={f:0.5};this.addInput("A","number");this.addInput("B","number");this.addOutput("out","number")}function y(){this.addInput("in","number");this.addOutput("out","number");this.size=[60,20]}function c(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30]}function x(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30]}function n(){this.addInput("in","number");this.addOutput("out", +"number");this.size=[80,30];this.properties={A:0,B:1}}function f(){this.addInput("in","number",{label:""});this.addOutput("out","number",{label:""});this.size=[80,30];this.addProperty("factor",1)}function A(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30];this.addProperty("samples",10);this._values=new Float32Array(10);this._current=0}function h(){this.addInput("in","number");this.addOutput("out","number");this.addProperty("factor",0.1);this.size=[80,30];this._value= +null}function s(){this.addInput("A","number");this.addInput("B","number");this.addOutput("=","number");this.addProperty("A",1);this.addProperty("B",1);this.addProperty("OP","+","enum",{values:s.values})}function v(){this.addInput("A","number");this.addInput("B","number");this.addOutput("A==B","boolean");this.addOutput("A!=B","boolean");this.addProperty("A",0);this.addProperty("B",0)}function a(){this.addInput("A","number");this.addInput("B","number");this.addOutput("out","boolean");this.addProperty("A", +1);this.addProperty("B",1);this.addProperty("OP",">","string",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function e(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function t(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number"); +this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,e){e.properties.formula=a});this.addWidget("toggle","allow",p.allow_scripts,function(a){p.allow_scripts=a});this._func=null}function l(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function G(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function J(){this.addInput("vec3", "vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function F(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function H(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]); this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var p=u.LiteGraph;d.title="Converter";d.desc="type A to type B";d.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;e=m.data[c];c=m.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return e*(1-a)+c*a};m.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=m.getValue(a,this.properties.smooth),b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0, this._last_v)};m.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};p.registerNodeType("math/noise",m);w.title="Spikes";w.desc="spike every random time";w.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+ this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};p.registerNodeType("math/spikes",w);B.title="Clamp";B.desc="Clamp number between min and max";B.filter="shader";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};B.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+ ","+this.properties.max+")");return a};p.registerNodeType("math/clamp",B);z.title="Lerp";z.desc="Linear Interpolation";z.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var e=this.properties.f,c=this.getInputData(2);void 0!==c&&(e=c);this.setOutputData(0,a*(1-e)+b*e)};z.prototype.onGetInputs=function(){return[["f","number"]]};p.registerNodeType("math/lerp",z);y.title="Abs";y.desc="Absolute";y.prototype.onExecute=function(){var a=this.getInputData(0); null!=a&&this.setOutputData(0,Math.abs(a))};p.registerNodeType("math/abs",y);c.title="Floor";c.desc="Floor number to remove fractional part";c.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};p.registerNodeType("math/floor",c);x.title="Frac";x.desc="Returns fractional part";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};p.registerNodeType("math/frac",x);n.title="Smoothstep";n.desc="Smoothstep"; n.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};p.registerNodeType("math/smoothstep",n);f.title="Scale";f.desc="v * factor";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};p.registerNodeType("math/scale",f);A.title="Average";A.desc="Average Filter";A.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var e=a=0;eb&&(b=1);this.properties.samples=Math.round(b);var e=this._values;this._values=new Float32Array(this.properties.samples);e.length<=this._values.length?this._values.set(e):this._values.set(e.subarray(0,this._values.length))};p.registerNodeType("math/average",A);k.title= -"TendTo";k.desc="moves the output value always closer to the input";k.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};p.registerNodeType("math/tendTo",k);t.values="+-*/%^".split("");t.title="Operation";t.desc="Easy math operators";t["@OP"]={type:"enum",title:"operation",values:t.values};t.size=[100,50];t.prototype.getTitle=function(){return"A "+this.properties.OP+ -" B"};t.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};t.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var e=0;switch(this.properties.OP){case "+":e=a+b;break;case "-":e=a-b;break;case "x":case "X":case "*":e=a*b;break;case "/":e=a/b;break;case "%":e=a%b;break;case "^":e=Math.pow(a,b);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,e)};t.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+p.NODE_TITLE_HEIGHT)),a.textAlign="left")};p.registerNodeType("math/operation",t);v.title="Compare";v.desc="compares between two values";v.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A; +null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var e=a=0;eb&&(b=1);this.properties.samples=Math.round(b);var e=this._values;this._values=new Float32Array(this.properties.samples);e.length<=this._values.length?this._values.set(e):this._values.set(e.subarray(0,this._values.length))};p.registerNodeType("math/average",A);h.title= +"TendTo";h.desc="moves the output value always closer to the input";h.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};p.registerNodeType("math/tendTo",h);s.values="+-*/%^".split("");s.title="Operation";s.desc="Easy math operators";s["@OP"]={type:"enum",title:"operation",values:s.values};s.size=[100,60];s.prototype.getTitle=function(){return"A "+this.properties.OP+ +" B"};s.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};s.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var e=0;switch(this.properties.OP){case "+":e=a+b;break;case "-":e=a-b;break;case "x":case "X":case "*":e=a*b;break;case "/":e=a/b;break;case "%":e=a%b;break;case "^":e=Math.pow(a,b);break;default:console.warn("Unknown operation: "+ +this.properties.OP)}this.setOutputData(0,e)};s.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+p.NODE_TITLE_HEIGHT)),a.textAlign="left")};p.registerNodeType("math/operation",s);v.title="Compare";v.desc="compares between two values";v.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A; void 0!==b?this.properties.B=b:b=this.properties.B;for(var e=0,c=this.outputs.length;eB":value=a>b;break;case "A=B":value=a>=b}this.setOutputData(e,value)}}};v.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]}; p.registerNodeType("math/compare",v);p.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});p.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});p.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});p.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});p.registerSearchboxExtra("math/compare", "<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >=".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var e=!0;switch(this.properties.OP){case ">":e=a>b;break;case "<":e=a=":e=a>=b}this.setOutputData(0,e)};p.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};p.registerNodeType("math/accumulate",b);e.title="Trigonometry"; e.desc="Sin Cos Tan";e.filter="shader";e.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,e=this.findInputSlot("amplitude");-1!=e&&(b=this.getInputData(e));var c=this.properties.offset,e=this.findInputSlot("offset");-1!=e&&(c=this.getInputData(e));for(var e=0,d=this.outputs.length;eVec2";G.desc="components to vector2";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var e=this._data;e[0]=a;e[1]=b;this.setOutputData(0,e)};p.registerNodeType("math3d/xy-to-vec2", +"COS()",{outputs:[["cos","number"]],title:"COS()"});p.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});t.title="Formula";t.desc="Compute formula";t.size=[160,100];A.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};t.prototype.onExecute=function(){if(p.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var e;try{this._func&& +this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),e=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,e)}};t.prototype.getTitle=function(){return this._func_code||"Formula"};t.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};p.registerNodeType("math/formula", +t);l.title="Vec2->XY";l.desc="vector 2 to components";l.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};p.registerNodeType("math3d/vec2-to-xyz",l);G.title="XY->Vec2";G.desc="components to vector2";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var e=this._data;e[0]=a;e[1]=b;this.setOutputData(0,e)};p.registerNodeType("math3d/xy-to-vec2", G);J.title="Vec3->XYZ";J.desc="vector 3 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};p.registerNodeType("math3d/vec3-to-xyz",J);F.title="XYZ->Vec3";F.desc="components to vector3";F.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var e=this.getInputData(2);null==e&&(e=this.properties.z); var c=this._data;c[0]=a;c[1]=b;c[2]=e;this.setOutputData(0,c)};p.registerNodeType("math3d/xyz-to-vec3",F);H.title="Vec4->XYZW";H.desc="vector 4 to components";H.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3,a[3]))};p.registerNodeType("math3d/vec4-to-xyzw",H);I.title="XYZW->Vec4";I.desc="components to vector4";I.prototype.onExecute=function(){var a=this.getInputData(0);null== a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var e=this.getInputData(2);null==e&&(e=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var d=this._data;d[0]=a;d[1]=b;d[2]=e;d[3]=c;this.setOutputData(0,d)};p.registerNodeType("math3d/xyzw-to-vec4",I);u.glMatrix&&(u=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},u.title="Quaternion",u.desc="quaternion",u.prototype.onExecute=function(){this._value[0]= @@ -321,42 +323,42 @@ this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.propertie var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},p.registerNodeType("math3d/rotation",u),u=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},u.title="Rot. Vec3",u.desc="rotate a point",u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0, vec3.transformQuat(vec3.create(),a,b))},p.registerNodeType("math3d/rotate_vec3",u),u=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},u.title="Mult. Quat",u.desc="rotate quaternion",u.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},p.registerNodeType("math3d/mult-quat",u),u=function(){this.addInputs([["A","quat"],["B", "quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},u.title="Quat Slerp",u.desc="quaternion spherical interpolation",u.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var e=this.properties.factor;null!=this.getInputData(2)&&(e=this.getInputData(2));a=quat.slerp(this._value,a,b,e);this.setOutputData(0,a)}}},p.registerNodeType("math3d/quat-slerp",u))})(this); -(function(u){function d(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function l(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function q(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function g(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties= -{x:0,y:0,z:0};this._data=new Float32Array(3)}function s(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function m(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}function w(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out","vec3");this.properties= +(function(u){function d(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function k(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function q(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function g(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties= +{x:0,y:0,z:0};this._data=new Float32Array(3)}function r(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function m(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}function w(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out","vec3");this.properties= {f:1};this._data=new Float32Array(3)}function B(){this.addInput("in","vec3");this.addOutput("out","number")}function z(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function y(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function c(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")}var x=u.LiteGraph;d.title= -"Vec2->XY";d.desc="vector 2 to components";d.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]))};x.registerNodeType("math3d/vec2-to-xyz",d);l.title="XY->Vec2";l.desc="components to vector2";l.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this._data;g[0]=c;g[1]=d;this.setOutputData(0,g)};x.registerNodeType("math3d/xy-to-vec2", -l);q.title="Vec3->XYZ";q.desc="vector 3 to components";q.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]),this.setOutputData(2,c[2]))};x.registerNodeType("math3d/vec3-to-xyz",q);g.title="XYZ->Vec3";g.desc="components to vector3";g.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this.getInputData(2);null==g&&(g=this.properties.z); -var k=this._data;k[0]=c;k[1]=d;k[2]=g;this.setOutputData(0,k)};x.registerNodeType("math3d/xyz-to-vec3",g);s.title="Vec4->XYZW";s.desc="vector 4 to components";s.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]),this.setOutputData(2,c[2]),this.setOutputData(3,c[3]))};x.registerNodeType("math3d/vec4-to-xyzw",s);m.title="XYZW->Vec4";m.desc="components to vector4";m.prototype.onExecute=function(){var c=this.getInputData(0);null== -c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this.getInputData(2);null==g&&(g=this.properties.z);var k=this.getInputData(3);null==k&&(k=this.properties.w);var m=this._data;m[0]=c;m[1]=d;m[2]=g;m[3]=k;this.setOutputData(0,m)};x.registerNodeType("math3d/xyzw-to-vec4",m);w.title="vec3_scale";w.desc="scales the components of a vec3";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);null==d&&(d=this.properties.f); +"Vec2->XY";d.desc="vector 2 to components";d.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]))};x.registerNodeType("math3d/vec2-to-xyz",d);k.title="XY->Vec2";k.desc="components to vector2";k.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this._data;g[0]=c;g[1]=d;this.setOutputData(0,g)};x.registerNodeType("math3d/xy-to-vec2", +k);q.title="Vec3->XYZ";q.desc="vector 3 to components";q.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]),this.setOutputData(2,c[2]))};x.registerNodeType("math3d/vec3-to-xyz",q);g.title="XYZ->Vec3";g.desc="components to vector3";g.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this.getInputData(2);null==g&&(g=this.properties.z); +var h=this._data;h[0]=c;h[1]=d;h[2]=g;this.setOutputData(0,h)};x.registerNodeType("math3d/xyz-to-vec3",g);r.title="Vec4->XYZW";r.desc="vector 4 to components";r.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.setOutputData(0,c[0]),this.setOutputData(1,c[1]),this.setOutputData(2,c[2]),this.setOutputData(3,c[3]))};x.registerNodeType("math3d/vec4-to-xyzw",r);m.title="XYZW->Vec4";m.desc="components to vector4";m.prototype.onExecute=function(){var c=this.getInputData(0);null== +c&&(c=this.properties.x);var d=this.getInputData(1);null==d&&(d=this.properties.y);var g=this.getInputData(2);null==g&&(g=this.properties.z);var h=this.getInputData(3);null==h&&(h=this.properties.w);var m=this._data;m[0]=c;m[1]=d;m[2]=g;m[3]=h;this.setOutputData(0,m)};x.registerNodeType("math3d/xyzw-to-vec4",m);w.title="vec3_scale";w.desc="scales the components of a vec3";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);null==d&&(d=this.properties.f); var g=this._data;g[0]=c[0]*d;g[1]=c[1]*d;g[2]=c[2]*d;this.setOutputData(0,g)}};x.registerNodeType("math3d/vec3-scale",w);B.title="vec3_length";B.desc="returns the module of a vector";B.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};x.registerNodeType("math3d/vec3-length",B);z.title="vec3_normalize";z.desc="returns the vector normalized";z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),g=this._data;g[0]=c[0]/d;g[1]=c[1]/d;g[2]=c[2]/d;this.setOutputData(0,g)}};x.registerNodeType("math3d/vec3-normalize",z);y.title="vec3_lerp";y.desc="returns the interpolated vector";y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);if(null!=d){var g=this.getInputOrProperty("f"),k=this._data;k[0]=c[0]*(1-g)+d[0]*g;k[1]=c[1]*(1-g)+d[1]*g;k[2]=c[2]*(1-g)+d[2]*g;this.setOutputData(0,k)}}};x.registerNodeType("math3d/vec3-lerp", +Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),g=this._data;g[0]=c[0]/d;g[1]=c[1]/d;g[2]=c[2]/d;this.setOutputData(0,g)}};x.registerNodeType("math3d/vec3-normalize",z);y.title="vec3_lerp";y.desc="returns the interpolated vector";y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);if(null!=d){var g=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-g)+d[0]*g;h[1]=c[1]*(1-g)+d[1]*g;h[2]=c[2]*(1-g)+d[2]*g;this.setOutputData(0,h)}}};x.registerNodeType("math3d/vec3-lerp", y);c.title="vec3_dot";c.desc="returns the dot product";c.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);null!=d&&this.setOutputData(0,c[0]*d[0]+c[1]*d[1]+c[2]*d[2])}};x.registerNodeType("math3d/vec3-dot",c);u.glMatrix&&(u=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},u.title="Quaternion",u.desc="quaternion",u.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y; this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},x.registerNodeType("math3d/quaternion",u),u=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},u.title="Rotation",u.desc="quaternion rotation",u.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var d=this.getInputData(1);null==d&& (d=this.properties.axis);c=quat.setAxisAngle(this._value,d,0.0174532925*c);this.setOutputData(0,c)},x.registerNodeType("math3d/rotation",u),u=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},u.title="Rot. Vec3",u.desc="rotate a point",u.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var d=this.getInputData(1);null==d?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), c,d))},x.registerNodeType("math3d/rotate_vec3",u),u=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},u.title="Mult. Quat",u.desc="rotate quaternion",u.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);null!=d&&(c=quat.multiply(this._value,c,d),this.setOutputData(0,c))}},x.registerNodeType("math3d/mult-quat",u),u=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", "quat");this.addProperty("factor",0.5);this._value=quat.create()},u.title="Quat Slerp",u.desc="quaternion spherical interpolation",u.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var d=this.getInputData(1);if(null!=d){var g=this.properties.factor;null!=this.getInputData(2)&&(g=this.getInputData(2));c=quat.slerp(this._value,c,d,g);this.setOutputData(0,c)}}},x.registerNodeType("math3d/quat-slerp",u))})(this); -(function(u){function d(d,g){return d==g}function l(d){return null!=d&&d.constructor===String?d.toUpperCase():d}u=u.LiteGraph;u.wrapFunctionAsNode("string/toString",d,["*"],"String");u.wrapFunctionAsNode("string/compare",d,["String","String"],"Boolean");u.wrapFunctionAsNode("string/concatenate",function(d,g){return void 0===d?g:void 0===g?d:d+g},["String","String"],"String");u.wrapFunctionAsNode("string/contains",function(d,g){return void 0===d||void 0===g?!1:-1!=d.indexOf(g)},["String","String"], -"Boolean");u.wrapFunctionAsNode("string/toUpperCase",l,["String"],"String");u.wrapFunctionAsNode("string/split",l,["String","String"],"Array")})(this); -(function(u){function d(){this.addInput("sel","number");this.addInput("A");this.addInput("B");this.addInput("C");this.addInput("D");this.addOutput("out");this.selected=0}function l(){this.properties={sequence:"A,B,C"};this.addInput("index","number");this.addInput("seq");this.addOutput("out");this.index=0;this.values=this.properties.sequence.split(",")}var q=u.LiteGraph;d.title="Selector";d.desc="selects an output";d.prototype.onDrawBackground=function(d){if(!this.flags.collapsed){d.fillStyle="#AFB"; -var l=(this.selected+1)*q.NODE_SLOT_HEIGHT+6;d.beginPath();d.moveTo(50,l);d.lineTo(50,l+q.NODE_SLOT_HEIGHT);d.lineTo(34,l+0.5*q.NODE_SLOT_HEIGHT);d.fill()}};d.prototype.onExecute=function(){var d=this.getInputData(0);null==d&&(d=0);this.selected=d=Math.round(d)%(this.inputs.length-1);d=this.getInputData(d+1);void 0!==d&&this.setOutputData(0,d)};d.prototype.onGetInputs=function(){return[["E",0],["F",0],["G",0],["H",0]]};q.registerNodeType("logic/selector",d);l.title="Sequence";l.desc="select one element from a sequence from a string"; -l.prototype.onPropertyChanged=function(d,l){"sequence"==d&&(this.values=l.split(","))};l.prototype.onExecute=function(){var d=this.getInputData(1);d&&d!=this.current_sequence&&(this.values=d.split(","),this.current_sequence=d);d=this.getInputData(0);null==d&&(d=0);this.index=d=Math.round(d)%this.values.length;this.setOutputData(0,this.values[d])};q.registerNodeType("logic/sequence",l)})(this); -(function(u){function d(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function l(){this.addOutput("frame","image");this.properties={url:""}}function q(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function g(){this.addInput("","image,canvas");this.size=[200,200]}function s(){this.addInputs([["img1", +(function(u){function d(d,g){return d==g}function k(d){return null!=d&&d.constructor===String?d.toUpperCase():d}u=u.LiteGraph;u.wrapFunctionAsNode("string/toString",d,["*"],"String");u.wrapFunctionAsNode("string/compare",d,["String","String"],"Boolean");u.wrapFunctionAsNode("string/concatenate",function(d,g){return void 0===d?g:void 0===g?d:d+g},["String","String"],"String");u.wrapFunctionAsNode("string/contains",function(d,g){return void 0===d||void 0===g?!1:-1!=d.indexOf(g)},["String","String"], +"Boolean");u.wrapFunctionAsNode("string/toUpperCase",k,["String"],"String");u.wrapFunctionAsNode("string/split",k,["String","String"],"Array");u.wrapFunctionAsNode("string/toFixed",function(d){return null!=d&&d.constructor===Number?d.toFixed(this.properties.precision):d},["Number"],"String",{precision:0})})(this); +(function(u){function d(){this.addInput("sel","number");this.addInput("A");this.addInput("B");this.addInput("C");this.addInput("D");this.addOutput("out");this.selected=0}function k(){this.properties={sequence:"A,B,C"};this.addInput("index","number");this.addInput("seq");this.addOutput("out");this.index=0;this.values=this.properties.sequence.split(",")}var q=u.LiteGraph;d.title="Selector";d.desc="selects an output";d.prototype.onDrawBackground=function(d){if(!this.flags.collapsed){d.fillStyle="#AFB"; +var k=(this.selected+1)*q.NODE_SLOT_HEIGHT+6;d.beginPath();d.moveTo(50,k);d.lineTo(50,k+q.NODE_SLOT_HEIGHT);d.lineTo(34,k+0.5*q.NODE_SLOT_HEIGHT);d.fill()}};d.prototype.onExecute=function(){var d=this.getInputData(0);null==d&&(d=0);this.selected=d=Math.round(d)%(this.inputs.length-1);d=this.getInputData(d+1);void 0!==d&&this.setOutputData(0,d)};d.prototype.onGetInputs=function(){return[["E",0],["F",0],["G",0],["H",0]]};q.registerNodeType("logic/selector",d);k.title="Sequence";k.desc="select one element from a sequence from a string"; +k.prototype.onPropertyChanged=function(d,k){"sequence"==d&&(this.values=k.split(","))};k.prototype.onExecute=function(){var d=this.getInputData(1);d&&d!=this.current_sequence&&(this.values=d.split(","),this.current_sequence=d);d=this.getInputData(0);null==d&&(d=0);this.index=d=Math.round(d)%this.values.length;this.setOutputData(0,this.values[d])};q.registerNodeType("logic/sequence",k)})(this); +(function(u){function d(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function k(){this.addOutput("frame","image");this.properties={url:""}}function q(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function g(){this.addInput("","image,canvas");this.size=[200,200]}function r(){this.addInputs([["img1", "image"],["img2","image"],["fade","number"]]);this.addOutput("","image");this.properties={fade:0.5,width:512,height:512}}function m(){this.addInput("","image");this.addOutput("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function w(){this.addInput("clear",x.ACTION);this.addOutput("","canvas");this.properties={width:512,height:512,autoclear:!0};this.canvas=document.createElement("canvas");this.ctx=this.canvas.getContext("2d")}function B(){this.addInput("canvas", "canvas");this.addInput("img","image,canvas");this.addInput("x","number");this.addInput("y","number");this.properties={x:0,y:0,opacity:1}}function z(){this.addInput("canvas","canvas");this.addInput("x","number");this.addInput("y","number");this.addInput("w","number");this.addInput("h","number");this.properties={x:0,y:0,w:10,h:10,color:"white",opacity:1}}function y(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:"",use_proxy:!0}} -function c(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var x=u.LiteGraph;d.title="Plot";d.desc="Plots data over time";d.colors=["#FFF","#F99","#9F9","#99F"];d.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var d=0;4>d;++d){var g=this.getInputData(d);if(null!=g){var k=this.values[d];k.push(g);k.length>c[0]&&k.shift()}}}};d.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var f=this.size,g=0.5*f[1]/ -this.properties.scale,k=d.colors,m=0.5*f[1];c.fillStyle="#000";c.fillRect(0,0,f[0],f[1]);c.strokeStyle="#555";c.beginPath();c.moveTo(0,m);c.lineTo(f[0],m);c.stroke();if(this.inputs)for(var l=0;4>l;++l){var a=this.values[l];if(this.inputs[l]&&this.inputs[l].link){c.strokeStyle=k[l];c.beginPath();var b=a[0]*g*-1+m;c.moveTo(0,Math.clamp(b,0,f[1]));for(var e=1;ed&&(d=0);if(0!=c.length){var g=[0,0,0];if(0==d)g=c[0];else if(1==d)g=c[c.length-1];else{var k=(c.length-1)*d,d=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);g[0]=d[0]* -(1-k)+c[0]*k;g[1]=d[1]*(1-k)+c[1]*k;g[2]=d[2]*(1-k)+c[2]*k}for(var m in g)g[m]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};x.registerNodeType("color/palette",q);g.title="Frame";g.desc="Frame viewerew";g.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];g.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame,0,0,this.size[0],this.size[1])};g.prototype.onExecute=function(){this.frame=this.getInputData(0); -this.setDirtyCanvas(!0)};g.prototype.onWidget=function(c,d){if("resize"==d.name&&this.frame){var g=this.frame.width,k=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,k=this.frame.videoHeight);g&&k&&(this.size=[g,k]);this.setDirtyCanvas(!0,!0)}else"view"==d.name&&this.show()};g.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};x.registerNodeType("graphics/frame",g);s.title="Image fade";s.desc="Fades between images";s.widgets=[{name:"resizeA",text:"Resize to A", -type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];s.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};s.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};s.prototype.onExecute=function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width; -var d=this.getInputData(0);null!=d&&c.drawImage(d,0,0,this.canvas.width,this.canvas.height);d=this.getInputData(2);null==d?d=this.properties.fade:this.properties.fade=d;c.globalAlpha=d;d=this.getInputData(1);null!=d&&c.drawImage(d,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};x.registerNodeType("graphics/imagefade",s);m.title="Crop";m.desc="Crop Image";m.prototype.onAdded=function(){this.createCanvas()};m.prototype.createCanvas= +function c(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var x=u.LiteGraph;d.title="Plot";d.desc="Plots data over time";d.colors=["#FFF","#F99","#9F9","#99F"];d.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var d=0;4>d;++d){var g=this.getInputData(d);if(null!=g){var h=this.values[d];h.push(g);h.length>c[0]&&h.shift()}}}};d.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var f=this.size,g=0.5*f[1]/ +this.properties.scale,h=d.colors,m=0.5*f[1];c.fillStyle="#000";c.fillRect(0,0,f[0],f[1]);c.strokeStyle="#555";c.beginPath();c.moveTo(0,m);c.lineTo(f[0],m);c.stroke();if(this.inputs)for(var k=0;4>k;++k){var a=this.values[k];if(this.inputs[k]&&this.inputs[k].link){c.strokeStyle=h[k];c.beginPath();var b=a[0]*g*-1+m;c.moveTo(0,Math.clamp(b,0,f[1]));for(var e=1;ed&&(d=0);if(0!=c.length){var g=[0,0,0];if(0==d)g=c[0];else if(1==d)g=c[c.length-1];else{var h=(c.length-1)*d,d=c[Math.floor(h)],c=c[Math.floor(h)+1],h=h-Math.floor(h);g[0]=d[0]* +(1-h)+c[0]*h;g[1]=d[1]*(1-h)+c[1]*h;g[2]=d[2]*(1-h)+c[2]*h}for(var m in g)g[m]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};x.registerNodeType("color/palette",q);g.title="Frame";g.desc="Frame viewerew";g.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];g.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame,0,0,this.size[0],this.size[1])};g.prototype.onExecute=function(){this.frame=this.getInputData(0); +this.setDirtyCanvas(!0)};g.prototype.onWidget=function(c,d){if("resize"==d.name&&this.frame){var g=this.frame.width,h=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,h=this.frame.videoHeight);g&&h&&(this.size=[g,h]);this.setDirtyCanvas(!0,!0)}else"view"==d.name&&this.show()};g.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};x.registerNodeType("graphics/frame",g);r.title="Image fade";r.desc="Fades between images";r.widgets=[{name:"resizeA",text:"Resize to A", +type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];r.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};r.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};r.prototype.onExecute=function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width; +var d=this.getInputData(0);null!=d&&c.drawImage(d,0,0,this.canvas.width,this.canvas.height);d=this.getInputData(2);null==d?d=this.properties.fade:this.properties.fade=d;c.globalAlpha=d;d=this.getInputData(1);null!=d&&c.drawImage(d,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};x.registerNodeType("graphics/imagefade",r);m.title="Crop";m.desc="Crop Image";m.prototype.onAdded=function(){this.createCanvas()};m.prototype.createCanvas= function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};m.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};m.prototype.onDrawBackground=function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas, 0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};m.prototype.onPropertyChanged=function(c,d){this.properties[c]=d;"scale"==c?(this.properties[c]=parseFloat(d),0==this.properties[c]&&(this.trace("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(d);this.createCanvas();return!0};x.registerNodeType("graphics/cropImage",m);w.title="Canvas";w.desc="Canvas to render stuff";w.prototype.onExecute=function(){var c=this.canvas,d=this.properties.width|0,g=this.properties.height| -0;c.width!=d&&(c.width=d);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};w.prototype.onAction=function(c,d){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};x.registerNodeType("graphics/canvas",w);B.title="DrawImage";B.desc="Draws image into a canvas";B.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var d=this.getInputOrProperty("img");if(d){var g=this.getInputOrProperty("x"),k=this.getInputOrProperty("y"); -c.getContext("2d").drawImage(d,g,k)}}};x.registerNodeType("graphics/drawImage",B);z.title="DrawRectangle";z.desc="Draws rectangle in canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var d=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),m=this.getInputOrProperty("h");c.getContext("2d").fillRect(d,g,k,m)}};x.registerNodeType("graphics/drawRectangle",z);y.title="Video";y.desc="Video playback";y.widgets=[{name:"play",text:"PLAY",type:"minibutton"}, +0;c.width!=d&&(c.width=d);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};w.prototype.onAction=function(c,d){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};x.registerNodeType("graphics/canvas",w);B.title="DrawImage";B.desc="Draws image into a canvas";B.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var d=this.getInputOrProperty("img");if(d){var g=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"); +c.getContext("2d").drawImage(d,g,h)}}};x.registerNodeType("graphics/drawImage",B);z.title="DrawRectangle";z.desc="Draws rectangle in canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var d=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),h=this.getInputOrProperty("w"),m=this.getInputOrProperty("h");c.getContext("2d").fillRect(d,g,h,m)}};x.registerNodeType("graphics/drawRectangle",z);y.title="Video";y.desc="Video playback";y.widgets=[{name:"play",text:"PLAY",type:"minibutton"}, {name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];y.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause());this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime); this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};y.prototype.onStart=function(){this.play()};y.prototype.onStop=function(){this.stop()};y.prototype.loadVideo=function(c){this._video_url=c;this.properties.use_proxy&&"http"==c.substr(0,4)&&x.proxy&&(c=x.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var d=this;this._video.addEventListener("loadedmetadata", function(c){d.trace("Duration: "+this.duration+" seconds");d.trace("Size: "+this.videoWidth+","+this.videoHeight);d.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){});this._video.addEventListener("error",function(c){console.log("Error loading video: "+this.src);d.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:d.trace("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:d.trace("Network error - please try again later."); @@ -366,136 +368,136 @@ this.pause())};y.prototype.stop=function(){this._video&&(this._video.pause(),thi d,this.closeStream(),this.openStream())};c.prototype.onRemoved=function(){this.closeStream()};c.prototype.streamReady=function(d){this._webcam_stream=d;this.boxcolor="green";var f=this._video;f||(f=document.createElement("video"),f.autoplay=!0,f.srcObject=d,this._video=f,f.onloadedmetadata=function(d){console.log(d);c.is_webcam_open=!0});this.trigger("stream_ready",f)};c.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){this._video.frame= ++this.frame;this._video.width=this._video.videoWidth;this._video.height=this._video.videoHeight;this.setOutputData(0,this._video);for(var c=1;c=this.size[1]||!this.properties.show||!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",x.EVENT],["stream_closed",x.EVENT],["stream_error",x.EVENT]]};x.registerNodeType("graphics/webcam",c)})(this); -(function(u){var d=u.LiteGraph;u.LGraphTexture=null;if("undefined"!=typeof GL){LGraphCanvas.link_type_colors.Texture="#987";var l=function(){this.addOutput("Texture","Texture");this.properties={name:"",filter:!0};this.size=[l.image_preview_size,l.image_preview_size]};u.LGraphTexture=l;l.title="Texture";l.desc="Texture";l.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}};l.loadTextureCallback=null;l.image_preview_size=256;l.PASS_THROUGH=1;l.COPY=2;l.LOW=3;l.HIGH=4;l.REUSE=5;l.DEFAULT= -2;l.MODE_VALUES={"pass through":l.PASS_THROUGH,copy:l.COPY,low:l.LOW,high:l.HIGH,reuse:l.REUSE,"default":l.DEFAULT};l.getTexturesContainer=function(){return gl.textures};l.loadTexture=function(a,b){b=b||{};var e=a;"http://"==e.substr(0,7)&&d.proxy&&(e=d.proxy+e.substr(7));return l.getTexturesContainer()[a]=GL.Texture.fromURL(e,b)};l.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a): -b};l.getTargetTexture=function(a,b,e){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var c=null;switch(e){case l.LOW:c=gl.UNSIGNED_BYTE;break;case l.HIGH:c=gl.HIGH_PRECISION_FORMAT;break;case l.REUSE:return a;default:c=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==c||(b=new GL.Texture(a.width,a.height,{type:c,format:gl.RGBA,filter:gl.LINEAR}));return b};l.getTextureType=function(a,b){var e=b?b.type:gl.UNSIGNED_BYTE;switch(a){case l.HIGH:e=gl.HIGH_PRECISION_FORMAT; -break;case l.LOW:e=gl.UNSIGNED_BYTE}return e};l.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})};l.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})}; -l.prototype.onDropFile=function(a,b,e){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([e]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""};l.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]};l.prototype.onExecute= -function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=l.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);for(var b=1;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=l.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex= -this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}};l.generateLowResTexturePreview=function(a){if(!a)return null;var b=l.image_preview_size,e=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)e=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=e=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(e);a=this._preview_canvas; -a||(this._preview_canvas=a=createCanvas(b,b));e&&e.toCanvas(a);return a};l.prototype.getResources=function(a){a[this.properties.name]=GL.Texture;return a};l.prototype.onGetInputs=function(){return[["in","Texture"]]};l.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]};d.registerNodeType("texture/texture",l);var q=function(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[l.image_preview_size,l.image_preview_size]};q.title= -"Preview";q.desc="Show a texture in the graph canvas";q.allow_preview=!1;q.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||q.allow_preview)){var b=this.getInputData(0);if(b){var e=null,e=!b.handle&&a.webgl?b:l.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(e,0,0,this.size[0],this.size[1]);a.restore()}}};d.registerNodeType("texture/preview",q);var g=function(){this.addInput("Texture","Texture");this.addOutput("", -"Texture");this.properties={name:""}};g.title="Save";g.desc="Save a texture in the repository";g.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.name&&(l.storeTexture?l.storeTexture(this.properties.name,a):l.getTexturesContainer()[this.properties.name]=a),this.setOutputData(0,a))};d.registerNodeType("texture/save",g);var s=function(){this.addInput("Texture","Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture"); -this.help="

pixelcode must be vec3

\t\t\t

uvcode must be vec2, is optional

\t\t\t

uv: tex. coords

color: texture

colorB: textureB

time: scene time

value: input value

";this.properties={value:1,uvcode:"",pixelcode:"color + colorB * value",precision:l.DEFAULT}};s.widgets_info={uvcode:{widget:"textarea",height:100},pixelcode:{widget:"textarea",height:100},precision:{widget:"combo", -values:l.MODE_VALUES}};s.title="Operation";s.desc="Texture shader operation";s.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]};s.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())};s.prototype.onExecute=function(){var a= -this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===l.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var e=512,c=512;a?(e=a.width,c=a.height):b&&(e=b.width,c=b.height);var d=l.getTextureType(this.properties.precision,a);this._tex=a||this._tex?l.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(e,c,{type:d,format:gl.RGBA,filter:gl.LINEAR});d="";this.properties.uvcode&& -(d="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(d=this.properties.uvcode));var k="";this.properties.pixelcode&&(k="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(k=this.properties.pixelcode));var f=this._shader;if(!f||this._shader_code!=d+"|"+k){try{this._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader,{UV_CODE:d,PIXEL_CODE:k}),this.boxcolor="#00FF00"}catch(g){console.log("Error compiling shader: ",g);this.boxcolor="#FF0000"; -return}this.boxcolor="#FF0000";this._shader_code=d+"|"+k;f=this._shader}if(f){this.boxcolor="green";var h=this.getInputData(2);null!=h?this.properties.value=h:h=parseFloat(this.properties.value);var m=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var d=Mesh.getScreenQuad();f.uniforms({u_texture:0,u_textureB:1,value:h,texSize:[e,c],time:m}).draw(d)});this.setOutputData(0,this._tex)}else this.boxcolor= -"red"}}};s.pixel_shader="precision highp float;\n\t\t\t\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform vec2 texSize;\n\t\t\tuniform float time;\n\t\t\tuniform float value;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tvec2 uv = v_coord;\n\t\t\t\tUV_CODE;\n\t\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\t\tvec3 color = color4.rgb;\n\t\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\t\tvec3 colorB = color4B.rgb;\n\t\t\t\tvec3 result = color;\n\t\t\t\tfloat alpha = 1.0;\n\t\t\t\tPIXEL_CODE;\n\t\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t\t}\n\t\t\t"; -d.registerNodeType("texture/operation",s);var m=function(){this.addOutput("out","Texture");this.properties={code:"",width:512,height:512,precision:l.DEFAULT};this.properties.code="\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n//your code here\n\ngl_FragColor = vec4(color, 1.0);\n}\n";this._uniforms={in_texture:0,texSize:vec2.create(),time:0}};m.title="Shader";m.desc="Texture shader";m.widgets_info={code:{type:"code"},precision:{widget:"combo",values:l.MODE_VALUES}};m.prototype.onPropertyChanged= -function(a,b){if("code"==a){var e=this.getShader();if(e){var c=e.uniformInfo;if(this.inputs)for(var d={},k=0;kb;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})}; +k.prototype.onDropFile=function(a,b,c){if(a){var e=null;"string"==typeof a?e=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?e=GL.Texture.fromDDSInMemory(a):(a=new Blob([c]),a=URL.createObjectURL(a),e=GL.Texture.fromURL(a));this._drop_texture=e;this.properties.name=b}else this._drop_texture=null,this.properties.name=""};k.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]};k.prototype.onExecute= +function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=k.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);for(var b=1;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=k.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex= +this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}};k.generateLowResTexturePreview=function(a){if(!a)return null;var b=k.image_preview_size,c=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)c=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=c=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(c);a=this._preview_canvas; +a||(this._preview_canvas=a=createCanvas(b,b));c&&c.toCanvas(a);return a};k.prototype.getResources=function(a){a[this.properties.name]=GL.Texture;return a};k.prototype.onGetInputs=function(){return[["in","Texture"]]};k.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]};d.registerNodeType("texture/texture",k);var q=function(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[k.image_preview_size,k.image_preview_size]};q.title= +"Preview";q.desc="Show a texture in the graph canvas";q.allow_preview=!1;q.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||q.allow_preview)){var b=this.getInputData(0);if(b){var c=null,c=!b.handle&&a.webgl?b:k.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(c,0,0,this.size[0],this.size[1]);a.restore()}}};d.registerNodeType("texture/preview",q);var g=function(){this.addInput("Texture","Texture");this.addOutput("", +"Texture");this.properties={name:""}};g.title="Save";g.desc="Save a texture in the repository";g.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.name&&(k.storeTexture?k.storeTexture(this.properties.name,a):k.getTexturesContainer()[this.properties.name]=a),this.setOutputData(0,a))};d.registerNodeType("texture/save",g);var r=function(){this.addInput("Texture","Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture"); +this.help="

pixelcode must be vec3

\t\t\t

uvcode must be vec2, is optional

\t\t\t

uv: tex. coords

color: texture

colorB: textureB

time: scene time

value: input value

";this.properties={value:1,uvcode:"",pixelcode:"color + colorB * value",precision:k.DEFAULT}};r.widgets_info={uvcode:{widget:"textarea",height:100},pixelcode:{widget:"textarea",height:100},precision:{widget:"combo", +values:k.MODE_VALUES}};r.title="Operation";r.desc="Texture shader operation";r.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]};r.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())};r.prototype.onExecute=function(){var a= +this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===k.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var c=512,e=512;a?(c=a.width,e=a.height):b&&(c=b.width,e=b.height);var d=k.getTextureType(this.properties.precision,a);this._tex=a||this._tex?k.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(c,e,{type:d,format:gl.RGBA,filter:gl.LINEAR});d="";this.properties.uvcode&& +(d="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(d=this.properties.uvcode));var h="";this.properties.pixelcode&&(h="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(h=this.properties.pixelcode));var f=this._shader;if(!f||this._shader_code!=d+"|"+h){try{this._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader,{UV_CODE:d,PIXEL_CODE:h}),this.boxcolor="#00FF00"}catch(g){console.log("Error compiling shader: ",g);this.boxcolor="#FF0000"; +return}this.boxcolor="#FF0000";this._shader_code=d+"|"+h;f=this._shader}if(f){this.boxcolor="green";var l=this.getInputData(2);null!=l?this.properties.value=l:l=parseFloat(this.properties.value);var m=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var d=Mesh.getScreenQuad();f.uniforms({u_texture:0,u_textureB:1,value:l,texSize:[c,e],time:m}).draw(d)});this.setOutputData(0,this._tex)}else this.boxcolor= +"red"}}};r.pixel_shader="precision highp float;\n\t\t\t\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform vec2 texSize;\n\t\t\tuniform float time;\n\t\t\tuniform float value;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tvec2 uv = v_coord;\n\t\t\t\tUV_CODE;\n\t\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\t\tvec3 color = color4.rgb;\n\t\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\t\tvec3 colorB = color4B.rgb;\n\t\t\t\tvec3 result = color;\n\t\t\t\tfloat alpha = 1.0;\n\t\t\t\tPIXEL_CODE;\n\t\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t\t}\n\t\t\t"; +d.registerNodeType("texture/operation",r);var m=function(){this.addOutput("out","Texture");this.properties={code:"",width:512,height:512,precision:k.DEFAULT};this.properties.code="\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n//your code here\n\ngl_FragColor = vec4(color, 1.0);\n}\n";this._uniforms={in_texture:0,texSize:vec2.create(),time:0}};m.title="Shader";m.desc="Texture shader";m.widgets_info={code:{type:"code"},precision:{widget:"combo",values:k.MODE_VALUES}};m.prototype.onPropertyChanged= +function(a,b){if("code"==a){var c=this.getShader();if(c){var e=c.uniformInfo;if(this.inputs)for(var d={},h=0;h lumaMax))\n\t\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\t\telse\n\t\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\t\tif(u_igamma != 1.0)\n\t\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t\t}\n\t\t\t"; z.gamma_pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform float u_igamma;\n\t\t\tvoid main() {\n\t\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t\t gl_FragColor = color;\n\t\t\t}\n\t\t\t";d.registerNodeType("texture/toviewport",z);g=function(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1, -precision:l.DEFAULT}};g.title="Copy";g.desc="Copy Texture";g.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo",values:l.MODE_VALUES}};g.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,e=a.height;0!=this.properties.size&&(e=b=this.properties.size);var c=this._temp_texture,d=a.type;this.properties.precision===l.LOW?d=gl.UNSIGNED_BYTE:this.properties.precision===l.HIGH&& -(d=gl.HIGH_PRECISION_FORMAT);c&&c.width==b&&c.height==e&&c.type==d||(c=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(e)&&(c=gl.LINEAR_MIPMAP_LINEAR),this._temp_texture=new GL.Texture(b,e,{type:d,format:gl.RGBA,minFilter:c,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}};d.registerNodeType("texture/copy", -g);var y=function(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:l.DEFAULT}};y.title="Downsample";y.desc="Downsample Texture";y.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:l.MODE_VALUES}};y.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0, -a);else{var b=y._shader;b||(y._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader));var e=a.width|0,c=a.height|0,d=a.type;this.properties.precision===l.LOW?d=gl.UNSIGNED_BYTE:this.properties.precision===l.HIGH&&(d=gl.HIGH_PRECISION_FORMAT);var k=this.properties.iterations||1,f=a,g=null,h=[],a={type:d,format:a.format},d=vec2.create(),m={u_offset:d};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var t=0;t>1||0;c=c>>1||0;g=GL.Texture.getTemporary(e, -c,a);h.push(g);f.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);f.copyTo(g,b,m);if(1==e&&1==c)break;f=g}this._texture=h.pop();for(t=0;tthis.properties.iterations)this.setOutputData(0, +a);else{var b=y._shader;b||(y._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader));var c=a.width|0,e=a.height|0,d=a.type;this.properties.precision===k.LOW?d=gl.UNSIGNED_BYTE:this.properties.precision===k.HIGH&&(d=gl.HIGH_PRECISION_FORMAT);var h=this.properties.iterations||1,f=a,g=null,l=[],a={type:d,format:a.format},d=vec2.create(),m={u_offset:d};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var s=0;s>1||0;e=e>>1||0;g=GL.Texture.getTemporary(c, +e,a);l.push(g);f.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);f.copyTo(g,b,m);if(1==c&&1==e)break;f=g}this._texture=l.pop();for(s=0;se;++e)b[e]=Math.random();c._shader.uniforms({u_samples_a:b.subarray(0,16),u_samples_b:b.subarray(16,32)})}e=this._temp_texture;b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);e&&e.type==b||(this._temp_texture=new GL.Texture(1,1,{type:b,format:gl.RGBA,filter:gl.NEAREST}));var d=c._shader,k=this._uniforms;k.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(d,k)});if(this.isOutputConnected(1)|| +new Float32Array(32),e=0;32>e;++e)b[e]=Math.random();c._shader.uniforms({u_samples_a:b.subarray(0,16),u_samples_b:b.subarray(16,32)})}e=this._temp_texture;b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);e&&e.type==b||(this._temp_texture=new GL.Texture(1,1,{type:b,format:gl.RGBA,filter:gl.NEAREST}));var d=c._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(d,h)});if(this.isOutputConnected(1)|| this.isOutputConnected(2))if(e=this._temp_texture.getPixels()){var f=this._luminance,b=this._temp_texture.type;f.set(e);b==gl.UNSIGNED_BYTE&&vec4.scale(f,f,1/255)}}};c.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tuniform mat4 u_samples_a;\n\t\t\tuniform mat4 u_samples_b;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform float u_mipmap_offset;\n\t\t\tvarying vec2 v_coord;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tvec4 color = vec4(0.0);\n\t\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\t}\n\t\t\t gl_FragColor = color * 0.03125;\n\t\t\t}\n\t\t\t"; d.registerNodeType("texture/average",c);var x=function(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}};x.title="Smooth";x.desc="Smooth texture over time";x.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){x._shader||(x._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var b=this._temp_texture; -b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.NEAREST}),this._temp_texture2=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.NEAREST}),a.copyTo(this._temp_texture2));var b=this._temp_texture,e=this._temp_texture2,c=x._shader,d=this._uniforms;d.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){e.bind(1);a.toViewport(c, -d)});this.setOutputData(0,b);this._temp_texture=e;this._temp_texture2=b}};x.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform float u_factor;\n\t\t\tvarying vec2 v_coord;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t\t}\n\t\t\t";d.registerNodeType("texture/temporal_smooth",x);g=function(){this.addInput("Image", -"image");this.addOutput("","Texture");this.properties={}};g.title="Image to Texture";g.desc="Uploads an image to the GPU";g.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,e=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==e||(this._temp_texture=new GL.Texture(b,e,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(d){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+ -d);return}this.setOutputData(0,this._temp_texture)}}};d.registerNodeType("texture/imageToTexture",g);var n=function(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={intensity:1,precision:l.DEFAULT,texture:null};n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))};n.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:l.MODE_VALUES}};n.title="LUT";n.desc= -"Apply LUT to Texture";n.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===l.PASS_THROUGH)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=l.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D, -null);var e=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=e=this.getInputData(2));this._tex=l.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(n._shader,{u_texture:0,u_textureB:1,u_amount:e})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}};n.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform float u_amount;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t\t mediump vec2 quad1;\n\t\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t\t mediump vec2 quad2;\n\t\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t\t highp vec2 texPos1;\n\t\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t\t highp vec2 texPos2;\n\t\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t\t}\n\t\t\t"; +b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.NEAREST}),this._temp_texture2=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.NEAREST}),a.copyTo(this._temp_texture2));var b=this._temp_texture,c=this._temp_texture2,e=x._shader,d=this._uniforms;d.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){c.bind(1);a.toViewport(e, +d)});this.setOutputData(0,b);this._temp_texture=c;this._temp_texture2=b}};x.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform float u_factor;\n\t\t\tvarying vec2 v_coord;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t\t}\n\t\t\t";d.registerNodeType("texture/temporal_smooth",x);g=function(){this.addInput("Image", +"image");this.addOutput("","Texture");this.properties={}};g.title="Image to Texture";g.desc="Uploads an image to the GPU";g.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,c=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var e=this._temp_texture;e&&e.width==b&&e.height==c||(this._temp_texture=new GL.Texture(b,c,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(d){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+ +d);return}this.setOutputData(0,this._temp_texture)}}};d.registerNodeType("texture/imageToTexture",g);var n=function(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={intensity:1,precision:k.DEFAULT,texture:null};n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))};n.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:k.MODE_VALUES}};n.title="LUT";n.desc= +"Apply LUT to Texture";n.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===k.PASS_THROUGH)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=k.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D, +null);var c=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=c=this.getInputData(2));this._tex=k.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(n._shader,{u_texture:0,u_textureB:1,u_amount:c})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}};n.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform float u_amount;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t\t mediump vec2 quad1;\n\t\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t\t mediump vec2 quad2;\n\t\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t\t highp vec2 texPos1;\n\t\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t\t highp vec2 texPos2;\n\t\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t\t}\n\t\t\t"; d.registerNodeType("texture/LUT",n);var f=function(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");this.properties={use_luminance:!0};f._shader||(f._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader))};f.title="Texture to Channels";f.desc="Split texture channels";f.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b= -this.properties.use_luminance?gl.LUMINANCE:gl.RGBA,e=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})),e++):this._channels[c]=null;if(e){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var d=Mesh.getScreenQuad(),k=f._shader,g=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0, -1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);k.uniforms({u_texture:0,u_mask:g[c]}).draw(d)}),this.setOutputData(c,this._channels[c]))}}};f.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec4 u_mask;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t\t}\n\t\t\t";d.registerNodeType("texture/textureChannels", -f);var A=function(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:l.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}};A.title="Channels to Texture";A.desc="Split texture channels";A.widgets_info={precision:{widget:"combo",values:l.MODE_VALUES}};A.prototype.onExecute=function(){var a= -l.getWhiteTexture(),b=this.getInputData(0)||a,e=this.getInputData(1)||a,c=this.getInputData(2)||a,d=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var k=Mesh.getScreenQuad();A._shader||(A._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,A.pixel_shader));var f=A._shader,a=Math.max(b.width,e.width,c.width,d.width),g=Math.max(b.height,e.height,c.height,d.height),h=this.properties.precision==l.HIGH?l.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&& -this._texture.height==g&&this._texture.type==h||(this._texture=new GL.Texture(a,g,{type:h,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var m=this._uniforms;this._texture.drawTo(function(){b.bind(0);e.bind(1);c.bind(2);d.bind(3);f.uniforms(m).draw(k)});this.setOutputData(0,this._texture)};A.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_textureR;\n\t\t\tuniform sampler2D u_textureG;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform sampler2D u_textureA;\n\t\t\tuniform vec4 u_color;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t gl_FragColor = u_color * vec4( \t\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t\t}\n\t\t\t"; -d.registerNodeType("texture/channelsTexture",A);g=function(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:l.DEFAULT}};g.title="Color";g.desc="Generates a 1x1 texture with a constant color";g.widgets_info={precision:{widget:"combo",values:l.MODE_VALUES}};g.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255* -Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])};g.prototype.onExecute=function(){var a=this.properties.precision==l.HIGH?l.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;b 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t\t}\n\t\t\t"; +this.properties.use_luminance?gl.LUMINANCE:gl.RGBA,c=0,e=0;4>e;e++)this.isOutputConnected(e)?(this._channels[e]&&this._channels[e].width==a.width&&this._channels[e].height==a.height&&this._channels[e].type==a.type&&this._channels[e].format==b||(this._channels[e]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})),c++):this._channels[e]=null;if(c){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var d=Mesh.getScreenQuad(),h=f._shader,g=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0, +1]],e=0;4>e;e++)this._channels[e]&&(this._channels[e].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:g[e]}).draw(d)}),this.setOutputData(e,this._channels[e]))}}};f.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec4 u_mask;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t\t}\n\t\t\t";d.registerNodeType("texture/textureChannels", +f);var A=function(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:k.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}};A.title="Channels to Texture";A.desc="Split texture channels";A.widgets_info={precision:{widget:"combo",values:k.MODE_VALUES}};A.prototype.onExecute=function(){var a= +k.getWhiteTexture(),b=this.getInputData(0)||a,e=this.getInputData(1)||a,c=this.getInputData(2)||a,d=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();A._shader||(A._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,A.pixel_shader));var f=A._shader,a=Math.max(b.width,e.width,c.width,d.width),g=Math.max(b.height,e.height,c.height,d.height),l=this.properties.precision==k.HIGH?k.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&& +this._texture.height==g&&this._texture.type==l||(this._texture=new GL.Texture(a,g,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var m=this._uniforms;this._texture.drawTo(function(){b.bind(0);e.bind(1);c.bind(2);d.bind(3);f.uniforms(m).draw(h)});this.setOutputData(0,this._texture)};A.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_textureR;\n\t\t\tuniform sampler2D u_textureG;\n\t\t\tuniform sampler2D u_textureB;\n\t\t\tuniform sampler2D u_textureA;\n\t\t\tuniform vec4 u_color;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t gl_FragColor = u_color * vec4( \t\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t\t}\n\t\t\t"; +d.registerNodeType("texture/channelsTexture",A);g=function(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:k.DEFAULT}};g.title="Color";g.desc="Generates a 1x1 texture with a constant color";g.widgets_info={precision:{widget:"combo",values:k.MODE_VALUES}};g.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255* +Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])};g.prototype.onExecute=function(){var a=this.properties.precision==k.HIGH?k.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;b 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t\t}\n\t\t\t"; d.registerNodeType("texture/edges",v);var a=function(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}};a.title="Depth Range";a.desc="Generates a texture with a depth range";a.prototype.onExecute=function(){if(this.isOutputConnected(0)){var b=this.getInputData(0); if(b){var e=gl.UNSIGNED_BYTE;this.properties.high_precision&&(e=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==e&&this._temp_texture.width==b.width&&this._temp_texture.height==b.height||(this._temp_texture=new GL.Texture(b.width,b.height,{type:e,format:gl.RGBA,filter:gl.LINEAR}));var c=this._uniforms,e=this.properties.distance;this.isInputConnected(1)&&(e=this.getInputData(1),this.properties.distance=e);var d=this.properties.range;this.isInputConnected(2)&& -(d=this.getInputData(2),this.properties.range=d);c.u_distance=e;c.u_range=d;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var k=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader),a._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader,{ONLY_DEPTH:""}));var f=this.properties.only_depth?a._shader_onlydepth:a._shader,e=null,e=b.near_far_planes?b.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes: -[0.1,1E3];c.u_camera_planes=e;this._temp_texture.drawTo(function(){b.bind(0);f.uniforms(c).draw(k)});this._temp_texture.near_far_planes=e;this.setOutputData(0,this._temp_texture)}}};a.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec2 u_camera_planes;\n\t\t\tuniform float u_distance;\n\t\t\tuniform float u_range;\n\t\t\t\n\t\t\tfloat LinearDepth()\n\t\t\t{\n\t\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\t}\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tfloat depth = LinearDepth();\n\t\t\t\t#ifdef ONLY_DEPTH\n\t\t\t\t gl_FragColor = vec4(depth);\n\t\t\t\t#else\n\t\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\t\tfloat dof = 1.0;\n\t\t\t\t\tif(diff <= u_range)\n\t\t\t\t\t\tdof = diff / u_range;\n\t\t\t\t gl_FragColor = vec4(dof);\n\t\t\t\t#endif\n\t\t\t}\n\t\t\t"; -d.registerNodeType("texture/depth_range",a);var b=function(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:l.DEFAULT}};b.title="Blur";b.desc="Blur a texture";b.widgets_info={precision:{widget:"combo",values:l.MODE_VALUES}};b.max_iterations=20;b.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var e= -this._final_texture;e&&e.width==a.width&&e.height==a.height&&e.type==a.type||(e=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var c=this.properties.iterations;this.isInputConnected(1)&&(c=this.getInputData(1),this.properties.iterations=c);c=Math.min(Math.floor(c),b.max_iterations);if(0==c)this.setOutputData(0,a);else{var k=this.properties.intensity;this.isInputConnected(2)&&(k=this.getInputData(2),this.properties.intensity=k);var f=d.camera_aspect; -f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],k,e);for(a=1;a>=1;1<(c|0)&&(c>>=1);if(2>b)break;m=g[q]=GL.Texture.getTemporary(b,c,d);r[0]=1/t.width;r[1]=1/t.height;t.blit(m,h.uniforms(f));t=m}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/t.width,r[1]=1/t.height,f.u_intensity= -v,f.u_delta=1,t.blit(b,h.uniforms(f)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);f.u_intensity=this.getInputOrProperty("persistence");f.u_delta=0.5;for(q-=2;0<=q;q--)m=g[q],g[q]=null,r[0]=1/t.width,r[1]=1/t.height,t.blit(m,h.uniforms(f)),GL.Texture.releaseTemporary(t),t=m;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==k&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:k, -format:a.format,filter:gl.LINEAR})),t.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture;g&&g.width==a.width&&g.height==a.height&&g.type==k&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:k,format:a.format,filter:gl.LINEAR}));var s=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");f.u_intensity=v;h=s?e._dirt_final_shader:e._final_shader;h||(h=s?e._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,e.final_pixel_shader, -{USE_DIRT:""}):e._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,e.final_pixel_shader));g.drawTo(function(){a.bind(0);t.bind(1);s&&(h.setUniform("u_dirt_factor",w),h.setUniform("u_dirt_texture",s.bind(2)));h.toViewport(f)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(t)}};e.cut_pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_threshold;\n\t\tvoid main() {\n\t\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t\t}"; +(d=this.getInputData(2),this.properties.range=d);c.u_distance=e;c.u_range=d;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader),a._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader,{ONLY_DEPTH:""}));var f=this.properties.only_depth?a._shader_onlydepth:a._shader,e=null,e=b.near_far_planes?b.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes: +[0.1,1E3];c.u_camera_planes=e;this._temp_texture.drawTo(function(){b.bind(0);f.uniforms(c).draw(h)});this._temp_texture.near_far_planes=e;this.setOutputData(0,this._temp_texture)}}};a.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec2 u_camera_planes;\n\t\t\tuniform float u_distance;\n\t\t\tuniform float u_range;\n\t\t\t\n\t\t\tfloat LinearDepth()\n\t\t\t{\n\t\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\t}\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tfloat depth = LinearDepth();\n\t\t\t\t#ifdef ONLY_DEPTH\n\t\t\t\t gl_FragColor = vec4(depth);\n\t\t\t\t#else\n\t\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\t\tfloat dof = 1.0;\n\t\t\t\t\tif(diff <= u_range)\n\t\t\t\t\t\tdof = diff / u_range;\n\t\t\t\t gl_FragColor = vec4(dof);\n\t\t\t\t#endif\n\t\t\t}\n\t\t\t"; +d.registerNodeType("texture/depth_range",a);var b=function(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:k.DEFAULT}};b.title="Blur";b.desc="Blur a texture";b.widgets_info={precision:{widget:"combo",values:k.MODE_VALUES}};b.max_iterations=20;b.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var e= +this._final_texture;e&&e.width==a.width&&e.height==a.height&&e.type==a.type||(e=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var c=this.properties.iterations;this.isInputConnected(1)&&(c=this.getInputData(1),this.properties.iterations=c);c=Math.min(Math.floor(c),b.max_iterations);if(0==c)this.setOutputData(0,a);else{var h=this.properties.intensity;this.isInputConnected(2)&&(h=this.getInputData(2),this.properties.intensity=h);var f=d.camera_aspect; +f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],h,e);for(a=1;a>=1;1<(c|0)&&(c>>=1);if(2>b)break;m=g[q]=GL.Texture.getTemporary(b,c,d);v[0]=1/s.width;v[1]=1/s.height;s.blit(m,l.uniforms(f));s=m}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),v[0]=1/s.width,v[1]=1/s.height,f.u_intensity= +t,f.u_delta=1,s.blit(b,l.uniforms(f)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);f.u_intensity=this.getInputOrProperty("persistence");f.u_delta=0.5;for(q-=2;0<=q;q--)m=g[q],g[q]=null,v[0]=1/s.width,v[1]=1/s.height,s.blit(m,l.uniforms(f)),GL.Texture.releaseTemporary(s),s=m;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h, +format:a.format,filter:gl.LINEAR})),s.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture;g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var r=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");f.u_intensity=t;l=r?e._dirt_final_shader:e._final_shader;l||(l=r?e._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,e.final_pixel_shader, +{USE_DIRT:""}):e._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,e.final_pixel_shader));g.drawTo(function(){a.bind(0);s.bind(1);r&&(l.setUniform("u_dirt_factor",w),l.setUniform("u_dirt_texture",r.bind(2)));l.toViewport(f)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(s)}};e.cut_pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_threshold;\n\t\tvoid main() {\n\t\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t\t}"; e.scale_pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_texel_size;\n\t\tuniform float u_delta;\n\t\tuniform float u_intensity;\n\t\t\n\t\tvec4 sampleBox(vec2 uv) {\n\t\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\t\treturn s * 0.25;\n\t\t}\n\t\tvoid main() {\n\t\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t\t}"; e.final_pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_glow_texture;\n\t\t#ifdef USE_DIRT\n\t\t\tuniform sampler2D u_dirt_texture;\n\t\t#endif\n\t\tuniform vec2 u_texel_size;\n\t\tuniform float u_delta;\n\t\tuniform float u_intensity;\n\t\tuniform float u_dirt_factor;\n\t\t\n\t\tvec4 sampleBox(vec2 uv) {\n\t\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\t\treturn s * 0.25;\n\t\t}\n\t\tvoid main() {\n\t\t\tvec4 glow = sampleBox( v_coord );\n\t\t\t#ifdef USE_DIRT\n\t\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t\t#endif\n\t\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t\t}"; -d.registerNodeType("texture/glow",e);var r=function(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1,radius:5}};r.title="Kuwahara Filter";r.desc="Filters a texture giving an artistic oil canvas painting";r.max_radius=10;r._shaders=[];r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width, -a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),r.max_radius);if(0==b)this.setOutputData(0,a);else{var e=this.properties.intensity,c=d.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;r._shaders[b]||(r._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader,{RADIUS:b.toFixed(0)}));var k=r._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){k.uniforms({u_texture:0, -u_intensity:e,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)});this.setOutputData(0,this._temp_texture)}}};r.pixel_shader="\n\tprecision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_intensity;\n\tuniform vec2 u_resolution;\n\tuniform vec2 u_iResolution;\n\t#ifndef RADIUS\n\t\t#define RADIUS 7\n\t#endif\n\tvoid main() {\n\t\n\t\tconst int radius = RADIUS;\n\t\tvec2 fragCoord = v_coord;\n\t\tvec2 src_size = u_iResolution;\n\t\tvec2 uv = v_coord;\n\t\tfloat n = float((radius + 1) * (radius + 1));\n\t\tint i;\n\t\tint j;\n\t\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\t\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\t\tvec3 c;\n\t\t\n\t\tfor (int j = -radius; j <= 0; ++j) {\n\t\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\t\tm0 += c;\n\t\t\t\ts0 += c * c;\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (int j = -radius; j <= 0; ++j) {\n\t\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\t\tm1 += c;\n\t\t\t\ts1 += c * c;\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (int j = 0; j <= radius; ++j) {\n\t\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\t\tm2 += c;\n\t\t\t\ts2 += c * c;\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (int j = 0; j <= radius; ++j) {\n\t\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\t\tm3 += c;\n\t\t\t\ts3 += c * c;\n\t\t\t}\n\t\t}\n\t\t\n\t\tfloat min_sigma2 = 1e+2;\n\t\tm0 /= n;\n\t\ts0 = abs(s0 / n - m0 * m0);\n\t\t\n\t\tfloat sigma2 = s0.r + s0.g + s0.b;\n\t\tif (sigma2 < min_sigma2) {\n\t\t\tmin_sigma2 = sigma2;\n\t\t\tgl_FragColor = vec4(m0, 1.0);\n\t\t}\n\t\t\n\t\tm1 /= n;\n\t\ts1 = abs(s1 / n - m1 * m1);\n\t\t\n\t\tsigma2 = s1.r + s1.g + s1.b;\n\t\tif (sigma2 < min_sigma2) {\n\t\t\tmin_sigma2 = sigma2;\n\t\t\tgl_FragColor = vec4(m1, 1.0);\n\t\t}\n\t\t\n\t\tm2 /= n;\n\t\ts2 = abs(s2 / n - m2 * m2);\n\t\t\n\t\tsigma2 = s2.r + s2.g + s2.b;\n\t\tif (sigma2 < min_sigma2) {\n\t\t\tmin_sigma2 = sigma2;\n\t\t\tgl_FragColor = vec4(m2, 1.0);\n\t\t}\n\t\t\n\t\tm3 /= n;\n\t\ts3 = abs(s3 / n - m3 * m3);\n\t\t\n\t\tsigma2 = s3.r + s3.g + s3.b;\n\t\tif (sigma2 < min_sigma2) {\n\t\t\tmin_sigma2 = sigma2;\n\t\t\tgl_FragColor = vec4(m3, 1.0);\n\t\t}\n\t}\n\t"; -d.registerNodeType("texture/kuwahara",r);var h=function(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0};h.title="Webcam";h.desc="Webcam texture";h.is_webcam_open=!1;h.prototype.openStream=function(){function a(e){h.is_webcam_open=!1;console.log("Webcam rejected",e);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1, -video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}};h.prototype.closeStream=function(){if(this._webcam_stream){var a=this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video,0,0,this.size[0],this.size[1]),a.restore())};h.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,e=this._video_texture;e&&e.width==a&&e.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB, -filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(l.getTexturesContainer()[this.properties.texture_name]=this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video,0,0,this.size[0],this.size[1]),a.restore())};l.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,e=this._video_texture;e&&e.width==a&&e.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB, +filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(k.getTexturesContainer()[this.properties.texture_name]=this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a=this.size[1]||a.webgl&&(gl.meshes.cube||(gl.meshes.cube=GL.Mesh.cube({size:1})))};d.registerNodeType("texture/cubemap",g)}})(this); -(function(u){var d=u.LiteGraph;if("undefined"!=typeof GL){var l=function(){this.addInput("Texture","Texture");this.addInput("Aberration","number");this.addInput("Distortion","number");this.addInput("Blur","number");this.addOutput("Texture","Texture");this.properties={aberration:1,distortion:1,blur:1,precision:LGraphTexture.DEFAULT};l._shader||(l._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,l.pixel_shader),l._texture=new GL.Texture(3,1,{format:gl.RGB,wrap:gl.CLAMP_TO_EDGE,magFilter:gl.LINEAR, -minFilter:gl.LINEAR,pixel_data:[255,0,0,0,255,0,0,0,255]}))};l.title="Lens";l.desc="Camera Lens distortion";l.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}};l.prototype.onExecute=function(){var d=this.getInputData(0);if(this.properties.precision===LGraphTexture.PASS_THROUGH)this.setOutputData(0,d);else if(d){this._tex=LGraphTexture.getTargetTexture(d,this._tex,this.properties.precision);var g=this.properties.aberration;this.isInputConnected(1)&&(g=this.getInputData(1), -this.properties.aberration=g);var q=this.properties.distortion;this.isInputConnected(2)&&(q=this.getInputData(2),this.properties.distortion=q);var s=this.properties.blur;this.isInputConnected(3)&&(s=this.getInputData(3),this.properties.blur=s);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var u=Mesh.getScreenQuad(),c=l._shader;this._tex.drawTo(function(){d.bind(0);c.uniforms({u_texture:0,u_aberration:g,u_distortion:q,u_blur:s}).draw(u)});this.setOutputData(0,this._tex)}};l.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec2 u_camera_planes;\n\t\t\tuniform float u_aberration;\n\t\t\tuniform float u_distortion;\n\t\t\tuniform float u_blur;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tvec2 coord = v_coord;\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\t\t\t\tdist_coord *= percent;\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\t\t\t\tgl_FragColor = color;\n\t\t\t}\n\t\t\t"; -d.registerNodeType("fx/lens",l);u.LGraphFXLens=l;var q=function(){this.addInput("Texture","Texture");this.addInput("Blurred","Texture");this.addInput("Mask","Texture");this.addInput("Threshold","number");this.addOutput("Texture","Texture");this.properties={shape:"",size:10,alpha:1,threshold:1,high_precision:!1}};q.title="Bokeh";q.desc="applies an Bokeh effect";q.widgets_info={shape:{widget:"texture"}};q.prototype.onExecute=function(){var d=this.getInputData(0),g=this.getInputData(1),l=this.getInputData(2); -if(d&&l&&this.properties.shape){g||(g=d);var s=LGraphTexture.getTexture(this.properties.shape);if(s){var u=this.properties.threshold;this.isInputConnected(3)&&(u=this.getInputData(3),this.properties.threshold=u);var c=gl.UNSIGNED_BYTE;this.properties.high_precision&&(c=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==c&&this._temp_texture.width==d.width&&this._temp_texture.height==d.height||(this._temp_texture=new GL.Texture(d.width,d.height,{type:c,format:gl.RGBA, -filter:gl.LINEAR}));var x=q._first_shader;x||(x=q._first_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q._first_pixel_shader));var n=q._second_shader;n||(n=q._second_shader=new GL.Shader(q._second_vertex_shader,q._second_pixel_shader));var f=this._points_mesh;f&&f._width==d.width&&f._height==d.height&&2==f._spacing||(f=this.createPointsMesh(d.width,d.height,2));var A=Mesh.getScreenQuad(),k=this.properties.size,t=this.properties.alpha;gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){d.bind(0); -g.bind(1);l.bind(2);x.uniforms({u_texture:0,u_texture_blur:1,u_mask:2,u_texsize:[d.width,d.height]}).draw(A)});this._temp_texture.drawTo(function(){gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);d.bind(0);s.bind(3);n.uniforms({u_texture:0,u_mask:2,u_shape:3,u_alpha:t,u_threshold:u,u_pointSize:k,u_itexsize:[1/d.width,1/d.height]}).draw(f,gl.POINTS)});this.setOutputData(0,this._temp_texture)}}else this.setOutputData(0,d)};q.prototype.createPointsMesh=function(d,g,l){for(var q=Math.round(d/l),s=Math.round(g/ -l),c=new Float32Array(q*s*2),u=-1,n=2/d*l,f=2/g*l,A=0;A=this.size[1]||a.webgl&&(gl.meshes.cube||(gl.meshes.cube=GL.Mesh.cube({size:1})))};d.registerNodeType("texture/cubemap",g)}})(this); +(function(u){var d=u.LiteGraph;if("undefined"!=typeof GL){var k=function(){this.addInput("Texture","Texture");this.addInput("Aberration","number");this.addInput("Distortion","number");this.addInput("Blur","number");this.addOutput("Texture","Texture");this.properties={aberration:1,distortion:1,blur:1,precision:LGraphTexture.DEFAULT};k._shader||(k._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,k.pixel_shader),k._texture=new GL.Texture(3,1,{format:gl.RGB,wrap:gl.CLAMP_TO_EDGE,magFilter:gl.LINEAR, +minFilter:gl.LINEAR,pixel_data:[255,0,0,0,255,0,0,0,255]}))};k.title="Lens";k.desc="Camera Lens distortion";k.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}};k.prototype.onExecute=function(){var d=this.getInputData(0);if(this.properties.precision===LGraphTexture.PASS_THROUGH)this.setOutputData(0,d);else if(d){this._tex=LGraphTexture.getTargetTexture(d,this._tex,this.properties.precision);var g=this.properties.aberration;this.isInputConnected(1)&&(g=this.getInputData(1), +this.properties.aberration=g);var q=this.properties.distortion;this.isInputConnected(2)&&(q=this.getInputData(2),this.properties.distortion=q);var r=this.properties.blur;this.isInputConnected(3)&&(r=this.getInputData(3),this.properties.blur=r);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var u=Mesh.getScreenQuad(),c=k._shader;this._tex.drawTo(function(){d.bind(0);c.uniforms({u_texture:0,u_aberration:g,u_distortion:q,u_blur:r}).draw(u)});this.setOutputData(0,this._tex)}};k.pixel_shader="precision highp float;\n\t\t\tprecision highp float;\n\t\t\tvarying vec2 v_coord;\n\t\t\tuniform sampler2D u_texture;\n\t\t\tuniform vec2 u_camera_planes;\n\t\t\tuniform float u_aberration;\n\t\t\tuniform float u_distortion;\n\t\t\tuniform float u_blur;\n\t\t\t\n\t\t\tvoid main() {\n\t\t\t\tvec2 coord = v_coord;\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\t\t\t\tdist_coord *= percent;\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\t\t\t\tgl_FragColor = color;\n\t\t\t}\n\t\t\t"; +d.registerNodeType("fx/lens",k);u.LGraphFXLens=k;var q=function(){this.addInput("Texture","Texture");this.addInput("Blurred","Texture");this.addInput("Mask","Texture");this.addInput("Threshold","number");this.addOutput("Texture","Texture");this.properties={shape:"",size:10,alpha:1,threshold:1,high_precision:!1}};q.title="Bokeh";q.desc="applies an Bokeh effect";q.widgets_info={shape:{widget:"texture"}};q.prototype.onExecute=function(){var d=this.getInputData(0),g=this.getInputData(1),k=this.getInputData(2); +if(d&&k&&this.properties.shape){g||(g=d);var r=LGraphTexture.getTexture(this.properties.shape);if(r){var u=this.properties.threshold;this.isInputConnected(3)&&(u=this.getInputData(3),this.properties.threshold=u);var c=gl.UNSIGNED_BYTE;this.properties.high_precision&&(c=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==c&&this._temp_texture.width==d.width&&this._temp_texture.height==d.height||(this._temp_texture=new GL.Texture(d.width,d.height,{type:c,format:gl.RGBA, +filter:gl.LINEAR}));var x=q._first_shader;x||(x=q._first_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q._first_pixel_shader));var n=q._second_shader;n||(n=q._second_shader=new GL.Shader(q._second_vertex_shader,q._second_pixel_shader));var f=this._points_mesh;f&&f._width==d.width&&f._height==d.height&&2==f._spacing||(f=this.createPointsMesh(d.width,d.height,2));var A=Mesh.getScreenQuad(),h=this.properties.size,s=this.properties.alpha;gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){d.bind(0); +g.bind(1);k.bind(2);x.uniforms({u_texture:0,u_texture_blur:1,u_mask:2,u_texsize:[d.width,d.height]}).draw(A)});this._temp_texture.drawTo(function(){gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);d.bind(0);r.bind(3);n.uniforms({u_texture:0,u_mask:2,u_shape:3,u_alpha:s,u_threshold:u,u_pointSize:h,u_itexsize:[1/d.width,1/d.height]}).draw(f,gl.POINTS)});this.setOutputData(0,this._temp_texture)}}else this.setOutputData(0,d)};q.prototype.createPointsMesh=function(d,g,k){for(var q=Math.round(d/k),r=Math.round(g/ +k),c=new Float32Array(q*r*2),u=-1,n=2/d*k,f=2/g*k,A=0;Ag&&(g=12+g);return d.notes[g]+(f?"":a)};d.NoteStringToPitch=function(c){c=c.toUpperCase();var f=c[0],g=4;"#"==c[1]?(f+="#",2this.properties.max_value)return;this.trigger("on_midi",f)}};f.registerNodeType("midi/filter",m);w.title="MIDIEvent";w.desc="Create a MIDI Event";w.color="#243";w.prototype.onAction=function(c,f){"assign"== c?(this.properties.channel=f.channel,this.properties.cmd=f.cmd,this.properties.value1=f.data[1],this.properties.value2=f.data[2],f.cmd==d.NOTEON?this.gate=!0:f.cmd==d.NOTEOFF&&(this.gate=!1)):(f=this.midi_event,f.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?f.setCommandFromString(this.properties.cmd):f.cmd=this.properties.cmd,f.data[0]=f.cmd|f.channel,f.data[1]=Number(this.properties.value1),f.data[2]=Number(this.properties.value2),this.trigger("on_midi", f))};w.prototype.onExecute=function(){var c=this.properties;if(this.inputs)for(var f=0;fc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var d= 1;12>d;++d){if(this.valid_notes[(c-d)%12]){this.offset_notes[c]=-d;break}if(this.valid_notes[(c+d)%12]){this.offset_notes[c]=d;break}}};c.prototype.onAction=function(c,f){f&&f.constructor===d&&(f.data[0]==d.NOTEON||f.data[0]==d.NOTEOFF?(this.midi_event=new d,this.midi_event.setup(f.data),this.midi_event.data[1]+=this.offset_notes[d.note_to_index[f.note]],this.trigger("out",this.midi_event)):this.trigger("out",f))};c.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& this.processScale(c)};f.registerNodeType("midi/quantize",c);x.title="MIDI Play";x.desc="Plays a MIDI note";x.color="#243";x.prototype.onAction=function(c,f){if(f&&f.constructor===d){if(this.instrument&&f.data[0]==d.NOTEON){var g=f.note;if(!g||"undefined"==g||g.constructor!==String)return;this.instrument.play(g,f.octave,this.properties.duration,this.properties.volume)}this.trigger("note",f)}};x.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); null!=c&&(this.properties.duration=c)};f.registerNodeType("midi/play",x);n.title="MIDI Keys";n.desc="Keyboard to play notes";n.color="#243";n.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];n.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var d=12*this.properties.num_octaves; -this.keys.length=d;var f=this.size[0]/(7*this.properties.num_octaves),a=this.size[1];c.globalAlpha=1;for(var b=0;2>b;b++)for(var e=0;eg+h||c[1]>e))return b}}return-1};n.prototype.onAction=function(c,f){if("reset"==c)for(var g=0;gf[1])){var g=this.getKeyIndex(f);this.keys[g]=!0;this._last_key= +this.keys.length=d;var f=this.size[0]/(7*this.properties.num_octaves),a=this.size[1];c.globalAlpha=1;for(var b=0;2>b;b++)for(var e=0;eg+l||c[1]>e))return b}}return-1};n.prototype.onAction=function(c,f){if("reset"==c)for(var g=0;gf[1])){var g=this.getKeyIndex(f);this.keys[g]=!0;this._last_key= g;var g=12*(this.properties.start_octave-1)+29+g,a=new d;a.setup([d.NOTEON,g,100]);this.trigger("note",a);return!0}};n.prototype.onMouseMove=function(c,f){if(!(0>f[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var g=this.getKeyIndex(f);if(this._last_key==g)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new d;b.setup([d.NOTEOFF,a,100]);this.trigger("note",b);this.keys[g]=!0;a=12*(this.properties.start_octave-1)+29+g;b=new d;b.setup([d.NOTEON, a,100]);this.trigger("note",b);this._last_key=g;return!0}};n.prototype.onMouseUp=function(c,f){if(!(0>f[1])){var g=this.getKeyIndex(f);this.keys[g]=!1;this._last_key=-1;var g=12*(this.properties.start_octave-1)+29+g,a=new d;a.setup([d.NOTEOFF,g,100]);this.trigger("note",a);return!0}};f.registerNodeType("midi/keys",n)})(this); -(function(u){function d(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=v.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function l(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +(function(u){function d(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=v.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function k(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=v.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function q(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=v.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function g(){this.properties={gain:1};this.audionode=v.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function s(){this.properties={impulse_src:"",normalize:!0};this.audionode=v.getAudioContext().createConvolver(); +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function g(){this.properties={gain:1};this.audionode=v.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function r(){this.properties={impulse_src:"",normalize:!0};this.audionode=v.getAudioContext().createConvolver(); this.addInput("in","audio");this.addOutput("out","audio")}function m(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=v.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function w(){this.properties={};this.audionode=v.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function B(){this.properties={gain1:0.5,gain2:0.5}; this.audionode=v.getAudioContext().createGain();this.audionode1=v.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=v.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function z(){this.properties= {A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=v.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function y(){this.properties={delayTime:0.5};this.audionode=v.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function c(){this.properties={frequency:350,detune:0,Q:1}; this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=v.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function x(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=v.getAudioContext().createOscillator();this.addOutput("out","audio")}function n(){this.properties= {continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function f(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var a=A.default_function.toString(),b=a.indexOf("{")+1,c=a.lastIndexOf("}");A.default_code=a.substr(b,c-b)}this.properties={code:A.default_code};a=v.getAudioContext();a.createScriptProcessor?this.audionode=a.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=a.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.audionode=v.getAudioContext().destination;this.addInput("in","audio")}var t=u.LiteGraph,v={};u.LGAudio=v;v.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=a.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.audionode=v.getAudioContext().destination;this.addInput("in","audio")}var s=u.LiteGraph,v={};u.LGAudio=v;v.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(a){console.log("msg",a)};this._audio_context.onended=function(a){console.log("ended",a)};this._audio_context.oncomplete=function(a){console.log("complete",a)}}return this._audio_context};v.connect=function(a,b){try{a.connect(b)}catch(c){console.warn("LGraphAudio:",c)}};v.disconnect=function(a,b){try{a.disconnect(b)}catch(c){console.warn("LGraphAudio:",c)}};v.changeAllAudiosConnections=function(a,b){if(a.inputs)for(var c= -0;c=this.size[0]&&(f=this.size[0]-1),a.strokeStyle="red",a.beginPath(),a.moveTo(f,d),a.lineTo(f,0),a.stroke())}};n.title="Visualization";n.desc="Audio Visualization";t.registerNodeType("audio/visualization",n);f.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var a=this.properties.band,b=this.getInputData(1);void 0!==b&&(a=b);b=v.getAudioContext().sampleRate/this._freqs.length;b=a/b*2;b>=this._freqs.length?b=this._freqs[this._freqs.length-1]:(a=b|0, -b-=a,b=this._freqs[a]*(1-b)+this._freqs[a+1]*b);this.setOutputData(0,b/255*this.properties.amplitude)}};f.prototype.onGetInputs=function(){return[["band","number"]]};f.title="Signal";f.desc="extract the signal of some frequency";t.registerNodeType("audio/signal",f);A.prototype.onAdded=function(a){a.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop= +f=this.properties.mark/b*2/c,f>=this.size[0]&&(f=this.size[0]-1),a.strokeStyle="red",a.beginPath(),a.moveTo(f,d),a.lineTo(f,0),a.stroke())}};n.title="Visualization";n.desc="Audio Visualization";s.registerNodeType("audio/visualization",n);f.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var a=this.properties.band,b=this.getInputData(1);void 0!==b&&(a=b);b=v.getAudioContext().sampleRate/this._freqs.length;b=a/b*2;b>=this._freqs.length?b=this._freqs[this._freqs.length-1]:(a=b|0, +b-=a,b=this._freqs[a]*(1-b)+this._freqs[a+1]*b);this.setOutputData(0,b/255*this.properties.amplitude)}};f.prototype.onGetInputs=function(){return[["band","number"]]};f.title="Signal";f.desc="extract the signal of some frequency";s.registerNodeType("audio/signal",f);A.prototype.onAdded=function(a){a.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop= function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode=function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback= this._script.onaudioprocess}catch(a){console.error("Error in onaudioprocess code",a),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(a,b){"code"==a&&(this.properties.code=b,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};A.default_function=function(){this.onaudioprocess=function(a){var b=a.inputBuffer;a=a.outputBuffer;for(var c=0;c - Defined in: ../src/litegraph.js:7219 + Defined in: ../src/litegraph.js:8395 @@ -123,7 +123,7 @@

Defined in - ../src/litegraph.js:7219 + ../src/litegraph.js:8395

diff --git a/doc/classes/LGraph.html b/doc/classes/LGraph.html index 5118294fa..d3c5c3a46 100644 --- a/doc/classes/LGraph.html +++ b/doc/classes/LGraph.html @@ -85,7 +85,7 @@
- Defined in: ../src/litegraph.js:405 + Defined in: ../src/litegraph.js:438
@@ -120,7 +120,7 @@

Defined in - ../src/litegraph.js:405 + ../src/litegraph.js:438

@@ -178,7 +178,7 @@
  • - addGlobalOutput + addOutput
  • @@ -190,16 +190,20 @@
  • - changeGlobalInputType + changeInputType
  • - changeGlobalOutputType + changeOutputType
  • clear +
  • +
  • + clearTriggeredSlots +
  • configure @@ -232,18 +236,14 @@
  • getFixedTime -
  • -
  • - getGlobalInputData - -
  • -
  • - getGlobalOutputData -
  • getGroupOnPos +
  • +
  • + getInputData +
  • getNodeById @@ -270,19 +270,23 @@
  • - removeGlobalInput + removeInput
  • - removeGlobalOutput + removeLink
  • - renameGlobalInput + removeOutput
  • - renameGlobalOutput + renameInput + +
  • +
  • + renameOutput
  • @@ -302,11 +306,7 @@
  • - setGlobalOutputData - -
  • -
  • - setInputData + setOutputData
  • @@ -352,7 +352,7 @@

    Defined in - ../src/litegraph.js:947 + ../src/litegraph.js:1004

    @@ -412,7 +412,7 @@

    Defined in - ../src/litegraph.js:1194 + ../src/litegraph.js:1251

    @@ -465,8 +465,8 @@
    -
    -

    addGlobalOutput

    +
    +

    addOutput

    (
      @@ -492,7 +492,7 @@

      Defined in - ../src/litegraph.js:1323 + ../src/litegraph.js:1375

      @@ -559,7 +559,7 @@

      Defined in - ../src/litegraph.js:827 + ../src/litegraph.js:885

      @@ -596,7 +596,7 @@

      Defined in - ../src/litegraph.js:489 + ../src/litegraph.js:531

      @@ -628,8 +628,8 @@
      -
      -

      changeGlobalInputType

      +
      +

      changeInputType

      (
        @@ -652,7 +652,7 @@

        Defined in - ../src/litegraph.js:1281 + ../src/litegraph.js:1333

        @@ -694,8 +694,8 @@
        -
        -

        changeGlobalOutputType

        +
        +

        changeOutputType

        (
        diff --git a/doc/data.json b/doc/data.json index bf60ccdd5..8629b46d7 100644 --- a/doc/data.json +++ b/doc/data.json @@ -26,7 +26,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 18, + "line": 6, "description": "The Global Scope. It contains all the registered node classes.", "is_constructor": 1 }, @@ -39,7 +39,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 405, + "line": 438, "description": "LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.", "is_constructor": 1, "params": [ @@ -59,7 +59,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 1692, + "line": 1830, "description": "Base Class for all the node type classes", "params": [ { @@ -78,7 +78,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 3624, + "line": 4164, "description": "marks as dirty the canvas, this way it will be rendered again", "is_constructor": 1, "params": [ @@ -109,7 +109,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 7219, + "line": 8395, "description": "ContextMenu from LiteGUI", "is_constructor": 1, "params": [ @@ -130,7 +130,7 @@ "classitems": [ { "file": "../src/litegraph.js", - "line": 82, + "line": 93, "description": "Register a node class so it can be listed when the user wants to create a new one", "itemtype": "method", "name": "registerNodeType", @@ -150,7 +150,7 @@ }, { "file": "../src/litegraph.js", - "line": 149, + "line": 160, "description": "Create a new node type by passing a function, it wraps it with a propper class and generates inputs according to the parameters of the function.\nUseful to wrap simple methods that do not require properties, and that only process some input to generate an output.", "itemtype": "method", "name": "wrapFunctionAsNode", @@ -174,13 +174,18 @@ "name": "return_type", "description": "[optional] string with the return type, otherwise it will be generic", "type": "String" + }, + { + "name": "properties", + "description": "[optional] properties to be configurable", + "type": "Object" } ], "class": "LiteGraph" }, { "file": "../src/litegraph.js", - "line": 179, + "line": 193, "description": "Adds this method to all nodetypes, existing and to be created\n(You can add it to LGraphNode.prototype but then existing node types wont have it)", "itemtype": "method", "name": "addNodeMethod", @@ -195,7 +200,7 @@ }, { "file": "../src/litegraph.js", - "line": 197, + "line": 211, "description": "Create a node of a given type with a name. The node is not attached to any graph yet.", "itemtype": "method", "name": "createNode", @@ -220,7 +225,7 @@ }, { "file": "../src/litegraph.js", - "line": 240, + "line": 270, "description": "Returns a registered node type with a given name", "itemtype": "method", "name": "getNodeType", @@ -239,7 +244,7 @@ }, { "file": "../src/litegraph.js", - "line": 253, + "line": 281, "description": "Returns a list of node types matching one category", "itemtype": "method", "name": "getNodeType", @@ -258,7 +263,7 @@ }, { "file": "../src/litegraph.js", - "line": 281, + "line": 309, "description": "Returns a list with all the node type categories", "itemtype": "method", "name": "getNodeTypesCategories", @@ -270,7 +275,7 @@ }, { "file": "../src/litegraph.js", - "line": 435, + "line": 468, "description": "Removes all nodes from this graph", "itemtype": "method", "name": "clear", @@ -278,7 +283,7 @@ }, { "file": "../src/litegraph.js", - "line": 489, + "line": 531, "description": "Attach Canvas to this graph", "itemtype": "method", "name": "attachCanvas", @@ -293,7 +298,7 @@ }, { "file": "../src/litegraph.js", - "line": 508, + "line": 550, "description": "Detach Canvas from this graph", "itemtype": "method", "name": "detachCanvas", @@ -308,14 +313,14 @@ }, { "file": "../src/litegraph.js", - "line": 525, + "line": 567, "description": "Starts running this graph every interval milliseconds.", "itemtype": "method", "name": "start", "params": [ { "name": "interval", - "description": "amount of milliseconds between executions, default is 1", + "description": "amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate", "type": "Number" } ], @@ -323,7 +328,7 @@ }, { "file": "../src/litegraph.js", - "line": 554, + "line": 609, "description": "Stops the execution loop of the graph", "itemtype": "method", "name": "stop execution", @@ -331,7 +336,7 @@ }, { "file": "../src/litegraph.js", - "line": 576, + "line": 634, "description": "Run N steps (cycles) of the graph", "itemtype": "method", "name": "runStep", @@ -346,7 +351,7 @@ }, { "file": "../src/litegraph.js", - "line": 658, + "line": 716, "description": "Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than\nnodes with only inputs.", "itemtype": "method", "name": "updateExecutionOrder", @@ -354,7 +359,7 @@ }, { "file": "../src/litegraph.js", - "line": 790, + "line": 848, "description": "Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.\nIt doesnt include the node itself", "itemtype": "method", "name": "getAncestors", @@ -366,7 +371,7 @@ }, { "file": "../src/litegraph.js", - "line": 827, + "line": 885, "description": "Positions every node in a more readable manner", "itemtype": "method", "name": "arrange", @@ -374,7 +379,7 @@ }, { "file": "../src/litegraph.js", - "line": 871, + "line": 929, "description": "Returns the amount of time the graph has been running in milliseconds", "itemtype": "method", "name": "getTime", @@ -386,7 +391,7 @@ }, { "file": "../src/litegraph.js", - "line": 881, + "line": 939, "description": "Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant", "itemtype": "method", "name": "getFixedTime", @@ -398,7 +403,7 @@ }, { "file": "../src/litegraph.js", - "line": 892, + "line": 950, "description": "Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct\nif the nodes are using graphical actions", "itemtype": "method", "name": "getElapsedTime", @@ -410,7 +415,7 @@ }, { "file": "../src/litegraph.js", - "line": 904, + "line": 962, "description": "Sends an event to all the nodes, useful to trigger stuff", "itemtype": "method", "name": "sendEventToAllNodes", @@ -430,7 +435,7 @@ }, { "file": "../src/litegraph.js", - "line": 947, + "line": 1004, "description": "Adds a new node instasnce to this graph", "itemtype": "method", "name": "add", @@ -445,7 +450,7 @@ }, { "file": "../src/litegraph.js", - "line": 1011, + "line": 1068, "description": "Removes a node from the graph", "itemtype": "method", "name": "remove", @@ -460,7 +465,7 @@ }, { "file": "../src/litegraph.js", - "line": 1092, + "line": 1149, "description": "Returns a node by its id.", "itemtype": "method", "name": "getNodeById", @@ -475,7 +480,7 @@ }, { "file": "../src/litegraph.js", - "line": 1105, + "line": 1162, "description": "Returns a list of nodes that matches a class", "itemtype": "method", "name": "findNodesByClass", @@ -494,7 +499,7 @@ }, { "file": "../src/litegraph.js", - "line": 1121, + "line": 1178, "description": "Returns a list of nodes that matches a type", "itemtype": "method", "name": "findNodesByType", @@ -513,7 +518,7 @@ }, { "file": "../src/litegraph.js", - "line": 1138, + "line": 1195, "description": "Returns a list of nodes that matches a name", "itemtype": "method", "name": "findNodesByTitle", @@ -532,7 +537,7 @@ }, { "file": "../src/litegraph.js", - "line": 1154, + "line": 1211, "description": "Returns the top-most node in this position of the canvas", "itemtype": "method", "name": "getNodeOnPos", @@ -561,7 +566,7 @@ }, { "file": "../src/litegraph.js", - "line": 1174, + "line": 1231, "description": "Returns the top-most group in that position", "itemtype": "method", "name": "getGroupOnPos", @@ -585,7 +590,7 @@ }, { "file": "../src/litegraph.js", - "line": 1194, + "line": 1251, "description": "Tell this graph it has a global graph input of this type", "itemtype": "method", "name": "addGlobalInput", @@ -610,7 +615,7 @@ }, { "file": "../src/litegraph.js", - "line": 1213, + "line": 1274, "description": "Assign a data to the global graph input", "itemtype": "method", "name": "setGlobalInputData", @@ -630,30 +635,10 @@ }, { "file": "../src/litegraph.js", - "line": 1227, - "description": "Assign a data to the global graph input (same as setGlobalInputData)", - "itemtype": "method", - "name": "setInputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "data", - "description": "", - "type": "*" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1236, + "line": 1288, "description": "Returns the current value of a global graph input", "itemtype": "method", - "name": "getGlobalInputData", + "name": "getInputData", "params": [ { "name": "name", @@ -669,10 +654,10 @@ }, { "file": "../src/litegraph.js", - "line": 1250, + "line": 1302, "description": "Changes the name of a global graph input", "itemtype": "method", - "name": "renameGlobalInput", + "name": "renameInput", "params": [ { "name": "old_name", @@ -689,10 +674,10 @@ }, { "file": "../src/litegraph.js", - "line": 1281, + "line": 1333, "description": "Changes the type of a global graph input", "itemtype": "method", - "name": "changeGlobalInputType", + "name": "changeInputType", "params": [ { "name": "name", @@ -709,10 +694,10 @@ }, { "file": "../src/litegraph.js", - "line": 1301, + "line": 1353, "description": "Removes a global graph input", "itemtype": "method", - "name": "removeGlobalInput", + "name": "removeInput", "params": [ { "name": "name", @@ -729,10 +714,10 @@ }, { "file": "../src/litegraph.js", - "line": 1323, + "line": 1375, "description": "Creates a global graph output", "itemtype": "method", - "name": "addGlobalOutput", + "name": "addOutput", "params": [ { "name": "name", @@ -754,10 +739,10 @@ }, { "file": "../src/litegraph.js", - "line": 1342, + "line": 1394, "description": "Assign a data to the global output", "itemtype": "method", - "name": "setGlobalOutputData", + "name": "setOutputData", "params": [ { "name": "name", @@ -774,28 +759,9 @@ }, { "file": "../src/litegraph.js", - "line": 1356, + "line": 1408, "description": "Returns the current value of a global graph output", "itemtype": "method", - "name": "getGlobalOutputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - } - ], - "return": { - "description": "the data", - "type": "*" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1370, - "description": "Returns the current value of a global graph output (sames as getGlobalOutputData)", - "itemtype": "method", "name": "getOutputData", "params": [ { @@ -812,10 +778,10 @@ }, { "file": "../src/litegraph.js", - "line": 1379, + "line": 1422, "description": "Renames a global graph output", "itemtype": "method", - "name": "renameGlobalOutput", + "name": "renameOutput", "params": [ { "name": "old_name", @@ -832,10 +798,10 @@ }, { "file": "../src/litegraph.js", - "line": 1407, + "line": 1450, "description": "Changes the type of a global graph output", "itemtype": "method", - "name": "changeGlobalOutputType", + "name": "changeOutputType", "params": [ { "name": "name", @@ -852,10 +818,10 @@ }, { "file": "../src/litegraph.js", - "line": 1427, + "line": 1470, "description": "Removes a global graph output", "itemtype": "method", - "name": "removeGlobalOutput", + "name": "removeOutput", "params": [ { "name": "name", @@ -867,7 +833,7 @@ }, { "file": "../src/litegraph.js", - "line": 1471, + "line": 1514, "description": "returns if the graph is in live mode", "itemtype": "method", "name": "isLive", @@ -875,7 +841,30 @@ }, { "file": "../src/litegraph.js", - "line": 1506, + "line": 1533, + "description": "clears the triggered slot animation in all links (stop visual animation)", + "itemtype": "method", + "name": "clearTriggeredSlots", + "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 1565, + "description": "Destroys a link", + "itemtype": "method", + "name": "removeLink", + "params": [ + { + "name": "link_id", + "description": "", + "type": "Number" + } + ], + "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 1582, "description": "Creates a Object containing all the info about this graph, it can be serialized", "itemtype": "method", "name": "serialize", @@ -887,7 +876,7 @@ }, { "file": "../src/litegraph.js", - "line": 1542, + "line": 1619, "description": "Configure a graph from a JSON string", "itemtype": "method", "name": "configure", @@ -907,7 +896,7 @@ }, { "file": "../src/litegraph.js", - "line": 1744, + "line": 1881, "description": "configure a node from an object containing the serialized info", "itemtype": "method", "name": "configure", @@ -915,7 +904,7 @@ }, { "file": "../src/litegraph.js", - "line": 1848, + "line": 1949, "description": "serialize the content", "itemtype": "method", "name": "serialize", @@ -923,7 +912,7 @@ }, { "file": "../src/litegraph.js", - "line": 1930, + "line": 2039, "description": "serialize and stringify", "itemtype": "method", "name": "toString", @@ -931,7 +920,7 @@ }, { "file": "../src/litegraph.js", - "line": 1942, + "line": 2051, "description": "get the title string", "itemtype": "method", "name": "getTitle", @@ -939,7 +928,7 @@ }, { "file": "../src/litegraph.js", - "line": 1955, + "line": 2064, "description": "sets the output data", "itemtype": "method", "name": "setOutputData", @@ -959,7 +948,27 @@ }, { "file": "../src/litegraph.js", - "line": 1991, + "line": 2100, + "description": "sets the output data type, useful when you want to be able to overwrite the data type", + "itemtype": "method", + "name": "setOutputDataType", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + }, + { + "name": "datatype", + "description": "", + "type": "String" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 2129, "description": "Retrieves the input data (data traveling through the connection) from one slot", "itemtype": "method", "name": "getInputData", @@ -983,7 +992,26 @@ }, { "file": "../src/litegraph.js", - "line": 2027, + "line": 2165, + "description": "Retrieves the input data type (in case this supports multiple input types)", + "itemtype": "method", + "name": "getInputDataType", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "datatype in string format", + "type": "String" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 2191, "description": "Retrieves the input data from one slot using its name instead of slot number", "itemtype": "method", "name": "getInputDataByName", @@ -1007,7 +1035,7 @@ }, { "file": "../src/litegraph.js", - "line": 2043, + "line": 2207, "description": "tells you if there is a connection in one input slot", "itemtype": "method", "name": "isInputConnected", @@ -1026,7 +1054,7 @@ }, { "file": "../src/litegraph.js", - "line": 2056, + "line": 2220, "description": "tells you info about an input connection (which node, type, etc)", "itemtype": "method", "name": "getInputInfo", @@ -1045,7 +1073,7 @@ }, { "file": "../src/litegraph.js", - "line": 2071, + "line": 2235, "description": "returns the node connected in the input slot", "itemtype": "method", "name": "getInputNode", @@ -1064,7 +1092,7 @@ }, { "file": "../src/litegraph.js", - "line": 2093, + "line": 2257, "description": "returns the value of an input with this name, otherwise checks if there is a property with that name", "itemtype": "method", "name": "getInputOrProperty", @@ -1083,7 +1111,7 @@ }, { "file": "../src/litegraph.js", - "line": 2117, + "line": 2284, "description": "tells you the last output data that went in that slot", "itemtype": "method", "name": "getOutputData", @@ -1102,7 +1130,7 @@ }, { "file": "../src/litegraph.js", - "line": 2135, + "line": 2302, "description": "tells you info about an output connection (which node, type, etc)", "itemtype": "method", "name": "getOutputInfo", @@ -1121,7 +1149,7 @@ }, { "file": "../src/litegraph.js", - "line": 2151, + "line": 2318, "description": "tells you if there is a connection in one output slot", "itemtype": "method", "name": "isOutputConnected", @@ -1140,7 +1168,7 @@ }, { "file": "../src/litegraph.js", - "line": 2164, + "line": 2331, "description": "tells you if there is any connection in the output slots", "itemtype": "method", "name": "isAnyOutputConnected", @@ -1152,7 +1180,7 @@ }, { "file": "../src/litegraph.js", - "line": 2180, + "line": 2347, "description": "retrieves all the nodes connected to this output slot", "itemtype": "method", "name": "getOutputNodes", @@ -1171,7 +1199,7 @@ }, { "file": "../src/litegraph.js", - "line": 2213, + "line": 2380, "description": "Triggers an event in this node, this will trigger any output with the same name", "itemtype": "method", "name": "trigger", @@ -1191,7 +1219,7 @@ }, { "file": "../src/litegraph.js", - "line": 2236, + "line": 2403, "description": "Triggers an slot event in this node", "itemtype": "method", "name": "triggerSlot", @@ -1205,13 +1233,38 @@ "name": "param", "description": "", "type": "*" + }, + { + "name": "link_id", + "description": "[optional] in case you want to trigger and specific output link in a slot", + "type": "Number" } ], "class": "LGraphNode" }, { "file": "../src/litegraph.js", - "line": 2283, + "line": 2453, + "description": "clears the trigger slot animation", + "itemtype": "method", + "name": "clearTriggeredSlot", + "params": [ + { + "name": "slot", + "description": "the index of the output slot", + "type": "Number" + }, + { + "name": "link_id", + "description": "[optional] in case you want to trigger and specific output link in a slot", + "type": "Number" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 2485, "description": "add a new property to this node", "itemtype": "method", "name": "addProperty", @@ -1241,7 +1294,7 @@ }, { "file": "../src/litegraph.js", - "line": 2309, + "line": 2511, "description": "add a new output slot to use in this node", "itemtype": "method", "name": "addOutput", @@ -1266,7 +1319,7 @@ }, { "file": "../src/litegraph.js", - "line": 2332, + "line": 2535, "description": "add a new output slot to use in this node", "itemtype": "method", "name": "addOutputs", @@ -1281,7 +1334,7 @@ }, { "file": "../src/litegraph.js", - "line": 2357, + "line": 2561, "description": "remove an existing output slot", "itemtype": "method", "name": "removeOutput", @@ -1296,7 +1349,7 @@ }, { "file": "../src/litegraph.js", - "line": 2371, + "line": 2590, "description": "add a new input slot to use in this node", "itemtype": "method", "name": "addInput", @@ -1321,7 +1374,7 @@ }, { "file": "../src/litegraph.js", - "line": 2395, + "line": 2615, "description": "add several new input slots in this node", "itemtype": "method", "name": "addInputs", @@ -1336,7 +1389,7 @@ }, { "file": "../src/litegraph.js", - "line": 2420, + "line": 2641, "description": "remove an existing input slot", "itemtype": "method", "name": "removeInput", @@ -1351,7 +1404,7 @@ }, { "file": "../src/litegraph.js", - "line": 2434, + "line": 2665, "description": "add an special connection to this node (used for special kinds of graphs)", "itemtype": "method", "name": "addConnection", @@ -1381,7 +1434,7 @@ }, { "file": "../src/litegraph.js", - "line": 2455, + "line": 2686, "description": "computes the size of a node according to its inputs and output slots", "itemtype": "method", "name": "computeSize", @@ -1400,19 +1453,19 @@ }, { "file": "../src/litegraph.js", - "line": 2510, + "line": 2753, "description": "Allows to pass", "itemtype": "method", "name": "addWidget", "return": { - "description": "the total size", - "type": "Float32Array[4]" + "description": "the created widget", + "type": "Object" }, "class": "LGraphNode" }, { "file": "../src/litegraph.js", - "line": 2538, + "line": 2791, "description": "returns the bounding of the object, used for rendering purposes\nbounding is: [topleft_cornerx, topleft_cornery, width, height]", "itemtype": "method", "name": "getBounding", @@ -1424,7 +1477,7 @@ }, { "file": "../src/litegraph.js", - "line": 2554, + "line": 2810, "description": "checks if a point is inside the shape of a node", "itemtype": "method", "name": "isPointInside", @@ -1448,7 +1501,7 @@ }, { "file": "../src/litegraph.js", - "line": 2578, + "line": 2836, "description": "checks if a point is inside a node slot, and returns info about which slot", "itemtype": "method", "name": "getSlotInPosition", @@ -1472,7 +1525,7 @@ }, { "file": "../src/litegraph.js", - "line": 2609, + "line": 2868, "description": "returns the input slot with a given name (used for dynamic slots), -1 if not found", "itemtype": "method", "name": "findInputSlot", @@ -1491,7 +1544,7 @@ }, { "file": "../src/litegraph.js", - "line": 2625, + "line": 2884, "description": "returns the output slot with a given name (used for dynamic slots), -1 if not found", "itemtype": "method", "name": "findOutputSlot", @@ -1510,7 +1563,7 @@ }, { "file": "../src/litegraph.js", - "line": 2640, + "line": 2899, "description": "connect this node output to the input of another node", "itemtype": "method", "name": "connect", @@ -1532,14 +1585,14 @@ } ], "return": { - "description": "if it was connected succesfully", - "type": "Boolean" + "description": "the link_info is created, otherwise null", + "type": "Object" }, "class": "LGraphNode" }, { "file": "../src/litegraph.js", - "line": 2768, + "line": 3024, "description": "disconnect one output to an specific node", "itemtype": "method", "name": "disconnectOutput", @@ -1563,7 +1616,7 @@ }, { "file": "../src/litegraph.js", - "line": 2870, + "line": 3132, "description": "disconnect one input", "itemtype": "method", "name": "disconnectInput", @@ -1582,7 +1635,7 @@ }, { "file": "../src/litegraph.js", - "line": 2939, + "line": 3206, "description": "returns the center of a connection point in canvas coords", "itemtype": "method", "name": "getConnectionPos", @@ -1596,6 +1649,11 @@ "name": "slot", "description": "(could be the number of the slot or the string with the name of the slot)", "type": "Number_or_string" + }, + { + "name": "out", + "description": "[optional] a place to store the output, to free garbage", + "type": "Vec2" } ], "return": { @@ -1606,7 +1664,7 @@ }, { "file": "../src/litegraph.js", - "line": 3072, + "line": 3390, "description": "Collapse the node to make it smaller on the canvas", "itemtype": "method", "name": "collapse", @@ -1614,7 +1672,7 @@ }, { "file": "../src/litegraph.js", - "line": 3088, + "line": 3406, "description": "Forces the node to do not move or realign on Z", "itemtype": "method", "name": "pin", @@ -1622,7 +1680,7 @@ }, { "file": "../src/litegraph.js", - "line": 3306, + "line": 3837, "description": "clears all the data inside", "itemtype": "method", "name": "clear", @@ -1630,7 +1688,7 @@ }, { "file": "../src/litegraph.js", - "line": 3348, + "line": 3880, "description": "assigns a graph, you can reasign graphs to the same canvas", "itemtype": "method", "name": "setGraph", @@ -1645,7 +1703,7 @@ }, { "file": "../src/litegraph.js", - "line": 3379, + "line": 3911, "description": "opens a graph contained inside a node in the current graph", "itemtype": "method", "name": "openSubgraph", @@ -1660,7 +1718,7 @@ }, { "file": "../src/litegraph.js", - "line": 3406, + "line": 3938, "description": "closes a subgraph contained inside a node", "itemtype": "method", "name": "closeSubgraph", @@ -1675,7 +1733,7 @@ }, { "file": "../src/litegraph.js", - "line": 3423, + "line": 3961, "description": "assigns a canvas", "itemtype": "method", "name": "setCanvas", @@ -1690,7 +1748,7 @@ }, { "file": "../src/litegraph.js", - "line": 3498, + "line": 4038, "description": "binds mouse, keyboard, touch and drag events to the canvas", "itemtype": "method", "name": "bindEvents", @@ -1698,7 +1756,7 @@ }, { "file": "../src/litegraph.js", - "line": 3550, + "line": 4090, "description": "unbinds mouse events from the canvas", "itemtype": "method", "name": "unbindEvents", @@ -1706,7 +1764,7 @@ }, { "file": "../src/litegraph.js", - "line": 3598, + "line": 4138, "description": "this function allows to render the canvas using WebGL instead of Canvas2D\nthis is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL", "itemtype": "method", "name": "enableWebGL", @@ -1714,7 +1772,7 @@ }, { "file": "../src/litegraph.js", - "line": 3640, + "line": 4180, "description": "Used to attach the canvas in a popup", "itemtype": "method", "name": "getCanvasWindow", @@ -1726,7 +1784,7 @@ }, { "file": "../src/litegraph.js", - "line": 3654, + "line": 4194, "description": "starts rendering the content of the canvas when needed", "itemtype": "method", "name": "startRendering", @@ -1734,7 +1792,7 @@ }, { "file": "../src/litegraph.js", - "line": 3678, + "line": 4218, "description": "stops rendering the content of the canvas (to save resources)", "itemtype": "method", "name": "stopRendering", @@ -1742,7 +1800,7 @@ }, { "file": "../src/litegraph.js", - "line": 3921, + "line": 4501, "description": "Called when a mouse move event has to be processed", "itemtype": "method", "name": "processMouseMove", @@ -1750,7 +1808,7 @@ }, { "file": "../src/litegraph.js", - "line": 4112, + "line": 4675, "description": "Called when a mouse up event has to be processed", "itemtype": "method", "name": "processMouseUp", @@ -1758,7 +1816,7 @@ }, { "file": "../src/litegraph.js", - "line": 4263, + "line": 4847, "description": "Called when a mouse wheel event has to be processed", "itemtype": "method", "name": "processMouseWheel", @@ -1766,7 +1824,7 @@ }, { "file": "../src/litegraph.js", - "line": 4296, + "line": 4876, "description": "retuns true if a position (in graph space) is on top of a node little corner box", "itemtype": "method", "name": "isOverNodeBox", @@ -1774,7 +1832,7 @@ }, { "file": "../src/litegraph.js", - "line": 4308, + "line": 4888, "description": "retuns true if a position (in graph space) is on top of a node input slot", "itemtype": "method", "name": "isOverNodeInput", @@ -1782,7 +1840,7 @@ }, { "file": "../src/litegraph.js", - "line": 4332, + "line": 4917, "description": "process a key event", "itemtype": "method", "name": "processKey", @@ -1790,7 +1848,7 @@ }, { "file": "../src/litegraph.js", - "line": 4485, + "line": 5074, "description": "process a item drop event on top the canvas", "itemtype": "method", "name": "processDrop", @@ -1798,7 +1856,7 @@ }, { "file": "../src/litegraph.js", - "line": 4603, + "line": 5192, "description": "selects a given node (or adds it to the current selection)", "itemtype": "method", "name": "selectNode", @@ -1806,7 +1864,7 @@ }, { "file": "../src/litegraph.js", - "line": 4615, + "line": 5204, "description": "selects several nodes (or adds them to the current selection)", "itemtype": "method", "name": "selectNodes", @@ -1814,7 +1872,7 @@ }, { "file": "../src/litegraph.js", - "line": 4653, + "line": 5242, "description": "removes a node from the current selection", "itemtype": "method", "name": "deselectNode", @@ -1822,7 +1880,7 @@ }, { "file": "../src/litegraph.js", - "line": 4679, + "line": 5268, "description": "removes all nodes from the current selection", "itemtype": "method", "name": "deselectAllNodes", @@ -1830,7 +1888,7 @@ }, { "file": "../src/litegraph.js", - "line": 4702, + "line": 5291, "description": "deletes all nodes in the current selection from the graph", "itemtype": "method", "name": "deleteSelectedNodes", @@ -1838,7 +1896,7 @@ }, { "file": "../src/litegraph.js", - "line": 4719, + "line": 5308, "description": "centers the camera on a given node", "itemtype": "method", "name": "centerOnNode", @@ -1846,7 +1904,7 @@ }, { "file": "../src/litegraph.js", - "line": 4730, + "line": 5319, "description": "adds some useful properties to a mouse event, like the position in graph coordinates", "itemtype": "method", "name": "adjustMouseEvent", @@ -1854,7 +1912,7 @@ }, { "file": "../src/litegraph.js", - "line": 4758, + "line": 5347, "description": "changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom", "itemtype": "method", "name": "setZoom", @@ -1862,23 +1920,23 @@ }, { "file": "../src/litegraph.js", - "line": 4786, - "description": "converts a coordinate in canvas2D space to graphcanvas space (NAME IS CONFUSION, SHOULD BE THE OTHER WAY AROUND)", + "line": 5378, + "description": "converts a coordinate from graph coordinates to canvas2D coordinates", "itemtype": "method", "name": "convertOffsetToCanvas", "class": "LGraphCanvas" }, { "file": "../src/litegraph.js", - "line": 4798, - "description": "converts a coordinate in graphcanvas space to canvas2D space (NAME IS CONFUSION, SHOULD BE THE OTHER WAY AROUND)", + "line": 5387, + "description": "converts a coordinate from Canvas2D coordinates to graph space", "itemtype": "method", "name": "convertCanvasToOffset", "class": "LGraphCanvas" }, { "file": "../src/litegraph.js", - "line": 4816, + "line": 5403, "description": "brings a node to front (above all other nodes)", "itemtype": "method", "name": "bringToFront", @@ -1886,7 +1944,7 @@ }, { "file": "../src/litegraph.js", - "line": 4829, + "line": 5416, "description": "sends a node to the back (below all other nodes)", "itemtype": "method", "name": "sendToBack", @@ -1894,7 +1952,7 @@ }, { "file": "../src/litegraph.js", - "line": 4849, + "line": 5436, "description": "checks which nodes are visible (inside the camera area)", "itemtype": "method", "name": "computeVisibleNodes", @@ -1902,7 +1960,7 @@ }, { "file": "../src/litegraph.js", - "line": 4874, + "line": 5461, "description": "renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)", "itemtype": "method", "name": "draw", @@ -1910,7 +1968,7 @@ }, { "file": "../src/litegraph.js", - "line": 4905, + "line": 5488, "description": "draws the front canvas (the one containing all the nodes)", "itemtype": "method", "name": "drawFrontCanvas", @@ -1918,7 +1976,7 @@ }, { "file": "../src/litegraph.js", - "line": 5038, + "line": 5632, "description": "draws some useful stats in the corner of the canvas", "itemtype": "method", "name": "renderInfo", @@ -1926,7 +1984,7 @@ }, { "file": "../src/litegraph.js", - "line": 5064, + "line": 5659, "description": "draws the back canvas (the one containing the background and the connections)", "itemtype": "method", "name": "drawBackCanvas", @@ -1934,7 +1992,7 @@ }, { "file": "../src/litegraph.js", - "line": 5196, + "line": 5807, "description": "draws the given node inside the canvas", "itemtype": "method", "name": "drawNode", @@ -1942,7 +2000,7 @@ }, { "file": "../src/litegraph.js", - "line": 5467, + "line": 6115, "description": "draws the shape of the given node in the canvas", "itemtype": "method", "name": "drawNodeShape", @@ -1950,7 +2008,7 @@ }, { "file": "../src/litegraph.js", - "line": 5639, + "line": 6311, "description": "draws every connection visible in the canvas\nOPTIMIZE THIS: precatch connections position instead of recomputing them every time", "itemtype": "method", "name": "drawConnections", @@ -1958,15 +2016,62 @@ }, { "file": "../src/litegraph.js", - "line": 5694, + "line": 6398, "description": "draws a link between two points", "itemtype": "method", "name": "renderLink", + "params": [ + { + "name": "a", + "description": "start pos", + "type": "Vec2" + }, + { + "name": "b", + "description": "end pos", + "type": "Vec2" + }, + { + "name": "link", + "description": "the link object with all the link info", + "type": "Object" + }, + { + "name": "skip_border", + "description": "ignore the shadow of the link", + "type": "Boolean" + }, + { + "name": "flow", + "description": "show flow animation (for events)", + "type": "Boolean" + }, + { + "name": "color", + "description": "the color for the link", + "type": "String" + }, + { + "name": "start_dir", + "description": "the direction enum", + "type": "Number" + }, + { + "name": "end_dir", + "description": "the direction enum", + "type": "Number" + }, + { + "name": "num_sublines", + "description": "number of sublines (useful to represent vec3 or rgb)", + "type": "Number" + } + ], "class": "LGraphCanvas" }, { "file": "../src/litegraph.js", - "line": 5815, + "line": 6663, "description": "draws the widgets stored inside a node", "itemtype": "method", "name": "drawNodeWidgets", @@ -1974,7 +2079,7 @@ }, { "file": "../src/litegraph.js", - "line": 5911, + "line": 6813, "description": "process an event on widgets", "itemtype": "method", "name": "processNodeWidgets", @@ -1982,7 +2087,7 @@ }, { "file": "../src/litegraph.js", - "line": 5990, + "line": 6924, "description": "draws every group area in the background", "itemtype": "method", "name": "drawGroups", @@ -1990,7 +2095,7 @@ }, { "file": "../src/litegraph.js", - "line": 6035, + "line": 6979, "description": "resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode", "itemtype": "method", "name": "resize", @@ -1998,7 +2103,7 @@ }, { "file": "../src/litegraph.js", - "line": 6058, + "line": 7002, "description": "switches to live mode (node shapes are not rendered, only the content)\nthis feature was designed when graphs where meant to create user interfaces", "itemtype": "method", "name": "switchLiveMode", diff --git a/doc/files/.._src_litegraph.js.html b/doc/files/.._src_litegraph.js.html index e1f237abe..069ec1ee6 100644 --- a/doc/files/.._src_litegraph.js.html +++ b/doc/files/.._src_litegraph.js.html @@ -89,18 +89,6 @@ // LiteGraph CLASS ******* // ************************************************************* -/* FYI: links are stored in graph.links with this structure per object -{ - id: number - type: string, - origin_id: number, - origin_slot: number, - target_id: number, - target_slot: number, - data: * -}; -*/ - /** * The Global Scope. It contains all the registered node classes. * @@ -110,26 +98,35 @@ var LiteGraph = global.LiteGraph = { - NODE_TITLE_HEIGHT: 20, - NODE_SLOT_HEIGHT: 15, + VERSION: 0.4, + + CANVAS_GRID_SIZE: 10, + + NODE_TITLE_HEIGHT: 30, + NODE_TITLE_TEXT_Y: 20, + NODE_SLOT_HEIGHT: 20, NODE_WIDGET_HEIGHT: 20, NODE_WIDTH: 140, NODE_MIN_WIDTH: 50, NODE_COLLAPSED_RADIUS: 10, NODE_COLLAPSED_WIDTH: 80, - CANVAS_GRID_SIZE: 10, NODE_TITLE_COLOR: "#999", NODE_TEXT_SIZE: 14, NODE_TEXT_COLOR: "#AAA", NODE_SUBTEXT_SIZE: 12, NODE_DEFAULT_COLOR: "#333", - NODE_DEFAULT_BGCOLOR: "#444", - NODE_DEFAULT_BOXCOLOR: "#888", + NODE_DEFAULT_BGCOLOR: "#353535", + NODE_DEFAULT_BOXCOLOR: "#666", NODE_DEFAULT_SHAPE: "box", + DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", + DEFAULT_GROUP_FONT: 24, + + LINK_COLOR: "#9A9", + EVENT_LINK_COLOR: "#A86", + CONNECTING_LINK_COLOR: "#AFA", + MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops DEFAULT_POSITION: [100,100],//default node position - node_images_path: "", - VALID_SHAPES: ["default","box","round","card"], //,"circle" //shapes are used for nodes but also for slots @@ -151,20 +148,34 @@ var LiteGraph = global.LiteGraph = { NEVER: 2, ON_TRIGGER: 3, + UP: 1, + DOWN:2, + LEFT:3, + RIGHT:4, + CENTER:5, + + STRAIGHT_LINK: 0, + LINEAR_LINK: 1, + SPLINE_LINK: 2, + NORMAL_TITLE: 0, NO_TITLE: 1, TRANSPARENT_TITLE: 2, AUTOHIDE_TITLE: 3, proxy: null, //used to redirect calls + node_images_path: "", debug: false, + catch_exceptions: true, throw_errors: true, - allow_scripts: true, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits registered_node_types: {}, //nodetypes by string node_types_by_file_extension: {}, //used for droping files in the canvas Nodes: {}, //node types by classname + searchbox_extras: {}, //used to add extra features to the search box + /** * Register a node class so it can be listed when the user wants to create a new one * @method registerNodeType @@ -240,8 +251,9 @@ var LiteGraph = global.LiteGraph = { * @param {Function} func * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable */ - wrapFunctionAsNode: function( name, func, param_types, return_type ) + wrapFunctionAsNode: function( name, func, param_types, return_type, properties ) { var params = Array(func.length); var code = ""; @@ -249,6 +261,8 @@ var LiteGraph = global.LiteGraph = { for(var i = 0; i < names.length; ++i) code += "this.addInput('"+names[i]+"',"+(param_types && param_types[i] ? "'" + param_types[i] + "'" : "0") + ");\n"; code += "this.addOutput('out',"+( return_type ? "'" + return_type + "'" : 0 )+");\n"; + if(properties) + code += "this.properties = " + JSON.stringify(properties) + ";\n"; var classobj = Function(code); classobj.title = name.split("/").pop(); classobj.desc = "Generated from " + func.name; @@ -302,7 +316,23 @@ var LiteGraph = global.LiteGraph = { title = title || base_class.title || type; - var node = new base_class( title ); + var node = null; + + if( LiteGraph.catch_exceptions ) + { + try + { + node = new base_class( title ); + } + catch (err) + { + console.error(err); + return null; + } + } + else + node = new base_class( title ); + node.type = type; if(!node.title && title) node.title = title; @@ -329,13 +359,11 @@ var LiteGraph = global.LiteGraph = { * @param {String} type full name of the node class. p.e. "math/sin" * @return {Class} the node class */ - getNodeType: function(type) { return this.registered_node_types[type]; }, - /** * Returns a list of node types matching one category * @method getNodeType @@ -463,6 +491,11 @@ var LiteGraph = global.LiteGraph = { return true; return false; + }, + + registerSearchboxExtra: function( node_type, description, data ) + { + this.searchbox_extras[ description ] = { type: node_type, desc: description, data: data }; } }; @@ -533,6 +566,15 @@ LGraph.prototype.clear = function() this._version = -1; //used to detect changes + //safe clear + if(this._nodes) + for(var i = 0; i < this._nodes.length; ++i) + { + var node = this._nodes[i]; + if(node.onRemoved) + node.onRemoved(); + } + //nodes this._nodes = []; this._nodes_by_id = {}; @@ -563,8 +605,8 @@ LGraph.prototype.clear = function() this.catch_errors = true; //subgraph_data - this.global_inputs = {}; - this.global_outputs = {}; + this.inputs = {}; + this.outputs = {}; //notify canvas to redraw this.change(); @@ -611,10 +653,10 @@ LGraph.prototype.detachCanvas = function(graphcanvas) /** * Starts running this graph every interval milliseconds. * @method start -* @param {number} interval amount of milliseconds between executions, default is 1 +* @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate */ -LGraph.prototype.start = function(interval) +LGraph.prototype.start = function( interval ) { if( this.status == LGraph.STATUS_RUNNING ) return; @@ -628,13 +670,26 @@ LGraph.prototype.start = function(interval) //launch this.starttime = LiteGraph.getTime(); this.last_update_time = this.starttime; - interval = interval || 1; + interval = interval || 0; var that = this; - this.execution_timer_id = setInterval( function() { - //execute - that.runStep(1, !this.catch_errors ); - },interval); + if(interval == 0 && typeof(window) != "undefined" && window.requestAnimationFrame ) + { + function on_frame() + { + if(that.execution_timer_id != -1) + return; + window.requestAnimationFrame(on_frame); + that.runStep(1, !this.catch_errors ); + } + this.execution_timer_id = -1; + on_frame(); + } + else + this.execution_timer_id = setInterval( function() { + //execute + that.runStep(1, !this.catch_errors ); + },interval); } /** @@ -653,8 +708,11 @@ LGraph.prototype.stop = function() this.onStopEvent(); if(this.execution_timer_id != null) - clearInterval(this.execution_timer_id); - this.execution_timer_id = null; + { + if( this.execution_timer_id != -1 ) + clearInterval(this.execution_timer_id); + this.execution_timer_id = null; + } this.sendEventToAllNodes("onStop"); } @@ -1005,15 +1063,14 @@ LGraph.prototype.sendEventToAllNodes = function( eventname, params, mode ) for( var j = 0, l = nodes.length; j < l; ++j ) { var node = nodes[j]; - if(node[eventname] && node.mode == mode ) - { - if(params === undefined) - node[eventname](); - else if(params && params.constructor === Array) - node[eventname].apply( node, params ); - else - node[eventname](params); - } + if( !node[eventname] || node.mode != mode ) + continue; + if(params === undefined) + node[eventname](); + else if(params && params.constructor === Array) + node[eventname].apply( node, params ); + else + node[eventname](params); } } @@ -1245,13 +1302,13 @@ LGraph.prototype.findNodesByTitle = function(title) * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph * @return {LGraphNode} the node at this position or null */ -LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) +LGraph.prototype.getNodeOnPos = function( x, y, nodes_list, margin ) { nodes_list = nodes_list || this._nodes; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; - if(n.isPointInside( x, y, 2 )) + if(n.isPointInside( x, y, margin )) return n; } return null; @@ -1269,7 +1326,7 @@ LGraph.prototype.getGroupOnPos = function(x,y) for (var i = this._groups.length - 1; i >= 0; i--) { var g = this._groups[i]; - if(g.isPointInside( x, y, 2 )) + if(g.isPointInside( x, y, 2, true )) return g; } return null; @@ -1284,16 +1341,20 @@ LGraph.prototype.getGroupOnPos = function(x,y) * @param {String} type * @param {*} value [optional] */ -LGraph.prototype.addGlobalInput = function(name, type, value) +LGraph.prototype.addInput = function(name, type, value) { - this.global_inputs[name] = { name: name, type: type, value: value }; + var input = this.inputs[ name ]; + if( input ) //already exist + return; + + this.inputs[ name ] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalInputAdded) - this.onGlobalInputAdded(name, type); + if(this.onInputAdded) + this.onInputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** @@ -1302,32 +1363,23 @@ LGraph.prototype.addGlobalInput = function(name, type, value) * @param {String} name * @param {*} data */ -LGraph.prototype.setGlobalInputData = function(name, data) +LGraph.prototype.setInputData = function(name, data) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return; input.value = data; } -/** -* Assign a data to the global graph input (same as setGlobalInputData) -* @method setInputData -* @param {String} name -* @param {*} data -*/ -LGraph.prototype.setInputData = LGraph.prototype.setGlobalInputData; - - /** * Returns the current value of a global graph input -* @method getGlobalInputData +* @method getInputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalInputData = function(name) +LGraph.prototype.getInputData = function(name) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return null; return input.value; @@ -1335,105 +1387,105 @@ LGraph.prototype.getGlobalInputData = function(name) /** * Changes the name of a global graph input -* @method renameGlobalInput +* @method renameInput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalInput = function(old_name, name) +LGraph.prototype.renameInput = function(old_name, name) { if(name == old_name) return; - if(!this.global_inputs[old_name]) + if(!this.inputs[old_name]) return false; - if(this.global_inputs[name]) + if(this.inputs[name]) { console.error("there is already one input with that name"); return false; } - this.global_inputs[name] = this.global_inputs[old_name]; - delete this.global_inputs[old_name]; + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; this._version++; - if(this.onGlobalInputRenamed) - this.onGlobalInputRenamed(old_name, name); + if(this.onInputRenamed) + this.onInputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph input -* @method changeGlobalInputType +* @method changeInputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalInputType = function(name, type) +LGraph.prototype.changeInputType = function(name, type) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - if(this.global_inputs[name].type && this.global_inputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.inputs[name].type && this.inputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_inputs[name].type = type; + this.inputs[name].type = type; this._version++; - if(this.onGlobalInputTypeChanged) - this.onGlobalInputTypeChanged(name, type); + if(this.onInputTypeChanged) + this.onInputTypeChanged(name, type); } /** * Removes a global graph input -* @method removeGlobalInput +* @method removeInput * @param {String} name * @param {String} type */ -LGraph.prototype.removeGlobalInput = function(name) +LGraph.prototype.removeInput = function(name) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - delete this.global_inputs[name]; + delete this.inputs[name]; this._version++; - if(this.onGlobalInputRemoved) - this.onGlobalInputRemoved(name); + if(this.onInputRemoved) + this.onInputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } /** * Creates a global graph output -* @method addGlobalOutput +* @method addOutput * @param {String} name * @param {String} type * @param {*} value */ -LGraph.prototype.addGlobalOutput = function(name, type, value) +LGraph.prototype.addOutput = function(name, type, value) { - this.global_outputs[name] = { name: name, type: type, value: value }; + this.outputs[name] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalOutputAdded) - this.onGlobalOutputAdded(name, type); + if(this.onOutputAdded) + this.onOutputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Assign a data to the global output -* @method setGlobalOutputData +* @method setOutputData * @param {String} name * @param {String} value */ -LGraph.prototype.setGlobalOutputData = function(name, value) +LGraph.prototype.setOutputData = function(name, value) { - var output = this.global_outputs[ name ]; + var output = this.outputs[ name ]; if (!output) return; output.value = value; @@ -1441,92 +1493,83 @@ LGraph.prototype.setGlobalOutputData = function(name, value) /** * Returns the current value of a global graph output -* @method getGlobalOutputData +* @method getOutputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalOutputData = function(name) +LGraph.prototype.getOutputData = function(name) { - var output = this.global_outputs[name]; + var output = this.outputs[name]; if (!output) return null; return output.value; } -/** -* Returns the current value of a global graph output (sames as getGlobalOutputData) -* @method getOutputData -* @param {String} name -* @return {*} the data -*/ -LGraph.prototype.getOutputData = LGraph.prototype.getGlobalOutputData; - - /** * Renames a global graph output -* @method renameGlobalOutput +* @method renameOutput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalOutput = function(old_name, name) +LGraph.prototype.renameOutput = function(old_name, name) { - if(!this.global_outputs[old_name]) + if(!this.outputs[old_name]) return false; - if(this.global_outputs[name]) + if(this.outputs[name]) { console.error("there is already one output with that name"); return false; } - this.global_outputs[name] = this.global_outputs[old_name]; - delete this.global_outputs[old_name]; + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; this._version++; - if(this.onGlobalOutputRenamed) - this.onGlobalOutputRenamed(old_name, name); + if(this.onOutputRenamed) + this.onOutputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph output -* @method changeGlobalOutputType +* @method changeOutputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalOutputType = function(name, type) +LGraph.prototype.changeOutputType = function(name, type) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - if(this.global_outputs[name].type && this.global_outputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.outputs[name].type && this.outputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_outputs[name].type = type; + this.outputs[name].type = type; this._version++; - if(this.onGlobalOutputTypeChanged) - this.onGlobalOutputTypeChanged(name, type); + if(this.onOutputTypeChanged) + this.onOutputTypeChanged(name, type); } /** * Removes a global graph output -* @method removeGlobalOutput +* @method removeOutput * @param {String} name */ -LGraph.prototype.removeGlobalOutput = function(name) +LGraph.prototype.removeOutput = function(name) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - delete this.global_outputs[name]; + delete this.outputs[name]; this._version++; - if(this.onGlobalOutputRemoved) - this.onGlobalOutputRemoved(name); + if(this.onOutputRemoved) + this.onOutputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } @@ -1545,7 +1588,7 @@ LGraph.prototype.setCallback = function(name,func) } -LGraph.prototype.connectionChange = function( node ) +LGraph.prototype.connectionChange = function( node, link_info ) { this.updateExecutionOrder(); if( this.onConnectionChange ) @@ -1573,6 +1616,23 @@ LGraph.prototype.isLive = function() return false; } +/** +* clears the triggered slot animation in all links (stop visual animation) +* @method clearTriggeredSlots +*/ +LGraph.prototype.clearTriggeredSlots = function() +{ + for(var i in this.links) + { + var link_info = this.links[i]; + if( !link_info ) + continue; + if( link_info._last_time ) + link_info._last_time = 0; + } +} + + /* Called when something visually changed (not the graph!) */ LGraph.prototype.change = function() { @@ -1588,6 +1648,22 @@ LGraph.prototype.setDirtyCanvas = function(fg,bg) this.sendActionToCanvas("setDirty",[fg,bg]); } +/** +* Destroys a link +* @method removeLink +* @param {Number} link_id +*/ +LGraph.prototype.removeLink = function(link_id) +{ + var link = this.links[ link_id ]; + if(!link) + return; + var node = this.getNodeById( link.target_id ); + if(node) + node.disconnectInput( link.target_slot ); +} + + //save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized @@ -1618,7 +1694,8 @@ LGraph.prototype.serialize = function() nodes: nodes_info, links: links, groups: groups_info, - config: this.config + config: this.config, + version: LiteGraph.VERSION }; return data; @@ -1647,8 +1724,10 @@ LGraph.prototype.configure = function( data, keep_old ) var links = []; for(var i = 0; i < data.links.length; ++i) { - var link = data.links[i]; - links[ link[0] ] = { id: link[0], origin_id: link[1], origin_slot: link[2], target_id: link[3], target_slot: link[4], type: link[5] }; + var link_data = data.links[i]; + var link = new LLink(); + link.configure( link_data ); + links[ link.id ] = link; } data.links = links; } @@ -1670,9 +1749,14 @@ LGraph.prototype.configure = function( data, keep_old ) if(!node) { if(LiteGraph.debug) - console.log("Node not found: " + n_info.type); + console.log("Node not found or has errors: " + n_info.type); + + //in case of error we create a replacement node to avoid losing info + node = new LGraphNode(); + node.last_serialization = n_info; + node.has_errors = true; error = true; - continue; + //continue; } node.id = n_info.id; //id it or it will create a new id @@ -1731,6 +1815,49 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) //TODO } +//this is the class in charge of storing link information +function LLink( id, type, origin_id, origin_slot, target_id, target_slot ) +{ + this.id = id; + this.type = type; + this.origin_id = origin_id; + this.origin_slot = origin_slot; + this.target_id = target_id; + this.target_slot = target_slot; + + this._data = null; + this._pos = new Float32Array(2); //center +} + +LLink.prototype.configure = function(o) +{ + if(o.constructor === Array) + { + this.id = o[0]; + this.origin_id = o[1]; + this.origin_slot = o[2]; + this.target_id = o[3]; + this.target_slot = o[4]; + this.type = o[5]; + } + else + { + this.id = o.id; + this.type = o.type; + this.origin_id = o.origin_id; + this.origin_slot = o.origin_slot; + this.target_id = o.target_id; + this.target_slot = o.target_slot; + } +} + +LLink.prototype.serialize = function() +{ + return [ this.id, this.type, this.origin_id, this.origin_slot, this.target_id, this.target_slot ]; +} + +LiteGraph.LLink = LLink; + // ************************************************************* // Node CLASS ******* // ************************************************************* @@ -1743,14 +1870,19 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) input|output: every connection + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); - flags: + general properties: + clip_area: if you render outside the node, it will be cliped + unsafe_execution: not allowed for safe execution + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected + resizable: if set to false it wont be resizable with the mouse + + horizontal: slots are distributed horizontally + + widgets_up: widgets start from the top of the node + + flags object: + + collapsed: if it is collapsed supported callbacks: - + onAdded: when added to graph + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + onRemoved: when removed from graph + onStart: when the graph starts playing + onStop: when the graph stops playing @@ -1765,14 +1897,20 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) + onGetInputs: returns an array of possible inputs + onGetOutputs: returns an array of possible outputs - + onDblClick - + onSerialize + + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) + + onDblClick: double clicked in the node + + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + onSelected + onDeselected + onDropItem : DOM item dropped over the node + onDropFile : file dropped over the node + onConnectInput : if returns false the incoming connection will be canceled + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) + + onAction: action slot triggered + + getExtraMenuOptions: to add option to context menu */ /** @@ -1823,7 +1961,6 @@ LGraphNode.prototype._ctor = function( title ) this.properties = {}; //for the values this.properties_info = []; //for the info - this.data = null; //persistent local data this.flags = {}; } @@ -1838,9 +1975,6 @@ LGraphNode.prototype.configure = function(info) for (var j in info) { - if(j == "console") - continue; - if(j == "properties") { //i dont want to clone properties, I want to reuse the old container @@ -1894,39 +2028,6 @@ LGraphNode.prototype.configure = function(info) } } - //FOR LEGACY, PLEASE REMOVE ON NEXT VERSION - for(var i in this.inputs) - { - var input = this.inputs[i]; - if(!input.link || !input.link.length ) - continue; - var link = input.link; - if(typeof(link) != "object") - continue; - input.link = link[0]; - if(this.graph) - this.graph.links[ link[0] ] = { - id: link[0], - origin_id: link[1], - origin_slot: link[2], - target_id: link[3], - target_slot: link[4] - }; - } - for(var i in this.outputs) - { - var output = this.outputs[i]; - if(!output.links || output.links.length == 0) - continue; - for(var j in output.links) - { - var link = output.links[j]; - if(typeof(link) != "object") - continue; - output.links[j] = link[0]; - } - } - if( this.onConfigure ) this.onConfigure( info ); } @@ -1944,11 +2045,14 @@ LGraphNode.prototype.serialize = function() type: this.type, pos: this.pos, size: this.size, - data: this.data, flags: LiteGraph.cloneObject(this.flags), mode: this.mode }; + //special case for when there were errors + if( this.constructor === LGraphNode && this.last_serialization ) + return this.last_serialization; + if( this.inputs ) o.inputs = this.inputs; @@ -1979,7 +2083,10 @@ LGraphNode.prototype.serialize = function() o.shape = this.shape; if(this.onSerialize) - this.onSerialize(o); + { + if( this.onSerialize(o) ) + console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); + } return o; } @@ -1989,6 +2096,8 @@ LGraphNode.prototype.serialize = function() LGraphNode.prototype.clone = function() { var node = LiteGraph.createNode(this.type); + if(!node) + return null; //we clone it because serialize returns shared containers var data = LiteGraph.cloneObject( this.serialize() ); @@ -2074,6 +2183,35 @@ LGraphNode.prototype.setOutputData = function(slot, data) } } +/** +* sets the output data type, useful when you want to be able to overwrite the data type +* @method setOutputDataType +* @param {number} slot +* @param {String} datatype +*/ +LGraphNode.prototype.setOutputDataType = function(slot, type) +{ + if(!this.outputs) + return; + if(slot == -1 || slot >= this.outputs.length) + return; + var output_info = this.outputs[slot]; + if(!output_info) + return; + //store data in the output itself in case we want to debug + output_info.type = type; + + //if there are connections, pass the data to the connections + if( this.outputs[slot].links ) + { + for(var i = 0; i < this.outputs[slot].links.length; i++) + { + var link_id = this.outputs[slot].links[i]; + this.graph.links[ link_id ].type = type; + } + } +} + /** * Retrieves the input data (data traveling through the connection) from one slot * @method getInputData @@ -2110,6 +2248,32 @@ LGraphNode.prototype.getInputData = function( slot, force_update ) return link.data; } +/** +* Retrieves the input data type (in case this supports multiple input types) +* @method getInputDataType +* @param {number} slot +* @return {String} datatype in string format +*/ +LGraphNode.prototype.getInputDataType = function( slot ) +{ + if(!this.inputs) + return null; //undefined; + + if(slot >= this.inputs.length || this.inputs[slot].link == null) + return null; + var link_id = this.inputs[slot].link; + var link = this.graph.links[ link_id ]; + if(!link) //bug: weird case but it happens sometimes + return null; + var node = this.graph.getNodeById( link.origin_id ); + if(!node) + return link.type; + var output_info = node.outputs[ link.origin_slot ]; + if(output_info) + return output_info.type; + return null; +} + /** * Retrieves the input data from one slot using its name instead of slot number * @method getInputDataByName @@ -2188,13 +2352,16 @@ LGraphNode.prototype.getInputOrProperty = function( name ) return this.properties ? this.properties[name] : null; for(var i = 0, l = this.inputs.length; i < l; ++i) - if(name == this.inputs[i].name) + { + var input_info = this.inputs[i]; + if(name == input_info.name && input_info.link != null) { - var link_id = this.inputs[i].link; - var link = this.graph.links[ link_id ]; - return link ? link.data : null; + var link = this.graph.links[ input_info.link ]; + if(link) + return link.data; } - return this.properties[name]; + } + return this.properties[ name ]; } @@ -2324,8 +2491,9 @@ LGraphNode.prototype.trigger = function( action, param ) * @method triggerSlot * @param {Number} slot the index of the output slot * @param {*} param +* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot */ -LGraphNode.prototype.triggerSlot = function( slot, param ) +LGraphNode.prototype.triggerSlot = function( slot, param, link_id ) { if( !this.outputs ) return; @@ -2344,16 +2512,18 @@ LGraphNode.prototype.triggerSlot = function( slot, param ) //for every link attached here for(var k = 0; k < links.length; ++k) { + var id = links[k]; + if( link_id != null && link_id != id ) //to skip links + continue; var link_info = this.graph.links[ links[k] ]; if(!link_info) //not connected continue; + link_info._last_time = LiteGraph.getTime(); var node = this.graph.getNodeById( link_info.target_id ); if(!node) //node not found? continue; //used to mark events in graph - link_info._last_time = LiteGraph.getTime(); - var target_connection = node.inputs[ link_info.target_slot ]; if(node.onAction) @@ -2366,6 +2536,38 @@ LGraphNode.prototype.triggerSlot = function( slot, param ) } } +/** +* clears the trigger slot animation +* @method clearTriggeredSlot +* @param {Number} slot the index of the output slot +* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot +*/ +LGraphNode.prototype.clearTriggeredSlot = function( slot, link_id ) +{ + if( !this.outputs ) + return; + + var output = this.outputs[ slot ]; + if( !output ) + return; + + var links = output.links; + if(!links || !links.length) + return; + + //for every link attached here + for(var k = 0; k < links.length; ++k) + { + var id = links[k]; + if( link_id != null && link_id != id ) //to skip links + continue; + var link_info = this.graph.links[ links[k] ]; + if(!link_info) //not connected + continue; + link_info._last_time = 0; + } +} + /** * add a new property to this node * @method addProperty @@ -2412,6 +2614,7 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) if(this.onOutputAdded) this.onOutputAdded(o); this.size = this.computeSize(); + this.setDirtyCanvas(true,true); return o; } @@ -2438,6 +2641,7 @@ LGraphNode.prototype.addOutputs = function(array) } this.size = this.computeSize(); + this.setDirtyCanvas(true,true); } /** @@ -2449,9 +2653,24 @@ LGraphNode.prototype.removeOutput = function(slot) { this.disconnectOutput(slot); this.outputs.splice(slot,1); + for(var i = slot; i < this.outputs.length; ++i) + { + if( !this.outputs[i] || !this.outputs[i].links ) + continue; + var links = this.outputs[i].links; + for(var j = 0; j < links.length; ++j) + { + var link = this.graph.links[ links[j] ]; + if(!link) + continue; + link.origin_slot -= 1; + } + } + this.size = this.computeSize(); if(this.onOutputRemoved) this.onOutputRemoved(slot); + this.setDirtyCanvas(true,true); } /** @@ -2475,6 +2694,7 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); if(this.onInputAdded) this.onInputAdded(o); + this.setDirtyCanvas(true,true); return o; } @@ -2501,6 +2721,7 @@ LGraphNode.prototype.addInputs = function(array) } this.size = this.computeSize(); + this.setDirtyCanvas(true,true); } /** @@ -2512,9 +2733,19 @@ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); this.inputs.splice(slot,1); + for(var i = slot; i < this.inputs.length; ++i) + { + if(!this.inputs[i]) + continue; + var link = this.graph.links[ this.inputs[i].link ]; + if(!link) + continue; + link.target_slot -= 1; + } this.size = this.computeSize(); if(this.onInputRemoved) this.onInputRemoved(slot); + this.setDirtyCanvas(true,true); } /** @@ -2546,11 +2777,16 @@ LGraphNode.prototype.addConnection = function(name,type,pos,direction) */ LGraphNode.prototype.computeSize = function( minHeight, out ) { + if( this.constructor.size ) + return this.constructor.size.concat(); + var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); var size = out || new Float32Array([0,0]); rows = Math.max(rows, 1); var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - size[1] = (this.constructor.slot_start_y || 0) + rows * (font_size + 1) + ( this.widgets ? this.widgets.length : 0 ) * (LiteGraph.NODE_WIDGET_HEIGHT + 4 ) + 4; + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + if( this.widgets && this.widgets.length ) + size[1] += this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8; var font_size = font_size; var title_width = compute_text_size( this.title ); @@ -2579,6 +2815,8 @@ LGraphNode.prototype.computeSize = function( minHeight, out ) size[0] = Math.max( input_width + output_width + 10, title_width ); size[0] = Math.max( size[0], LiteGraph.NODE_WIDTH ); + if(this.widgets && this.widgets.length) + size[0] = Math.max( size[0], LiteGraph.NODE_WIDTH * 1.5 ); if(this.onResize) this.onResize(size); @@ -2590,6 +2828,11 @@ LGraphNode.prototype.computeSize = function( minHeight, out ) return font_size * text.length * 0.6; } + if(this.constructor.min_height && size[1] < this.constructor.min_height) + size[1] = this.constructor.min_height; + + size[1] += 6; //margin + return size; } @@ -2597,7 +2840,7 @@ LGraphNode.prototype.computeSize = function( minHeight, out ) * Allows to pass * * @method addWidget -* @return {Float32Array[4]} the total size +* @return {Object} the created widget */ LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) { @@ -2614,12 +2857,22 @@ LGraphNode.prototype.addWidget = function( type, name, value, callback, options if(w.options.y !== undefined ) w.y = w.options.y; + if( !callback ) + console.warn("LiteGraph addWidget('button',...) without a callback"); if( type == "combo" && !w.options.values ) throw("LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"); this.widgets.push(w); return w; } +LGraphNode.prototype.addCustomWidget = function( custom_widget ) +{ + if(!this.widgets) + this.widgets = []; + this.widgets.push(custom_widget); + return custom_widget; +} + /** * returns the bounding of the object, used for rendering purposes @@ -2634,6 +2887,9 @@ LGraphNode.prototype.getBounding = function( out ) out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; out[2] = this.size[0] + 4; out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; + + if( this.onBounding ) + this.onBounding( out ); return out; } @@ -2644,11 +2900,13 @@ LGraphNode.prototype.getBounding = function( out ) * @param {number} y * @return {boolean} */ -LGraphNode.prototype.isPointInside = function(x,y, margin) +LGraphNode.prototype.isPointInside = function( x, y, margin, skip_title ) { margin = margin || 0; var margin_top = this.graph && this.graph.isLive() ? 0 : 20; + if(skip_title) + margin_top = 0; if(this.flags && this.flags.collapsed) { //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) @@ -2671,11 +2929,12 @@ LGraphNode.prototype.isPointInside = function(x,y, margin) LGraphNode.prototype.getSlotInPosition = function( x, y ) { //search for inputs + var link_pos = new Float32Array(2); if(this.inputs) for(var i = 0, l = this.inputs.length; i < l; ++i) { var input = this.inputs[i]; - var link_pos = this.getConnectionPos( true,i ); + this.getConnectionPos( true,i, link_pos ); if( isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20,10) ) return { input: input, slot: i, link_pos: link_pos, locked: input.locked }; } @@ -2684,7 +2943,7 @@ LGraphNode.prototype.getSlotInPosition = function( x, y ) for(var i = 0, l = this.outputs.length; i < l; ++i) { var output = this.outputs[i]; - var link_pos = this.getConnectionPos(false,i); + this.getConnectionPos(false,i,link_pos); if( isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20,10) ) return { output: output, slot: i, link_pos: link_pos, locked: output.locked }; } @@ -2729,7 +2988,7 @@ LGraphNode.prototype.findOutputSlot = function(name) * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) * @param {LGraphNode} node the target node * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) -* @return {boolean} if it was connected succesfully +* @return {Object} the link_info is created, otherwise null */ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) { @@ -2738,7 +2997,7 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) if(!this.graph) //could be connected before adding it to a graph { console.log("Connect: Error, node doesnt belong to any graph. Nodes must be added first to a graph before connecting them."); //due to link ids being associated with graphs - return false; + return null; } @@ -2750,14 +3009,14 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) { if(LiteGraph.debug) console.log("Connect: Error, no slot of name " + slot); - return false; + return null; } } else if(!this.outputs || slot >= this.outputs.length) { if(LiteGraph.debug) console.log("Connect: Error, slot number not found"); - return false; + return null; } if(target_node && target_node.constructor === Number) @@ -2767,7 +3026,7 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) //avoid loopback if(target_node == this) - return false; + return null; //you can specify the slot by name if(target_slot.constructor === String) @@ -2777,7 +3036,7 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) { if(LiteGraph.debug) console.log("Connect: Error, no slot of name " + target_slot); - return false; + return null; } } else if( target_slot === LiteGraph.EVENT ) @@ -2789,13 +3048,13 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) target_slot = target_node.inputs.length - 1; //last one is the one created target_node.mode = LiteGraph.ON_TRIGGER; */ - return false; + return null; } else if( !target_node.inputs || target_slot >= target_node.inputs.length ) { if(LiteGraph.debug) console.log("Connect: Error, slot number not found"); - return false; + return null; } //if there is something already plugged there, disconnect @@ -2811,20 +3070,14 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) //allows nodes to block connection if(target_node.onConnectInput) if( target_node.onConnectInput( target_slot, output.type, output ) === false) - return false; + return null; var input = target_node.inputs[target_slot]; + var link_info = null; if( LiteGraph.isValidConnection( output.type, input.type ) ) { - var link_info = { - id: this.graph.last_link_id++, - type: input.type, - origin_id: this.id, - origin_slot: slot, - target_id: target_node.id, - target_slot: target_slot - }; + link_info = new LLink( this.graph.last_link_id++, input.type, this.id, slot, target_node.id, target_slot ); //add to graph links list this.graph.links[ link_info.id ] = link_info; @@ -2842,13 +3095,16 @@ LGraphNode.prototype.connect = function( slot, target_node, target_slot ) if(target_node.onConnectionsChange) target_node.onConnectionsChange( LiteGraph.INPUT, target_slot, true, link_info, input ); if( this.graph && this.graph.onNodeConnectionChange ) + { + this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, target_slot, this, slot ); this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot, target_node, target_slot ); + } } this.setDirtyCanvas(false,true); - this.graph.connectionChange( this ); + this.graph.connectionChange( this, link_info ); - return true; + return link_info; } /** @@ -2879,7 +3135,7 @@ LGraphNode.prototype.disconnectOutput = function( slot, target_node ) //get output slot var output = this.outputs[slot]; - if(!output.links || output.links.length == 0) + if(!output || !output.links || output.links.length == 0) return false; //one of the output links in this slot @@ -2911,7 +3167,10 @@ LGraphNode.prototype.disconnectOutput = function( slot, target_node ) if( this.graph && this.graph.onNodeConnectionChange ) this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot ); if( this.graph && this.graph.onNodeConnectionChange ) + { + this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, link_info.target_slot ); + } break; } } @@ -2942,7 +3201,10 @@ LGraphNode.prototype.disconnectOutput = function( slot, target_node ) if(this.onConnectionsChange) this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, output ); if( this.graph && this.graph.onNodeConnectionChange ) + { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot ); + this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, link_info.target_slot ); + } } output.links = null; } @@ -3015,6 +3277,11 @@ LGraphNode.prototype.disconnectInput = function( slot ) this.onConnectionsChange( LiteGraph.INPUT, slot, false, link_info, input ); if( target_node.onConnectionsChange ) target_node.onConnectionsChange( LiteGraph.OUTPUT, i, false, link_info, output ); + if( this.graph && this.graph.onNodeConnectionChange ) + { + this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, target_node, i ); + this.graph.onNodeConnectionChange( LiteGraph.INPUT, this, slot ); + } } this.setDirtyCanvas(false,true); @@ -3027,31 +3294,82 @@ LGraphNode.prototype.disconnectInput = function( slot ) * @method getConnectionPos * @param {boolean} is_input true if if a input slot, false if it is an output * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @param {vec2} out [optional] a place to store the output, to free garbage * @return {[x,y]} the position **/ -LGraphNode.prototype.getConnectionPos = function( is_input, slot_number ) +LGraphNode.prototype.getConnectionPos = function( is_input, slot_number, out ) { + out = out || new Float32Array(2); + var num_slots = 0; + if( is_input && this.inputs ) + num_slots = this.inputs.length; + if( !is_input && this.outputs ) + num_slots = this.outputs.length; + + var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + if(this.flags.collapsed) { - if(is_input) - return [this.pos[0], this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5]; + var w = (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH); + if( this.horizontal ) + { + out[0] = this.pos[0] + w * 0.5; + if(is_input) + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + else + out[1] = this.pos[1]; + } else - return [this.pos[0] + (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH), this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5]; + { + if(is_input) + out[0] = this.pos[0]; + else + out[0] = this.pos[0] + w; + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; + } + return out; } + //weird feature that never got finished if(is_input && slot_number == -1) { - return [this.pos[0] + 10, this.pos[1] + 10]; + out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + return out; } - if(is_input && this.inputs.length > slot_number && this.inputs[slot_number].pos) - return [this.pos[0] + this.inputs[slot_number].pos[0],this.pos[1] + this.inputs[slot_number].pos[1]]; - else if(!is_input && this.outputs.length > slot_number && this.outputs[slot_number].pos) - return [this.pos[0] + this.outputs[slot_number].pos[0],this.pos[1] + this.outputs[slot_number].pos[1]]; + //hardcoded pos + if(is_input && num_slots > slot_number && this.inputs[ slot_number ].pos) + { + out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; + return out; + } + else if(!is_input && num_slots > slot_number && this.outputs[ slot_number ].pos) + { + out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; + return out; + } - if(!is_input) //output - return [this.pos[0] + this.size[0] + 1, this.pos[1] + 10 + slot_number * LiteGraph.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0)]; - return [this.pos[0] , this.pos[1] + 10 + slot_number * LiteGraph.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0) ]; + //horizontal distributed slots + if(this.horizontal) + { + out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); + if(is_input) + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + else + out[1] = this.pos[1] + this.size[1]; + return out; + } + + //default vertical slots + if(is_input) + out[0] = this.pos[0] + offset; + else + out[0] = this.pos[0] + this.size[0] + 1 - offset; + out[1] = this.pos[1] + (slot_number + 0.7 ) * LiteGraph.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); + return out; } /* Force align to grid */ @@ -3204,11 +3522,12 @@ global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; LGraphGroup.prototype._ctor = function( title ) { this.title = title || "Group"; + this.font_size = 24; + this.color = LGraphCanvas.node_colors.pale_blue ? LGraphCanvas.node_colors.pale_blue.groupcolor : "#AAA"; this._bounding = new Float32Array([10,10,140,80]); this._pos = this._bounding.subarray(0,2); this._size = this._bounding.subarray(2,4); this._nodes = []; - this.color = LGraphCanvas.node_colors.pale_blue ? LGraphCanvas.node_colors.pale_blue.groupcolor : "#AAA"; this.graph = null; Object.defineProperty( this, "pos", { @@ -3247,6 +3566,7 @@ LGraphGroup.prototype.configure = function(o) this.title = o.title; this._bounding.set( o.bounding ); this.color = o.color; + this.font = o.font; } LGraphGroup.prototype.serialize = function() @@ -3254,8 +3574,9 @@ LGraphGroup.prototype.serialize = function() var b = this._bounding; return { title: this.title, - bounding: [ b[0], b[1], b[2], b[3] ], - color: this.color + bounding: [ Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), Math.round(b[3]) ], + color: this.color, + font: this.font }; } @@ -3292,6 +3613,202 @@ LGraphGroup.prototype.recomputeInsideNodes = function() LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; + + +//**************************************** + +//Scale and Offset +function DragAndScale( element, skip_events ) +{ + this.offset = new Float32Array([0,0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = true; + this.last_mouse = [0,0]; + this.element = null; + this.visible_area = new Float32Array(4); + + if(element) + { + this.element = element; + if(!skip_events) + this.bindEvents( element ); + } +} + +LiteGraph.DragAndScale = DragAndScale; + +DragAndScale.prototype.bindEvents = function( element ) +{ + this.last_mouse = new Float32Array(2); + + this._binded_mouse_callback = this.onMouse.bind(this); + + element.addEventListener("mousedown", this._binded_mouse_callback ); + element.addEventListener("mousemove", this._binded_mouse_callback ); + + element.addEventListener("mousewheel", this._binded_mouse_callback, false); + element.addEventListener("wheel", this._binded_mouse_callback, false); +} + +DragAndScale.prototype.computeVisibleArea = function() +{ + if(!this.element) + { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + return; + } + var width = this.element.width; + var height = this.element.height; + var startx = -this.offset[0]; + var starty = -this.offset[1]; + var endx = startx + width / this.scale; + var endy = starty + height / this.scale; + this.visible_area[0] = startx; + this.visible_area[1] = starty; + this.visible_area[2] = endx - startx; + this.visible_area[3] = endy - starty; +} + +DragAndScale.prototype.onMouse = function(e) +{ + if(!this.enabled) + return; + + var canvas = this.element; + var rect = canvas.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + e.canvasx = x; + e.canvasy = y; + e.dragging = this.dragging; + + var ignore = false; + if(this.onmouse) + ignore = this.onmouse(e); + + if(e.type == "mousedown") + { + this.dragging = true; + canvas.removeEventListener("mousemove", this._binded_mouse_callback ); + document.body.addEventListener("mousemove", this._binded_mouse_callback ); + document.body.addEventListener("mouseup", this._binded_mouse_callback ); + } + else if(e.type == "mousemove") + { + if(!ignore) + { + var deltax = x - this.last_mouse[0]; + var deltay = y - this.last_mouse[1]; + if( this.dragging ) + this.mouseDrag( deltax, deltay ); + } + } + else if(e.type == "mouseup") + { + this.dragging = false; + document.body.removeEventListener("mousemove", this._binded_mouse_callback ); + document.body.removeEventListener("mouseup", this._binded_mouse_callback ); + canvas.addEventListener("mousemove", this._binded_mouse_callback ); + } + else if(e.type == "mousewheel" || e.type == "wheel" || e.type == "DOMMouseScroll") + { + e.eventType = "mousewheel"; + if(e.type == "wheel") + e.wheel = -e.deltaY; + else + e.wheel = (e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60); + + //from stack overflow + e.delta = e.wheelDelta ? e.wheelDelta/40 : e.deltaY ? -e.deltaY/3 : 0; + this.changeDeltaScale(1.0 + e.delta * 0.05); + } + + this.last_mouse[0] = x; + this.last_mouse[1] = y; + + e.preventDefault(); + e.stopPropagation(); + return false; +} + +DragAndScale.prototype.toCanvasContext = function( ctx ) +{ + ctx.scale( this.scale, this.scale ); + ctx.translate( this.offset[0], this.offset[1] ); +} + +DragAndScale.prototype.convertOffsetToCanvas = function(pos) +{ + //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + return [ (pos[0] + this.offset[0]) * this.scale, (pos[1] + this.offset[1]) * this.scale ]; +} + +DragAndScale.prototype.convertCanvasToOffset = function(pos, out) +{ + out = out || [0,0]; + out[0] = pos[0] / this.scale - this.offset[0]; + out[1] = pos[1] / this.scale - this.offset[1]; + return out; +} + +DragAndScale.prototype.mouseDrag = function(x,y) +{ + this.offset[0] += x / this.scale; + this.offset[1] += y / this.scale; + + if( this.onredraw ) + this.onredraw( this ); +} + +DragAndScale.prototype.changeScale = function( value, zooming_center ) +{ + if(value < this.min_scale) + value = this.min_scale; + else if(value > this.max_scale) + value = this.max_scale; + + if(value == this.scale) + return; + + if(!this.element) + return; + + var rect = this.element.getBoundingClientRect(); + if(!rect) + return; + + zooming_center = zooming_center || [rect.width * 0.5,rect.height * 0.5]; + var center = this.convertCanvasToOffset( zooming_center ); + this.scale = value; + if( Math.abs( this.scale - 1 ) < 0.01 ) + this.scale = 1; + + var new_center = this.convertCanvasToOffset( zooming_center ); + var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + + if( this.onredraw ) + this.onredraw( this ); +} + +DragAndScale.prototype.changeDeltaScale = function( value, zooming_center ) +{ + this.changeScale( this.scale * value, zooming_center ); +} + +DragAndScale.prototype.reset = function() +{ + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; +} + + //********************************************************************************* // LGraphCanvas: LGraph renderer CLASS //********************************************************************************* @@ -3317,18 +3834,17 @@ function LGraphCanvas( canvas, graph, options ) if(canvas && canvas.constructor === String ) canvas = document.querySelector( canvas ); - this.max_zoom = 10; - this.min_zoom = 0.1; + this.ds = new DragAndScale(); this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much - this.title_text_font = "bold "+LiteGraph.NODE_TEXT_SIZE+"px Arial"; + this.title_text_font = ""+LiteGraph.NODE_TEXT_SIZE+"px Arial"; this.inner_text_font = "normal "+LiteGraph.NODE_SUBTEXT_SIZE+"px Arial"; this.node_title_color = LiteGraph.NODE_TITLE_COLOR; - this.default_link_color = "#AAC"; + this.default_link_color = LiteGraph.LINK_COLOR; this.default_connection_color = { - input_off: "#AAB", + input_off: "#778", input_on: "#7F7", - output_off: "#AAB", + output_off: "#778", output_on: "#7F7" }; @@ -3336,7 +3852,6 @@ function LGraphCanvas( canvas, graph, options ) this.use_gradients = false; //set to true to render titlebar with gradients this.editor_alpha = 1; //used for transition this.pause_rendering = false; - this.render_shadows = true; this.clear_background = true; this.render_only_selected = true; @@ -3346,17 +3861,25 @@ function LGraphCanvas( canvas, graph, options ) this.allow_dragnodes = true; this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc this.allow_searchbox = true; + this.allow_reconnect_links = false; //allows to change a connection with having to redo it again + this.drag_mode = false; this.dragging_rectangle = null; this.filter = null; //allows to filter to only accept some type of nodes in a graph this.always_render_background = false; + this.render_shadows = true; this.render_canvas_border = true; this.render_connections_shadows = false; //too much cpu this.render_connections_border = true; - this.render_curved_connections = true; - this.render_connection_arrows = true; + this.render_curved_connections = false; + this.render_connection_arrows = false; + this.render_collapsed_slots = true; + this.render_execution_order = false; + this.render_title_colored = true; + + this.links_render_mode = LiteGraph.SPLINE_LINK; this.canvas_mouse = [0,0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle @@ -3364,12 +3887,20 @@ function LGraphCanvas( canvas, graph, options ) this.onSearchBox = null; this.onSearchBoxSelection = null; + //callbacks + this.onMouse = null; + this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) + this.connections_width = 3; this.round_radius = 8; this.current_node = null; this.node_widget = null; //used for widgets this.last_mouse_position = [0,0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; //link canvas and graph if(graph) @@ -3386,7 +3917,7 @@ function LGraphCanvas( canvas, graph, options ) global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; -LGraphCanvas.link_type_colors = {"-1":"#F85",'number':"#AAC","node":"#DCA"}; +LGraphCanvas.link_type_colors = {"-1": LiteGraph.EVENT_LINK_COLOR,'number':"#AAA","node":"#DCA"}; LGraphCanvas.gradients = {}; //cache of gradients /** @@ -3401,8 +3932,8 @@ LGraphCanvas.prototype.clear = function() this.render_time = 0; this.fps = 0; - this.scale = 1; - this.offset = [0,0]; + //this.scale = 1; + //this.offset = [0,0]; this.dragging_rectangle = null; @@ -3425,6 +3956,7 @@ LGraphCanvas.prototype.clear = function() this.last_mouse = [0,0]; this.last_mouseclick = 0; + this.visible_area.set([0,0,0,0]); if(this.onClear) this.onClear(); @@ -3499,11 +4031,17 @@ LGraphCanvas.prototype.closeSubgraph = function() { if(!this._graph_stack || this._graph_stack.length == 0) return; + var subraph_node = this.graph._subgraph_node; var graph = this._graph_stack.pop(); this.selected_nodes = {}; this.highlighted_links = {}; graph.attachCanvas(this); this.setDirty(true,true); + if( subraph_node ) + { + this.centerOnNode( subraph_node ); + this.selectNodes( [subraph_node] ); + } } /** @@ -3537,6 +4075,7 @@ LGraphCanvas.prototype.setCanvas = function( canvas, skip_events ) } this.canvas = canvas; + this.ds.element = canvas; if(!canvas) return; @@ -3544,6 +4083,7 @@ LGraphCanvas.prototype.setCanvas = function( canvas, skip_events ) //this.canvas.tabindex = "1000"; canvas.className += " lgraphcanvas"; canvas.data = this; + canvas.tabindex = '1'; //to allow key events //bg canvas: used for non changing stuff this.bgcanvas = null; @@ -3797,7 +4337,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) ref_window.document.addEventListener("mousemove", this._mousemove_callback, true ); //catch for the entire window ref_window.document.addEventListener("mouseup", this._mouseup_callback, true ); - var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes ); + var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 ); var skip_dragging = false; var skip_action = false; var now = LiteGraph.getTime(); @@ -3805,9 +4345,16 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.canvas_mouse[0] = e.canvasX; this.canvas_mouse[1] = e.canvasY; + this.canvas.focus(); LiteGraph.closeAllContextMenus( ref_window ); + if(this.onMouse) + { + if( this.onMouse(e) == true ) + return; + } + if(e.which == 1) //left button mouse { if( e.ctrlKey ) @@ -3832,72 +4379,89 @@ LGraphCanvas.prototype.processMouseDown = function(e) //not dragging mouse to connect two slots if(!this.connecting_node && !node.flags.collapsed && !this.live_mode) { - //search for outputs - if(node.outputs) - for(var i = 0, l = node.outputs.length; i < l; ++i) - { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false,i); - if( isInsideRectangle( e.canvasX, e.canvasY, link_pos[0] - 10, link_pos[1] - 5, 20,10) ) - { - this.connecting_node = node; - this.connecting_output = output; - this.connecting_pos = node.getConnectionPos(false,i); - this.connecting_slot = i; - - if (is_double_click) { - if (node.onOutputDblClick) - node.onOutputDblClick(i, e); - } else { - if (node.onOutputClick) - node.onOutputClick(i, e); - } - - skip_action = true; - break; - } - } - - //search for inputs - if(node.inputs) - for(var i = 0, l = node.inputs.length; i < l; ++i) - { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos( true, i ); - if( isInsideRectangle(e.canvasX, e.canvasY, link_pos[0] - 10, link_pos[1] - 5, 20,10) ) - { - if (is_double_click) { - if (node.onInputDblClick) - node.onInputDblClick(i, e); - } else { - if (node.onInputClick) - node.onInputClick(i, e); - } - - if(input.link !== null) - { - node.disconnectInput(i); - this.dirty_bgcanvas = true; - skip_action = true; - } - } - } - - //Search for corner - if( !skip_action && node.flags.resizable !== false && isInsideRectangle(e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,5,5 )) + //Search for corner for resize + if( !skip_action && node.resizable !== false && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,10,10 )) { this.resizing_node = node; this.canvas.style.cursor = "se-resize"; skip_action = true; } + else + { + //search for outputs + if(node.outputs) + for(var i = 0, l = node.outputs.length; i < l; ++i) + { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false,i); + if( isInsideRectangle( e.canvasX, e.canvasY, link_pos[0] - 15, link_pos[1] - 10, 30,20) ) + { + this.connecting_node = node; + this.connecting_output = output; + this.connecting_pos = node.getConnectionPos(false,i); + this.connecting_slot = i; + + if( e.shiftKey ) + node.disconnectOutput(i); + + if (is_double_click) { + if (node.onOutputDblClick) + node.onOutputDblClick(i, e); + } else { + if (node.onOutputClick) + node.onOutputClick(i, e); + } + + skip_action = true; + break; + } + } + + //search for inputs + if(node.inputs) + for(var i = 0, l = node.inputs.length; i < l; ++i) + { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos( true, i ); + if( isInsideRectangle(e.canvasX, e.canvasY, link_pos[0] - 15, link_pos[1] - 10, 30,20) ) + { + if (is_double_click) { + if (node.onInputDblClick) + node.onInputDblClick(i, e); + } else { + if (node.onInputClick) + node.onInputClick(i, e); + } + + if(input.link !== null) + { + var link_info = this.graph.links[ input.link ]; //before disconnecting + node.disconnectInput(i); + + if( this.allow_reconnect_links || e.shiftKey ) + { + this.connecting_node = this.graph._nodes_by_id[ link_info.origin_id ]; + this.connecting_slot = link_info.origin_slot; + this.connecting_output = this.connecting_node.outputs[ this.connecting_slot ]; + this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); + } + + this.dirty_bgcanvas = true; + skip_action = true; + } + } + } + } //not resizing } - //Search for corner - if( !skip_action && isInsideRectangle(e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) + //Search for corner for collapsing + /* + if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) { node.collapse(); skip_action = true; } + */ //it wasnt clicked on the links boxes if(!skip_action) @@ -3917,14 +4481,16 @@ LGraphCanvas.prototype.processMouseDown = function(e) { //double click node if( node.onDblClick) - node.onDblClick(e); + node.onDblClick(e,[e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this); this.processNodeDblClicked( node ); block_drag_node = true; } //if do not capture mouse - if( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]] ) ) + if( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) + { block_drag_node = true; + } else if(this.live_mode) { clicking_canvas_bg = true; @@ -3944,6 +4510,19 @@ LGraphCanvas.prototype.processMouseDown = function(e) } else //clicked outside of nodes { + + //search for link connector + for(var i = 0; i < this.visible_links.length; ++i) + { + var link = this.visible_links[i]; + var center = link._pos; + if( !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 ) + continue; + //link clicked + this.showLinkMenu( link, e ); + break; + } + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); this.selected_group_resizing = false; if( this.selected_group ) @@ -3952,7 +4531,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.dragging_rectangle = null; var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if( (dist * this.scale) < 10 ) + if( (dist * this.ds.scale) < 10 ) this.selected_group_resizing = true; else this.selected_group.recomputeInsideNodes(); @@ -3985,6 +4564,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.last_mouse[0] = e.localX; this.last_mouse[1] = e.localY; this.last_mouseclick = LiteGraph.getTime(); + this.last_mouse_dragging = true; /* if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) @@ -4023,6 +4603,7 @@ LGraphCanvas.prototype.processMouseMove = function(e) this.last_mouse = mouse; this.canvas_mouse[0] = e.canvasX; this.canvas_mouse[1] = e.canvasY; + e.dragging = this.last_mouse_dragging; if( this.node_widget ) { @@ -4036,14 +4617,14 @@ LGraphCanvas.prototype.processMouseMove = function(e) this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; } - else if (this.selected_group) + else if (this.selected_group) //moving/resizing a group { if( this.selected_group_resizing ) this.selected_group.size = [ e.canvasX - this.selected_group.pos[0], e.canvasY - this.selected_group.pos[1] ]; else { - var deltax = delta[0] / this.scale; - var deltay = delta[1] / this.scale; + var deltax = delta[0] / this.ds.scale; + var deltay = delta[1] / this.ds.scale; this.selected_group.move( deltax, deltay, e.ctrlKey ); if( this.selected_group._nodes.length) this.dirty_canvas = true; @@ -4052,8 +4633,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) } else if(this.dragging_canvas) { - this.offset[0] += delta[0] / this.scale; - this.offset[1] += delta[1] / this.scale; + this.ds.offset[0] += delta[0] / this.ds.scale; + this.ds.offset[1] += delta[1] / this.ds.scale; this.dirty_canvas = true; this.dirty_bgcanvas = true; } @@ -4095,7 +4676,7 @@ LGraphCanvas.prototype.processMouseMove = function(e) //in case the node wants to do something if(node.onMouseMove) - node.onMouseMove(e); + node.onMouseMove(e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this); //if dragging a link if(this.connecting_node) @@ -4128,11 +4709,11 @@ LGraphCanvas.prototype.processMouseMove = function(e) if( isInsideRectangle(e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,5,5 )) this.canvas.style.cursor = "se-resize"; else - this.canvas.style.cursor = null; + this.canvas.style.cursor = "crosshair"; } } else if(this.canvas) - this.canvas.style.cursor = null; + this.canvas.style.cursor = ""; if(this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove) { @@ -4142,21 +4723,11 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.node_dragged && !this.live_mode) { - /* - this.node_dragged.pos[0] += delta[0] / this.scale; - this.node_dragged.pos[1] += delta[1] / this.scale; - this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); - this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); - */ - for(var i in this.selected_nodes) { var n = this.selected_nodes[i]; - - n.pos[0] += delta[0] / this.scale; - n.pos[1] += delta[1] / this.scale; - //n.pos[0] = Math.round(n.pos[0]); - //n.pos[1] = Math.round(n.pos[1]); + n.pos[0] += delta[0] / this.ds.scale; + n.pos[1] += delta[1] / this.ds.scale; } this.dirty_canvas = true; @@ -4183,16 +4754,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) } } - /* - if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - e.preventDefault(); - //e.stopPropagation(); return false; - //this is not really optimal - //this.graph.change(); } /** @@ -4216,11 +4779,23 @@ LGraphCanvas.prototype.processMouseUp = function(e) this.adjustMouseEvent(e); var now = LiteGraph.getTime(); e.click_time = (now - this.last_mouseclick); + this.last_mouse_dragging = false; if (e.which == 1) //left button { this.node_widget = null; - this.selected_group = null; + + if( this.selected_group ) + { + var diffx = this.selected_group.pos[0] - Math.round( this.selected_group.pos[0] ); + var diffy = this.selected_group.pos[1] - Math.round( this.selected_group.pos[1] ); + this.selected_group.move( diffx, diffy, e.ctrlKey ); + this.selected_group.pos[0] = Math.round( this.selected_group.pos[0] ); + this.selected_group.pos[1] = Math.round( this.selected_group.pos[1] ); + if( this.selected_group._nodes.length ) + this.dirty_canvas = true; + this.selected_group = null; + } this.selected_group_resizing = false; if( this.dragging_rectangle ) @@ -4230,21 +4805,25 @@ LGraphCanvas.prototype.processMouseUp = function(e) var nodes = this.graph._nodes; var node_bounding = new Float32Array(4); this.deselectAllNodes(); - if( this.dragging_rectangle[2] < 0 ) //flip if negative width - this.dragging_rectangle[0] += this.dragging_rectangle[2]; - if( this.dragging_rectangle[3] < 0 ) //flip if negative height - this.dragging_rectangle[1] += this.dragging_rectangle[3]; - this.dragging_rectangle[2] = Math.abs( this.dragging_rectangle[2] * this.scale ); //abs to convert negative width - this.dragging_rectangle[3] = Math.abs( this.dragging_rectangle[3] * this.scale ); //abs to convert negative height + //compute bounding and flip if left to right + var w = Math.abs( this.dragging_rectangle[2] ); + var h = Math.abs( this.dragging_rectangle[3] ); + var startx = this.dragging_rectangle[2] < 0 ? this.dragging_rectangle[0] - w : this.dragging_rectangle[0]; + var starty = this.dragging_rectangle[3] < 0 ? this.dragging_rectangle[1] - h : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = startx; this.dragging_rectangle[1] = starty; this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h; + //test against all nodes (not visible becasue the rectangle maybe start outside + var to_select = []; for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; node.getBounding( node_bounding ); if(!overlapBounding( this.dragging_rectangle, node_bounding )) continue; //out of the visible area - this.selectNode( node, true ); + to_select.push(node); } + if(to_select.length) + this.selectNodes(to_select); } this.dragging_rectangle = null; } @@ -4297,6 +4876,10 @@ LGraphCanvas.prototype.processMouseUp = function(e) } else if(this.node_dragged) //node being dragged? { + var node = this.node_dragged; + if( node && e.click_time < 300 && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) + node.collapse(); + this.dirty_canvas = true; this.dirty_bgcanvas = true; this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); @@ -4309,6 +4892,7 @@ LGraphCanvas.prototype.processMouseUp = function(e) { //get node over var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes ); + if ( !node && e.click_time < 300 ) this.deselectAllNodes(); @@ -4316,7 +4900,7 @@ LGraphCanvas.prototype.processMouseUp = function(e) this.dragging_canvas = false; if( this.node_over && this.node_over.onMouseUp ) - this.node_over.onMouseUp(e, [e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1]] ); + this.node_over.onMouseUp(e, [e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1]], this ); if( this.node_capturing_input && this.node_capturing_input.onMouseUp ) this.node_capturing_input.onMouseUp(e, [e.canvasX - this.node_capturing_input.pos[0], e.canvasY - this.node_capturing_input.pos[1]] ); } @@ -4359,19 +4943,15 @@ LGraphCanvas.prototype.processMouseWheel = function(e) this.adjustMouseEvent(e); - var zoom = this.scale; + var scale = this.ds.scale; if (delta > 0) - zoom *= 1.1; + scale *= 1.1; else if (delta < 0) - zoom *= 1/(1.1); + scale *= 1/(1.1); - this.setZoom( zoom, [ e.localX, e.localY ] ); - - /* - if(this.rendering_timer_id == null) - this.draw(); - */ + //this.setZoom( scale, [ e.localX, e.localY ] ); + this.ds.changeScale( scale, [ e.localX, e.localY ] ); this.graph.change(); @@ -4386,7 +4966,7 @@ LGraphCanvas.prototype.processMouseWheel = function(e) LGraphCanvas.prototype.isOverNodeBox = function( node, canvasx, canvasy ) { var title_height = LiteGraph.NODE_TITLE_HEIGHT; - if( isInsideRectangle( canvasx, canvasy, node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4,title_height - 4) ) + if( isInsideRectangle( canvasx, canvasy, node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4, title_height - 4) ) return true; return false; } @@ -4401,8 +4981,13 @@ LGraphCanvas.prototype.isOverNodeInput = function(node, canvasx, canvasy, slot_p for(var i = 0, l = node.inputs.length; i < l; ++i) { var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true,i); - if( isInsideRectangle(canvasx, canvasy, link_pos[0] - 10, link_pos[1] - 5, 20,10) ) + var link_pos = node.getConnectionPos( true, i ); + var is_inside = false; + if( node.horizontal ) + is_inside = isInsideRectangle(canvasx, canvasy, link_pos[0] - 5, link_pos[1] - 10, 10,20) + else + is_inside = isInsideRectangle(canvasx, canvasy, link_pos[0] - 10, link_pos[1] - 5, 40,10) + if(is_inside) { if(slot_pos) { @@ -4432,7 +5017,7 @@ LGraphCanvas.prototype.processKey = function(e) if(e.type == "keydown") { - if(e.keyCode == 32) + if(e.keyCode == 32) //esc { this.dragging_canvas = true; block_default = true; @@ -4462,8 +5047,11 @@ LGraphCanvas.prototype.processKey = function(e) //delete or backspace if(e.keyCode == 46 || e.keyCode == 8) { - this.deleteSelectedNodes(); - block_default = true; + if(e.target.localName != "input" && e.target.localName != "textarea") + { + this.deleteSelectedNodes(); + block_default = true; + } } //collapse @@ -4491,6 +5079,7 @@ LGraphCanvas.prototype.processKey = function(e) if(block_default) { e.preventDefault(); + e.stopImmediatePropagation(); return false; } } @@ -4711,24 +5300,24 @@ LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; - if(node.selected) + if(node.is_selected) continue; - if( !node.selected && node.onSelected ) + if( !node.is_selected && node.onSelected ) node.onSelected(); - node.selected = true; + node.is_selected = true; this.selected_nodes[ node.id ] = node; if(node.inputs) - for(var i = 0; i < node.inputs.length; ++i) - this.highlighted_links[ node.inputs[i].link ] = true; + for(var j = 0; j < node.inputs.length; ++j) + this.highlighted_links[ node.inputs[j].link ] = true; if(node.outputs) - for(var i = 0; i < node.outputs.length; ++i) + for(var j = 0; j < node.outputs.length; ++j) { - var out = node.outputs[i]; + var out = node.outputs[j]; if( out.links ) - for(var j = 0; j < out.links.length; ++j) - this.highlighted_links[ out.links[j] ] = true; + for(var k = 0; k < out.links.length; ++k) + this.highlighted_links[ out.links[k] ] = true; } } @@ -4742,11 +5331,11 @@ LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) **/ LGraphCanvas.prototype.deselectNode = function( node ) { - if(!node.selected) + if(!node.is_selected) return; if(node.onDeselected) node.onDeselected(); - node.selected = false; + node.is_selected = false; //remove highlighted if(node.inputs) @@ -4774,11 +5363,11 @@ LGraphCanvas.prototype.deselectAllNodes = function() for(var i = 0, l = nodes.length; i < l; ++i) { var node = nodes[i]; - if(!node.selected) + if(!node.is_selected) continue; if(node.onDeselected) node.onDeselected(); - node.selected = false; + node.is_selected = false; } this.selected_nodes = {}; this.highlighted_links = {}; @@ -4808,8 +5397,8 @@ LGraphCanvas.prototype.deleteSelectedNodes = function() **/ LGraphCanvas.prototype.centerOnNode = function(node) { - this.offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.scale); - this.offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.scale); + this.ds.offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.ds.scale); + this.ds.offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.ds.scale); this.setDirty(true,true); } @@ -4822,13 +5411,13 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) if(this.canvas) { var b = this.canvas.getBoundingClientRect(); - e.localX = e.pageX - b.left; - e.localY = e.pageY - b.top; + e.localX = e.clientX - b.left; + e.localY = e.clientY - b.top; } else { - e.localX = e.pageX; - e.localY = e.pageY; + e.localX = e.clientX; + e.localY = e.clientY; } e.deltaX = e.localX - this.last_mouse_position[0]; @@ -4837,8 +5426,8 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) this.last_mouse_position[0] = e.localX; this.last_mouse_position[1] = e.localY; - e.canvasX = e.localX / this.scale - this.offset[0]; - e.canvasY = e.localY / this.scale - this.offset[1]; + e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; + e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; } /** @@ -4847,12 +5436,14 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) **/ LGraphCanvas.prototype.setZoom = function(value, zooming_center) { + this.ds.changeScale( value, zooming_center); + /* if(!zooming_center && this.canvas) zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; var center = this.convertOffsetToCanvas( zooming_center ); - this.scale = value; + this.ds.scale = value; if(this.scale > this.max_zoom) this.scale = this.max_zoom; @@ -4864,39 +5455,35 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) this.offset[0] += delta_offset[0]; this.offset[1] += delta_offset[1]; + */ this.dirty_canvas = true; this.dirty_bgcanvas = true; } /** -* converts a coordinate in canvas2D space to graphcanvas space (NAME IS CONFUSION, SHOULD BE THE OTHER WAY AROUND) +* converts a coordinate from graph coordinates to canvas2D coordinates * @method convertOffsetToCanvas **/ LGraphCanvas.prototype.convertOffsetToCanvas = function( pos, out ) { - out = out || []; - out[0] = pos[0] / this.scale - this.offset[0]; - out[1] = pos[1] / this.scale - this.offset[1]; - return out; + return this.ds.convertOffsetToCanvas( pos, out ); } /** -* converts a coordinate in graphcanvas space to canvas2D space (NAME IS CONFUSION, SHOULD BE THE OTHER WAY AROUND) +* converts a coordinate from Canvas2D coordinates to graph space * @method convertCanvasToOffset **/ LGraphCanvas.prototype.convertCanvasToOffset = function( pos, out ) { - out = out || []; - out[0] = (pos[0] + this.offset[0]) * this.scale; - out[1] = (pos[1] + this.offset[1]) * this.scale; - return out; + return this.ds.convertCanvasToOffset( pos, out ); } -LGraphCanvas.prototype.convertEventToCanvas = function(e) +//converts event coordinates from canvas2D to graph coordinates +LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { var rect = this.canvas.getBoundingClientRect(); - return this.convertOffsetToCanvas([e.pageX - rect.left,e.pageY - rect.top]); + return this.convertCanvasToOffset([e.clientX - rect.left,e.clientY - rect.top]); } /** @@ -4946,7 +5533,7 @@ LGraphCanvas.prototype.computeVisibleNodes = function( nodes, out ) var n = nodes[i]; //skip rendering nodes in live mode - if(this.live_mode && !n.onDrawBackground && !n.onDrawForeground) + if( this.live_mode && !n.onDrawBackground && !n.onDrawForeground ) continue; if(!overlapBounding( this.visible_area, n.getBounding( temp ) )) @@ -4972,11 +5559,7 @@ LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) this.last_draw_time = now; if(this.graph) - { - var start = [-this.offset[0], -this.offset[1] ]; - var end = [start[0] + this.canvas.width / this.scale, start[1] + this.canvas.height / this.scale]; - this.visible_area = new Float32Array([ start[0], start[1], end[0] - start[0], end[1] - start[1] ]); - } + this.ds.computeVisibleArea(); if(this.dirty_bgcanvas || force_bgcanvas || this.always_render_background || (this.graph && this.graph._last_trigger_time && (now - this.graph._last_trigger_time) < 1000) ) this.drawBackCanvas(); @@ -5043,8 +5626,7 @@ LGraphCanvas.prototype.drawFrontCanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.scale,this.scale); - ctx.translate( this.offset[0],this.offset[1] ); + this.ds.toCanvasContext( ctx ); //draw nodes var drawn_nodes = 0; @@ -5066,24 +5648,31 @@ LGraphCanvas.prototype.drawFrontCanvas = function() ctx.restore(); } + //on top (debug) + if( this.render_execution_order) + this.drawExecutionOrder(ctx); + + //connections ontop? if(this.graph.config.links_ontop) if(!this.live_mode) this.drawConnections(ctx); - //current connection + //current connection (the one being dragged by the mouse) if(this.connecting_pos != null) { ctx.lineWidth = this.connections_width; var link_color = null; + switch( this.connecting_output.type ) { - case LiteGraph.EVENT: link_color = "#F85"; break; + case LiteGraph.EVENT: link_color = LiteGraph.EVENT_LINK_COLOR; break; default: - link_color = "#AFA"; + link_color = LiteGraph.CONNECTING_LINK_COLOR; } + //the connection being dragged by the mouse - this.renderLink( ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]], null, false, null, link_color ); + this.renderLink( ctx, this.connecting_pos, [ this.canvas_mouse[0], this.canvas_mouse[1] ], null, false, null, link_color, this.connecting_output.dir || (this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT), LiteGraph.CENTER ); ctx.beginPath(); if( this.connecting_output.type === LiteGraph.EVENT || this.connecting_output.shape === LiteGraph.BOX_SHAPE ) @@ -5107,10 +5696,15 @@ LGraphCanvas.prototype.drawFrontCanvas = function() ctx.strokeRect( this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], this.dragging_rectangle[3] ); } + if( this.onDrawForeground ) + this.onDrawForeground( ctx, this.visible_rect ); ctx.restore(); } + if( this.onDrawOverlay ) + this.onDrawOverlay( ctx ); + if(this.dirty_area) { ctx.restore(); @@ -5139,8 +5733,9 @@ LGraphCanvas.prototype.renderInfo = function( ctx, x, y ) { ctx.fillText( "T: " + this.graph.globaltime.toFixed(2)+"s",5,13*1 ); ctx.fillText( "I: " + this.graph.iteration,5,13*2 ); - ctx.fillText( "V: " + this.graph._version,5,13*3 ); - ctx.fillText( "FPS:" + this.fps.toFixed(2),5,13*4 ); + ctx.fillText( "N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]",5,13*3 ); + ctx.fillText( "V: " + this.graph._version,5,13*4 ); + ctx.fillText( "FPS:" + this.fps.toFixed(2),5,13*5 ); } else ctx.fillText( "No graph selected",5,13*1 ); @@ -5173,10 +5768,21 @@ LGraphCanvas.prototype.drawBackCanvas = function() if(this._graph_stack && this._graph_stack.length) { - ctx.strokeStyle = this._graph_stack[ this._graph_stack.length - 1].bgcolor; + ctx.save(); + var parent_graph = this._graph_stack[ this._graph_stack.length - 1]; + var subgraph_node = this.graph._subgraph_node; + ctx.strokeStyle = subgraph_node.bgcolor; ctx.lineWidth = 10; ctx.strokeRect(1,1,canvas.width-2,canvas.height-2); ctx.lineWidth = 1; + ctx.font = "40px Arial" + ctx.textAlign = "center"; + ctx.fillStyle = subgraph_node.bgcolor; + var title = ""; + for(var i = 1; i < this._graph_stack.length; ++i) + title += this._graph_stack[i]._subgraph_node.getTitle() + " >> "; + ctx.fillText( title + subgraph_node.getTitle(), canvas.width * 0.5, 40 ); + ctx.restore(); } var bg_already_painted = false; @@ -5186,19 +5792,19 @@ LGraphCanvas.prototype.drawBackCanvas = function() //reset in case of error ctx.restore(); ctx.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; if(this.graph) { //apply transformations ctx.save(); - ctx.scale(this.scale,this.scale); - ctx.translate(this.offset[0],this.offset[1]); + this.ds.toCanvasContext(ctx); //render BG - if(this.background_image && this.scale > 0.5 && !bg_already_painted) + if(this.background_image && this.ds.scale > 0.5 && !bg_already_painted) { if (this.zoom_modify_alpha) - ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha; + ctx.globalAlpha = (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; else ctx.globalAlpha = this.editor_alpha; ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; @@ -5237,8 +5843,13 @@ LGraphCanvas.prototype.drawBackCanvas = function() if(this.graph._groups.length && !this.live_mode) this.drawGroups(canvas, ctx); - if(this.onBackgroundRender) - this.onBackgroundRender(canvas, ctx); + if( this.onDrawBackground ) + this.onDrawBackground( ctx, this.visible_area ); + if( this.onBackgroundRender ) //LEGACY + { + console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "); + this.onBackgroundRender = null; + } //DEBUG: show clipping area //ctx.fillStyle = "red"; @@ -5295,25 +5906,6 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if (node.mouseOver) glow = true; - if(node.selected) - { - /* - ctx.shadowColor = "#EEEEFF";//glow ? "#AAF" : "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = 1; - */ - } - else if(this.render_shadows) - { - ctx.shadowColor = "rgba(0,0,0,0.5)"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - } - else - ctx.shadowColor = "transparent"; - //only render if it forces it to do it if(this.live_mode) { @@ -5321,33 +5913,47 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) { ctx.shadowColor = "transparent"; if(node.onDrawForeground) - node.onDrawForeground(ctx, this); + node.onDrawForeground(ctx, this, this.canvas ); } - return; } - //custom draw collapsed method - if(node.flags.collapsed && node.onDrawCollaped && node.onDrawCollapsed(ctx, this) == true) - return; - var editor_alpha = this.editor_alpha; ctx.globalAlpha = editor_alpha; + if(this.render_shadows) + { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + ctx.shadowOffsetX = 2 * this.ds.scale; + ctx.shadowOffsetY = 2 * this.ds.scale; + ctx.shadowBlur = 3 * this.ds.scale; + } + else + ctx.shadowColor = "transparent"; + + //custom draw collapsed method (draw after shadows because they are affected) + if(node.flags.collapsed && node.onDrawCollaped && node.onDrawCollapsed(ctx, this) == true) + return; + //clip if required (mask) var shape = node._shape || LiteGraph.BOX_SHAPE; var size = temp_vec2; temp_vec2.set( node.size ); + var horizontal = node.horizontal;// || node.flags.horizontal; + if( node.flags.collapsed ) { ctx.font = this.inner_text_font; var title = node.getTitle ? node.getTitle() : node.title; - node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + 40 );//LiteGraph.NODE_COLLAPSED_WIDTH; - size[0] = node._collapsed_width; - size[1] = 0; + if(title != null) + { + node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + LiteGraph.NODE_TITLE_HEIGHT * 2 );//LiteGraph.NODE_COLLAPSED_WIDTH; + size[0] = node._collapsed_width; + size[1] = 0; + } } - if( node.flags.clip_area ) //Start clipping + if( node.clip_area ) //Start clipping { ctx.save(); ctx.beginPath(); @@ -5361,19 +5967,26 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } //draw shape - this.drawNodeShape( node, ctx, size, color, bgcolor, node.selected, node.mouseOver ); + if( node.has_errors ) + bgcolor = "red"; + this.drawNodeShape( node, ctx, size, color, bgcolor, node.is_selected, node.mouseOver ); ctx.shadowColor = "transparent"; + //draw foreground + if(node.onDrawForeground) + node.onDrawForeground( ctx, this, this.canvas ); + //connection slots - ctx.textAlign = "left"; + ctx.textAlign = horizontal ? "center" : "left"; ctx.font = this.inner_text_font; - var render_text = this.scale > 0.6; + var render_text = this.ds.scale > 0.6; var out_slot = this.connecting_output; ctx.lineWidth = 1; var max_y = 0; + var slot_pos = new Float32Array(2); //to reuse //render inputs and outputs if(!node.flags.collapsed) @@ -5389,9 +6002,9 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if ( this.connecting_node && LiteGraph.isValidConnection( slot.type && out_slot.type ) ) ctx.globalAlpha = 0.4 * editor_alpha; - ctx.fillStyle = slot.link != null ? (slot.colorOn || this.default_connection_color.input_on) : (slot.colorOff || this.default_connection_color.input_off); + ctx.fillStyle = slot.link != null ? (slot.color_on || this.default_connection_color.input_on) : (slot.color_off || this.default_connection_color.input_off); - var pos = node.getConnectionPos( true, i ); + var pos = node.getConnectionPos( true, i, slot_pos ); pos[0] -= node.pos[0]; pos[1] -= node.pos[1]; if( max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5 ) @@ -5399,8 +6012,12 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) ctx.beginPath(); - if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { - ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5, 14, 10); + if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) + { + if (horizontal) + ctx.rect((pos[0] - 5) + 0.5, (pos[1] - 8) + 0.5, 10, 14); + else + ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5, 14, 10); } else if (slot.shape === LiteGraph.ARROW_SHAPE) { ctx.moveTo(pos[0] + 8, pos[1] + 0.5); ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5); @@ -5419,7 +6036,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(text) { ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - ctx.fillText(text,pos[0] + 10,pos[1] + 5); + if( horizontal || slot.dir == LiteGraph.UP ) + ctx.fillText(text,pos[0],pos[1] - 10); + else + ctx.fillText(text,pos[0] + 10,pos[1] + 5); } } } @@ -5428,25 +6048,29 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(this.connecting_node) ctx.globalAlpha = 0.4 * editor_alpha; - ctx.textAlign = "right"; + ctx.textAlign = horizontal ? "center" : "right"; ctx.strokeStyle = "black"; if(node.outputs) for(var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; - var pos = node.getConnectionPos(false,i); + var pos = node.getConnectionPos(false,i, slot_pos ); pos[0] -= node.pos[0]; pos[1] -= node.pos[1]; if( max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5) max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5; - ctx.fillStyle = slot.links && slot.links.length ? (slot.colorOn || this.default_connection_color.output_on) : (slot.colorOff || this.default_connection_color.output_off); + ctx.fillStyle = slot.links && slot.links.length ? (slot.color_on || this.default_connection_color.output_on) : (slot.color_off || this.default_connection_color.output_off); ctx.beginPath(); //ctx.rect( node.size[0] - 14,i*14,10,10); - if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { - ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10); + if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) + { + if( horizontal ) + ctx.rect((pos[0] - 5) + 0.5,(pos[1] - 8) + 0.5,10,14); + else + ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10); } else if (slot.shape === LiteGraph.ARROW_SHAPE) { ctx.moveTo(pos[0] + 8, pos[1] + 0.5); ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5); @@ -5471,7 +6095,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(text) { ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - ctx.fillText(text, pos[0] - 10,pos[1] + 5); + if( horizontal || slot.dir == LiteGraph.DOWN ) + ctx.fillText(text,pos[0],pos[1] - 8); + else + ctx.fillText(text, pos[0] - 10,pos[1] + 5); } } } @@ -5480,19 +6107,18 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) ctx.globalAlpha = 1; if(node.widgets) - this.drawNodeWidgets( node, max_y, ctx, (this.node_widget && this.node_widget[0] == node) ? this.node_widget[1] : null ); - - //draw foreground - if(node.onDrawForeground) { - //immediate gui stuff - if( node.gui_rects ) - node.gui_rects.length = 0; - node.onDrawForeground( ctx, this ); + if( horizontal || node.widgets_up ) + max_y = 2; + this.drawNodeWidgets( node, max_y, ctx, (this.node_widget && this.node_widget[0] == node) ? this.node_widget[1] : null ); } } - else //if collapsed + else if(this.render_collapsed_slots)//if collapsed { + var input_slot = null; + var output_slot = null; + + //get first connected slot to render if(node.inputs) { for(var i = 0; i < node.inputs.length; i++) @@ -5500,23 +6126,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) var slot = node.inputs[i]; if( slot.link == null ) continue; - ctx.fillStyle = slot.colorOn || this.default_connection_color.input_on; - ctx.beginPath(); - if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { - ctx.rect(0.5, 4 - LiteGraph.NODE_TITLE_HEIGHT + 0.5,14,LiteGraph.NODE_TITLE_HEIGHT - 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(8, LiteGraph.NODE_TITLE_HEIGHT * -0.5); - ctx.lineTo(-4, LiteGraph.NODE_TITLE_HEIGHT * -0.8); - ctx.lineTo(-4, LiteGraph.NODE_TITLE_HEIGHT * -0.2); - ctx.closePath(); - } else { - ctx.arc(0, LiteGraph.NODE_TITLE_HEIGHT * -0.5, 4, 0, Math.PI * 2); - } - ctx.fill(); + input_slot = slot; break; } } - if(node.outputs) { for(var i = 0; i < node.outputs.length; i++) @@ -5524,27 +6137,62 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) var slot = node.outputs[i]; if(!slot.links || !slot.links.length) continue; - ctx.fillStyle = slot.colorOn || this.default_connection_color.output_on; - ctx.strokeStyle = "black"; - ctx.beginPath(); - if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { - ctx.rect( node._collapsed_width - 4 + 0.5, 4 - LiteGraph.NODE_TITLE_HEIGHT + 0.5,14,LiteGraph.NODE_TITLE_HEIGHT - 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(node._collapsed_width + 6, LiteGraph.NODE_TITLE_HEIGHT * -0.5); - ctx.lineTo(node._collapsed_width - 6, LiteGraph.NODE_TITLE_HEIGHT * -0.8); - ctx.lineTo(node._collapsed_width - 6, LiteGraph.NODE_TITLE_HEIGHT * -0.2); - ctx.closePath(); - } else { - ctx.arc(node._collapsed_width, LiteGraph.NODE_TITLE_HEIGHT * -0.5, 4, 0, Math.PI * 2); - } - ctx.fill(); - ctx.stroke(); + output_slot = slot; } } - + + if(input_slot) + { + var x = 0; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if( horizontal ) + { + x = node._collapsed_width * 0.5; + y = -LiteGraph.NODE_TITLE_HEIGHT; + } + ctx.fillStyle = "#686"; + ctx.beginPath(); + if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { + ctx.rect(x - 7 + 0.5, y-4,14,8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 8, y); + ctx.lineTo(x + -4, y - 4); + ctx.lineTo(x + -4, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + } + + if(output_slot) + { + var x = node._collapsed_width; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if( horizontal ) + { + x = node._collapsed_width * 0.5; + y = 0; + } + ctx.fillStyle = "#686"; + ctx.strokeStyle = "black"; + ctx.beginPath(); + if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) { + ctx.rect( x - 7 + 0.5, y - 4,14,8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 6, y); + ctx.lineTo(x - 6, y - 4); + ctx.lineTo(x - 6, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + //ctx.stroke(); + } } - if(node.flags.clip_area) + if(node.clip_area) ctx.restore(); ctx.globalAlpha = 1.0; @@ -5554,6 +6202,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) * draws the shape of the given node in the canvas * @method drawNodeShape **/ +var tmp_area = new Float32Array(4); + LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcolor, selected, mouse_over ) { //bg rect @@ -5561,9 +6211,11 @@ LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcol ctx.fillStyle = bgcolor; var title_height = LiteGraph.NODE_TITLE_HEIGHT; + var low_quality = this.ds.scale < 0.5; //render node area depending on shape - var shape = node._shape || node.constructor.shape || LiteGraph.BOX_SHAPE; + var shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; + var title_mode = node.constructor.title_mode; var render_title = true; @@ -5572,107 +6224,114 @@ LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcol else if( title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) render_title = true; - var areax = 0; - var areay = render_title ? -title_height : 0; - var areaw = size[0]+1; - var areah = render_title ? size[1] + title_height : size[1]; + var area = tmp_area; + area[0] = 0; //x + area[1] = render_title ? -title_height : 0; //y + area[2] = size[0]+1; //w + area[3] = render_title ? size[1] + title_height : size[1]; //h - if(!node.flags.collapsed) + var old_alpha = ctx.globalAlpha; + + //full node shape + //if(node.flags.collapsed) { - if(shape == LiteGraph.BOX_SHAPE || this.scale < 0.5) - { - ctx.beginPath(); - ctx.rect( areax, areay, areaw, areah ); - ctx.fill(); - } + ctx.beginPath(); + if(shape == LiteGraph.BOX_SHAPE || low_quality ) + ctx.fillRect( area[0], area[1], area[2], area[3] ); else if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE) - { - ctx.beginPath(); - ctx.roundRect( areax, areay, areaw, areah, this.round_radius, shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius); - ctx.fill(); - } + ctx.roundRect( area[0], area[1], area[2], area[3], this.round_radius, shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius); else if (shape == LiteGraph.CIRCLE_SHAPE) - { - ctx.beginPath(); ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI*2); - ctx.fill(); - } + ctx.fill(); + + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0,-1, area[2],2); } ctx.shadowColor = "transparent"; - //image - if (node.bgImage && node.bgImage.width) - ctx.drawImage( node.bgImage, (size[0] - node.bgImage.width) * 0.5 , (size[1] - node.bgImage.height) * 0.5); - - if(node.bgImageUrl && !node.bgImage) - node.bgImage = node.loadImage(node.bgImageUrl); - if( node.onDrawBackground ) - { - //immediate gui stuff - if( node.gui_rects ) - node.gui_rects.length = 0; - node.onDrawBackground( ctx, this ); - } + node.onDrawBackground( ctx, this, this.canvas ); //title bg (remember, it is rendered ABOVE the node) - if(render_title || title_mode == LiteGraph.TRANSPARENT_TITLE ) + if( render_title || title_mode == LiteGraph.TRANSPARENT_TITLE ) { //title bar - if(title_mode != LiteGraph.TRANSPARENT_TITLE) //!node.flags.collapsed) + if(node.onDrawTitleBar) { + node.onDrawTitleBar(ctx, title_height, size, this.ds.scale, fgcolor); + } + else if(title_mode != LiteGraph.TRANSPARENT_TITLE && (node.constructor.title_color || this.render_title_colored )) + { + var title_color = node.constructor.title_color || fgcolor; + + if(node.flags.collapsed) + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + //* gradient test if(this.use_gradients) { - var grad = LGraphCanvas.gradients[ fgcolor ]; + var grad = LGraphCanvas.gradients[ title_color ]; if(!grad) { - grad = LGraphCanvas.gradients[ fgcolor ] = ctx.createLinearGradient(0,0,400,0); - grad.addColorStop(0, fgcolor); + grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0,0,400,0); + grad.addColorStop(0, title_color); grad.addColorStop(1, "#000"); } ctx.fillStyle = grad; } else - ctx.fillStyle = fgcolor; + ctx.fillStyle = title_color; - var old_alpha = ctx.globalAlpha; //ctx.globalAlpha = 0.5 * old_alpha; ctx.beginPath(); - if(shape == LiteGraph.BOX_SHAPE || this.scale < 0.5) - { + if( shape == LiteGraph.BOX_SHAPE || low_quality ) ctx.rect(0, -title_height, size[0]+1, title_height); - ctx.fill() - //ctx.stroke(); - } - else if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE) - { + else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) ctx.roundRect(0,-title_height,size[0]+1, title_height, this.round_radius, node.flags.collapsed ? this.round_radius : 0); - ctx.fill(); - } - - /* - else if (shape == LiteGraph.CIRCLE_SHAPE) - { - ctx.beginPath(); - ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 6) *0.5,0,Math.PI*2); - ctx.fill(); - } - */ + ctx.fill(); + ctx.shadowColor = "transparent"; } //title box - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.beginPath(); - if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CIRCLE_SHAPE || shape == LiteGraph.CARD_SHAPE) - ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 8) *0.5,0,Math.PI*2); + var box_size = 10; + if(node.onDrawTitleBox) + { + node.onDrawTitleBox( ctx, title_height, size, this.ds.scale ); + } + else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CIRCLE_SHAPE || shape == LiteGraph.CARD_SHAPE ) + { + if( low_quality ) + { + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(title_height * 0.5, title_height * -0.5, box_size*0.5+1,0,Math.PI*2); + ctx.fill(); + } + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.beginPath(); + ctx.arc(title_height * 0.5, title_height * -0.5, box_size*0.5,0,Math.PI*2); + ctx.fill(); + } else - ctx.rect(4,-title_height + 4,title_height - 8,title_height - 8); - ctx.fill(); + { + if( low_quality ) + { + ctx.fillStyle = "black"; + ctx.fillRect( (title_height - box_size) * 0.5 - 1, (title_height + box_size ) * -0.5 - 1, box_size + 2, box_size + 2); + } + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.fillRect( (title_height - box_size) * 0.5, (title_height + box_size ) * -0.5, box_size, box_size ); + } ctx.globalAlpha = old_alpha; //title text - if( this.scale > 0.5 ) + if(node.onDrawTitleText) + { + node.onDrawTitleText( ctx, title_height, size, this.ds.scale, this.title_text_font, selected); + } + if( !low_quality ) { ctx.font = this.title_text_font; var title = node.getTitle(); @@ -5686,42 +6345,55 @@ LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcol { ctx.textAlign = "center"; var measure = ctx.measureText(title); - ctx.fillText( title, title_height + measure.width * 0.5, -title_height * 0.2 ); + ctx.fillText( title, title_height + measure.width * 0.5, LiteGraph.NODE_TITLE_TEXT_Y - title_height ); ctx.textAlign = "left"; } else { ctx.textAlign = "left"; - ctx.fillText( title, title_height, -title_height * 0.2 ); + ctx.fillText( title, title_height, LiteGraph.NODE_TITLE_TEXT_Y - title_height ); } } } + + if(node.onDrawTitle) + node.onDrawTitle(ctx); } //render selection marker if(selected) { + if( node.onBounding ) + node.onBounding( area ); + if( title_mode == LiteGraph.TRANSPARENT_TITLE ) { - areay -= title_height; - areah += title_height; + area[1] -= title_height; + area[3] += title_height; } ctx.lineWidth = 1; + ctx.globalAlpha = 0.8; ctx.beginPath(); - if(shape == LiteGraph.BOX_SHAPE) - ctx.rect(-6 + areax,-6 + areay, 12 + areaw, 12 + areah ); + if( shape == LiteGraph.BOX_SHAPE ) + ctx.rect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] ); else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) ) - ctx.roundRect(-6 + areax,-6 + areay, 12 + areaw, 12 + areah , this.round_radius * 2); + ctx.roundRect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] , this.round_radius * 2); else if (shape == LiteGraph.CARD_SHAPE) - ctx.roundRect(-6 + areax,-6 + areay, 12 + areaw, 12 + areah , this.round_radius * 2, 2); + ctx.roundRect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] , this.round_radius * 2, 2); else if (shape == LiteGraph.CIRCLE_SHAPE) ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI*2); - ctx.strokeStyle = "#DDD"; + ctx.strokeStyle = "#FFF"; ctx.stroke(); ctx.strokeStyle = fgcolor; + ctx.globalAlpha = 1; } } +var margin_area = new Float32Array(4); +var link_bounding = new Float32Array(4); +var tempA = new Float32Array(2); +var tempB = new Float32Array(2); + /** * draws every connection visible in the canvas * OPTIMIZE THIS: precatch connections position instead of recomputing them every time @@ -5730,6 +6402,8 @@ LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcol LGraphCanvas.prototype.drawConnections = function(ctx) { var now = LiteGraph.getTime(); + var visible_area = this.visible_area; + margin_area[0] = visible_area[0] - 20; margin_area[1] = visible_area[1] - 20; margin_area[2] = visible_area[2] + 40; margin_area[3] = visible_area[3] + 40; //draw connections ctx.lineWidth = this.connections_width; @@ -5738,41 +6412,71 @@ LGraphCanvas.prototype.drawConnections = function(ctx) ctx.strokeStyle = "#AAA"; ctx.globalAlpha = this.editor_alpha; //for every node - for (var n = 0, l = this.graph._nodes.length; n < l; ++n) + var nodes = this.graph._nodes; + for (var n = 0, l = nodes.length; n < l; ++n) { - var node = this.graph._nodes[n]; + var node = nodes[n]; //for every input (we render just inputs because it is easier as every slot can only have one input) - if(node.inputs && node.inputs.length) - for(var i = 0; i < node.inputs.length; ++i) - { - var input = node.inputs[i]; - if(!input || input.link == null) - continue; - var link_id = input.link; - var link = this.graph.links[ link_id ]; - if(!link) - continue; + if(!node.inputs || !node.inputs.length) + continue; + + for(var i = 0; i < node.inputs.length; ++i) + { + var input = node.inputs[i]; + if(!input || input.link == null) + continue; + var link_id = input.link; + var link = this.graph.links[ link_id ]; + if(!link) + continue; - var start_node = this.graph.getNodeById( link.origin_id ); - if(start_node == null) continue; - var start_node_slot = link.origin_slot; - var start_node_slotpos = null; + //find link info + var start_node = this.graph.getNodeById( link.origin_id ); + if(start_node == null) continue; + var start_node_slot = link.origin_slot; + var start_node_slotpos = null; + if(start_node_slot == -1) + start_node_slotpos = [start_node.pos[0] + 10, start_node.pos[1] + 10]; + else + start_node_slotpos = start_node.getConnectionPos( false, start_node_slot, tempA ); + var end_node_slotpos = node.getConnectionPos( true, i, tempB ); - if(start_node_slot == -1) - start_node_slotpos = [start_node.pos[0] + 10, start_node.pos[1] + 10]; - else - start_node_slotpos = start_node.getConnectionPos(false, start_node_slot); - - this.renderLink( ctx, start_node_slotpos, node.getConnectionPos(true,i), link ); - - //event triggered rendered on top - if(link && link._last_time && (now - link._last_time) < 1000 ) - { - var f = 2.0 - (now - link._last_time) * 0.002; - var color = "rgba(255,255,255, " + f.toFixed(2) + ")"; - this.renderLink( ctx, start_node_slotpos, node.getConnectionPos(true,i), link, true, f, color ); - } + //compute link bounding + link_bounding[0] = start_node_slotpos[0]; + link_bounding[1] = start_node_slotpos[1]; + link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; + link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; + if( link_bounding[2] < 0 ){ + link_bounding[0] += link_bounding[2]; + link_bounding[2] = Math.abs( link_bounding[2] ); } + if( link_bounding[3] < 0 ){ + link_bounding[1] += link_bounding[3]; + link_bounding[3] = Math.abs( link_bounding[3] ); + } + + //skip links outside of the visible area of the canvas + if( !overlapBounding( link_bounding, margin_area ) ) + continue; + + var start_slot = start_node.outputs[ start_node_slot ]; + var end_slot = node.inputs[i]; + if(!start_slot || !end_slot) continue; + var start_dir = start_slot.dir || (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); + var end_dir = end_slot.dir || (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); + + this.renderLink( ctx, start_node_slotpos, end_node_slotpos, link, false, 0, null, start_dir, end_dir ); + + //event triggered rendered on top + if(link && link._last_time && (now - link._last_time) < 1000 ) + { + var f = 2.0 - (now - link._last_time) * 0.002; + var tmp = ctx.globalAlpha; + ctx.globalAlpha = tmp * f; + this.renderLink( ctx, start_node_slotpos, end_node_slotpos, link, true, f, "white", start_dir, end_dir ); + ctx.globalAlpha = tmp; + } + } } ctx.globalAlpha = 1; } @@ -5780,52 +6484,125 @@ LGraphCanvas.prototype.drawConnections = function(ctx) /** * draws a link between two points * @method renderLink +* @param {vec2} a start pos +* @param {vec2} b end pos +* @param {Object} link the link object with all the link info +* @param {boolean} skip_border ignore the shadow of the link +* @param {boolean} flow show flow animation (for events) +* @param {string} color the color for the link +* @param {number} start_dir the direction enum +* @param {number} end_dir the direction enum +* @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) **/ -LGraphCanvas.prototype.renderLink = function( ctx, a, b, link, skip_border, flow, color ) +LGraphCanvas.prototype.renderLink = function( ctx, a, b, link, skip_border, flow, color, start_dir, end_dir, num_sublines ) { - if(!this.highquality_render) - { - ctx.beginPath(); - ctx.moveTo(a[0],a[1]); - ctx.lineTo(b[0],b[1]); - ctx.stroke(); - return; - } - - var dist = distance(a,b); - - if(this.render_connections_border && this.scale > 0.6) - ctx.lineWidth = this.connections_width + 4; + if(link) + this.visible_links.push( link ); //choose color if( !color && link ) - color = LGraphCanvas.link_type_colors[ link.type ]; - if(!color) + color = link.color || LGraphCanvas.link_type_colors[ link.type ]; + if( !color ) color = this.default_link_color; - if( link != null && this.highlighted_links[ link.id ] ) color = "#FFF"; + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a,b); + + if(this.render_connections_border && this.ds.scale > 0.6) + ctx.lineWidth = this.connections_width + 4; + ctx.lineJoin = "round"; + num_sublines = num_sublines || 1; + if(num_sublines > 1) + ctx.lineWidth = 0.5; + //begin line shape ctx.beginPath(); + for(var i = 0; i < num_sublines; i += 1) + { + var offsety = (i - (num_sublines-1)*0.5)*5; - if(this.render_curved_connections) //splines - { - ctx.moveTo(a[0],a[1]); - ctx.bezierCurveTo(a[0] + dist*0.25, a[1], - b[0] - dist*0.25 , b[1], - b[0] ,b[1] ); - } - else //lines - { - ctx.moveTo(a[0]+10,a[1]); - ctx.lineTo(((a[0]+10) + (b[0]-10))*0.5,a[1]); - ctx.lineTo(((a[0]+10) + (b[0]-10))*0.5,b[1]); - ctx.lineTo(b[0]-10,b[1]); + if(this.links_render_mode == LiteGraph.SPLINE_LINK) + { + ctx.moveTo(a[0],a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch(start_dir) + { + case LiteGraph.LEFT: start_offset_x = dist*-0.25; break; + case LiteGraph.RIGHT: start_offset_x = dist*0.25; break; + case LiteGraph.UP: start_offset_y = dist*-0.25; break; + case LiteGraph.DOWN: start_offset_y = dist*0.25; break; + } + switch(end_dir) + { + case LiteGraph.LEFT: end_offset_x = dist*-0.25; break; + case LiteGraph.RIGHT: end_offset_x = dist*0.25; break; + case LiteGraph.UP: end_offset_y = dist*-0.25; break; + case LiteGraph.DOWN: end_offset_y = dist*0.25; break; + } + ctx.bezierCurveTo(a[0] + start_offset_x, a[1] + start_offset_y + offsety, + b[0] + end_offset_x , b[1] + end_offset_y + offsety, + b[0], b[1] + offsety); + } + else if(this.links_render_mode == LiteGraph.LINEAR_LINK) + { + ctx.moveTo(a[0],a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch(start_dir) + { + case LiteGraph.LEFT: start_offset_x = -1; break; + case LiteGraph.RIGHT: start_offset_x = 1; break; + case LiteGraph.UP: start_offset_y = -1; break; + case LiteGraph.DOWN: start_offset_y = 1; break; + } + switch(end_dir) + { + case LiteGraph.LEFT: end_offset_x = -1; break; + case LiteGraph.RIGHT: end_offset_x = 1; break; + case LiteGraph.UP: end_offset_y = -1; break; + case LiteGraph.DOWN: end_offset_y = 1; break; + } + var l = 15; + ctx.lineTo(a[0] + start_offset_x * l, a[1] + start_offset_y * l + offsety); + ctx.lineTo(b[0] + end_offset_x * l, b[1] + end_offset_y * l + offsety); + ctx.lineTo(b[0],b[1] + offsety); + } + else if(this.links_render_mode == LiteGraph.STRAIGHT_LINK) + { + ctx.moveTo(a[0], a[1]); + var start_x = a[0]; + var start_y = a[1]; + var end_x = b[0]; + var end_y = b[1]; + if( start_dir == LiteGraph.RIGHT ) + start_x += 10; + else + start_y += 10; + if( end_dir == LiteGraph.LEFT ) + end_x -= 10; + else + end_y -= 10; + ctx.lineTo(start_x, start_y); + ctx.lineTo((start_x + end_x)*0.5,start_y); + ctx.lineTo((start_x + end_x)*0.5,end_y); + ctx.lineTo(end_x, end_y); + ctx.lineTo(b[0],b[1]); + } + else + return; //unknown } //rendering the outline of the connection can be a little bit slow - if(this.render_connections_border && this.scale > 0.6 && !skip_border) + if(this.render_connections_border && this.ds.scale > 0.6 && !skip_border) { ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.stroke(); @@ -5836,43 +6613,71 @@ LGraphCanvas.prototype.renderLink = function( ctx, a, b, link, skip_border, flow ctx.stroke(); //end line shape + var pos = this.computeConnectionPoint( a, b, 0.5, start_dir, end_dir ); + if(link && link._pos) + { + link._pos[0] = pos[0]; + link._pos[1] = pos[1]; + } + //render arrow in the middle - if( this.render_connection_arrows && this.scale >= 0.6 ) + if( this.ds.scale >= 0.6 && this.highquality_render && end_dir != LiteGraph.CENTER ) { //render arrow - if(this.render_connection_arrows && this.scale > 0.6) + if( this.render_connection_arrows ) { //compute two points in the connection - var pos = this.computeConnectionPoint(a,b,0.5); - var pos2 = this.computeConnectionPoint(a,b,0.51); + var posA = this.computeConnectionPoint( a, b, 0.25, start_dir, end_dir ); + var posB = this.computeConnectionPoint( a, b, 0.26, start_dir, end_dir ); + var posC = this.computeConnectionPoint( a, b, 0.75, start_dir, end_dir ); + var posD = this.computeConnectionPoint( a, b, 0.76, start_dir, end_dir ); //compute the angle between them so the arrow points in the right direction - var angle = 0; + var angleA = 0; + var angleB = 0; if(this.render_curved_connections) - angle = -Math.atan2( pos2[0] - pos[0], pos2[1] - pos[1]); + { + angleA = -Math.atan2( posB[0] - posA[0], posB[1] - posA[1]); + angleB = -Math.atan2( posD[0] - posC[0], posD[1] - posC[1]); + } else - angle = b[1] > a[1] ? 0 : Math.PI; + angleB = angleA = b[1] > a[1] ? 0 : Math.PI; //render arrow ctx.save(); - ctx.translate(pos[0],pos[1]); - ctx.rotate(angle); + ctx.translate(posA[0],posA[1]); + ctx.rotate(angleA); ctx.beginPath(); - ctx.moveTo(-5,-5); - ctx.lineTo(0,+5); - ctx.lineTo(+5,-5); + ctx.moveTo(-5,-3); + ctx.lineTo(0,+7); + ctx.lineTo(+5,-3); + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.translate(posC[0],posC[1]); + ctx.rotate(angleB); + ctx.beginPath(); + ctx.moveTo(-5,-3); + ctx.lineTo(0,+7); + ctx.lineTo(+5,-3); ctx.fill(); ctx.restore(); } + + //circle + ctx.beginPath(); + ctx.arc(pos[0],pos[1],5,0,Math.PI*2); + ctx.fill(); } //render flowing points if(flow) { + ctx.fillStyle = color; for(var i = 0; i < 5; ++i) { var f = (LiteGraph.getTime() * 0.001 + (i * 0.2)) % 1; - var pos = this.computeConnectionPoint(a,b,f); + var pos = this.computeConnectionPoint(a,b,f, start_dir, end_dir); ctx.beginPath(); ctx.arc(pos[0],pos[1],5,0,2*Math.PI); ctx.fill(); @@ -5880,14 +6685,33 @@ LGraphCanvas.prototype.renderLink = function( ctx, a, b, link, skip_border, flow } } -LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t) +//returns the link center point based on curvature +LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t,start_dir,end_dir) { + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + var dist = distance(a,b); var p0 = a; - var p1 = [ a[0] + dist*0.25, a[1] ]; - var p2 = [ b[0] - dist*0.25, b[1] ]; + var p1 = [ a[0], a[1] ]; + var p2 = [ b[0], b[1] ]; var p3 = b; + switch(start_dir) + { + case LiteGraph.LEFT: p1[0] += dist*-0.25; break; + case LiteGraph.RIGHT: p1[0] += dist*0.25; break; + case LiteGraph.UP: p1[1] += dist*-0.25; break; + case LiteGraph.DOWN: p1[1] += dist*0.25; break; + } + switch(end_dir) + { + case LiteGraph.LEFT: p2[0] += dist*-0.25; break; + case LiteGraph.RIGHT: p2[0] += dist*0.25; break; + case LiteGraph.UP: p2[1] += dist*-0.25; break; + case LiteGraph.DOWN: p2[1] += dist*0.25; break; + } + var c1 = (1-t)*(1-t)*(1-t); var c2 = 3*((1-t)*(1-t))*t; var c3 = 3*(1-t)*(t*t); @@ -5898,6 +6722,30 @@ LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t) return [x,y]; } +LGraphCanvas.prototype.drawExecutionOrder = function(ctx) +{ + ctx.shadowColor = "transparent"; + ctx.globalAlpha = 0.25; + + ctx.textAlign = "center"; + ctx.strokeStyle = "white"; + ctx.globalAlpha = 0.75; + + var visible_nodes = this.visible_nodes; + for (var i = 0; i < visible_nodes.length; ++i) + { + var node = visible_nodes[i]; + ctx.fillStyle = "black"; + ctx.fillRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ); + if(node.order == 0) + ctx.strokeRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ); + ctx.fillStyle = "#FFF"; + ctx.fillText( node.order, node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, node.pos[1] - 6 ); + } + ctx.globalAlpha = 1; +} + + /** * draws the widgets stored inside a node * @method drawNodeWidgets @@ -5910,7 +6758,12 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge var widgets = node.widgets; posY += 2; var H = LiteGraph.NODE_WIDGET_HEIGHT; - var show_text = this.scale > 0.5; + var show_text = this.ds.scale > 0.5; + ctx.save(); + ctx.globalAlpha = this.editor_alpha; + var outline_color = "#666"; + var background_color = "#222"; + var margin = 15; for(var i = 0; i < widgets.length; ++i) { @@ -5919,7 +6772,7 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge if(w.y) y = w.y; w.last_y = y; - ctx.strokeStyle = "#AAA"; + ctx.strokeStyle = outline_color; ctx.fillStyle = "#222"; ctx.textAlign = "left"; @@ -5932,8 +6785,8 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge w.clicked = false; this.dirty_canvas = true; } - ctx.fillRect(10,y,width-20,H); - ctx.strokeRect(10,y,width-20,H); + ctx.fillRect(margin,y,width-margin*2,H); + ctx.strokeRect(margin,y,width-margin*2,H); if(show_text) { ctx.textAlign = "center"; @@ -5941,14 +6794,42 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge ctx.fillText( w.name, width*0.5, y + H*0.7 ); } break; + case "toggle": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 ); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = w.value ? "#89A" : "#333"; + ctx.beginPath(); + ctx.arc( width - margin*2, y + H*0.5, H * 0.36, 0, Math.PI * 2 ); + ctx.fill(); + if(show_text) + { + ctx.fillStyle = "#999"; + if(w.name != null) + ctx.fillText( w.name, margin*2, y + H*0.7 ); + ctx.fillStyle = w.value ? "#DDD" : "#888"; + ctx.textAlign = "right"; + ctx.fillText( w.value ? (w.options.on || "true") : (w.options.off || "false"), width - 40, y + H*0.7 ); + } + break; case "slider": - ctx.fillStyle = "#111"; - ctx.fillRect(10,y,width-20,H); + ctx.fillStyle = background_color; + ctx.fillRect(margin,y,width-margin*2,H); var range = w.options.max - w.options.min; var nvalue = (w.value - w.options.min) / range; ctx.fillStyle = active_widget == w ? "#89A" : "#678"; - ctx.fillRect(10,y,nvalue*(width-20),H); - ctx.strokeRect(10,y,width-20,H); + ctx.fillRect(margin,y,nvalue*(width-margin*2),H); + ctx.strokeRect(margin,y,width-margin*2,H); + if( w.marker ) + { + var marker_nvalue = (w.marker - w.options.min) / range; + ctx.fillStyle = "#AA9"; + ctx.fillRect(margin + marker_nvalue*(width-margin*2),y,2,H); + } if(show_text) { ctx.textAlign = "center"; @@ -5959,39 +6840,60 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge case "number": case "combo": ctx.textAlign = "left"; - ctx.strokeStyle = "#AAA"; - ctx.fillStyle = "#111"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; ctx.beginPath(); - ctx.roundRect( 10, posY, width - 20, H,H*0.5 ); + ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 ); ctx.fill(); ctx.stroke(); - ctx.fillStyle = "#AAA"; - ctx.beginPath(); - ctx.moveTo( 26, posY + 5 ); - ctx.lineTo( 16, posY + H*0.5 ); - ctx.lineTo( 26, posY + H - 5 ); - ctx.moveTo( width - 26, posY + 5 ); - ctx.lineTo( width - 16, posY + H*0.5 ); - ctx.lineTo( width - 26, posY + H - 5 ); - ctx.fill(); if(show_text) { + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.moveTo( margin + 16, posY + 5 ); + ctx.lineTo( margin + 6, posY + H*0.5 ); + ctx.lineTo( margin + 16, posY + H - 5 ); + ctx.moveTo( width - margin - 16, posY + 5 ); + ctx.lineTo( width - margin - 6, posY + H*0.5 ); + ctx.lineTo( width - margin - 16, posY + H - 5 ); + ctx.fill(); ctx.fillStyle = "#999"; - ctx.fillText( w.name, 30, y + H*0.7 ); + ctx.fillText( w.name, margin*2 + 5, y + H*0.7 ); ctx.fillStyle = "#DDD"; ctx.textAlign = "right"; if(w.type == "number") - ctx.fillText( Number(w.value).toFixed( w.options.precision !== undefined ? w.options.precision : 3), width - 40, y + H*0.7 ); + ctx.fillText( Number(w.value).toFixed( w.options.precision !== undefined ? w.options.precision : 3), width - margin*2 - 20, y + H*0.7 ); else - ctx.fillText( w.value, width - 40, y + H*0.7 ); + ctx.fillText( w.value, width - margin*2 - 20, y + H*0.7 ); + } + break; + case "string": + case "text": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 ); + ctx.fill(); + ctx.stroke(); + if(show_text) + { + ctx.fillStyle = "#999"; + if(w.name != null) + ctx.fillText( w.name, margin*2, y + H*0.7 ); + ctx.fillStyle = "#DDD"; + ctx.textAlign = "right"; + ctx.fillText( w.value, width - margin*2, y + H*0.7 ); } break; default: + if(w.draw) + w.draw(ctx,node,w,y,H); break; } posY += H + 4; } - ctx.textAlign = "left"; + ctx.restore(); } /** @@ -6007,6 +6909,7 @@ LGraphCanvas.prototype.processNodeWidgets = function( node, pos, event, active_w var y = pos[1] - node.pos[1]; var width = node.size[0]; var that = this; + var ref_window = this.getCanvasWindow(); for(var i = 0; i < node.widgets.length; ++i) { @@ -6042,6 +6945,10 @@ LGraphCanvas.prototype.processNodeWidgets = function( node, pos, event, active_w } else if( event.type == "mousedown" ) { + var values = w.options.values; + if(values && values.constructor === Function) + values = w.options.values( w, node ); + var delta = ( x < 40 ? -1 : ( x > width - 40 ? 1 : 0) ); if (w.type == "number") { @@ -6053,18 +6960,45 @@ LGraphCanvas.prototype.processNodeWidgets = function( node, pos, event, active_w } else if(delta) { - var index = w.options.values.indexOf( w.value ) + delta; - if( index >= w.options.values.length ) + var index = values.indexOf( w.value ) + delta; + if( index >= values.length ) index = 0; if( index < 0 ) - index = w.options.values.length - 1; - w.value = w.options.values[ index ]; + index = values.length - 1; + w.value = values[ index ]; + } + else + { + var menu = new LiteGraph.ContextMenu( values, { scale: Math.max(1,this.ds.scale), event: event, className: "dark", callback: inner_clicked.bind(w) }, ref_window ); + function inner_clicked( v, option, event ) + { + this.value = v; + that.dirty_canvas = true; + return false; + } } } if(w.callback) - setTimeout( function(){ w.callback( w.value, that, node, pos ); }, 20 ); + setTimeout( (function(){ this.callback( this.value, that, node, pos ); }).bind(w), 20 ); this.dirty_canvas = true; break; + case "toggle": + if( event.type == "mousedown" ) + { + w.value = !w.value; + if(w.callback) + setTimeout( function(){ w.callback( w.value, that, node, pos ); }, 20 ); + } + break; + case "string": + case "text": + if( event.type == "mousedown" ) + this.prompt( "Value", w.value, (function(v){ this.value = v; if(w.callback) w.callback(v, that, node ); }).bind(w), event ); + break; + default: + if( w.mouse ) + w.mouse( ctx, event, [x,y], node ); + break; } return w; @@ -6085,8 +7019,7 @@ LGraphCanvas.prototype.drawGroups = function(canvas, ctx) var groups = this.graph._groups; ctx.save(); - ctx.globalAlpha = 0.5; - ctx.font = "24px Arial"; + ctx.globalAlpha = 0.5 * this.editor_alpha; for(var i = 0; i < groups.length; ++i) { @@ -6099,11 +7032,11 @@ LGraphCanvas.prototype.drawGroups = function(canvas, ctx) ctx.strokeStyle = group.color || "#335"; var pos = group._pos; var size = group._size; - ctx.globalAlpha = 0.25; + ctx.globalAlpha = 0.25 * this.editor_alpha; ctx.beginPath(); ctx.rect( pos[0] + 0.5, pos[1] + 0.5, size[0], size[1] ); ctx.fill(); - ctx.globalAlpha = 1; + ctx.globalAlpha = this.editor_alpha;; ctx.stroke(); ctx.beginPath(); @@ -6112,12 +7045,23 @@ LGraphCanvas.prototype.drawGroups = function(canvas, ctx) ctx.lineTo( pos[0] + size[0], pos[1] + size[1] - 10 ); ctx.fill(); - ctx.fillText( group.title, pos[0] + 4, pos[1] + 24 ); + var font_size = (group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE); + ctx.font = font_size + "px Arial"; + ctx.fillText( group.title, pos[0] + 4, pos[1] + font_size ); } ctx.restore(); } +LGraphCanvas.prototype.adjustNodesSize = function() +{ + var nodes = this.graph._nodes; + for(var i = 0; i < nodes.length; ++i) + nodes[i].size = nodes[i].computeSize(); + this.setDirty(true,true); +} + + /** * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode * @method resize @@ -6227,7 +7171,7 @@ LGraphCanvas.onGroupAdd = function(info,entry,mouse_event) var ref_window = canvas.getCanvasWindow(); var group = new LiteGraph.LGraphGroup(); - group.pos = canvas.convertEventToCanvas( mouse_event ); + group.pos = canvas.convertEventToCanvasOffset( mouse_event ); canvas.graph.add( group ); } @@ -6242,6 +7186,7 @@ LGraphCanvas.onMenuAdd = function( node, options, e, prev_menu ) if(values[i]) entries.push({ value: values[i], content: values[i], has_submenu: true }); + //show categories var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window); function inner_clicked( v, option, e ) @@ -6250,7 +7195,8 @@ LGraphCanvas.onMenuAdd = function( node, options, e, prev_menu ) var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); var values = []; for(var i in node_types) - values.push( { content: node_types[i].title, value: node_types[i].type }); + if (!node_types[i].skip_list) + values.push( { content: node_types[i].title, value: node_types[i].type }); new LiteGraph.ContextMenu( values, {event: e, callback: inner_create, parentMenu: menu }, ref_window); return false; @@ -6262,7 +7208,7 @@ LGraphCanvas.onMenuAdd = function( node, options, e, prev_menu ) var node = LiteGraph.createNode( v.value ); if(node) { - node.pos = canvas.convertEventToCanvas( first_event ); + node.pos = canvas.convertEventToCanvasOffset( first_event ); canvas.graph.add( node ); } } @@ -6462,18 +7408,42 @@ LGraphCanvas.onResizeNode = function( value, options, e, menu, node ) node.setDirtyCanvas(true,true); } +LGraphCanvas.prototype.showLinkMenu = function( link, e ) +{ + var that = this; -LGraphCanvas.onShowTitleEditor = function( value, options, e, menu, node ) + new LiteGraph.ContextMenu(["Delete"], { event: e, callback: inner_clicked }); + + function inner_clicked(v) + { + switch(v) + { + case "Delete": that.graph.removeLink( link.id ); break; + default: + } + } + + return false; +} + +LGraphCanvas.onShowPropertyEditor = function( item, options, e, menu, node ) { var input_html = ""; + var property = item.property || "title"; + var value = node[ property ]; var dialog = document.createElement("div"); dialog.className = "graphdialog"; - dialog.innerHTML = "<span class='name'>Title</span><input autofocus type='text' class='value'/><button>OK</button>"; + dialog.innerHTML = "<span class='name'></span><input autofocus type='text' class='value'/><button>OK</button>"; + var title = dialog.querySelector(".name"); + title.innerText = property; var input = dialog.querySelector("input"); if(input) { - input.value = node.title; + input.value = value; + input.addEventListener("blur", function(e){ + this.focus(); + }); input.addEventListener("keydown", function(e){ if(e.keyCode != 13) return; @@ -6497,8 +7467,8 @@ LGraphCanvas.onShowTitleEditor = function( value, options, e, menu, node ) if( event ) { - dialog.style.left = (event.pageX + offsetx) + "px"; - dialog.style.top = (event.pageY + offsety)+ "px"; + dialog.style.left = (event.clientX + offsetx) + "px"; + dialog.style.top = (event.clientY + offsety)+ "px"; } else { @@ -6517,28 +7487,147 @@ LGraphCanvas.onShowTitleEditor = function( value, options, e, menu, node ) function setValue(value) { - node.title = value; - dialog.parentNode.removeChild( dialog ); + if( item.type == "Number" ) + value = Number(value); + else if( item.type == "Boolean" ) + value = Boolean(value); + node[ property ] = value; + if(dialog.parentNode) + dialog.parentNode.removeChild( dialog ); node.setDirtyCanvas(true,true); } } +LGraphCanvas.prototype.prompt = function( title, value, callback, event ) +{ + var that = this; + var input_html = ""; + title = title || ""; + + var modified = false; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog rounded"; + dialog.innerHTML = "<span class='name'></span> <input autofocus type='text' class='value'/><button class='rounded'>OK</button>"; + dialog.close = function() + { + that.prompt_box = null; + if(dialog.parentNode) + dialog.parentNode.removeChild( dialog ); + } + + if(this.ds.scale > 1) + dialog.style.transform = "scale("+this.ds.scale+")"; + + dialog.addEventListener("mouseleave",function(e){ + if(!modified) + dialog.close(); + }); + + if(that.prompt_box) + that.prompt_box.close(); + that.prompt_box = dialog; + + var first = null; + var timeout = null; + var selected = null; + + var name_element = dialog.querySelector(".name"); + name_element.innerText = title; + var value_element = dialog.querySelector(".value"); + value_element.value = value; + + var input = dialog.querySelector("input"); + input.addEventListener("keydown", function(e){ + modified = true; + if(e.keyCode == 27) //ESC + dialog.close(); + else if(e.keyCode == 13) + { + if( callback ) + callback( this.value ); + dialog.close(); + } + else + return; + e.preventDefault(); + e.stopPropagation(); + }); + + var button = dialog.querySelector("button"); + button.addEventListener("click", function(e){ + if( callback ) + callback( input.value ); + that.setDirty(true); + dialog.close(); + }); + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if(rect) + { + offsetx -= rect.left; + offsety -= rect.top; + } + + if( event ) + { + dialog.style.left = (event.clientX + offsetx) + "px"; + dialog.style.top = (event.clientY + offsety)+ "px"; + } + else + { + dialog.style.left = (canvas.width * 0.5 + offsetx) + "px"; + dialog.style.top = (canvas.height * 0.5 + offsety) + "px"; + } + + canvas.parentNode.appendChild( dialog ); + setTimeout( function(){ input.focus(); },10 ); + + return dialog; +} + + +LGraphCanvas.search_limit = -1; LGraphCanvas.prototype.showSearchBox = function(event) { var that = this; var input_html = ""; var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = "<span class='name'>Search</span> <input autofocus type='text' class='value'/><div class='helper'></div>"; + dialog.className = "litegraph litesearchbox graphdialog rounded"; + dialog.innerHTML = "<span class='name'>Search</span> <input autofocus type='text' class='value rounded'/><div class='helper'></div>"; dialog.close = function() { that.search_box = null; - dialog.parentNode.removeChild( dialog ); + document.body.focus(); + setTimeout( function(){ that.canvas.focus(); },20 ); //important, if canvas loses focus keys wont be captured + if(dialog.parentNode) + dialog.parentNode.removeChild( dialog ); } + var timeout_close = null; + + if(this.ds.scale > 1) + dialog.style.transform = "scale("+this.ds.scale+")"; + + dialog.addEventListener("mouseenter",function(e){ + if(timeout_close) + { + clearTimeout(timeout_close); + timeout_close = null; + } + }); + dialog.addEventListener("mouseleave",function(e){ - dialog.close(); + //dialog.close(); + timeout_close = setTimeout(function(){ + dialog.close(); + },500); }); if(that.search_box) @@ -6554,6 +7643,9 @@ LGraphCanvas.prototype.showSearchBox = function(event) var input = dialog.querySelector("input"); if(input) { + input.addEventListener("blur", function(e){ + this.focus(); + }); input.addEventListener("keydown", function(e){ if(e.keyCode == 38) //UP @@ -6567,7 +7659,7 @@ LGraphCanvas.prototype.showSearchBox = function(event) if(selected) select( selected.innerHTML ) else if(first) - select(first); + select( first ); else dialog.close(); } @@ -6597,8 +7689,8 @@ LGraphCanvas.prototype.showSearchBox = function(event) if( event ) { - dialog.style.left = (event.pageX + offsetx) + "px"; - dialog.style.top = (event.pageY + offsety)+ "px"; + dialog.style.left = (event.clientX + offsetx) + "px"; + dialog.style.top = (event.clientY + offsety)+ "px"; } else { @@ -6617,12 +7709,39 @@ LGraphCanvas.prototype.showSearchBox = function(event) that.onSearchBoxSelection( name, event, graphcanvas ); else { + var extra = LiteGraph.searchbox_extras[ name ]; + if( extra ) + name = extra.type; + var node = LiteGraph.createNode( name ); if(node) { - node.pos = graphcanvas.convertEventToCanvas( event ); + node.pos = graphcanvas.convertEventToCanvasOffset( event ); graphcanvas.graph.add( node ); } + + if( extra && extra.data ) + { + if(extra.data.properties) + for(var i in extra.data.properties) + node.addProperty( extra.data.properties[i][0], extra.data.properties[i][0] ); + if(extra.data.inputs) + { + node.inputs = []; + for(var i in extra.data.inputs) + node.addOutput( extra.data.inputs[i][0],extra.data.inputs[i][1] ); + } + if(extra.data.outputs) + { + node.outputs = []; + for(var i in extra.data.outputs) + node.addOutput( extra.data.outputs[i][0],extra.data.outputs[i][1] ); + } + if(extra.data.title) + node.title = extra.data.title; + if(extra.data.json) + node.configure( extra.data.json ); + } } } @@ -6648,30 +7767,73 @@ LGraphCanvas.prototype.showSearchBox = function(event) selected.scrollIntoView(); } - function refreshHelper() - { - timeout = null; - var str = input.value; - first = null; - helper.innerHTML = ""; - if(!str) - return; + function refreshHelper() { + timeout = null; + var str = input.value; + first = null; + helper.innerHTML = ""; + if (!str) + return; - if( that.onSearchBox ) - that.onSearchBox( help, str, graphcanvas ); - else - for( var i in LiteGraph.registered_node_types ) - if(i.indexOf(str) != -1) + if (that.onSearchBox) { + var list = that.onSearchBox( help, str, graphcanvas ); + if(list) + for( var i = 0; i < list.length; ++i ) + addResult( list[i] ); + } else { + var c = 0; + str = str.toLowerCase(); + //extras + for(var i in LiteGraph.searchbox_extras) + { + var extra = LiteGraph.searchbox_extras[i]; + if( extra.desc.toLowerCase().indexOf(str) === -1 ) + continue; + addResult( extra.desc, "searchbox_extra" ); + if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) + break; + } + + if(Array.prototype.filter)//filter supported + { + //types + var keys = Object.keys( LiteGraph.registered_node_types ); + var filtered = keys.filter(function (item) { + return item.toLowerCase().indexOf(str) !== -1; + }); + for(var i = 0; i < filtered.length; i++) { - var help = document.createElement("div"); - if(!first) first = i; - help.innerText = i; - help.className = "help-item"; - help.addEventListener("click", function(e){ - select( this.innerText ); - }); - helper.appendChild(help); + addResult(filtered[i]); + if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit) + break; } + } else { + for (var i in LiteGraph.registered_node_types) + { + if (i.indexOf(str) != -1) { + addResult(i); + if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit) + break; + } + } + } + } + + function addResult( type, className ) + { + var help = document.createElement("div"); + if (!first) + first = type; + help.innerText = type; + help.dataset["type"] = escape(type); + help.className = "litegraph lite-search-item"; + if( className ) + help.className += " " + className; + help.addEventListener("click", function (e) { + select( unescape( this.dataset["type"] ) ); + }); + helper.appendChild(help); + } } return dialog; @@ -6765,6 +7927,9 @@ LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options var input = dialog.querySelector("input"); if(input) { + input.addEventListener("blur", function(e){ + this.focus(); + }); input.value = node.properties[ property ] !== undefined ? node.properties[ property ] : ""; input.addEventListener("keydown", function(e){ if(e.keyCode != 13) @@ -6824,8 +7989,8 @@ LGraphCanvas.prototype.createDialog = function( html, options ) } else if( options.event ) { - offsetx += options.event.pageX; - offsety += options.event.pageY; + offsetx += options.event.clientX; + offsety += options.event.clientY; } else //centered { @@ -6989,7 +8154,7 @@ LGraphCanvas.prototype.getCanvasMenuOptions = function() ]; if(this._graph_stack && this._graph_stack.length > 0) - options = [{content:"Close subgraph", callback: this.closeSubgraph.bind(this) },null].concat(options); + options.push(null,{content:"Close subgraph", callback: this.closeSubgraph.bind(this) }); } if(this.getExtraMenuOptions) @@ -7016,7 +8181,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function( node ) null, {content:"Properties", has_submenu: true, callback: LGraphCanvas.onShowMenuNodeProperties }, null, - {content:"Title", callback: LGraphCanvas.onShowTitleEditor }, + {content:"Title", callback: LGraphCanvas.onShowPropertyEditor }, {content:"Mode", has_submenu: true, callback: LGraphCanvas.onMenuNodeMode }, {content:"Resize", callback: LGraphCanvas.onResizeNode }, {content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse }, @@ -7026,6 +8191,20 @@ LGraphCanvas.prototype.getNodeMenuOptions = function( node ) null ]; + if(node.onGetInputs) + { + var inputs = node.onGetInputs(); + if(inputs && inputs.length) + options[0].disabled = false; + } + + if(node.onGetOutputs) + { + var outputs = node.onGetOutputs(); + if(outputs && outputs.length ) + options[1].disabled = false; + } + if(node.getExtraMenuOptions) { var extra = node.getExtraMenuOptions(this); @@ -7041,20 +8220,6 @@ LGraphCanvas.prototype.getNodeMenuOptions = function( node ) if( node.removable !== false ) options.push(null,{content:"Remove", callback: LGraphCanvas.onMenuNodeRemove }); - if(node.onGetInputs) - { - var inputs = node.onGetInputs(); - if(inputs && inputs.length) - options[0].disabled = false; - } - - if(node.onGetOutputs) - { - var outputs = node.onGetOutputs(); - if(outputs && outputs.length ) - options[1].disabled = false; - } - if(node.graph && node.graph.onGetNodeMenuOptions ) node.graph.onGetNodeMenuOptions( options, node ); @@ -7064,8 +8229,9 @@ LGraphCanvas.prototype.getNodeMenuOptions = function( node ) LGraphCanvas.prototype.getGroupMenuOptions = function( node ) { var o = [ - {content:"Title", callback: LGraphCanvas.onShowTitleEditor }, + {content:"Title", callback: LGraphCanvas.onShowPropertyEditor }, {content:"Color", has_submenu: true, callback: LGraphCanvas.onMenuNodeColors }, + {content:"Font size", property: "font_size", type:"Number", callback: LGraphCanvas.onShowPropertyEditor }, null, {content:"Remove", callback: LGraphCanvas.onMenuNodeRemove } ]; @@ -7080,7 +8246,7 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) var ref_window = canvas.getCanvasWindow(); var menu_info = null; - var options = { event: event, callback: inner_option_clicked, node: node }; + var options = { event: event, callback: inner_option_clicked, extra: node }; //check if mouse is in input var slot = null; @@ -7093,8 +8259,10 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) if(slot) //on slot { menu_info = []; + if(slot && slot.output && slot.output.links && slot.output.links.length) + menu_info.push( { content: "Disconnect Links", slot: slot } ); menu_info.push( slot.locked ? "Cannot remove" : { content: "Remove Slot", slot: slot } ); - menu_info.push( { content: "Rename Slot", slot: slot } ); + menu_info.push( slot.nameLocked ? "Cannot rename" : { content: "Rename Slot", slot: slot } ); options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; if(slot.input && slot.input.type == LiteGraph.ACTION) options.title = "Action"; @@ -7107,14 +8275,10 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) menu_info = this.getNodeMenuOptions(node); else { + menu_info = this.getCanvasMenuOptions(); var group = this.graph.getGroupOnPos( event.canvasX, event.canvasY ); if( group ) //on group - { - options.node = group; - menu_info = this.getGroupMenuOptions( group ); - } - else - menu_info = this.getCanvasMenuOptions(); + menu_info.push(null,{content:"Edit Group", has_submenu: true, submenu: { title:"Group", extra: group, options: this.getGroupMenuOptions( group ) }}); } } @@ -7138,15 +8302,27 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) node.removeOutput( info.slot ); return; } + else if(v.content == "Disconnect Links") + { + var info = v.slot; + if(info.output) + node.disconnectOutput( info.slot ); + else if(info.input) + node.disconnectInput( info.slot ); + return; + } else if( v.content == "Rename Slot") { var info = v.slot; - var dialog = that.createDialog( "<span class='name'>Name</span><input type='text'/><button>OK</button>" , options ); + var slot_info = info.input ? node.getInputInfo( info.slot ) : node.getOutputInfo( info.slot ); + var dialog = that.createDialog( "<span class='name'>Name</span><input autofocus type='text'/><button>OK</button>" , options ); var input = dialog.querySelector("input"); + if(input && slot_info){ + input.value = slot_info.label || ""; + } dialog.querySelector("button").addEventListener("click",function(e){ if(input.value) { - var slot_info = info.input ? node.getInputInfo( info.slot ) : node.getOutputInfo( info.slot ); if( slot_info ) slot_info.label = input.value; that.setDirty(true); @@ -7344,6 +8520,8 @@ function ContextMenu( values, options ) var root = document.createElement("div"); root.className = "litegraph litecontextmenu litemenubar-panel"; + if( options.className) + root.className += " " + options.className; root.style.minWidth = 100; root.style.minHeight = 100; root.style.pointerEvents = "none"; @@ -7371,11 +8549,14 @@ function ContextMenu( values, options ) function on_mouse_wheel(e) { var pos = parseInt( root.style.top ); - root.style.top = (pos + e.deltaY * 0.1).toFixed() + "px"; + root.style.top = (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; e.preventDefault(); return true; } + if(!options.scroll_speed) + options.scroll_speed = 0.1; + root.addEventListener("wheel", on_mouse_wheel, true); root.addEventListener("mousewheel", on_mouse_wheel, true); @@ -7407,7 +8588,15 @@ function ContextMenu( values, options ) root.addEventListener("mouseleave", function(e) { if(that.lock) return; - that.close(e); + if(root.closing_timer) + clearTimeout( root.closing_timer ); + root.closing_timer = setTimeout( that.close.bind(that, e), 500 ); + //that.close(e); + }); + + root.addEventListener("mouseenter", function(e) { + if(root.closing_timer) + clearTimeout( root.closing_timer ); }); //insert before checking position @@ -7424,8 +8613,8 @@ function ContextMenu( values, options ) var top = options.top || 0; if(options.event) { - left = (options.event.pageX - 10); - top = (options.event.pageY - 10); + left = (options.event.clientX - 10); + top = (options.event.clientY - 10); if(options.title) top -= 20; @@ -7446,6 +8635,9 @@ function ContextMenu( values, options ) root.style.left = left + "px"; root.style.top = top + "px"; + + if(options.scale) + root.style.transform = "scale("+options.scale+")"; } ContextMenu.prototype.addItem = function( name, value, options ) @@ -7503,6 +8695,7 @@ ContextMenu.prototype.addItem = function( name, value, options ) var value = this.value; if(!value || !value.has_submenu) return; + //if it is a submenu, autoopen like the item was clicked inner_onclick.call(this,e); } @@ -7527,7 +8720,7 @@ ContextMenu.prototype.addItem = function( name, value, options ) { if (value.callback && !options.ignore_item_callbacks && value.disabled !== true ) //item callback { - var r = value.callback.call( this, value, options, e, that, options.node ); + var r = value.callback.call( this, value, options, e, that, options.extra ); if(r === true) close_parent = false; } @@ -7541,6 +8734,7 @@ ContextMenu.prototype.addItem = function( name, value, options ) parentMenu: that, ignore_item_callbacks: value.submenu.ignore_item_callbacks, title: value.submenu.title, + extra: value.submenu.extra, autoopen: options.autoopen }); close_parent = false; @@ -7571,10 +8765,13 @@ ContextMenu.prototype.close = function(e, ignore_parent_menu) } if(this.current_submenu) this.current_submenu.close(e, true); + + if(this.root.closing_timer) + clearTimeout( this.root.closing_timer ); } //this code is used to trigger events easily (used in the context menu mouseleave -ContextMenu.trigger = function(element, event_name, params, origin) +ContextMenu.trigger = function( element, event_name, params, origin ) { var evt = document.createEvent( 'CustomEvent' ); evt.initCustomEvent( event_name, true,true, params ); //canBubble, cancelable, detail @@ -7606,8 +8803,8 @@ ContextMenu.prototype.getFirstEvent = function() ContextMenu.isCursorOverElement = function( event, element ) { - var left = event.pageX; - var top = event.pageY; + var left = event.clientX; + var top = event.clientY; var rect = element.getBoundingClientRect(); if(!rect) return false; diff --git a/src/litegraph.js b/src/litegraph.js index 1372a41a7..6995a89cb 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -83,7 +83,7 @@ var LiteGraph = global.LiteGraph = { debug: false, catch_exceptions: true, throw_errors: true, - allow_scripts: false, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits registered_node_types: {}, //nodetypes by string node_types_by_file_extension: {}, //used for droping files in the canvas Nodes: {}, //node types by classname @@ -165,8 +165,9 @@ var LiteGraph = global.LiteGraph = { * @param {Function} func * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable */ - wrapFunctionAsNode: function( name, func, param_types, return_type ) + wrapFunctionAsNode: function( name, func, param_types, return_type, properties ) { var params = Array(func.length); var code = ""; @@ -174,6 +175,8 @@ var LiteGraph = global.LiteGraph = { for(var i = 0; i < names.length; ++i) code += "this.addInput('"+names[i]+"',"+(param_types && param_types[i] ? "'" + param_types[i] + "'" : "0") + ");\n"; code += "this.addOutput('out',"+( return_type ? "'" + return_type + "'" : 0 )+");\n"; + if(properties) + code += "this.properties = " + JSON.stringify(properties) + ";\n"; var classobj = Function(code); classobj.title = name.split("/").pop(); classobj.desc = "Generated from " + func.name; @@ -516,8 +519,8 @@ LGraph.prototype.clear = function() this.catch_errors = true; //subgraph_data - this.global_inputs = {}; - this.global_outputs = {}; + this.inputs = {}; + this.outputs = {}; //notify canvas to redraw this.change(); @@ -1252,16 +1255,20 @@ LGraph.prototype.getGroupOnPos = function(x,y) * @param {String} type * @param {*} value [optional] */ -LGraph.prototype.addGlobalInput = function(name, type, value) +LGraph.prototype.addInput = function(name, type, value) { - this.global_inputs[name] = { name: name, type: type, value: value }; + var input = this.inputs[ name ]; + if( input ) //already exist + return; + + this.inputs[ name ] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalInputAdded) - this.onGlobalInputAdded(name, type); + if(this.onInputAdded) + this.onInputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** @@ -1270,32 +1277,23 @@ LGraph.prototype.addGlobalInput = function(name, type, value) * @param {String} name * @param {*} data */ -LGraph.prototype.setGlobalInputData = function(name, data) +LGraph.prototype.setInputData = function(name, data) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return; input.value = data; } -/** -* Assign a data to the global graph input (same as setGlobalInputData) -* @method setInputData -* @param {String} name -* @param {*} data -*/ -LGraph.prototype.setInputData = LGraph.prototype.setGlobalInputData; - - /** * Returns the current value of a global graph input -* @method getGlobalInputData +* @method getInputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalInputData = function(name) +LGraph.prototype.getInputData = function(name) { - var input = this.global_inputs[name]; + var input = this.inputs[name]; if (!input) return null; return input.value; @@ -1303,105 +1301,105 @@ LGraph.prototype.getGlobalInputData = function(name) /** * Changes the name of a global graph input -* @method renameGlobalInput +* @method renameInput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalInput = function(old_name, name) +LGraph.prototype.renameInput = function(old_name, name) { if(name == old_name) return; - if(!this.global_inputs[old_name]) + if(!this.inputs[old_name]) return false; - if(this.global_inputs[name]) + if(this.inputs[name]) { console.error("there is already one input with that name"); return false; } - this.global_inputs[name] = this.global_inputs[old_name]; - delete this.global_inputs[old_name]; + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; this._version++; - if(this.onGlobalInputRenamed) - this.onGlobalInputRenamed(old_name, name); + if(this.onInputRenamed) + this.onInputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph input -* @method changeGlobalInputType +* @method changeInputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalInputType = function(name, type) +LGraph.prototype.changeInputType = function(name, type) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - if(this.global_inputs[name].type && this.global_inputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.inputs[name].type && this.inputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_inputs[name].type = type; + this.inputs[name].type = type; this._version++; - if(this.onGlobalInputTypeChanged) - this.onGlobalInputTypeChanged(name, type); + if(this.onInputTypeChanged) + this.onInputTypeChanged(name, type); } /** * Removes a global graph input -* @method removeGlobalInput +* @method removeInput * @param {String} name * @param {String} type */ -LGraph.prototype.removeGlobalInput = function(name) +LGraph.prototype.removeInput = function(name) { - if(!this.global_inputs[name]) + if(!this.inputs[name]) return false; - delete this.global_inputs[name]; + delete this.inputs[name]; this._version++; - if(this.onGlobalInputRemoved) - this.onGlobalInputRemoved(name); + if(this.onInputRemoved) + this.onInputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } /** * Creates a global graph output -* @method addGlobalOutput +* @method addOutput * @param {String} name * @param {String} type * @param {*} value */ -LGraph.prototype.addGlobalOutput = function(name, type, value) +LGraph.prototype.addOutput = function(name, type, value) { - this.global_outputs[name] = { name: name, type: type, value: value }; + this.outputs[name] = { name: name, type: type, value: value }; this._version++; - if(this.onGlobalOutputAdded) - this.onGlobalOutputAdded(name, type); + if(this.onOutputAdded) + this.onOutputAdded(name, type); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Assign a data to the global output -* @method setGlobalOutputData +* @method setOutputData * @param {String} name * @param {String} value */ -LGraph.prototype.setGlobalOutputData = function(name, value) +LGraph.prototype.setOutputData = function(name, value) { - var output = this.global_outputs[ name ]; + var output = this.outputs[ name ]; if (!output) return; output.value = value; @@ -1409,92 +1407,83 @@ LGraph.prototype.setGlobalOutputData = function(name, value) /** * Returns the current value of a global graph output -* @method getGlobalOutputData +* @method getOutputData * @param {String} name * @return {*} the data */ -LGraph.prototype.getGlobalOutputData = function(name) +LGraph.prototype.getOutputData = function(name) { - var output = this.global_outputs[name]; + var output = this.outputs[name]; if (!output) return null; return output.value; } -/** -* Returns the current value of a global graph output (sames as getGlobalOutputData) -* @method getOutputData -* @param {String} name -* @return {*} the data -*/ -LGraph.prototype.getOutputData = LGraph.prototype.getGlobalOutputData; - - /** * Renames a global graph output -* @method renameGlobalOutput +* @method renameOutput * @param {String} old_name * @param {String} new_name */ -LGraph.prototype.renameGlobalOutput = function(old_name, name) +LGraph.prototype.renameOutput = function(old_name, name) { - if(!this.global_outputs[old_name]) + if(!this.outputs[old_name]) return false; - if(this.global_outputs[name]) + if(this.outputs[name]) { console.error("there is already one output with that name"); return false; } - this.global_outputs[name] = this.global_outputs[old_name]; - delete this.global_outputs[old_name]; + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; this._version++; - if(this.onGlobalOutputRenamed) - this.onGlobalOutputRenamed(old_name, name); + if(this.onOutputRenamed) + this.onOutputRenamed(old_name, name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); } /** * Changes the type of a global graph output -* @method changeGlobalOutputType +* @method changeOutputType * @param {String} name * @param {String} type */ -LGraph.prototype.changeGlobalOutputType = function(name, type) +LGraph.prototype.changeOutputType = function(name, type) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - if(this.global_outputs[name].type && this.global_outputs[name].type.toLowerCase() == type.toLowerCase() ) + if(this.outputs[name].type && this.outputs[name].type.toLowerCase() == type.toLowerCase() ) return; - this.global_outputs[name].type = type; + this.outputs[name].type = type; this._version++; - if(this.onGlobalOutputTypeChanged) - this.onGlobalOutputTypeChanged(name, type); + if(this.onOutputTypeChanged) + this.onOutputTypeChanged(name, type); } /** * Removes a global graph output -* @method removeGlobalOutput +* @method removeOutput * @param {String} name */ -LGraph.prototype.removeGlobalOutput = function(name) +LGraph.prototype.removeOutput = function(name) { - if(!this.global_outputs[name]) + if(!this.outputs[name]) return false; - delete this.global_outputs[name]; + delete this.outputs[name]; this._version++; - if(this.onGlobalOutputRemoved) - this.onGlobalOutputRemoved(name); + if(this.onOutputRemoved) + this.onOutputRemoved(name); - if(this.onGlobalsChange) - this.onGlobalsChange(); + if(this.onInputsOutputsChange) + this.onInputsOutputsChange(); return true; } @@ -1807,7 +1796,7 @@ LiteGraph.LLink = LLink; + collapsed: if it is collapsed supported callbacks: - + onAdded: when added to graph + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + onRemoved: when removed from graph + onStart: when the graph starts playing + onStop: when the graph stops playing @@ -1826,6 +1815,7 @@ LiteGraph.LLink = LLink; + onDblClick: double clicked in the node + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + onSelected + onDeselected @@ -7428,6 +7418,8 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) var input_html = ""; title = title || ""; + var modified = false; + var dialog = document.createElement("div"); dialog.className = "graphdialog rounded"; dialog.innerHTML = " "; @@ -7442,7 +7434,8 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) dialog.style.transform = "scale("+this.ds.scale+")"; dialog.addEventListener("mouseleave",function(e){ - dialog.close(); + if(!modified) + dialog.close(); }); if(that.prompt_box) @@ -7460,6 +7453,7 @@ LGraphCanvas.prototype.prompt = function( title, value, callback, event ) var input = dialog.querySelector("input"); input.addEventListener("keydown", function(e){ + modified = true; if(e.keyCode == 27) //ESC dialog.close(); else if(e.keyCode == 13) @@ -8238,7 +8232,7 @@ LGraphCanvas.prototype.processContextMenu = function( node, event ) var dialog = that.createDialog( "Name" , options ); var input = dialog.querySelector("input"); if(input && slot_info){ - input.value = slot_info.label; + input.value = slot_info.label || ""; } dialog.querySelector("button").addEventListener("click",function(e){ if(input.value) diff --git a/src/nodes/base.js b/src/nodes/base.js index 25331735e..563e23cdb 100755 --- a/src/nodes/base.js +++ b/src/nodes/base.js @@ -26,20 +26,23 @@ function Subgraph() { var that = this; this.size = [140,80]; + this.properties = { enabled: true }; + this.addInput("enabled","boolean"); //create inner graph this.subgraph = new LGraph(); this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = true; - this.subgraph.onGlobalInputAdded = this.onSubgraphNewGlobalInput.bind(this); - this.subgraph.onGlobalInputRenamed = this.onSubgraphRenamedGlobalInput.bind(this); - this.subgraph.onGlobalInputTypeChanged = this.onSubgraphTypeChangeGlobalInput.bind(this); - - this.subgraph.onGlobalOutputAdded = this.onSubgraphNewGlobalOutput.bind(this); - this.subgraph.onGlobalOutputRenamed = this.onSubgraphRenamedGlobalOutput.bind(this); - this.subgraph.onGlobalOutputTypeChanged = this.onSubgraphTypeChangeGlobalOutput.bind(this); + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } Subgraph.title = "Subgraph"; @@ -69,7 +72,6 @@ Subgraph.prototype.onDblClick = function(e,pos,graphcanvas) setTimeout(function(){ graphcanvas.openSubgraph( that.subgraph ); },10 ); } - Subgraph.prototype.onMouseDown = function(e,pos,graphcanvas) { if( !this.flags.collapsed && pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) @@ -79,13 +81,43 @@ Subgraph.prototype.onMouseDown = function(e,pos,graphcanvas) } } -Subgraph.prototype.onSubgraphNewGlobalInput = function(name, type) +Subgraph.prototype.onExecute = function() { - //add input to the node - this.addInput(name, type); + if( !this.getInputOrProperty("enabled") ) + return; + + //send inputs to subgraph global inputs + if(this.inputs) + for(var i = 0; i < this.inputs.length; i++) + { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData( input.name, value ); + } + + //execute + this.subgraph.runStep(); + + //send subgraph global outputs to outputs + if(this.outputs) + for(var i = 0; i < this.outputs.length; i++) + { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData( output.name ); + this.setOutputData(i, value); + } } -Subgraph.prototype.onSubgraphRenamedGlobalInput = function(oldname, name) +//**** INPUTS *********************************** +Subgraph.prototype.onSubgraphNewInput = function(name, type) +{ + //add input to the node + var slot = this.findInputSlot(name); + if(slot == -1) + this.addInput(name, type); +} + +Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { var slot = this.findInputSlot( oldname ); if(slot == -1) @@ -94,7 +126,7 @@ Subgraph.prototype.onSubgraphRenamedGlobalInput = function(oldname, name) info.name = name; } -Subgraph.prototype.onSubgraphTypeChangeGlobalInput = function(name, type) +Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { var slot = this.findInputSlot( name ); if(slot == -1) @@ -103,15 +135,23 @@ Subgraph.prototype.onSubgraphTypeChangeGlobalInput = function(name, type) info.type = type; } - -Subgraph.prototype.onSubgraphNewGlobalOutput = function(name, type) +Subgraph.prototype.onSubgraphRemovedInput = function(name) { - //add output to the node - this.addOutput(name, type); + var slot = this.findInputSlot( name ); + if(slot == -1) + return; + this.removeInput(slot); } +//**** OUTPUTS *********************************** +Subgraph.prototype.onSubgraphNewOutput = function(name, type) +{ + var slot = this.findOutputSlot(name); + if(slot == -1) + this.addOutput(name, type); +} -Subgraph.prototype.onSubgraphRenamedGlobalOutput = function(oldname, name) +Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { var slot = this.findOutputSlot( oldname ); if(slot == -1) @@ -120,7 +160,7 @@ Subgraph.prototype.onSubgraphRenamedGlobalOutput = function(oldname, name) info.name = name; } -Subgraph.prototype.onSubgraphTypeChangeGlobalOutput = function(name, type) +Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { var slot = this.findOutputSlot( name ); if(slot == -1) @@ -129,6 +169,14 @@ Subgraph.prototype.onSubgraphTypeChangeGlobalOutput = function(name, type) info.type = type; } +Subgraph.prototype.onSubgraphRemovedOutput = function(name) +{ + var slot = this.findInputSlot( name ); + if(slot == -1) + return; + this.removeOutput(slot); +} +// ***************************************************** Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { @@ -145,42 +193,13 @@ Subgraph.prototype.onResize = function(size) size[1] += 20; } -Subgraph.prototype.onExecute = function() -{ - //send inputs to subgraph global inputs - if(this.inputs) - for(var i = 0; i < this.inputs.length; i++) - { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setGlobalInputData( input.name, value ); - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if(this.outputs) - for(var i = 0; i < this.outputs.length; i++) - { - var output = this.outputs[i]; - var value = this.subgraph.getGlobalOutputData( output.name ); - this.setOutputData(i, value); - } -} - -Subgraph.prototype.configure = function(o) -{ - LGraphNode.prototype.configure.call(this, o); - //this.subgraph.configure(o.graph); -} - Subgraph.prototype.serialize = function() { var data = LGraphNode.prototype.serialize.call(this); data.subgraph = this.subgraph.serialize(); return data; } +//no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() Subgraph.prototype.clone = function() { @@ -193,38 +212,31 @@ Subgraph.prototype.clone = function() return node; } - LiteGraph.registerNodeType("graph/subgraph", Subgraph ); //Input for a subgraph -function GlobalInput() +function GraphInput() { + this.addOutput("",""); - //random name to avoid problems with other outputs when added - var input_name = "input_" + (Math.random()*1000).toFixed(); - - this.addOutput(input_name, null ); - - this.properties = { name: input_name, type: null }; - + this.name_in_graph = ""; + this.properties = {}; var that = this; Object.defineProperty( this.properties, "name", { get: function() { - return input_name; + return that.name_in_graph; }, set: function(v) { - if(v == "") + if( v == "" || v == that.name_in_graph || v == "enabled" ) return; - - var info = that.getOutputInfo(0); - if(info.name == v) - return; - info.name = v; - if(that.graph) - that.graph.renameGlobalInput(input_name, v); - input_name = v; + if(that.name_in_graph) //already added + that.graph.renameInput( that.name_in_graph, v ); + else + that.graph.addInput( v, that.properties.type ); + that.name_widget.value = v; + that.name_in_graph = v; }, enumerable: true }); @@ -232,103 +244,132 @@ function GlobalInput() Object.defineProperty( this.properties, "type", { get: function() { return that.outputs[0].type; }, set: function(v) { - that.outputs[0].type = v; - if(that.graph) - that.graph.changeGlobalInputType(input_name, that.outputs[0].type); + that.outputs[0].type = v; + if(that.name_in_graph) //already added + that.graph.changeInputType( that.name_in_graph, that.outputs[0].type); + that.type_widget.value = v; }, enumerable: true }); + + this.name_widget = this.addWidget("text","Name", this.properties.name, function(v){ + if(!v) return; + that.properties.name = v; + }); + this.type_widget = this.addWidget("text","Type", this.properties.type, function(v){ + v = v || ""; + that.properties.type = v; + }); + + this.widgets_up = true; + this.size = [180,60]; } -GlobalInput.title = "Input"; -GlobalInput.desc = "Input of the graph"; +GraphInput.title = "Input"; +GraphInput.desc = "Input of the graph"; -//When added to graph tell the graph this is a new global input -GlobalInput.prototype.onAdded = function() +GraphInput.prototype.getTitle = function() { - this.graph.addGlobalInput( this.properties.name, this.properties.type ); + if(this.flags.collapsed) + return this.properties.name; + return this.title; } -GlobalInput.prototype.onExecute = function() +GraphInput.prototype.onExecute = function() { var name = this.properties.name; //read from global input - var data = this.graph.global_inputs[name]; - if(!data) return; + var data = this.graph.inputs[name]; + if(!data) + return; //put through output this.setOutputData(0,data.value); } -LiteGraph.registerNodeType("graph/input", GlobalInput); +GraphInput.prototype.onRemoved = function() +{ + if(this.name_in_graph) + this.graph.removeInput( this.name_in_graph ); +} +LiteGraph.registerNodeType("graph/input", GraphInput); //Output for a subgraph -function GlobalOutput() +function GraphOutput() { - //random name to avoid problems with other outputs when added - var output_name = "output_" + (Math.random()*1000).toFixed(); - - this.addInput(output_name, null); - - this._value = null; - - this.properties = {name: output_name, type: null }; + this.addInput("",""); + this.name_in_graph = ""; + this.properties = {}; var that = this; - Object.defineProperty(this.properties, "name", { + Object.defineProperty( this.properties, "name", { get: function() { - return output_name; + return that.name_in_graph; }, set: function(v) { - if(v == "") + if( v == "" || v == that.name_in_graph ) return; - - var info = that.getInputInfo(0); - if(info.name == v) - return; - info.name = v; - if(that.graph) - that.graph.renameGlobalOutput(output_name, v); - output_name = v; + if(that.name_in_graph) //already added + that.graph.renameOutput( that.name_in_graph, v ); + else + that.graph.addOutput( v, that.properties.type ); + that.name_widget.value = v; + that.name_in_graph = v; }, enumerable: true }); - Object.defineProperty(this.properties, "type", { + Object.defineProperty( this.properties, "type", { get: function() { return that.inputs[0].type; }, set: function(v) { that.inputs[0].type = v; - if(that.graph) - that.graph.changeGlobalInputType( output_name, that.inputs[0].type ); + if(that.name_in_graph) //already added + that.graph.changeOutputType( that.name_in_graph, that.inputs[0].type); + that.type_widget.value = v || ""; }, enumerable: true }); + + this.name_widget = this.addWidget("text","Name", this.properties.name, function(v){ + if(!v) return; + that.properties.name = v; + }); + this.type_widget = this.addWidget("text","Type", this.properties.type, function(v){ + v = v || ""; + that.properties.type = v; + }); + + this.widgets_up = true; + this.size = [180,60]; } -GlobalOutput.title = "Output"; -GlobalOutput.desc = "Output of the graph"; +GraphOutput.title = "Output"; +GraphOutput.desc = "Output of the graph"; -GlobalOutput.prototype.onAdded = function() -{ - var name = this.graph.addGlobalOutput( this.properties.name, this.properties.type ); -} - -GlobalOutput.prototype.getValue = function() -{ - return this._value; -} - -GlobalOutput.prototype.onExecute = function() +GraphOutput.prototype.onExecute = function() { this._value = this.getInputData(0); - this.graph.setGlobalOutputData( this.properties.name, this._value ); + this.graph.setOutputData( this.properties.name, this._value ); } -LiteGraph.registerNodeType("graph/output", GlobalOutput); +GraphOutput.prototype.onRemoved = function() +{ + if(this.name_in_graph) + this.graph.removeOutput( this.name_in_graph ); +} + +GraphOutput.prototype.getTitle = function() +{ + if(this.flags.collapsed) + return this.properties.name; + return this.title; +} + +LiteGraph.registerNodeType("graph/output", GraphOutput); diff --git a/src/nodes/interface.js b/src/nodes/interface.js index fe525ba1f..80cb2f4e0 100755 --- a/src/nodes/interface.js +++ b/src/nodes/interface.js @@ -66,7 +66,7 @@ var LiteGraph = global.LiteGraph; this.addOutput( "v", "boolean" ); this.addOutput( "e", LiteGraph.EVENT ); this.properties = { font: "", value: false }; - this.size = [124,64]; + this.size = [160,44]; } WidgetToggle.title = "Toggle"; @@ -80,17 +80,19 @@ var LiteGraph = global.LiteGraph; var size = this.size[1] * 0.5; var margin = 0.25; var h = this.size[1] * 0.8; + ctx.font = this.properties.font || ((size * 0.8).toFixed(0) + "px Arial"); + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size) ) * 0.5; ctx.fillStyle = "#AAA"; - ctx.fillRect(10, h - size,size,size); + ctx.fillRect(x, h - size,size,size); ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect(10+size*margin,h - size + size*margin,size*(1-margin*2),size*(1-margin*2)); + ctx.fillRect(x + size*margin,h - size + size*margin,size*(1-margin*2),size*(1-margin*2) ); ctx.textAlign = "left"; - ctx.font = this.properties.font || ((size * 0.8).toFixed(0) + "px Arial"); ctx.fillStyle = "#AAA"; - ctx.fillText( this.title, size + 20, h * 0.85 ); + ctx.fillText( this.title, size * 1.2 + x, h * 0.85 ); ctx.textAlign = "left"; } diff --git a/src/nodes/math.js b/src/nodes/math.js index 23852c936..8d5bc95cf 100755 --- a/src/nodes/math.js +++ b/src/nodes/math.js @@ -404,7 +404,7 @@ function MathFloor() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; } MathFloor.title = "Floor"; @@ -425,7 +425,7 @@ function MathFrac() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; } MathFrac.title = "Frac"; @@ -447,7 +447,7 @@ function MathSmoothStep() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; this.properties = { A: 0, B: 1 }; } @@ -478,7 +478,7 @@ function MathScale() { this.addInput("in","number",{label:""}); this.addOutput("out","number",{label:""}); - this.size = [60,20]; + this.size = [80,30]; this.addProperty( "factor", 1 ); } @@ -500,7 +500,7 @@ function MathAverageFilter() { this.addInput("in","number"); this.addOutput("out","number"); - this.size = [60,20]; + this.size = [80,30]; this.addProperty( "samples", 10 ); this._values = new Float32Array(10); this._current = 0; @@ -552,7 +552,7 @@ function MathTendTo() this.addInput("in","number"); this.addOutput("out","number"); this.addProperty( "factor", 0.1 ); - this.size = [60,20]; + this.size = [80,30]; this._value = null; } @@ -591,7 +591,7 @@ MathOperation.values = ["+","-","*","/","%","^"]; MathOperation.title = "Operation"; MathOperation.desc = "Easy math operators"; MathOperation["@OP"] = { type:"enum", title: "operation", values: MathOperation.values }; -MathOperation.size = [100,50]; +MathOperation.size = [100,60]; MathOperation.prototype.getTitle = function() { @@ -720,7 +720,7 @@ function MathCondition() this.addProperty( "B", 1 ); this.addProperty( "OP", ">", "string", { values: MathCondition.values } ); - this.size = [60,40]; + this.size = [80,60]; } MathCondition.values = [">","<","==","!=","<=",">="]; diff --git a/src/nodes/strings.js b/src/nodes/strings.js index 727d7624f..22d99f7b7 100644 --- a/src/nodes/strings.js +++ b/src/nodes/strings.js @@ -55,6 +55,13 @@ LiteGraph.wrapFunctionAsNode("string/split",toUpperCase, ["String","String"],"Array"); + function toFixed(a) + { + if(a != null && a.constructor === Number) + return a.toFixed(this.properties.precision); + return a; + } + LiteGraph.wrapFunctionAsNode("string/toFixed", toFixed, ["Number"], "String", { precision: 0 } ); })(this); \ No newline at end of file