diff --git a/build/litegraph.js b/build/litegraph.js index 1c2f763fb..0eb78a211 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -4,6 +4,12 @@ // LiteGraph CLASS ******* // ************************************************************* +/** +* The Global Scope. It contains all the registered node classes. +* +* @class LiteGraph +* @constructor +*/ var LiteGraph = { @@ -25,6 +31,13 @@ var LiteGraph = { registered_node_types: {}, graphs: [], + /** + * Register a node class so it can be listed when the user wants to create a new one + * @method registerNodeType + * @param {String} type name of the node and path + * @param {Class} base_class class containing the structure of a node + */ + registerNodeType: function(type, base_class) { var title = type; @@ -52,6 +65,14 @@ var LiteGraph = { this.registered_node_types[type] = base_class; }, + /** + * Create a node of a given type with a name. The node is not attached to any graph yet. + * @method createNode + * @param {String} type full name of the node class. p.e. "math/sin" + * @param {String} name a name to distinguish from other nodes + * @param {Object} options to set options + */ + createNode: function(type,name, options) { var base_class = this.registered_node_types[type]; @@ -120,12 +141,26 @@ var LiteGraph = { return node; }, + /** + * Returns a registered node type with a given name + * @method getNodeType + * @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 + * @param {String} category category name + * @return {Array} array with all the node classes + */ + getNodeTypesInCategory: function(category) { var r = []; @@ -141,6 +176,12 @@ var LiteGraph = { return r; }, + /** + * Returns a list with all the node type categories + * @method getNodeTypesCategories + * @return {Array} array with all the names of the categories + */ + getNodeTypesCategories: function() { var categories = {"":1}; @@ -259,6 +300,13 @@ var LiteGraph = { // LGraph CLASS //********************************************************************************* +/** +* 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. +* +* @class LGraph +* @constructor +*/ + function LGraph() { if (LiteGraph.debug) @@ -271,6 +319,11 @@ function LGraph() LGraph.STATUS_STOPPED = 1; LGraph.STATUS_RUNNING = 2; +/** +* Removes all nodes from this graph +* @method clear +*/ + LGraph.prototype.clear = function() { this.stop(); @@ -309,7 +362,13 @@ LGraph.prototype.clear = function() this.canvas.clear(); } -LGraph.prototype.run = function(interval) +/** +* Starts running this graph every interval milliseconds. +* @method start +* @param {number} interval amount of milliseconds between executions, default is 1 +*/ + +LGraph.prototype.start = function(interval) { if(this.status == LGraph.STATUS_RUNNING) return; this.status = LGraph.STATUS_RUNNING; @@ -321,21 +380,20 @@ LGraph.prototype.run = function(interval) //launch this.starttime = new Date().getTime(); - interval = interval || 1000; + interval = interval || 1; var that = this; this.execution_timer_id = setInterval( function() { //execute that.runStep(1); - //redraw - /* - if(that.canvas && that.canvas.rendering_timer_id == null && (that.canvas.dirty_canvas || that.canvas.dirty_bgcanvas)) - that.canvas.draw(); - */ - },interval); } +/** +* Stops the execution loop of the graph +* @method stop +*/ + LGraph.prototype.stop = function() { if(this.status == LGraph.STATUS_STOPPED) @@ -353,6 +411,12 @@ LGraph.prototype.stop = function() this.sendEventToAllNodes("onStop"); } +/** +* Run N steps (cycles) of the graph +* @method runStep +* @param {number} num number of steps to run, default is 1 +*/ + LGraph.prototype.runStep = function(num) { num = num || 1; @@ -391,6 +455,18 @@ LGraph.prototype.runStep = function(num) this.iteration += 1; } +/** +* Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than +* nodes with only inputs. +* @method updateExecutionOrder +*/ + +LGraph.prototype.updateExecutionOrder = function() +{ + this.nodes_in_order = this.computeExecutionOrder(); +} + +//This is more internal, it computes the order and returns it LGraph.prototype.computeExecutionOrder = function() { var L = []; @@ -474,21 +550,48 @@ LGraph.prototype.computeExecutionOrder = function() return L; } + +/** +* Returns the amount of time the graph has been running in milliseconds +* @method getTime +* @return {number} number of milliseconds the graph has been running +*/ + LGraph.prototype.getTime = function() { return this.globaltime; } +/** +* Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant +* @method getFixedTime +* @return {number} number of milliseconds the graph has been running +*/ + LGraph.prototype.getFixedTime = function() { return this.fixedtime; } +/** +* Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct +* if the nodes are using graphical actions +* @method getElapsedTime +* @return {number} number of milliseconds it took the last cycle +*/ + LGraph.prototype.getElapsedTime = function() { return this.elapsed_time; } +/** +* Sends an event to all the nodes, useful to trigger stuff +* @method sendEventToAllNodes +* @param {String} eventname the name of the event +* @param {Object} param an object containing the info +*/ + LGraph.prototype.sendEventToAllNodes = function(eventname, param) { var M = this.nodes_in_order ? this.nodes_in_order : this.nodes; @@ -497,13 +600,19 @@ LGraph.prototype.sendEventToAllNodes = function(eventname, param) M[j][eventname](param); } +/** +* Adds a new node instasnce to this graph +* @method add +* @param {LGraphNode} node the instance of the node +*/ + LGraph.prototype.add = function(node) { if(!node || (node.id != -1 && this.nodes_by_id[node.id] != null)) return; //already added if(this.nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) - throw("LiteGraph: max number of nodes attached"); + throw("LiteGraph: max number of nodes in a graph reached"); //give him an id if(node.id == null || node.id == -1) @@ -536,11 +645,20 @@ LGraph.prototype.add = function(node) return node; //to chain actions } +/** +* Removes a node from the graph +* @method remove +* @param {LGraphNode} node the instance of the node +*/ + LGraph.prototype.remove = function(node) { if(this.nodes_by_id[node.id] == null) return; //not found + if(node.ignore_remove) + return; //cannot be removed + //disconnect inputs if(node.inputs) for(var i = 0; i < node.inputs.length; i++) @@ -588,6 +706,12 @@ LGraph.prototype.remove = function(node) this.updateExecutionOrder(); } +/** +* Returns a node by its id. +* @method getNodeById +* @param {String} id +*/ + LGraph.prototype.getNodeById = function(id) { if(id==null) return null; @@ -595,6 +719,13 @@ LGraph.prototype.getNodeById = function(id) } +/** +* Returns a list of nodes that matches a type +* @method findNodesByType +* @param {String} type the name of the node type +* @return {Array} a list with all the nodes of this type +*/ + LGraph.prototype.findNodesByType = function(type) { var r = []; @@ -604,15 +735,31 @@ LGraph.prototype.findNodesByType = function(type) return r; } +/** +* Returns a list of nodes that matches a name +* @method findNodesByName +* @param {String} name the name of the node to search +* @return {Array} a list with all the nodes with this name +*/ + LGraph.prototype.findNodesByName = function(name) { var result = []; for (var i in this.nodes) if(this.nodes[i].name == name) - result.push(name); + result.push(this.nodes[i]); return result; } +/** +* Returns the top-most node in this position of the canvas +* @method getNodeOnPos +* @param {number} x the x coordinate in canvas space +* @param {number} y the y coordinate in canvas space +* @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph +* @return {Array} a list with all the nodes that intersect this coordinate +*/ + LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) { nodes_list = nodes_list || this.nodes; @@ -625,6 +772,14 @@ LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) return null; } +/** +* Assigns a value to all the nodes that matches this name. This is used to create global variables of the node that +* can be easily accesed from the outside of the graph +* @method setInputData +* @param {String} name the name of the node +* @param {*} value value to assign to this node +*/ + LGraph.prototype.setInputData = function(name,value) { var m = this.findNodesByName(name); @@ -632,6 +787,13 @@ LGraph.prototype.setInputData = function(name,value) m[i].setValue(value); } +/** +* Returns the value of the first node with this name. This is used to access global variables of the graph from the outside +* @method setInputData +* @param {String} name the name of the node +* @return {*} value of the node +*/ + LGraph.prototype.getOutputData = function(name) { var n = this.findNodesByName(name); @@ -640,6 +802,8 @@ LGraph.prototype.getOutputData = function(name) return null; } +//This feature is not finished yet, is to create graphs where nodes are not executed unless a trigger message is received + LGraph.prototype.triggerInput = function(name,value) { var m = this.findNodesByName(name); @@ -661,11 +825,6 @@ LGraph.prototype.onConnectionChange = function() { this.updateExecutionOrder(); } - -LGraph.prototype.updateExecutionOrder = function() -{ - this.nodes_in_order = this.computeExecutionOrder(); -} LGraph.prototype.isLive = function() { @@ -682,6 +841,11 @@ LGraph.prototype.change = function() } //save and recover app state *************************************** +/** +* Creates a JSON String containing all the info about this graph +* @method serialize +* @return {String} value of the node +*/ LGraph.prototype.serialize = function() { var nodes_info = []; @@ -703,6 +867,11 @@ LGraph.prototype.serialize = function() return JSON.stringify(data); } +/** +* Configure a graph from a JSON string +* @method unserialize +* @param {String} str configure a graph from a JSON string +*/ LGraph.prototype.unserialize = function(str, keep_old) { if(!keep_old) @@ -776,6 +945,11 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) + onDeselected */ +/** +* Base Class for all the node type classes +* @class LGraphNode +* @param {String} name a name for the node +*/ function LGraphNode(name) { @@ -1020,6 +1194,8 @@ LGraphNode.prototype.findOutputSlot = function(name) //connect this node output to the input of another node LGraphNode.prototype.connect = function(slot, node, target_slot) { + target_slot = target_slot || 0; + //seek for the output slot if( slot.constructor === String ) { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index d5e991d4d..835367380 100644 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -5,18 +5,18 @@ a?null==this.registered_node_types[c].category&&b.push(this.registered_node_type [],d;for(d in b)c.push(b[d]);b=document.getElementsByTagName("head")[0];a=document.location.href+a;for(d in c){var e=c[d].src;if(e&&e.substr(0,a.length)==a)try{LiteGraph.debug&&console.log("Reloading: "+e);var f=document.createElement("script");f.type="text/javascript";f.src=e;b.appendChild(f);b.removeChild(c[d])}catch(g){if(LiteGraph.throw_errors)throw g;LiteGraph.debug&&console.log("Error while reloading "+e)}}for(d in LiteGraph.graphs)for(var h in LiteGraph.graphs[d].nodes)if(a=LiteGraph.graphs[d].nodes[h], c=LiteGraph.getNodeType(n.type))for(var k in c)"function"==typeof c[k]&&(a[k]=c[k]);LiteGraph.debug&&console.log("Nodes reloaded")}};function LGraph(){LiteGraph.debug&&console.log("Graph created");this.canvas=null;LiteGraph.graphs.push(this);this.clear()}LGraph.STATUS_STOPPED=1;LGraph.STATUS_RUNNING=2; LGraph.prototype.clear=function(){this.stop();this.status=LGraph.STATUS_STOPPED;this.last_node_id=0;this.nodes=[];this.nodes_by_id={};this.last_link_id=0;this.links={};this.iteration=0;this.config={canvas_offset:[0,0],canvas_scale:1};this.fixedtime=this.runningtime=this.globaltime=0;this.elapsed_time=this.fixedtime_lapse=0.01;this.starttime=0;this.graph={};this.debug=!0;this.change();this.canvas&&this.canvas.clear()}; -LGraph.prototype.run=function(a){if(this.status!=LGraph.STATUS_RUNNING){this.status=LGraph.STATUS_RUNNING;if(this.onPlayEvent)this.onPlayEvent();this.sendEventToAllNodes("onStart");this.starttime=(new Date).getTime();var b=this;this.execution_timer_id=setInterval(function(){b.runStep(1)},a||1E3)}}; +LGraph.prototype.start=function(a){if(this.status!=LGraph.STATUS_RUNNING){this.status=LGraph.STATUS_RUNNING;if(this.onPlayEvent)this.onPlayEvent();this.sendEventToAllNodes("onStart");this.starttime=(new Date).getTime();var b=this;this.execution_timer_id=setInterval(function(){b.runStep(1)},a||1)}}; LGraph.prototype.stop=function(){if(this.status!=LGraph.STATUS_STOPPED){this.status=LGraph.STATUS_STOPPED;if(this.onStopEvent)this.onStopEvent();null!=this.execution_timer_id&&clearInterval(this.execution_timer_id);this.execution_timer_id=null;this.sendEventToAllNodes("onStop")}}; LGraph.prototype.runStep=function(a){a=a||1;var b=(new Date).getTime();this.globaltime=0.001*(b-this.starttime);try{for(var c=0;c=LiteGraph.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes attached";if(null==a.id||-1==a.id)a.id=this.last_node_id++;a.graph=this;this.nodes.push(a);this.nodes_by_id[a.id]=a;if(a.onInit)a.onInit();this.config.align_to_grid&&a.alignToGrid();this.updateExecutionOrder();this.canvas&&(this.canvas.dirty_canvas=!0);this.change();return a}}; -LGraph.prototype.remove=function(a){if(null!=this.nodes_by_id[a.id]){if(a.inputs)for(var b=0;b=LiteGraph.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";if(null==a.id||-1==a.id)a.id=this.last_node_id++;a.graph=this;this.nodes.push(a);this.nodes_by_id[a.id]=a;if(a.onInit)a.onInit();this.config.align_to_grid&&a.alignToGrid();this.updateExecutionOrder();this.canvas&&(this.canvas.dirty_canvas=!0);this.change();return a}}; +LGraph.prototype.remove=function(a){if(null!=this.nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(var b=0;ba&&this.pos[1]-cb)return!0;return!1};LGraphNode.prototype.findInputSlot=function(a){if(!this.inputs)return-1;for(var b=0,c=this.inputs.length;b=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;if(b==this)return!1;if(c.constructor===String){if(c=b.findInputSlot(c),-1==c)return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+c),!1}else if(!b.inputs||c>=b.inputs.length)return LiteGraph.debug&& +LGraphNode.prototype.connect=function(a,b,c){c=c||0;if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;if(b==this)return!1;if(c.constructor===String){if(c=b.findInputSlot(c),-1==c)return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+c),!1}else if(!b.inputs||c>=b.inputs.length)return LiteGraph.debug&& console.log("Connect: Error, slot number not found"),!1;-1!=c&&null!=b.inputs[c].link&&b.disconnectInput(c);var d=this.outputs[a];if(-1==c)null==d.links&&(d.links=[]),d.links.push({id:b.id,slot:-1});else if(0==d.type||0==b.inputs[c].type||d.type==b.inputs[c].type)a=[this.graph.last_link_id++,this.id,a,b.id,c],null==d.links&&(d.links=[]),d.links.push(a),b.inputs[c].link=a,this.setDirtyCanvas(!1,!0),this.graph.onConnectionChange();return!0}; LGraphNode.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var c=this.outputs[a];if(!c.links||0==c.links.length)return!1;if(b)for(var d=0,e=c.links.length;d