From c9c11e6c46109abb27040f53a8f0718634ba6c44 Mon Sep 17 00:00:00 2001 From: tamat Date: Fri, 21 Mar 2014 19:56:50 +0100 Subject: [PATCH] demo improved, editor included, graph canvas detached --- build/litegraph.js | 895 ++++++---- build/litegraph.min.js | 175 +- css/litegraph-editor.css | 187 ++ demo/code.js | 545 +----- demo/code_old.js | 547 ++++++ demo/index.html | 5 + demo/style.css | 146 +- doc/api.js | 1 + doc/classes/LGraph.html | 498 ++++-- doc/classes/LGraphCanvas.html | 277 +++ doc/classes/LGraphNode.html | 2612 +++++++++++++++++++++++++++- doc/classes/LiteGraph.html | 2 + doc/data.json | 551 +++++- doc/files/.._src_litegraph.js.html | 1559 ++++++++++------- doc/index.html | 2 + src/litegraph-editor.js | 165 ++ src/litegraph.js | 882 +++++++--- src/nodes/uinodes.js | 12 +- 18 files changed, 6948 insertions(+), 2113 deletions(-) create mode 100644 css/litegraph-editor.css create mode 100644 demo/code_old.js create mode 100644 doc/classes/LGraphCanvas.html create mode 100644 src/litegraph-editor.js diff --git a/build/litegraph.js b/build/litegraph.js index 94a243de8..799f36f70 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -30,7 +30,6 @@ var LiteGraph = { debug: false, registered_node_types: {}, - graphs: [], /** * Register a node class so it can be listed when the user wants to create a new one @@ -233,28 +232,19 @@ var LiteGraph = { } } - for (var i in LiteGraph.graphs) - { - for (var j in LiteGraph.graphs[i].nodes) - { - var m = LiteGraph.graphs[i].nodes[j]; - var t = LiteGraph.getNodeType(n.type); - if(!t) continue; - - for (var k in t) - if( typeof(t[k]) == "function" ) - m[k] = t[k]; - } - } - if(LiteGraph.debug) console.log("Nodes reloaded"); }, //separated just to improve if it doesnt work - cloneObject: function(obj) + cloneObject: function(obj, target) { - return JSON.parse( JSON.stringify( obj ) ); + var r = JSON.parse( JSON.stringify( obj ) ); + if(!target) return r; + + for(var i in r) + target[i] = r[i]; + return target; } /* @@ -263,11 +253,11 @@ var LiteGraph = { mode = mode || "all"; trace("Benchmarking " + mode + "..."); - trace(" Num. nodes: " + this.nodes.length ); + trace(" Num. nodes: " + this._nodes.length ); var links = 0; - for(var i in this.nodes) - for(var j in this.nodes[i].outputs) - if(this.nodes[i].outputs[j].node_id != null) + for(var i in this._nodes) + for(var j in this._nodes[i].outputs) + if(this._nodes[i].outputs[j].node_id != null) links++; trace(" Num. links: " + links ); @@ -318,8 +308,7 @@ function LGraph() { if (LiteGraph.debug) console.log("Graph created"); - this.canvas = null; - LiteGraph.graphs.push(this); + this.list_of_graphcanvas = null; this.clear(); } @@ -338,8 +327,8 @@ LGraph.prototype.clear = function() this.last_node_id = 0; //nodes - this.nodes = []; - this.nodes_by_id = {}; + this._nodes = []; + this._nodes_by_id = {}; //links this.last_link_id = 0; @@ -349,8 +338,6 @@ LGraph.prototype.clear = function() this.iteration = 0; this.config = { - canvas_offset: [0,0], - canvas_scale: 1.0 }; //timing @@ -361,12 +348,48 @@ LGraph.prototype.clear = function() this.elapsed_time = 0.01; this.starttime = 0; + //globals + this.globals = {}; + this.graph = {}; this.debug = true; this.change(); - if(this.canvas) - this.canvas.clear(); + + this.sendActionToCanvas("clear"); +} + +/** +* Attach Canvas to this graph +* @method attachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.attachCanvas = function(graphcanvas) +{ + if(graphcanvas.constructor != LGraphCanvas) + throw("attachCanvas expects a LGraphCanvas instance"); + if(graphcanvas.graph && graphcanvas.graph != this) + graphcanvas.graph.detachCanvas( graphcanvas ); + + graphcanvas.graph = this; + if(!this.list_of_graphcanvas) + this.list_of_graphcanvas = []; + this.list_of_graphcanvas.push(graphcanvas); +} + +/** +* Detach Canvas from this graph +* @method detachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.detachCanvas = function(graphcanvas) +{ + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if(pos == -1) return; + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos,1); } /** @@ -386,7 +409,7 @@ LGraph.prototype.start = function(interval) this.sendEventToAllNodes("onStart"); //launch - this.starttime = new Date().getTime(); + this.starttime = window.performance.now(); interval = interval || 1; var that = this; @@ -398,7 +421,7 @@ LGraph.prototype.start = function(interval) /** * Stops the execution loop of the graph -* @method stop +* @method stop execution */ LGraph.prototype.stop = function() @@ -428,7 +451,7 @@ LGraph.prototype.runStep = function(num) { num = num || 1; - var start = new Date().getTime(); + var start = window.performance.now(); this.globaltime = 0.001 * (start - this.starttime); try @@ -455,7 +478,7 @@ LGraph.prototype.runStep = function(num) this.stop(); } - var elapsed = (new Date().getTime()) - start; + var elapsed = window.performance.now() - start; if (elapsed == 0) elapsed = 1; this.elapsed_time = 0.001 * elapsed; this.globaltime += 0.001 * elapsed; @@ -470,7 +493,7 @@ LGraph.prototype.runStep = function(num) LGraph.prototype.updateExecutionOrder = function() { - this.nodes_in_order = this.computeExecutionOrder(); + this._nodes_in_order = this.computeExecutionOrder(); } //This is more internal, it computes the order and returns it @@ -483,9 +506,9 @@ LGraph.prototype.computeExecutionOrder = function() var remaining_links = {}; //to a //search for the nodes without inputs (starting nodes) - for (var i in this.nodes) + for (var i in this._nodes) { - var n = this.nodes[i]; + var n = this._nodes[i]; M[n.id] = n; //add to pending nodes var num = 0; //num of input connections @@ -547,7 +570,7 @@ LGraph.prototype.computeExecutionOrder = function() for(var i in M) L.push(M[i]); - if(L.length != this.nodes.length && LiteGraph.debug) + if(L.length != this._nodes.length && LiteGraph.debug) console.log("something went wrong, nodes missing"); //save order number in the node @@ -601,12 +624,25 @@ LGraph.prototype.getElapsedTime = function() LGraph.prototype.sendEventToAllNodes = function(eventname, param) { - var M = this.nodes_in_order ? this.nodes_in_order : this.nodes; + var M = this._nodes_in_order ? this._nodes_in_order : this._nodes; for(var j in M) if(M[j][eventname]) M[j][eventname](param); } +LGraph.prototype.sendActionToCanvas = function(action, params) +{ + if(!this.list_of_graphcanvas) + return; + + for(var i in this.list_of_graphcanvas) + { + var c = this.list_of_graphcanvas[i]; + if( c[action] ) + c[action].apply(c, params); + } +} + /** * Adds a new node instasnce to this graph * @method add @@ -615,10 +651,10 @@ LGraph.prototype.sendEventToAllNodes = function(eventname, param) LGraph.prototype.add = function(node) { - if(!node || (node.id != -1 && this.nodes_by_id[node.id] != null)) + if(!node || (node.id != -1 && this._nodes_by_id[node.id] != null)) return; //already added - if(this.nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) + if(this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) throw("LiteGraph: max number of nodes in a graph reached"); //give him an id @@ -627,8 +663,8 @@ LGraph.prototype.add = function(node) node.graph = this; - this.nodes.push(node); - this.nodes_by_id[node.id] = node; + this._nodes.push(node); + this._nodes_by_id[node.id] = node; /* // rendering stuf... @@ -660,7 +696,7 @@ LGraph.prototype.add = function(node) LGraph.prototype.remove = function(node) { - if(this.nodes_by_id[node.id] == null) + if(this._nodes_by_id[node.id] == null) return; //not found if(node.ignore_remove) @@ -700,10 +736,10 @@ LGraph.prototype.remove = function(node) } //remove from containers - var pos = this.nodes.indexOf(node); + var pos = this._nodes.indexOf(node); if(pos != -1) - this.nodes.splice(pos,1); - delete this.nodes_by_id[node.id]; + this._nodes.splice(pos,1); + delete this._nodes_by_id[node.id]; if(this.canvas) this.canvas.setDirty(true,true); @@ -722,7 +758,7 @@ LGraph.prototype.remove = function(node) LGraph.prototype.getNodeById = function(id) { if(id==null) return null; - return this.nodes_by_id[id]; + return this._nodes_by_id[id]; } @@ -736,9 +772,9 @@ LGraph.prototype.getNodeById = function(id) LGraph.prototype.findNodesByType = function(type) { var r = []; - for(var i in this.nodes) - if(this.nodes[i].type == type) - r.push(this.nodes[i]); + for(var i in this._nodes) + if(this._nodes[i].type == type) + r.push(this._nodes[i]); return r; } @@ -752,9 +788,9 @@ LGraph.prototype.findNodesByType = function(type) LGraph.prototype.findNodesByName = function(name) { var result = []; - for (var i in this.nodes) - if(this.nodes[i].name == name) - result.push(this.nodes[i]); + for (var i in this._nodes) + if(this._nodes[i].name == name) + result.push(this._nodes[i]); return result; } @@ -769,7 +805,7 @@ LGraph.prototype.findNodesByName = function(name) LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) { - nodes_list = nodes_list || this.nodes; + nodes_list = nodes_list || this._nodes; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; @@ -825,39 +861,46 @@ LGraph.prototype.setCallback = function(name,func) m[i].setTrigger(func); } -//********** - LGraph.prototype.onConnectionChange = function() { this.updateExecutionOrder(); } +/** +* returns if the graph is in live mode +* @method isLive +*/ + LGraph.prototype.isLive = function() { if(!this.canvas) return false; return this.canvas.live_mode; } +/* Called when something visually changed */ LGraph.prototype.change = function() { if(LiteGraph.debug) console.log("Graph changed"); + + this.sendActionToCanvas("setDirty",[true,true]); + if(this.on_change) this.on_change(this); } //save and recover app state *************************************** /** -* Creates a JSON String containing all the info about this graph +* Creates a Object containing all the info about this graph, it can be serialized * @method serialize -* @return {String} value of the node +* @return {Object} value of the node */ LGraph.prototype.serialize = function() { var nodes_info = []; - for (var i in this.nodes) - nodes_info.push( this.nodes[i].objectivize() ); + for (var i in this._nodes) + nodes_info.push( this._nodes[i].serialize() ); var data = { graph: this.graph, @@ -871,20 +914,20 @@ LGraph.prototype.serialize = function() nodes: nodes_info }; - return JSON.stringify(data); + return data; } + /** * Configure a graph from a JSON string -* @method unserialize +* @method configure * @param {String} str configure a graph from a JSON string */ -LGraph.prototype.unserialize = function(str, keep_old) +LGraph.prototype.configure = function(data, keep_old) { if(!keep_old) this.clear(); - var data = JSON.parse(str); var nodes = data.nodes; //copy all stored fields @@ -894,7 +937,7 @@ LGraph.prototype.unserialize = function(str, keep_old) var error = false; //create nodes - this.nodes = []; + this._nodes = []; for (var i in nodes) { var n_info = nodes[i]; //stored info @@ -907,7 +950,7 @@ LGraph.prototype.unserialize = function(str, keep_old) continue; } - n.copyFromObject(n_info); + n.configure(n_info); this.add(n); } @@ -981,9 +1024,59 @@ function LGraphNode(name) }; } -//serialization ************************* +/** +* configure a node from an object +* @method configure +*/ +LGraphNode.prototype.configure = function(info) +{ + for (var j in info) + { + if(j == "console") continue; -LGraphNode.prototype.objectivize = function() + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j], this[j] || {} ); + else //value + this[j] = info[j]; + } +} + +/* Copy all the info from one object to this node (used for serialization) */ +LGraphNode.prototype.copyFromObject = function(info, ignore_connections) +{ + var outputs = null; + var inputs = null; + var properties = null; + var local_data = null; + + for (var j in info) + { + if(ignore_connections && (j == "outputs" || j == "inputs")) + continue; + + if(j == "console") continue; + + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j]); + else //value + this[j] = info[j]; + } +} + +/** +* serialize the content +* @method serialize +*/ + +LGraphNode.prototype.serialize = function() { var o = { id: this.id, @@ -1010,10 +1103,14 @@ LGraphNode.prototype.objectivize = function() if(this.shape) o.shape = this.shape; + if(this.onSerialize) + this.onSerialize(o); + return o; } //reduced version of objectivize: NOT FINISHED +/* LGraphNode.prototype.reducedObjectivize = function() { var o = this.objectivize(); @@ -1031,19 +1128,27 @@ LGraphNode.prototype.reducedObjectivize = function() return o; } +*/ +/** +* serialize and stringify +* @method toString +*/ -LGraphNode.prototype.serialize = function() +LGraphNode.prototype.toString = function() { - if(this.onSerialize) - this.onSerialize(); - return JSON.stringify( this.reducedObjectivize() ); + return JSON.stringify( this.serialize() ); } //LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph // Execution ************************* - +/** +* sets the output data +* @method setOutputData +* @param {number} slot +* @param {*} data +*/ LGraphNode.prototype.setOutputData = function(slot,data) { if(!this.outputs) return; @@ -1054,6 +1159,12 @@ LGraphNode.prototype.setOutputData = function(slot,data) } } +/** +* retrieves the input data from one slot +* @method getInputData +* @param {number} slot +* @return {*} data +*/ LGraphNode.prototype.getInputData = function(slot) { if(!this.inputs) return null; @@ -1062,12 +1173,24 @@ LGraphNode.prototype.getInputData = function(slot) return null; } +/** +* tells you if there is a connection in one input slot +* @method isInputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isInputConnected = function(slot) { if(!this.inputs) return null; return (slot < this.inputs.length && this.inputs[slot].link != null); } +/** +* tells you info about an input connection (which node, type, etc) +* @method getInputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getInputInfo = function(slot) { if(!this.inputs) return null; @@ -1077,6 +1200,12 @@ LGraphNode.prototype.getInputInfo = function(slot) } +/** +* tells you info about an output connection (which node, type, etc) +* @method getOutputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getOutputInfo = function(slot) { if(!this.outputs) return null; @@ -1085,12 +1214,25 @@ LGraphNode.prototype.getOutputInfo = function(slot) return null; } + +/** +* tells you if there is a connection in one output slot +* @method isOutputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isOutputConnected = function(slot) { if(!this.outputs) return null; return (slot < this.outputs.length && this.outputs[slot].links && this.outputs[slot].links.length); } +/** +* retrieves all the nodes connected to this output slot +* @method getOutputNodes +* @param {number} slot +* @return {array} +*/ LGraphNode.prototype.getOutputNodes = function(slot) { if(!this.outputs || this.outputs.length == 0) return null; @@ -1114,6 +1256,13 @@ LGraphNode.prototype.triggerOutput = function(slot,param) //connections +/** +* add a new output slot to use in this node +* @method addOutput +* @param {string} name +* @param {string} type string defining the output type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an output (special color, position, etc) +*/ LGraphNode.prototype.addOutput = function(name,type,extra_info) { var o = {name:name,type:type,links:null}; @@ -1126,6 +1275,11 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing output slot +* @method removeOutput +* @param {number} slot +*/ LGraphNode.prototype.removeOutput = function(slot) { this.disconnectOutput(slot); @@ -1133,6 +1287,13 @@ LGraphNode.prototype.removeOutput = function(slot) this.size = this.computeSize(); } +/** +* add a new input slot to use in this node +* @method addInput +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an input (special color, position, etc) +*/ LGraphNode.prototype.addInput = function(name,type,extra_info) { var o = {name:name,type:type,link:null}; @@ -1145,6 +1306,11 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing input slot +* @method removeInput +* @param {number} slot +*/ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); @@ -1152,13 +1318,25 @@ LGraphNode.prototype.removeInput = function(slot) this.size = this.computeSize(); } -//trigger connection +/** +* add an special connection to this node (used for special kinds of graphs) +* @method addConnection +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {[x,y]} pos position of the connection inside the node +* @param {string} direction if is input or output +*/ LGraphNode.prototype.addConnection = function(name,type,pos,direction) { this.connections.push( {name:name,type:type,pos:pos,direction:direction,links:null}); } - +/** +* computes the size of a node according to its inputs and output slots +* @method computeSize +* @param {number} minHeight +* @return {number} the total size +*/ LGraphNode.prototype.computeSize = function(minHeight) { var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); @@ -1171,13 +1349,23 @@ LGraphNode.prototype.computeSize = function(minHeight) return size; } -//returns the bounding of the object, used for rendering purposes +/** +* returns the bounding of the object, used for rendering purposes +* @method getBounding +* @return {Float32Array[4]} the total size +*/ LGraphNode.prototype.getBounding = function() { return new Float32Array([this.pos[0] - 4, this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, this.pos[0] + this.size[0] + 4, this.pos[1] + this.size[1] + LGraph.NODE_TITLE_HEIGHT]); } -//checks if a point is inside the shape of a node +/** +* checks if a point is inside the shape of a node +* @method isPointInsideNode +* @param {number} x +* @param {number} y +* @return {boolean} +*/ LGraphNode.prototype.isPointInsideNode = function(x,y) { var margin_top = this.graph.isLive() ? 0 : 20; @@ -1211,7 +1399,14 @@ LGraphNode.prototype.findOutputSlot = function(name) return -1; } -//connect this node output to the input of another node +/** +* connect this node output to the input of another node +* @method connect +* @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) +* @return {boolean} if it was connected succesfully +*/ LGraphNode.prototype.connect = function(slot, node, target_slot) { target_slot = target_slot || 0; @@ -1285,6 +1480,13 @@ LGraphNode.prototype.connect = function(slot, node, target_slot) return true; } +/** +* disconnect one output to an specific node +* @method disconnectOutput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectOutput = function(slot, target_node) { if( slot.constructor === String ) @@ -1341,6 +1543,12 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node) return true; } +/** +* disconnect one input +* @method disconnectInput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectInput = function(slot) { //seek for the output slot @@ -1389,7 +1597,13 @@ LGraphNode.prototype.disconnectInput = function(slot) return true; } -//returns the center of a connection point in canvas coords +/** +* returns the center of a connection point in canvas coords +* @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) +* @return {[x,y]} the position +**/ LGraphNode.prototype.getConnectionPos = function(is_input,slot_number) { if(this.flags.collapsed) @@ -1423,50 +1637,6 @@ LGraphNode.prototype.alignToGrid = function() this.pos[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); } -/* Copy all the info from one object to this node (used for serialization) */ -LGraphNode.prototype.copyFromObject = function(info, ignore_connections) -{ - var outputs = null; - var inputs = null; - var properties = null; - var local_data = null; - - for (var j in info) - { - if(ignore_connections && (j == "outputs" || j == "inputs")) - continue; - - if(j == "console") continue; - - if(info[j] == null) - continue; - else if( info[j].concat ) //array - this[j] = info[j].concat(); - else if (typeof(info[j]) == 'object') //object - this[j] = LiteGraph.cloneObject(info[j]); - else //value - this[j] = info[j]; - } - - //redo the connections - /* - if(outputs) - this.outputs = outputs.concat(); - if(inputs) - this.inputs = inputs.concat(); - - if(local_data) - this.data = local_data; - if(properties) - { - //copy only the ones defined - for (var j in properties) - if (this.properties[j] != null) - this.properties[j] = properties[j]; - } - */ -} - /* Creates a clone of this node */ LGraphNode.prototype.clone = function() { @@ -1506,13 +1676,9 @@ LGraphNode.prototype.trace = function(msg) /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function(dirty_foreground, dirty_background) { - if(!this.graph || !this.graph.canvas) + if(!this.graph) return; - - if(dirty_foreground) - this.graph.canvas.dirty_canvas = true; - if(dirty_background) - this.graph.canvas.dirty_bgcanvas = true; + this.graph.sendActionToCanvas("setDirty",[dirty_foreground, dirty_background]); } LGraphNode.prototype.loadImage = function(url) @@ -1569,20 +1735,29 @@ LGraphNode.prototype.executeAction = function(action) /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ LGraphNode.prototype.captureInput = function(v) { - if(!this.graph || !this.graph.canvas) + if(!this.graph || !this.graph.list_of_graphcanvas) return; - //releasing somebody elses capture?! - if(!v && this.graph.canvas.node_capturing_input != this) - return; + var list = this.graph.list_of_graphcanvas; - //change - this.graph.canvas.node_capturing_input = v ? this : null; - if(this.graph.debug) - console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + for(var i in list) + { + var c = list[i]; + //releasing somebody elses capture?! + if(!v && c.node_capturing_input != this) + continue; + + //change + c.node_capturing_input = v ? this : null; + if(this.graph.debug) + console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + } } -/* Collapse the node */ +/** +* Collapse the node to make it smaller on the canvas +* @method collapse +**/ LGraphNode.prototype.collapse = function() { if(!this.flags.collapsed) @@ -1592,19 +1767,23 @@ LGraphNode.prototype.collapse = function() this.setDirtyCanvas(true,true); } -/* Forces the node to do not move or realign on Z */ -LGraphNode.prototype.pin = function() +/** +* Forces the node to do not move or realign on Z +* @method pin +**/ + +LGraphNode.prototype.pin = function(v) { - if(!this.flags.pinned) - this.flags.pinned = true; + if(v === undefined) + this.flags.pinned = !this.flags.pinned; else - this.flags.pinned = false; + this.flags.pinned = v; } -LGraphNode.prototype.localToScreen = function(x,y) +LGraphNode.prototype.localToScreen = function(x,y, graphcanvas) { - return [(x + this.pos[0]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[0], - (y + this.pos[1]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[1]]; + return [(x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]]; } @@ -1613,27 +1792,28 @@ LGraphNode.prototype.localToScreen = function(x,y) // LGraphCanvas: LGraph renderer CLASS //********************************************************************************* +/** +* The Global Scope. It contains all the registered node classes. +* +* @class LGraphCanvas +* @constructor +* @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format) +* @param {LGraph} graph +*/ function LGraphCanvas(canvas, graph) { - if(graph === undefined) - throw ("No graph assigned"); + //if(graph === undefined) + // throw ("No graph assigned"); - if( typeof(window) != "undefined" ) - { - window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function( callback ){ - window.setTimeout(callback, 1000 / 60); - }; - })(); - } + if(typeof(canvas) == "string") + canvas = document.querySelector(canvas); + + if(!canvas) + throw("no canvas found"); //link canvas and graph - this.graph = graph; if(graph) - graph.canvas = this; + graph.attachCanvas(this); this.setCanvas(canvas); this.clear(); @@ -1642,7 +1822,6 @@ function LGraphCanvas(canvas, graph) } LGraphCanvas.link_type_colors = {'number':"#AAC",'node':"#DCA"}; -LGraphCanvas.link_width = 2; LGraphCanvas.prototype.clear = function() { @@ -1651,6 +1830,9 @@ LGraphCanvas.prototype.clear = function() this.render_time = 0; this.fps = 0; + this.scale = 1; + this.offset = [0,0]; + this.selected_nodes = {}; this.node_dragged = null; this.node_over = null; @@ -1658,6 +1840,7 @@ LGraphCanvas.prototype.clear = function() this.connecting_node = null; this.highquality_render = true; + this.editor_alpha = 1; //used for transition this.pause_rendering = false; this.render_shadows = true; this.dirty_canvas = true; @@ -1675,6 +1858,16 @@ LGraphCanvas.prototype.clear = function() this.last_mouse = [0,0]; this.last_mouseclick = 0; + this.title_text_font = "bold 14px Arial"; + this.inner_text_font = "normal 12px Arial"; + + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = true; + this.render_connection_arrows = true; + + this.connections_width = 4; + if(this.onClear) this.onClear(); //this.UIinit(); } @@ -1682,29 +1875,25 @@ LGraphCanvas.prototype.clear = function() LGraphCanvas.prototype.setGraph = function(graph) { if(this.graph == graph) return; - this.clear(); + + if(!graph && this.graph) + { + this.graph.detachCanvas(this); + return; + } + + /* if(this.graph) this.graph.canvas = null; //remove old graph link to the canvas this.graph = graph; if(this.graph) this.graph.canvas = this; + */ + graph.attachCanvas(this); this.setDirty(true,true); } -LGraphCanvas.prototype.resize = function(width, height) -{ - if(this.canvas.width == width && this.canvas.height == height) - return; - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true,true); -} - - LGraphCanvas.prototype.setCanvas = function(canvas) { var that = this; @@ -1829,6 +2018,13 @@ LGraphCanvas.prototype.setDirty = function(fgcanvas,bgcanvas) this.dirty_bgcanvas = true; } +//Used to attach the canvas in a popup +LGraphCanvas.prototype.getCanvasWindow = function() +{ + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; +} + LGraphCanvas.prototype.startRendering = function() { if(this.is_rendering) return; //already rendering @@ -1841,8 +2037,9 @@ LGraphCanvas.prototype.startRendering = function() if(!this.pause_rendering) this.draw(); + var window = this.getCanvasWindow(); if(this.is_rendering) - window.requestAnimFrame( renderFrame.bind(this) ); + window.requestAnimationFrame( renderFrame.bind(this) ); } @@ -1874,9 +2071,12 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.adjustMouseEvent(e); + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + this.canvas.removeEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mouseup", this._mouseup_callback ); + ref_window.document.addEventListener("mousemove", this._mousemove_callback ); //catch for the entire window + ref_window.document.addEventListener("mouseup", this._mouseup_callback ); var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); var skip_dragging = false; @@ -1957,7 +2157,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) var block_drag_node = false; //double clicking - var now = new Date().getTime(); + var now = window.performance.now(); if ((now - this.last_mouseclick) < 300 && this.selected_nodes[n.id]) { //double click node @@ -2012,7 +2212,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.last_mouse[0] = e.localX; this.last_mouse[1] = e.localY; - this.last_mouseclick = new Date().getTime(); + this.last_mouseclick = window.performance.now(); this.canvas_mouse = [e.canvasX, e.canvasY]; /* @@ -2023,7 +2223,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.graph.change(); //this is to ensure to defocus(blur) if a text input element is on focus - if(!document.activeElement || (document.activeElement.nodeName.toLowerCase() != "input" && document.activeElement.nodeName.toLowerCase() != "textarea")) + if(!ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != "input" && ref_window.document.activeElement.nodeName.toLowerCase() != "textarea")) e.preventDefault(); e.stopPropagation(); return false; @@ -2041,8 +2241,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.dragging_canvas) { - this.graph.config.canvas_offset[0] += delta[0] / this.graph.config.canvas_scale; - this.graph.config.canvas_offset[1] += delta[1] / this.graph.config.canvas_scale; + this.offset[0] += delta[0] / this.scale; + this.offset[1] += delta[1] / this.scale; this.dirty_canvas = true; this.dirty_bgcanvas = true; } @@ -2055,12 +2255,12 @@ LGraphCanvas.prototype.processMouseMove = function(e) var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); //remove mouseover flag - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - if(this.graph.nodes[i].mouseOver && n != this.graph.nodes[i]) + if(this.graph._nodes[i].mouseOver && n != this.graph._nodes[i]) { //mouse leave - this.graph.nodes[i].mouseOver = false; + this.graph._nodes[i].mouseOver = false; if(this.node_over && this.node_over.onMouseLeave) this.node_over.onMouseLeave(e); this.node_over = null; @@ -2117,8 +2317,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.node_dragged && !this.live_mode) { /* - this.node_dragged.pos[0] += delta[0] / this.graph.config.canvas_scale; - this.node_dragged.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); */ @@ -2127,8 +2327,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) { var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.graph.config.canvas_scale; - n.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); } @@ -2139,8 +2339,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.resizing_node && !this.live_mode) { - this.resizing_node.size[0] += delta[0] / this.graph.config.canvas_scale; - this.resizing_node.size[1] += delta[1] / this.graph.config.canvas_scale; + this.resizing_node.size[0] += delta[0] / this.scale; + this.resizing_node.size[1] += delta[1] / this.scale; var max_slots = Math.max( this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); if(this.resizing_node.size[1] < max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4) this.resizing_node.size[1] = max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4; @@ -2169,6 +2369,9 @@ LGraphCanvas.prototype.processMouseUp = function(e) { if(!this.graph) return; + var window = this.getCanvasWindow(); + var document = window.document; + document.removeEventListener("mousemove", this._mousemove_callback, true ); this.canvas.addEventListener("mousemove", this._mousemove_callback, true); document.removeEventListener("mouseup", this._mouseup_callback, true ); @@ -2334,7 +2537,7 @@ LGraphCanvas.prototype.processMouseWheel = function(e) this.adjustMouseEvent(e); - var zoom = this.graph.config.canvas_scale; + var zoom = this.scale; if (delta > 0) zoom *= 1.1; @@ -2419,13 +2622,13 @@ LGraphCanvas.prototype.selectNode = function(node) LGraphCanvas.prototype.selectAllNodes = function() { - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; if(!n.selected && n.onSelected) n.onSelected(); n.selected = true; - this.selected_nodes[this.graph.nodes[i].id] = n; + this.selected_nodes[this.graph._nodes[i].id] = n; } this.setDirty(true); @@ -2458,8 +2661,8 @@ LGraphCanvas.prototype.deleteSelectedNodes = function() LGraphCanvas.prototype.centerOnNode = function(node) { - this.graph.config.canvas_offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.graph.config.canvas_scale); - this.graph.config.canvas_offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.graph.config.canvas_scale); + 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.setDirty(true,true); } @@ -2469,8 +2672,8 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) e.localX = e.pageX - b.left; e.localY = e.pageY - b.top; - e.canvasX = e.localX / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0]; - e.canvasY = e.localY / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]; + e.canvasX = e.localX / this.scale - this.offset[0]; + e.canvasY = e.localY / this.scale - this.offset[1]; } LGraphCanvas.prototype.setZoom = function(value, zooming_center) @@ -2480,18 +2683,18 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) var center = this.convertOffsetToCanvas( zooming_center ); - this.graph.config.canvas_scale = value; + this.scale = value; - if(this.graph.config.canvas_scale > 4) - this.graph.config.canvas_scale = 4; - else if(this.graph.config.canvas_scale < 0.1) - this.graph.config.canvas_scale = 0.1; + if(this.scale > 4) + this.scale = 4; + else if(this.scale < 0.1) + this.scale = 0.1; var new_center = this.convertOffsetToCanvas( zooming_center ); var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - this.graph.config.canvas_offset[0] += delta_offset[0]; - this.graph.config.canvas_offset[1] += delta_offset[1]; + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -2499,13 +2702,13 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) LGraphCanvas.prototype.convertOffsetToCanvas = function(pos) { - return [pos[0] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0], pos[1] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]]; + return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; } LGraphCanvas.prototype.convertCanvasToOffset = function(pos) { - return [(pos[0] + this.graph.config.canvas_offset[0]) * this.graph.config.canvas_scale, - (pos[1] + this.graph.config.canvas_offset[1]) * this.graph.config.canvas_scale ]; + return [(pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale ]; } LGraphCanvas.prototype.convertEventToCanvas = function(e) @@ -2516,20 +2719,20 @@ LGraphCanvas.prototype.convertEventToCanvas = function(e) LGraphCanvas.prototype.bringToFront = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.push(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.push(n); } LGraphCanvas.prototype.sendToBack = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.unshift(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.unshift(n); } /* Interaction */ @@ -2541,9 +2744,9 @@ LGraphCanvas.prototype.sendToBack = function(n) LGraphCanvas.prototype.computeVisibleNodes = function() { var visible_nodes = []; - for (var i in this.graph.nodes) + for (var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; //skip rendering nodes in live mode if(this.live_mode && !n.onDrawBackground && !n.onDrawForeground) @@ -2560,14 +2763,14 @@ LGraphCanvas.prototype.computeVisibleNodes = function() LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { //fps counting - var now = new Date().getTime(); + var now = window.performance.now(); this.render_time = (now - this.last_draw_time)*0.001; this.last_draw_time = now; if(this.graph) { - var start = [-this.graph.config.canvas_offset[0], -this.graph.config.canvas_offset[1] ]; - var end = [start[0] + this.canvas.width / this.graph.config.canvas_scale, start[1] + this.canvas.height / this.graph.config.canvas_scale]; + 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],end[1]]); } @@ -2626,8 +2829,8 @@ LGraphCanvas.prototype.drawFrontCanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //draw nodes var drawn_nodes = 0; @@ -2658,10 +2861,9 @@ LGraphCanvas.prototype.drawFrontCanvas = function() //current connection if(this.connecting_pos != null) { - ctx.lineWidth = LGraphCanvas.link_width; - ctx.fillStyle = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; - ctx.strokeStyle = ctx.fillStyle; - this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]] ); + ctx.lineWidth = this.connections_width; + var link_color = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; + this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]], link_color ); ctx.beginPath(); ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2); @@ -2710,13 +2912,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //render BG - if(this.background_image && this.graph.config.canvas_scale > 0.5) + if(this.background_image && this.scale > 0.5) { - ctx.globalAlpha = 1.0 - 0.5 / this.graph.config.canvas_scale; + ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha; ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false if(!this._bg_img || this._bg_img.name != this.background_image) { @@ -2756,8 +2958,7 @@ LGraphCanvas.prototype.drawBgcanvas = function() ctx.strokeStyle = "#235"; ctx.strokeRect(0,0,canvas.width,canvas.height); - /* - if(this.render_shadows) + if(this.render_connections_shadows) { ctx.shadowColor = "#000"; ctx.shadowOffsetX = 0; @@ -2766,12 +2967,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() } else ctx.shadowColor = "rgba(0,0,0,0)"; - */ //draw connections if(!this.live_mode) this.drawConnections(ctx); + ctx.shadowColor = "rgba(0,0,0,0)"; + //restore state ctx.restore(); } @@ -2822,8 +3024,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(!node.flags.collapsed) { ctx.shadowColor = "transparent"; - if(node.onDrawBackground) - node.onDrawBackground(ctx); + //if(node.onDrawBackground) + // node.onDrawBackground(ctx); if(node.onDrawForeground) node.onDrawForeground(ctx); } @@ -2841,13 +3043,16 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } */ + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + //clip if required (mask) var shape = node.shape || "box"; var size = new Float32Array(node.size); if(node.flags.collapsed) size.set([LiteGraph.NODE_COLLAPSED_WIDTH, 0]); - //Start cliping + //Start clipping if(node.flags.clip_area) { ctx.save(); @@ -2874,9 +3079,9 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) //connection slots ctx.textAlign = "left"; - ctx.font = "12px Arial"; + ctx.font = this.inner_text_font; - var render_text = node.graph.config.canvas_scale > 0.6; + var render_text = this.scale > 0.6; //render inputs and outputs if(!node.flags.collapsed) @@ -2887,9 +3092,9 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) { var slot = node.inputs[i]; - ctx.globalAlpha = 1.0; + ctx.globalAlpha = editor_alpha; if (this.connecting_node != null && this.connecting_output.type != 0 && node.inputs[i].type != 0 && this.connecting_output.type != node.inputs[i].type) - ctx.globalAlpha = 0.4; + ctx.globalAlpha = 0.4 * editor_alpha; ctx.fillStyle = slot.link != null ? "#7F7" : "#AAA"; @@ -2920,7 +3125,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) //output connection slots if(this.connecting_node) - ctx.globalAlpha = 0.4; + ctx.globalAlpha = 0.4 * editor_alpha; ctx.lineWidth = 1; @@ -2965,7 +3170,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } ctx.textAlign = "left"; - ctx.globalAlpha = 1.0; + ctx.globalAlpha = 1; if(node.onDrawForeground) node.onDrawForeground(ctx); @@ -2973,6 +3178,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(node.flags.clip_area) ctx.restore(); + + ctx.globalAlpha = 1.0; } /* Renders the node shape */ @@ -2988,7 +3195,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo grad.addColorStop(0.5, fgcolor || LiteGraph.NODE_DEFAULT_COLOR); grad.addColorStop(1, bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR); ctx.fillStyle = grad; - */ + //*/ var title_height = LiteGraph.NODE_TITLE_HEIGHT; @@ -3004,7 +3211,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo } ctx.beginPath(); - ctx.rect(0,no_title ? 0.5 : -title_height + 1,size[0]+1, no_title ? size[1] : size[1] + title_height); + ctx.rect(0,no_title ? 0.5 : -title_height + 0.5,size[0]+1, no_title ? size[1] : size[1] + title_height); } else if (node.shape == "round") { @@ -3035,7 +3242,8 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo if(!no_title) { ctx.fillStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; - + var old_alpha = ctx.globalAlpha; + ctx.globalAlpha = 0.5 * old_alpha; if(shape == "box") { ctx.beginPath(); @@ -3058,10 +3266,11 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo else ctx.rect(3,-title_height + 3,title_height - 6,title_height - 6); ctx.fill(); + ctx.globalAlpha = old_alpha; //title text - ctx.font = "bold 12px Arial"; - if(node.name != "" && node.graph.config.canvas_scale > 0.8) + ctx.font = this.title_text_font; + if(node.name != "" && this.scale > 0.8) { ctx.fillStyle = "#222"; ctx.fillText(node.name,16,13-title_height ); @@ -3128,14 +3337,15 @@ LGraphCanvas.link_colors = ["#AAC","#ACA","#CAA"]; LGraphCanvas.prototype.drawConnections = function(ctx) { //draw connections - ctx.lineWidth = LGraphCanvas.link_width; + ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; //for every node - for (var n in this.graph.nodes) + for (var n in this.graph._nodes) { - var node = this.graph.nodes[n]; + var node = this.graph._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 in node.inputs) @@ -3157,16 +3367,14 @@ LGraphCanvas.prototype.drawConnections = function(ctx) var color = LGraphCanvas.link_type_colors[node.inputs[i].type]; if(color == null) color = LGraphCanvas.link_colors[node.id % LGraphCanvas.link_colors.length]; - ctx.fillStyle = ctx.strokeStyle = color; - this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i) ); + this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i), color ); } } + ctx.globalAlpha = 1; } -LGraphCanvas.prototype.renderLink = function(ctx,a,b) +LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) { - var curved_lines = true; - if(!this.highquality_render) { ctx.beginPath(); @@ -3178,32 +3386,44 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b) var dist = distance(a,b); + if(this.render_connections_border) + ctx.lineWidth = this.connections_width + 4; + ctx.beginPath(); - if(curved_lines) + 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 + 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.render_connections_border) + { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); //render arrow - if(this.graph.config.canvas_scale > 0.6) + if(this.render_connection_arrows && this.scale > 0.6) { //get two points in the bezier curve var pos = this.computeConnectionPoint(a,b,0.5); var pos2 = this.computeConnectionPoint(a,b,0.51); var angle = 0; - if(curved_lines) + if(this.render_curved_connections) angle = -Math.atan2( pos2[0] - pos[0], pos2[1] - pos[1]); else angle = b[1] > a[1] ? 0 : Math.PI; @@ -3238,6 +3458,7 @@ LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t) return [x,y]; } +/* LGraphCanvas.prototype.resizeCanvas = function(width,height) { this.canvas.width = width; @@ -3248,12 +3469,63 @@ LGraphCanvas.prototype.resizeCanvas = function(width,height) this.bgcanvas.height = this.canvas.height; this.draw(true,true); } +*/ -LGraphCanvas.prototype.switchLiveMode = function() +LGraphCanvas.prototype.resize = function(width, height) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; + if(!width && !height) + { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if(this.canvas.width == width && this.canvas.height == height) + return; + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true,true); +} + + +LGraphCanvas.prototype.switchLiveMode = function(transition) +{ + if(!transition) + { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if(this.live_mode) + { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if(delta < 1 && self.editor_alpha < 0.01) + { + clearInterval(t); + if(delta < 1) + self.live_mode = true; + } + if(delta > 1 && self.editor_alpha > 0.99) + { + clearInterval(t); + self.editor_alpha = 1; + } + },1); } LGraphCanvas.prototype.onNodeSelectionChange = function(node) @@ -3280,6 +3552,9 @@ LGraphCanvas.prototype.touchHandler = function(event) //initMouseEvent(type, canBubble, cancelable, view, clickCount, // screenX, screenY, clientX, clientY, ctrlKey, // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); simulatedEvent.initMouseEvent(type, true, true, window, 1, @@ -3294,13 +3569,15 @@ LGraphCanvas.prototype.touchHandler = function(event) LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) { + var window = canvas.getCanvasWindow(); + var values = LiteGraph.getNodeTypesCategories(); var entries = {}; for(var i in values) if(values[i]) entries[ i ] = { value: values[i], content: values[i] , is_menu: true }; - var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, window); function inner_clicked(v, e) { @@ -3310,7 +3587,7 @@ LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) for(var i in node_types) values.push( { content: node_types[i].title, value: node_types[i].type }); - LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}); + LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}, window); return false; } @@ -3391,7 +3668,19 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu) function inner_clicked(v) { if(!node) return; - node.addOutput(v.value[0],v.value[1]); + + var value = v.value[1]; + + if(value && (value.constructor === Object || value.constructor === Array)) //submenu + { + var entries = []; + for(var i in value) + entries.push({content: i, value: value[i]}); + LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + return false; + } + else + node.addOutput(v.value[0],v.value[1]); } return false; @@ -3403,6 +3692,11 @@ LGraphCanvas.onMenuNodeCollapse = function(node) node.graph.canvas.setDirty(true,true); } +LGraphCanvas.onMenuNodePin = function(node) +{ + node.pin(); +} + LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) { var values = []; @@ -3482,6 +3776,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) {content:"Outputs", is_menu: true, disabled:true, callback: LGraphCanvas.onMenuNodeOutputs }, null, {content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse }, + {content:"Pin", callback: LGraphCanvas.onMenuNodePin }, {content:"Colors", is_menu: true, callback: LGraphCanvas.onMenuNodeColors }, {content:"Shapes", is_menu: true, callback: LGraphCanvas.onMenuNodeShapes }, null, @@ -3506,7 +3801,9 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) LGraphCanvas.prototype.processContextualMenu = function(node,event) { var that = this; - var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}); + var win = this.getCanvasWindow(); + + var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}, win); function inner_option_clicked(v,e) { @@ -3642,15 +3939,18 @@ function num2hex(triplet) { /* LiteGraph GUI elements *************************************/ -LiteGraph.createContextualMenu = function(values,options) +LiteGraph.createContextualMenu = function(values,options, ref_window) { options = options || {}; this.options = options; + //allows to create graph canvas in separate window + ref_window = ref_window || window; + if(!options.from) LiteGraph.closeAllContextualMenus(); - var root = document.createElement("div"); + var root = ref_window.document.createElement("div"); root.className = "litecontextualmenu litemenubar-panel"; this.root = root; var style = root.style; @@ -3672,7 +3972,7 @@ LiteGraph.createContextualMenu = function(values,options) for(var i in values) { var item = values[i]; - var element = document.createElement("div"); + var element = ref_window.document.createElement("div"); element.className = "litemenu-entry"; if(item == null) @@ -3707,7 +4007,7 @@ LiteGraph.createContextualMenu = function(values,options) root.addEventListener("mouseout", function(e) { //console.log("OUT!"); var aux = e.toElement; - while(aux != this && aux != document) + while(aux != this && aux != ref_window.document) aux = aux.parentNode; if(aux == this) return; @@ -3716,17 +4016,8 @@ LiteGraph.createContextualMenu = function(values,options) this.closeMenu(); }); - /* MS specific - root.addEventListener("mouseleave", function(e) { - - this.mouse_inside = false; - if(!this.block_close) - this.closeMenu(); - }); - */ - //insert before checking position - document.body.appendChild(root); + ref_window.document.body.appendChild(root); var root_rect = root.getClientRects()[0]; @@ -3745,7 +4036,7 @@ LiteGraph.createContextualMenu = function(values,options) if(options.left) left = options.left; - var rect = document.body.getClientRects()[0]; + var rect = ref_window.document.body.getClientRects()[0]; if(options.from) { @@ -3786,7 +4077,7 @@ LiteGraph.createContextualMenu = function(values,options) options.from.closeMenu(); } if(this.parentNode) - document.body.removeChild(this); + ref_window.document.body.removeChild(this); }; return root; @@ -3813,7 +4104,27 @@ LiteGraph.extendClass = function(origin, target) if(origin.prototype) //copy prototype properties for(var i in origin.prototype) target.prototype[i] = origin.prototype[i]; -} +} + +/* +LiteGraph.createNodetypeWrapper = function( class_object ) +{ + //create Nodetype object +} +//LiteGraph.registerNodeType("scene/global", LGraphGlobal ); +*/ + +if( !window["requestAnimationFrame"] ) +{ + window.requestAnimationFrame = window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + (function( callback ){ + window.setTimeout(callback, 1000 / 60); + }); +} + + + //basic nodes LiteGraph.registerNodeType("basic/const",{ @@ -4633,7 +4944,7 @@ LiteGraph.registerNodeType("network/network_callback",{ ctx.textAlign = "left"; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.onDrawImageKnob(ctx); }, @@ -4781,7 +5092,7 @@ LiteGraph.registerNodeType("network/network_callback",{ ctx.drawImage(this.imgfg, 2+(this.size[0]-4)*this.value - this.imgfg.width*0.5,-this.imgfg.height*0.5 + 10); }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.onDrawImage(ctx); }, @@ -5005,7 +5316,7 @@ LiteGraph.registerNodeType("network/network_callback",{ ctx.textAlign = "left"; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.drawBevelShape(ctx); }, @@ -5069,7 +5380,7 @@ LiteGraph.registerNodeType("network/network_callback",{ if( v != undefined ) this.properties["value"] = v; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { //border ctx.lineWidth = 1; @@ -5089,7 +5400,7 @@ LiteGraph.registerNodeType("network/network_callback",{ inputs: [["",0]], properties:{value:"...",font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { //ctx.fillStyle="#000"; //ctx.fillRect(0,0,100,60); @@ -5199,7 +5510,7 @@ LiteGraph.registerNodeType("network/network_callback",{ this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { if(this.lineargradient == null) this.createGradient(ctx); diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 5b884266d..f3387ecb4 100644 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,28 +1,29 @@ -var LiteGraph={NODE_TITLE_HEIGHT:16,NODE_SLOT_HEIGHT:15,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,CANVAS_GRID_SIZE:10,NODE_DEFAULT_COLOR:"#999",NODE_DEFAULT_BGCOLOR:"#444",NODE_DEFAULT_BOXCOLOR:"#AEF",NODE_DEFAULT_SHAPE:"box",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],node_images_path:"",debug:!1,registered_node_types:{},graphs:[],registerNodeType:function(a,b){b.type=a;LiteGraph.debug&&console.log("Node registered: "+a);a.split("/");var c=a.lastIndexOf("/"); +var LiteGraph={NODE_TITLE_HEIGHT:16,NODE_SLOT_HEIGHT:15,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,CANVAS_GRID_SIZE:10,NODE_DEFAULT_COLOR:"#999",NODE_DEFAULT_BGCOLOR:"#444",NODE_DEFAULT_BOXCOLOR:"#AEF",NODE_DEFAULT_SHAPE:"box",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],node_images_path:"",debug:!1,registered_node_types:{},registerNodeType:function(a,b){b.type=a;LiteGraph.debug&&console.log("Node registered: "+a);a.split("/");var c=a.lastIndexOf("/"); b.category=a.substr(0,c);if(b.prototype)for(var d in LGraphNode.prototype)b.prototype[d]||(b.prototype[d]=LGraphNode.prototype[d]);this.registered_node_types[a]=b},createNode:function(a,b,c){var d=this.registered_node_types[a];if(!d)return LiteGraph.debug&&console.log('GraphNode type "'+a+'" not registered.'),null;var e=d.prototype||d;b=b||e.title||d.title||a;var f=null;if(d.prototype)f=new d(b);else{f=new LGraphNode(b);f.inputs=[];f.outputs=[];for(var g in e)if("inputs"==g)for(var h in e[g])f.addInput(e[g][h][0], e[g][h][1],e[g][h][2]);else if("outputs"==g)for(h in e[g])f.addOutput(e[g][h][0],e[g][h][1],e[g][h][2]);else f[g]=e[g].concat?e[g].concat():"object"==typeof e[g]?LiteGraph.cloneObject(e[g]):e[g];d.size&&(f.size=d.size.concat())}f.type=a;f.name||(f.name=b);f.flags||(f.flags={});f.size||(f.size=f.computeSize());f.pos||(f.pos=LiteGraph.DEFAULT_POSITION.concat());if(c)for(g in c)f[g]=c[g];return f},getNodeType:function(a){return this.registered_node_types[a]},getNodeTypesInCategory:function(a){var b= [],c;for(c in this.registered_node_types)""==a?null==this.registered_node_types[c].category&&b.push(this.registered_node_types[c]):this.registered_node_types[c].category==a&&b.push(this.registered_node_types[c]);return b},getNodeTypesCategories:function(){var a={"":1},b;for(b in this.registered_node_types)this.registered_node_types[b].category&&!this.registered_node_types[b].skip_list&&(a[this.registered_node_types[b].category]=1);var c=[];for(b in a)c.push(b);return c},reloadNodes:function(a){var b= -document.getElementsByTagName("script"),c=[],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")},cloneObject:function(a){return JSON.parse(JSON.stringify(a))}};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.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)}}; +document.getElementsByTagName("script"),c=[],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)}}LiteGraph.debug&&console.log("Nodes reloaded")}, +cloneObject:function(a,b){var c=JSON.parse(JSON.stringify(a));if(!b)return c;for(var d in c)b[d]=c[d];return b}};function LGraph(){LiteGraph.debug&&console.log("Graph created");this.list_of_graphcanvas=null;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={};this.fixedtime=this.runningtime=this.globaltime=0;this.elapsed_time=this.fixedtime_lapse=0.01;this.starttime=0;this.globals={};this.graph={};this.debug=!0;this.change();this.sendActionToCanvas("clear")}; +LGraph.prototype.attachCanvas=function(a){if(a.constructor!=LGraphCanvas)throw"attachCanvas expects a LGraphCanvas instance";a.graph&&a.graph!=this&&a.graph.detachCanvas(a);a.graph=this;this.list_of_graphcanvas||(this.list_of_graphcanvas=[]);this.list_of_graphcanvas.push(a)};LGraph.prototype.detachCanvas=function(a){var b=this.list_of_graphcanvas.indexOf(a);-1!=b&&(a.graph=null,this.list_of_graphcanvas.splice(b,1))}; +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=window.performance.now();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 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;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;bb&&this.inputs[b].pos?[this.pos[0]+this.inputs[b].pos[0],this.pos[1]+this.inputs[b].pos[1]]:!a&&this.outputs.length>b&&this.outputs[b].pos?[this.pos[0]+this.outputs[b].pos[0],this.pos[1]+this.outputs[b].pos[1]]: a?[this.pos[0],this.pos[1]+10+b*LiteGraph.NODE_SLOT_HEIGHT]:[this.pos[0]+this.size[0]+1,this.pos[1]+10+b*LiteGraph.NODE_SLOT_HEIGHT]};LGraphNode.prototype.alignToGrid=function(){this.pos[0]=LiteGraph.CANVAS_GRID_SIZE*Math.round(this.pos[0]/LiteGraph.CANVAS_GRID_SIZE);this.pos[1]=LiteGraph.CANVAS_GRID_SIZE*Math.round(this.pos[1]/LiteGraph.CANVAS_GRID_SIZE)}; -LGraphNode.prototype.copyFromObject=function(a,b){for(var c in a)(!b||"outputs"!=c&&"inputs"!=c)&&"console"!=c&&null!=a[c]&&(this[c]=a[c].concat?a[c].concat():"object"==typeof a[c]?LiteGraph.cloneObject(a[c]):a[c])}; LGraphNode.prototype.clone=function(){var a=LiteGraph.createNode(this.type);a.size=this.size.concat();if(this.inputs)for(var b=0,c=this.inputs.length;bLGraphNode.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};LGraphNode.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.canvas&&(a&&(this.graph.canvas.dirty_canvas=!0),b&&(this.graph.canvas.dirty_bgcanvas=!0))}; -LGraphNode.prototype.loadImage=function(a){var b=new Image;b.src=LiteGraph.node_images_path+a;b.ready=!1;var c=this;b.onload=function(){this.ready=!0;c.setDirtyCanvas(!0)};return b}; +LGraphNode.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>LGraphNode.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};LGraphNode.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};LGraphNode.prototype.loadImage=function(a){var b=new Image;b.src=LiteGraph.node_images_path+a;b.ready=!1;var c=this;b.onload=function(){this.ready=!0;c.setDirtyCanvas(!0)};return b}; LGraphNode.prototype.executeAction=function(a){if(""==a)return!1;if(-1!=a.indexOf(";")||-1!=a.indexOf("}"))return this.trace("Error: Action contains unsafe characters"),!1;var b=a.split("(")[0];if("function"!=typeof this[b])return this.trace("Error: Action not found on node: "+b),!1;try{b=eval,eval=null,(new Function("with(this) { "+a+"}")).call(this),eval=b}catch(c){return this.trace("Error executing action {"+a+"} :"+c),!1}return!0}; -LGraphNode.prototype.captureInput=function(a){this.graph&&this.graph.canvas&&(a||this.graph.canvas.node_capturing_input==this)&&(this.graph.canvas.node_capturing_input=a?this:null,this.graph.debug&&console.log(this.name+": Capturing input "+(a?"ON":"OFF")))};LGraphNode.prototype.collapse=function(){this.flags.collapsed=this.flags.collapsed?!1:!0;this.setDirtyCanvas(!0,!0)};LGraphNode.prototype.pin=function(){this.flags.pinned=this.flags.pinned?!1:!0}; -LGraphNode.prototype.localToScreen=function(a,b){return[(a+this.pos[0])*this.graph.config.canvas_scale+this.graph.config.canvas_offset[0],(b+this.pos[1])*this.graph.config.canvas_scale+this.graph.config.canvas_offset[1]]}; -function LGraphCanvas(a,b){if(void 0===b)throw"No graph assigned";"undefined"!=typeof window&&(window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}());if(this.graph=b)b.canvas=this;this.setCanvas(a);this.clear();this.startRendering()}LGraphCanvas.link_type_colors={number:"#AAC",node:"#DCA"};LGraphCanvas.link_width=2; -LGraphCanvas.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.selected_nodes={};this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highquality_render=!0;this.pause_rendering=!1;this.dirty_bgcanvas=this.dirty_canvas=this.render_shadows=!0;this.dirty_area=null;this.render_only_selected=!0;this.live_mode=!1;this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.node_in_panel=null;this.last_mouse=[0,0];this.last_mouseclick= -0;if(this.onClear)this.onClear()};LGraphCanvas.prototype.setGraph=function(a){if(this.graph!=a){this.clear();this.graph&&(this.graph.canvas=null);if(this.graph=a)this.graph.canvas=this;this.setDirty(!0,!0)}};LGraphCanvas.prototype.resize=function(a,b){if(this.canvas.width!=a||this.canvas.height!=b)this.canvas.width=a,this.canvas.height=b,this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height,this.setDirty(!0,!0)}; +LGraphNode.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas){var b=this.graph.list_of_graphcanvas,c;for(c in b){var d=b[c];if(a||d.node_capturing_input==this)d.node_capturing_input=a?this:null,this.graph.debug&&console.log(this.name+": Capturing input "+(a?"ON":"OFF"))}}};LGraphNode.prototype.collapse=function(){this.flags.collapsed=this.flags.collapsed?!1:!0;this.setDirtyCanvas(!0,!0)}; +LGraphNode.prototype.pin=function(a){this.flags.pinned=void 0===a?!this.flags.pinned:a};LGraphNode.prototype.localToScreen=function(a,b,c){return[(a+this.pos[0])*c.scale+c.offset[0],(b+this.pos[1])*c.scale+c.offset[1]]};function LGraphCanvas(a,b){"string"==typeof a&&(a=document.querySelector(a));if(!a)throw"no canvas found";b&&b.attachCanvas(this);this.setCanvas(a);this.clear();this.startRendering()}LGraphCanvas.link_type_colors={number:"#AAC",node:"#DCA"}; +LGraphCanvas.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.scale=1;this.offset=[0,0];this.selected_nodes={};this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highquality_render=!0;this.editor_alpha=1;this.pause_rendering=!1;this.dirty_bgcanvas=this.dirty_canvas=this.render_shadows=!0;this.dirty_area=null;this.render_only_selected=!0;this.live_mode=!1;this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.node_in_panel= +null;this.last_mouse=[0,0];this.last_mouseclick=0;this.title_text_font="bold 14px Arial";this.inner_text_font="normal 12px Arial";this.render_connections_shadows=!1;this.render_connection_arrows=this.render_curved_connections=this.render_connections_border=!0;this.connections_width=4;if(this.onClear)this.onClear()};LGraphCanvas.prototype.setGraph=function(a){this.graph!=a&&(this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))}; LGraphCanvas.prototype.setCanvas=function(a){var b=this;"string"==typeof a&&(a=document.getElementById(a));if(null==a)throw"Error creating LiteGraph canvas: Canvas not found";if(a!=this.canvas){this.canvas=a;this.canvas.className+=" lgraphcanvas";this.canvas.data=this;this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==this.canvas.getContext)throw"This browser doesnt support Canvas"; this.ctx=this.canvas.getContext("2d");this.bgctx=this.bgcanvas.getContext("2d");this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);this.canvas.addEventListener("mousedown",this.processMouseDown.bind(this));this.canvas.addEventListener("mousemove",this._mousemove_callback);this.canvas.addEventListener("contextmenu",function(a){a.preventDefault();return!1});this.canvas.addEventListener("mousewheel",this.processMouseWheel.bind(this),!1);this.canvas.addEventListener("DOMMouseScroll", this.processMouseWheel.bind(this),!1);this.canvas.addEventListener("touchstart",this.touchHandler,!0);this.canvas.addEventListener("touchmove",this.touchHandler,!0);this.canvas.addEventListener("touchend",this.touchHandler,!0);this.canvas.addEventListener("touchcancel",this.touchHandler,!0);this.canvas.addEventListener("keydown",function(a){b.processKeyDown(a)});this.canvas.addEventListener("keyup",function(a){b.processKeyUp(a)})}}; -LGraphCanvas.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};LGraphCanvas.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();this.is_rendering&&window.requestAnimFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};LGraphCanvas.prototype.stopRendering=function(){this.is_rendering=!1}; -LGraphCanvas.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);this.canvas.removeEventListener("mousemove",this._mousemove_callback);document.addEventListener("mousemove",this._mousemove_callback);document.addEventListener("mouseup",this._mouseup_callback);var b=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(1==a.which){if(!a.shiftKey){var c=[],d;for(d in this.selected_nodes)this.selected_nodes[d]!=b&&c.push(this.selected_nodes[d]);for(d in c)this.processNodeDeselected(c[d])}c= -!1;if(b){this.live_mode||b.flags.pinned||this.bringToFront(b);var e=!1;if(!this.connecting_node&&!b.flags.collapsed&&!this.live_mode){if(b.outputs){d=0;for(var f=b.outputs.length;d(new Date).getTime()-this.last_mouseclick&&this.selected_nodes[b.id]){if(b.onDblClick)b.onDblClick(a);this.processNodeDblClicked(b);d=!0}b.onMouseDown&&b.onMouseDown(a)?d=!0:this.live_mode&&(d=c=!0);d||(this.allow_dragnodes&& -(this.node_dragged=b),this.selected_nodes[b.id]||this.processNodeSelected(b,a));this.dirty_canvas=!0}}else c=!0;c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&this.processContextualMenu(b,a);this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=(new Date).getTime();this.canvas_mouse=[a.canvasX,a.canvasY];this.graph.change();(!document.activeElement||"input"!=document.activeElement.nodeName.toLowerCase()&&"textarea"!=document.activeElement.nodeName.toLowerCase())&& -a.preventDefault();a.stopPropagation();return!1}}; -LGraphCanvas.prototype.processMouseMove=function(a){if(this.graph){this.adjustMouseEvent(a);var b=[a.localX,a.localY],c=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse=[a.canvasX,a.canvasY];if(this.dragging_canvas)this.graph.config.canvas_offset[0]+=c[0]/this.graph.config.canvas_scale,this.graph.config.canvas_offset[1]+=c[1]/this.graph.config.canvas_scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else{this.connecting_node&&(this.dirty_canvas=!0);var b=this.graph.getNodeOnPos(a.canvasX, -a.canvasY,this.visible_nodes),d;for(d in this.graph.nodes)if(this.graph.nodes[d].mouseOver&&b!=this.graph.nodes[d]){this.graph.nodes[d].mouseOver=!1;if(this.node_over&&this.node_over.onMouseLeave)this.node_over.onMouseLeave(a);this.node_over=null;this.dirty_canvas=!0}if(b){if(!b.mouseOver&&(b.mouseOver=!0,this.node_over=b,this.dirty_canvas=!0,b.onMouseEnter))b.onMouseEnter(a);if(b.onMouseMove)b.onMouseMove(a);if(this.connecting_node){var e=this._highlight_input||[0,0],f=this.isOverNodeInput(b,a.canvasX, -a.canvasY,e);if(-1!=f&&b.inputs[f]){if(f=b.inputs[f].type,f==this.connecting_output.type||"*"==f||"*"==this.connecting_output.type)this._highlight_input=e}else this._highlight_input=null}isInsideRectangle(a.canvasX,a.canvasY,b.pos[0]+b.size[0]-5,b.pos[1]+b.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor=null}else this.canvas.style.cursor=null;if(this.node_capturing_input&&this.node_capturing_input!=b&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a); -if(this.node_dragged&&!this.live_mode){for(d in this.selected_nodes)b=this.selected_nodes[d],b.pos[0]+=c[0]/this.graph.config.canvas_scale,b.pos[1]+=c[1]/this.graph.config.canvas_scale,b.pos[0]=Math.round(b.pos[0]),b.pos[1]=Math.round(b.pos[1]);this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]+=c[0]/this.graph.config.canvas_scale,this.resizing_node.size[1]+=c[1]/this.graph.config.canvas_scale,c=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: -0,this.resizing_node.outputs?this.resizing_node.outputs.length:0),this.resizing_node.size[1]window.performance.now()-this.last_mouseclick&&this.selected_nodes[c.id]){if(c.onDblClick)c.onDblClick(a);this.processNodeDblClicked(c);e=!0}c.onMouseDown&&c.onMouseDown(a)? +e=!0:this.live_mode&&(e=d=!0);e||(this.allow_dragnodes&&(this.node_dragged=c),this.selected_nodes[c.id]||this.processNodeSelected(c,a));this.dirty_canvas=!0}}else d=!0;d&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&this.processContextualMenu(c,a);this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=window.performance.now();this.canvas_mouse=[a.canvasX,a.canvasY];this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&& +"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();return!1}}; +LGraphCanvas.prototype.processMouseMove=function(a){if(this.graph){this.adjustMouseEvent(a);var b=[a.localX,a.localY],c=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse=[a.canvasX,a.canvasY];if(this.dragging_canvas)this.offset[0]+=c[0]/this.scale,this.offset[1]+=c[1]/this.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else{this.connecting_node&&(this.dirty_canvas=!0);var b=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),d;for(d in this.graph._nodes)if(this.graph._nodes[d].mouseOver&& +b!=this.graph._nodes[d]){this.graph._nodes[d].mouseOver=!1;if(this.node_over&&this.node_over.onMouseLeave)this.node_over.onMouseLeave(a);this.node_over=null;this.dirty_canvas=!0}if(b){if(!b.mouseOver&&(b.mouseOver=!0,this.node_over=b,this.dirty_canvas=!0,b.onMouseEnter))b.onMouseEnter(a);if(b.onMouseMove)b.onMouseMove(a);if(this.connecting_node){var e=this._highlight_input||[0,0],f=this.isOverNodeInput(b,a.canvasX,a.canvasY,e);if(-1!=f&&b.inputs[f]){if(f=b.inputs[f].type,f==this.connecting_output.type|| +"*"==f||"*"==this.connecting_output.type)this._highlight_input=e}else this._highlight_input=null}isInsideRectangle(a.canvasX,a.canvasY,b.pos[0]+b.size[0]-5,b.pos[1]+b.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor=null}else this.canvas.style.cursor=null;if(this.node_capturing_input&&this.node_capturing_input!=b&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a);if(this.node_dragged&&!this.live_mode){for(d in this.selected_nodes)b=this.selected_nodes[d], +b.pos[0]+=c[0]/this.scale,b.pos[1]+=c[1]/this.scale,b.pos[0]=Math.round(b.pos[0]),b.pos[1]=Math.round(b.pos[1]);this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]+=c[0]/this.scale,this.resizing_node.size[1]+=c[1]/this.scale,c=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0),this.resizing_node.size[1]b&&(c*=1/1.1);this.setZoom(c,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}}; +LGraphCanvas.prototype.processKeyUp=function(a){if(this.graph){if(this.selected_nodes)for(var b in this.selected_nodes)if(this.selected_nodes[b].onKeyUp)this.selected_nodes[b].onKeyUp(a);this.graph.change()}};LGraphCanvas.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var c=this.scale;0b&&(c*=1/1.1);this.setZoom(c,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}}; LGraphCanvas.prototype.processNodeSelected=function(a,b){a.selected=!0;if(a.onSelected)a.onSelected();b&&b.shiftKey||(this.selected_nodes={});this.selected_nodes[a.id]=a;this.dirty_canvas=!0;if(this.onNodeSelected)this.onNodeSelected(a)};LGraphCanvas.prototype.processNodeDeselected=function(a){a.selected=!1;if(a.onDeselected)a.onDeselected();delete this.selected_nodes[a.id];if(this.onNodeDeselected)this.onNodeDeselected();this.dirty_canvas=!0}; LGraphCanvas.prototype.processNodeDblClicked=function(a){if(this.onShowNodePanel)this.onShowNodePanel(a);if(this.onNodeDblClicked)this.onNodeDblClicked(a);this.setDirty(!0)};LGraphCanvas.prototype.selectNode=function(a){this.deselectAllNodes();if(a){if(!a.selected&&a.onSelected)a.onSelected();a.selected=!0;this.selected_nodes[a.id]=a;this.setDirty(!0)}}; -LGraphCanvas.prototype.selectAllNodes=function(){for(var a in this.graph.nodes){var b=this.graph.nodes[a];if(!b.selected&&b.onSelected)b.onSelected();b.selected=!0;this.selected_nodes[this.graph.nodes[a].id]=b}this.setDirty(!0)};LGraphCanvas.prototype.deselectAllNodes=function(){for(var a in this.selected_nodes){var b=this.selected_nodes;if(b.onDeselected)b.onDeselected();b.selected=!1}this.selected_nodes={};this.setDirty(!0)}; -LGraphCanvas.prototype.deleteSelectedNodes=function(){for(var a in this.selected_nodes)this.graph.remove(this.selected_nodes[a]);this.selected_nodes={};this.setDirty(!0)};LGraphCanvas.prototype.centerOnNode=function(a){this.graph.config.canvas_offset[0]=-a.pos[0]-0.5*a.size[0]+0.5*this.canvas.width/this.graph.config.canvas_scale;this.graph.config.canvas_offset[1]=-a.pos[1]-0.5*a.size[1]+0.5*this.canvas.height/this.graph.config.canvas_scale;this.setDirty(!0,!0)}; -LGraphCanvas.prototype.adjustMouseEvent=function(a){var b=this.canvas.getBoundingClientRect();a.localX=a.pageX-b.left;a.localY=a.pageY-b.top;a.canvasX=a.localX/this.graph.config.canvas_scale-this.graph.config.canvas_offset[0];a.canvasY=a.localY/this.graph.config.canvas_scale-this.graph.config.canvas_offset[1]}; -LGraphCanvas.prototype.setZoom=function(a,b){b||(b=[0.5*this.canvas.width,0.5*this.canvas.height]);var c=this.convertOffsetToCanvas(b);this.graph.config.canvas_scale=a;4this.graph.config.canvas_scale&&(this.graph.config.canvas_scale=0.1);var d=this.convertOffsetToCanvas(b),c=[d[0]-c[0],d[1]-c[1]];this.graph.config.canvas_offset[0]+=c[0];this.graph.config.canvas_offset[1]+=c[1];this.dirty_bgcanvas=this.dirty_canvas=!0}; -LGraphCanvas.prototype.convertOffsetToCanvas=function(a){return[a[0]/this.graph.config.canvas_scale-this.graph.config.canvas_offset[0],a[1]/this.graph.config.canvas_scale-this.graph.config.canvas_offset[1]]};LGraphCanvas.prototype.convertCanvasToOffset=function(a){return[(a[0]+this.graph.config.canvas_offset[0])*this.graph.config.canvas_scale,(a[1]+this.graph.config.canvas_offset[1])*this.graph.config.canvas_scale]}; -LGraphCanvas.prototype.convertEventToCanvas=function(a){var b=this.canvas.getClientRects()[0];return this.convertOffsetToCanvas([a.pageX-b.left,a.pageY-b.top])};LGraphCanvas.prototype.bringToFront=function(a){var b=this.graph.nodes.indexOf(a);-1!=b&&(this.graph.nodes.splice(b,1),this.graph.nodes.push(a))};LGraphCanvas.prototype.sendToBack=function(a){var b=this.graph.nodes.indexOf(a);-1!=b&&(this.graph.nodes.splice(b,1),this.graph.nodes.unshift(a))}; -LGraphCanvas.prototype.computeVisibleNodes=function(){var a=[],b;for(b in this.graph.nodes){var c=this.graph.nodes[b];(!this.live_mode||c.onDrawBackground||c.onDrawForeground)&&overlapBounding(this.visible_area,c.getBounding())&&a.push(c)}return a}; -LGraphCanvas.prototype.draw=function(a,b){var c=(new Date).getTime();this.render_time=0.001*(c-this.last_draw_time);this.last_draw_time=c;if(this.graph){var c=[-this.graph.config.canvas_offset[0],-this.graph.config.canvas_offset[1]],d=[c[0]+this.canvas.width/this.graph.config.canvas_scale,c[1]+this.canvas.height/this.graph.config.canvas_scale];this.visible_area=new Float32Array([c[0],c[1],d[0],d[1]])}(this.dirty_bgcanvas||b)&&this.drawBgcanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps= -this.render_time?1/this.render_time:0;this.frame+=1}; +LGraphCanvas.prototype.selectAllNodes=function(){for(var a in this.graph._nodes){var b=this.graph._nodes[a];if(!b.selected&&b.onSelected)b.onSelected();b.selected=!0;this.selected_nodes[this.graph._nodes[a].id]=b}this.setDirty(!0)};LGraphCanvas.prototype.deselectAllNodes=function(){for(var a in this.selected_nodes){var b=this.selected_nodes;if(b.onDeselected)b.onDeselected();b.selected=!1}this.selected_nodes={};this.setDirty(!0)}; +LGraphCanvas.prototype.deleteSelectedNodes=function(){for(var a in this.selected_nodes)this.graph.remove(this.selected_nodes[a]);this.selected_nodes={};this.setDirty(!0)};LGraphCanvas.prototype.centerOnNode=function(a){this.offset[0]=-a.pos[0]-0.5*a.size[0]+0.5*this.canvas.width/this.scale;this.offset[1]=-a.pos[1]-0.5*a.size[1]+0.5*this.canvas.height/this.scale;this.setDirty(!0,!0)}; +LGraphCanvas.prototype.adjustMouseEvent=function(a){var b=this.canvas.getBoundingClientRect();a.localX=a.pageX-b.left;a.localY=a.pageY-b.top;a.canvasX=a.localX/this.scale-this.offset[0];a.canvasY=a.localY/this.scale-this.offset[1]}; +LGraphCanvas.prototype.setZoom=function(a,b){b||(b=[0.5*this.canvas.width,0.5*this.canvas.height]);var c=this.convertOffsetToCanvas(b);this.scale=a;4this.scale&&(this.scale=0.1);var d=this.convertOffsetToCanvas(b),c=[d[0]-c[0],d[1]-c[1]];this.offset[0]+=c[0];this.offset[1]+=c[1];this.dirty_bgcanvas=this.dirty_canvas=!0};LGraphCanvas.prototype.convertOffsetToCanvas=function(a){return[a[0]/this.scale-this.offset[0],a[1]/this.scale-this.offset[1]]}; +LGraphCanvas.prototype.convertCanvasToOffset=function(a){return[(a[0]+this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};LGraphCanvas.prototype.convertEventToCanvas=function(a){var b=this.canvas.getClientRects()[0];return this.convertOffsetToCanvas([a.pageX-b.left,a.pageY-b.top])};LGraphCanvas.prototype.bringToFront=function(a){var b=this.graph._nodes.indexOf(a);-1!=b&&(this.graph._nodes.splice(b,1),this.graph._nodes.push(a))}; +LGraphCanvas.prototype.sendToBack=function(a){var b=this.graph._nodes.indexOf(a);-1!=b&&(this.graph._nodes.splice(b,1),this.graph._nodes.unshift(a))};LGraphCanvas.prototype.computeVisibleNodes=function(){var a=[],b;for(b in this.graph._nodes){var c=this.graph._nodes[b];(!this.live_mode||c.onDrawBackground||c.onDrawForeground)&&overlapBounding(this.visible_area,c.getBounding())&&a.push(c)}return a}; +LGraphCanvas.prototype.draw=function(a,b){var c=window.performance.now();this.render_time=0.001*(c-this.last_draw_time);this.last_draw_time=c;if(this.graph){var c=[-this.offset[0],-this.offset[1]],d=[c[0]+this.canvas.width/this.scale,c[1]+this.canvas.height/this.scale];this.visible_area=new Float32Array([c[0],c[1],d[0],d[1]])}(this.dirty_bgcanvas||b)&&this.drawBgcanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}; LGraphCanvas.prototype.drawFrontCanvas=function(){var a=this.ctx,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());a.clearRect(0,0,b.width,b.height);a.drawImage(this.bgcanvas,0,0);this.show_info&&(a.font="10px Arial",a.fillStyle="#888",this.graph?(a.fillText("T: "+this.graph.globaltime.toFixed(2)+"s",5,13),a.fillText("I: "+this.graph.iteration,5,26),a.fillText("F: "+ -this.frame,5,39),a.fillText("FPS:"+this.fps.toFixed(2),5,52)):a.fillText("No graph selected",5,13));if(this.graph){a.save();a.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale);a.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]);this.visible_nodes=b=this.computeVisibleNodes();for(var c in b){var d=b[c];a.save();a.translate(d.pos[0],d.pos[1]);this.drawNode(d,a);a.restore()}this.graph.config.links_ontop&&(this.live_mode||this.drawConnections(a));null!= -this.connecting_pos&&(a.lineWidth=LGraphCanvas.link_width,a.fillStyle="node"==this.connecting_output.type?"#F85":"#AFA",a.strokeStyle=a.fillStyle,this.renderLink(a,this.connecting_pos,[this.canvas_mouse[0],this.canvas_mouse[1]]),a.beginPath(),a.arc(this.connecting_pos[0],this.connecting_pos[1],4,0,2*Math.PI),a.fill(),a.fillStyle="#ffcc00",this._highlight_input&&(a.beginPath(),a.arc(this._highlight_input[0],this._highlight_input[1],6,0,2*Math.PI),a.fill()));a.restore()}this.dirty_area&&a.restore(); -this.dirty_canvas=!1}; -LGraphCanvas.prototype.drawBgcanvas=function(){var a=this.bgcanvas,b=this.bgctx;a.width=a.width;b.restore();b.setTransform(1,0,0,1,0,0);if(this.graph){b.save();b.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale);b.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]);if(this.background_image&&0.5b[1]?0:Math.PI;a.save();a.translate(d[0],d[1]);a.rotate(f);a.beginPath();a.moveTo(-5,-5);a.lineTo(0,5);a.lineTo(5,-5);a.fill();a.restore()}}else a.beginPath(),a.moveTo(b[0],b[1]),a.lineTo(c[0],c[1]),a.stroke()}; +LGraphCanvas.prototype.computeConnectionPoint=function(a,b,c){var d=distance(a,b),e=[a[0]+0.25*d,a[1]],d=[b[0]-0.25*d,b[1]],f=(1-c)*(1-c)*(1-c),g=3*(1-c)*(1-c)*c,h=3*(1-c)*c*c;c*=c*c;return[f*a[0]+g*e[0]+h*d[0]+c*b[0],f*a[1]+g*e[1]+h*d[1]+c*b[1]]}; +LGraphCanvas.prototype.resize=function(a,b){if(!a&&!b){var c=this.canvas.parentNode;a=c.offsetWidth;b=c.offsetHeight}if(this.canvas.width!=a||this.canvas.height!=b)this.canvas.width=a,this.canvas.height=b,this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height,this.setDirty(!0,!0)}; +LGraphCanvas.prototype.switchLiveMode=function(a){if(a){var b=this,c=this.live_mode?1.1:0.9;this.live_mode&&(this.live_mode=!1,this.editor_alpha=0.1);var d=setInterval(function(){b.editor_alpha*=c;b.dirty_canvas=!0;b.dirty_bgcanvas=!0;1>c&&0.01>b.editor_alpha&&(clearInterval(d),1>c&&(b.live_mode=!0));1"+e+""})}LiteGraph.createContextualMenu(d,{event:b,callback:function(b){a&&(b=LGraphCanvas.node_colors[b.value])&&(a.color=b.color,a.bgcolor=b.bgcolor,a.graph.canvas.setDirty(!0))},from:c});return!1}; LGraphCanvas.onMenuNodeShapes=function(a,b){LiteGraph.createContextualMenu(["box","round"],{event:b,callback:function(b){a&&(a.shape=b,a.graph.canvas.setDirty(!0))}});return!1};LGraphCanvas.onMenuNodeRemove=function(a){!1!=a.removable&&(a.graph.remove(a),a.graph.canvas.setDirty(!0,!0))};LGraphCanvas.onMenuNodeClone=function(a){if(!1!=a.clonable){var b=a.clone();b&&(b.pos=[a.pos[0]+5,a.pos[1]+5],a.graph.add(b),a.graph.canvas.setDirty(!0,!0))}}; LGraphCanvas.node_colors={red:{color:"#FAA",bgcolor:"#A44"},green:{color:"#AFA",bgcolor:"#4A4"},blue:{color:"#AAF",bgcolor:"#44A"},white:{color:"#FFF",bgcolor:"#AAA"}};LGraphCanvas.prototype.getCanvasMenuOptions=function(){return[{content:"Add Node",is_menu:!0,callback:LGraphCanvas.onMenuAdd}]}; -LGraphCanvas.prototype.getNodeMenuOptions=function(a){var b=[{content:"Inputs",is_menu:!0,disabled:!0,callback:LGraphCanvas.onMenuNodeInputs},{content:"Outputs",is_menu:!0,disabled:!0,callback:LGraphCanvas.onMenuNodeOutputs},null,{content:"Collapse",callback:LGraphCanvas.onMenuNodeCollapse},{content:"Colors",is_menu:!0,callback:LGraphCanvas.onMenuNodeColors},{content:"Shapes",is_menu:!0,callback:LGraphCanvas.onMenuNodeShapes},null,{content:"Clone",callback:LGraphCanvas.onMenuNodeClone},null,{content:"Remove", -callback:LGraphCanvas.onMenuNodeRemove}];!1==a.clonable&&(b[7].disabled=!0);!1==a.removable&&(b[9].disabled=!0);a.onGetInputs&&a.onGetInputs().length&&(b[0].disabled=!1);a.onGetOutputs&&a.onGetOutputs().length&&(b[1].disabled=!1);return b};LGraphCanvas.prototype.processContextualMenu=function(a,b){var c=this,d=LiteGraph.createContextualMenu(a?this.getNodeMenuOptions(a):this.getCanvasMenuOptions(),{event:b,callback:function(e,f){if(e&&e.callback)return e.callback(a,f,d,c,b)}})}; +LGraphCanvas.prototype.getNodeMenuOptions=function(a){var b=[{content:"Inputs",is_menu:!0,disabled:!0,callback:LGraphCanvas.onMenuNodeInputs},{content:"Outputs",is_menu:!0,disabled:!0,callback:LGraphCanvas.onMenuNodeOutputs},null,{content:"Collapse",callback:LGraphCanvas.onMenuNodeCollapse},{content:"Pin",callback:LGraphCanvas.onMenuNodePin},{content:"Colors",is_menu:!0,callback:LGraphCanvas.onMenuNodeColors},{content:"Shapes",is_menu:!0,callback:LGraphCanvas.onMenuNodeShapes},null,{content:"Clone", +callback:LGraphCanvas.onMenuNodeClone},null,{content:"Remove",callback:LGraphCanvas.onMenuNodeRemove}];!1==a.clonable&&(b[7].disabled=!0);!1==a.removable&&(b[9].disabled=!0);a.onGetInputs&&a.onGetInputs().length&&(b[0].disabled=!1);a.onGetOutputs&&a.onGetOutputs().length&&(b[1].disabled=!1);return b}; +LGraphCanvas.prototype.processContextualMenu=function(a,b){var c=this,d=this.getCanvasWindow(),e=LiteGraph.createContextualMenu(a?this.getNodeMenuOptions(a):this.getCanvasMenuOptions(),{event:b,callback:function(d,g){if(d&&d.callback)return d.callback(a,g,e,c,b)}},d)}; CanvasRenderingContext2D.prototype.roundRect=function(a,b,c,d,e,f){void 0===e&&(e=5);void 0===f&&(f=e);this.beginPath();this.moveTo(a+e,b);this.lineTo(a+c-e,b);this.quadraticCurveTo(a+c,b,a+c,b+e);this.lineTo(a+c,b+d-f);this.quadraticCurveTo(a+c,b+d,a+c-f,b+d);this.lineTo(a+f,b+d);this.quadraticCurveTo(a,b+d,a,b+d-f);this.lineTo(a,b+e);this.quadraticCurveTo(a,b,a+e,b)};function compareObjects(a,b){for(var c in a)if(a[c]!=b[c])return!1;return!0} function distance(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function colorToString(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")+")"}function isInsideRectangle(a,b,c,d,e,f){return ca&&db?!0:!1}function growBounding(a,b,c){ba[2]&&(a[2]=b);ca[3]&&(a[3]=c)} function isInsideBounding(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0}function overlapBounding(a,b){return a[0]>b[2]||a[1]>b[3]||a[2]f;f+=2)d="0123456789ABCDEF".indexOf(a.charAt(f)),e="0123456789ABCDEF".indexOf(a.charAt(f+1)),b[c]=16*d+e,c++;return b} function num2hex(a){for(var b="#",c,d,e=0;3>e;e++)c=a[e]/16,d=a[e]%16,b+="0123456789ABCDEF".charAt(c)+"0123456789ABCDEF".charAt(d);return b} -LiteGraph.createContextualMenu=function(a,b){function c(a){var c=!0;b.callback&&(a=b.callback.call(d,this.data,a),void 0!=a&&(c=a));c&&LiteGraph.closeAllContextualMenus()}this.options=b=b||{};b.from||LiteGraph.closeAllContextualMenus();var d=document.createElement("div");d.className="litecontextualmenu litemenubar-panel";this.root=d;var e=d.style;e.minWidth="100px";e.minHeight="20px";e.position="fixed";e.top="100px";e.left="100px";e.color="#AAF";e.padding="2px";e.borderBottom="2px solid #AAF";e.backgroundColor= -"#444";d.addEventListener("contextmenu",function(a){a.preventDefault();return!1});for(var f in a){var e=a[f],g=document.createElement("div");g.className="litemenu-entry";null==e?g.className="litemenu-entry separator":(e.is_menu&&(g.className+=" submenu"),e.disabled&&(g.className+=" disabled"),g.style.cursor="pointer",g.dataset.value="string"==typeof e?e:e.value,g.data=e,g.innerHTML="string"==typeof e?a.constructor==Array?a[f]:f:e.content?e.content:f,g.addEventListener("click",c));d.appendChild(g)}d.addEventListener("mouseover", -function(a){this.mouse_inside=!0});d.addEventListener("mouseout",function(a){for(a=a.toElement;a!=this&&a!=document;)a=a.parentNode;a!=this&&(this.mouse_inside=!1,this.block_close||this.closeMenu())});document.body.appendChild(d);f=d.getClientRects()[0];b.from&&(b.from.block_close=!0);var h=b.left||0,e=b.top||0;b.event&&(h=b.event.pageX-10,e=b.event.pageY-10,b.left&&(h=b.left),g=document.body.getClientRects()[0],b.from&&(h=b.from.getClientRects()[0],h=h.left+h.width),h>g.width-f.width-10&&(h=g.width- -f.width-10),e>g.height-f.height-10&&(e=g.height-f.height-10));d.style.left=h+"px";d.style.top=e+"px";d.closeMenu=function(){b.from&&(b.from.block_close=!1,b.from.mouse_inside||b.from.closeMenu());this.parentNode&&document.body.removeChild(this)};return d};LiteGraph.closeAllContextualMenus=function(){var a=document.querySelectorAll(".litecontextualmenu");if(a.length){for(var b=[],c=0;cf.width-a.width-10&&(h=f.width-a.width-10),g>f.height-a.height-10&&(g=f.height-a.height-10));e.style.left=h+"px";e.style.top=g+"px";e.closeMenu=function(){b.from&&(b.from.block_close=!1,b.from.mouse_inside||b.from.closeMenu());this.parentNode&&c.document.body.removeChild(this)};return e};LiteGraph.closeAllContextualMenus=function(){var a=document.querySelectorAll(".litecontextualmenu");if(a.length){for(var b=[],c=0;c null==b&&(b=this.properties.axis);a=quat.setAxisAngle(quat.create(),b,0.0174532925*a);this.setOutputData(0,a)}}),LiteGraph.registerNodeType("math3d/rotate_vec3",{title:"Rot. Vec3",desc:"rotate a point",inputs:[["vec3","vec3"],["quat","quat"]],outputs:[["result","vec3"]],properties:{vec:[0,0,1]},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))}}),LiteGraph.registerNodeType("math3d/mult-quat", {title:"Mult. Quat",desc:"rotate quaternion",inputs:[["A","quat"],["B","quat"]],outputs:[["A*B","quat"]],onExecute:function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(quat.create(),a,b),this.setOutputData(0,a))}}})); LiteGraph.registerNodeType("widget/knob",{title:"Knob",desc:"Circular controller",size:[64,84],outputs:[["","number"]],properties:{min:0,max:1,value:0.5,wcolor:"#7AF",size:50},widgets:[{name:"increase",text:"+",type:"minibutton"},{name:"decrease",text:"-",type:"minibutton"}],onInit:function(){this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min);this.imgbg=this.loadImage("imgs/knob_bg.png");this.imgfg=this.loadImage("imgs/knob_fg.png")},onDrawImageKnob:function(a){if(this.imgfg&& -this.imgfg.width){var b=0.5*this.imgbg.width,c=this.size[0]/this.imgfg.width;a.save();a.translate(0,20);a.scale(c,c);a.drawImage(this.imgbg,0,0);a.translate(b,b);a.rotate(12*this.value*Math.PI/8+10*Math.PI/8);a.translate(-b,-b);a.drawImage(this.imgfg,0,0);a.restore();a.font="bold 16px Criticized,Tahoma";a.fillStyle="rgba(100,100,100,0.8)";a.textAlign="center";a.fillText(this.name.toUpperCase(),0.5*this.size[0],18);a.textAlign="left"}},onDrawVectorKnob:function(a){if(this.imgfg&&this.imgfg.width){a.lineWidth= +this.imgfg.width){var b=0.5*this.imgbg.width,c=this.size[0]/this.imgfg.width;a.save();a.translate(0,20);a.scale(c,c);a.drawImage(this.imgbg,0,0);a.translate(b,b);a.rotate(2*this.value*Math.PI*6/8+10*Math.PI/8);a.translate(-b,-b);a.drawImage(this.imgfg,0,0);a.restore();a.font="bold 16px Criticized,Tahoma";a.fillStyle="rgba(100,100,100,0.8)";a.textAlign="center";a.fillText(this.name.toUpperCase(),0.5*this.size[0],18);a.textAlign="left"}},onDrawVectorKnob:function(a){if(this.imgfg&&this.imgfg.width){a.lineWidth= 1;a.strokeStyle=this.mouseOver?"#FFF":"#AAA";a.fillStyle="#000";a.beginPath();a.arc(0.5*this.size[0],0.5*this.size[1]+10,0.5*this.properties.size,0,2*Math.PI,!0);a.stroke();0a.canvasY-this.pos[1]||distance([a.canvasX, +var b=this.properties.value;"number"==typeof b&&(b=b.toFixed(2));a.fillText(b,0.5*this.size[0],0.65*this.size[1]);a.textAlign="left"}},onDrawForeground:function(a){this.onDrawImageKnob(a)},onExecute:function(){this.setOutputData(0,this.properties.value);this.boxcolor=colorToString([this.value,this.value,this.value])},onMouseDown:function(a){if(this.imgfg&&this.imgfg.width){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>a.canvasY-this.pos[1]||distance([a.canvasX, a.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[a.canvasX-this.pos[0],a.canvasY-this.pos[1]];this.captureInput(!0);return!0}},onMouseMove:function(a){if(this.oldmouse){a=[a.canvasX-this.pos[0],a.canvasY-this.pos[1]];var b=this.value,b=b-0.01*(a[1]-this.oldmouse[1]);1b&&(b=0);this.value=b;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=a;this.setDirtyCanvas(!0)}},onMouseUp:function(a){this.oldmouse&& (this.oldmouse=null,this.captureInput(!1))},onMouseLeave:function(a){},onWidget:function(a,b){if("increase"==b.name)this.onPropertyChange("size",this.properties.size+10);else if("decrease"==b.name)this.onPropertyChange("size",this.properties.size-10)},onPropertyChange:function(a,b){if("wcolor"==a)this.properties[a]=b;else if("size"==a)b=parseInt(b),this.properties[a]=b,this.size=[b+4,b+24],this.setDirtyCanvas(!0,!0);else if("min"==a||"max"==a||"value"==a)this.properties[a]=parseFloat(b);else return!1; return!0}}); LiteGraph.registerNodeType("widget/hslider",{title:"H.Slider",desc:"Linear slider controller",size:[160,26],outputs:[["","number"]],properties:{wcolor:"#7AF",min:0,max:1,value:0.5},onInit:function(){this.value=0.5;this.imgfg=this.loadImage("imgs/slider_fg.png")},onDrawVectorial:function(a){this.imgfg&&this.imgfg.width&&(a.lineWidth=1,a.strokeStyle=this.mouseOver?"#FFF":"#AAA",a.fillStyle="#000",a.beginPath(),a.rect(2,0,this.size[0]-4,20),a.stroke(),a.fillStyle=this.properties.wcolor,a.beginPath(),a.rect(2+ -(this.size[0]-4-20)*this.value,0,20,20),a.fill())},onDrawImage:function(a){this.imgfg&&this.imgfg.width&&(a.lineWidth=1,a.fillStyle="#000",a.fillRect(2,9,this.size[0]-4,2),a.strokeStyle="#333",a.beginPath(),a.moveTo(2,9),a.lineTo(this.size[0]-4,9),a.stroke(),a.strokeStyle="#AAA",a.beginPath(),a.moveTo(2,11),a.lineTo(this.size[0]-4,11),a.stroke(),a.drawImage(this.imgfg,2+(this.size[0]-4)*this.value-0.5*this.imgfg.width,0.5*-this.imgfg.height+10))},onDrawBackground:function(a){this.onDrawImage(a)}, +(this.size[0]-4-20)*this.value,0,20,20),a.fill())},onDrawImage:function(a){this.imgfg&&this.imgfg.width&&(a.lineWidth=1,a.fillStyle="#000",a.fillRect(2,9,this.size[0]-4,2),a.strokeStyle="#333",a.beginPath(),a.moveTo(2,9),a.lineTo(this.size[0]-4,9),a.stroke(),a.strokeStyle="#AAA",a.beginPath(),a.moveTo(2,11),a.lineTo(this.size[0]-4,11),a.stroke(),a.drawImage(this.imgfg,2+(this.size[0]-4)*this.value-0.5*this.imgfg.width,0.5*-this.imgfg.height+10))},onDrawForeground:function(a){this.onDrawImage(a)}, onExecute:function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=colorToString([this.value,this.value,this.value])},onMouseDown:function(a){if(0>a.canvasY-this.pos[1])return!1;this.oldmouse=[a.canvasX-this.pos[0],a.canvasY-this.pos[1]];this.captureInput(!0);return!0},onMouseMove:function(a){if(this.oldmouse){a=[a.canvasX-this.pos[0],a.canvasY-this.pos[1]];var b=this.value,b=b+(a[0]-this.oldmouse[0])/ this.size[0];1b&&(b=0);this.value=b;this.oldmouse=a;this.setDirtyCanvas(!0)}},onMouseUp:function(a){this.oldmouse=null;this.captureInput(!1)},onMouseLeave:function(a){},onPropertyChange:function(a,b){if("wcolor"==a)this.properties[a]=b;else return!1;return!0}}); LiteGraph.registerNodeType("widget/kpad",{title:"KPad",desc:"bidimensional slider",size:[200,200],outputs:[["x","number"],["y","number"]],properties:{x:0,y:0,borderColor:"#333",bgcolorTop:"#444",bgcolorBottom:"#000",shadowSize:1,borderRadius:2},createGradient:function(a){this.lineargradient=a.createLinearGradient(0,0,0,this.size[1]);this.lineargradient.addColorStop(0,this.properties.bgcolorTop);this.lineargradient.addColorStop(1,this.properties.bgcolorBottom)},onDrawBackground:function(a){this.lineargradient|| @@ -156,16 +155,16 @@ this.properties.y=1:0>this.properties.y&&(this.properties.y=0),this.oldmouse=a,t LiteGraph.registerNodeType("widget/button",{title:"Button",desc:"A send command button",widgets:[{name:"test",text:"Test Button",type:"button"}],size:[100,40],properties:{text:"clickme",command:"",color:"#7AF",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",fontsize:"16"},outputs:[["M","module"]],createGradient:function(a){this.lineargradient=a.createLinearGradient(0,0,0,this.size[1]);this.lineargradient.addColorStop(0,this.properties.bgcolorTop);this.lineargradient.addColorStop(1,this.properties.bgcolorBottom)}, drawVectorShape:function(a){a.fillStyle=this.mouseOver?this.properties.color:"#AAA";this.clicking&&(a.fillStyle="#FFF");a.strokeStyle="#AAA";a.roundRect(5,5,this.size[0]-10,this.size[1]-10,4);a.stroke();this.mouseOver&&a.fill();a.fillStyle=this.mouseOver?"#000":"#AAA";a.font="bold "+this.properties.fontsize+"px Criticized,Tahoma";a.textAlign="center";a.fillText(this.properties.text,0.5*this.size[0],0.5*this.size[1]+0.5*parseInt(this.properties.fontsize));a.textAlign="left"},drawBevelShape:function(a){a.shadowColor= "#000";a.shadowOffsetX=0;a.shadowOffsetY=0;a.shadowBlur=this.properties.shadowSize;this.lineargradient||this.createGradient(a);a.fillStyle=this.mouseOver?this.properties.color:this.lineargradient;this.clicking&&(a.fillStyle="#444");a.strokeStyle="#FFF";a.roundRect(5,5,this.size[0]-10,this.size[1]-10,4);a.fill();a.shadowColor="rgba(0,0,0,0)";a.stroke();a.fillStyle=this.mouseOver?"#000":"#444";a.font="bold "+this.properties.fontsize+"px Century Gothic";a.textAlign="center";a.fillText(this.properties.text, -0.5*this.size[0],0.5*this.size[1]+0.4*parseInt(this.properties.fontsize));a.textAlign="left"},onDrawBackground:function(a){this.drawBevelShape(a)},clickButton:function(){var a=this.getOutputModule(0);if(this.properties.command&&""!=this.properties.command)a.executeAction(this.properties.command)||this.trace("Error executing action in other module");else if(a&&a.onTrigger)a.onTrigger()},onMouseDown:function(a){if(2>a.canvasY-this.pos[1])return!1;this.clickButton();return this.clicking=!0},onMouseUp:function(a){this.clicking= +0.5*this.size[0],0.5*this.size[1]+0.4*parseInt(this.properties.fontsize));a.textAlign="left"},onDrawForeground:function(a){this.drawBevelShape(a)},clickButton:function(){var a=this.getOutputModule(0);if(this.properties.command&&""!=this.properties.command)a.executeAction(this.properties.command)||this.trace("Error executing action in other module");else if(a&&a.onTrigger)a.onTrigger()},onMouseDown:function(a){if(2>a.canvasY-this.pos[1])return!1;this.clickButton();return this.clicking=!0},onMouseUp:function(a){this.clicking= !1},onExecute:function(){},onWidget:function(a,b){"test"==b.name&&this.clickButton()},onPropertyChange:function(a,b){this.properties[a]=b;return!0}}); -LiteGraph.registerNodeType("widget/progress",{title:"Progress",desc:"Shows data in linear progress",size:[160,26],inputs:[["","number"]],properties:{min:0,max:1,value:0,wcolor:"#AAF"},onExecute:function(){var a=this.getInputData(0);void 0!=a&&(this.properties.value=a)},onDrawBackground:function(a){a.lineWidth=1;a.fillStyle=this.properties.wcolor;var b=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),b=Math.min(1,b),b=Math.max(0,b);a.fillRect(2,2,(this.size[0]- +LiteGraph.registerNodeType("widget/progress",{title:"Progress",desc:"Shows data in linear progress",size:[160,26],inputs:[["","number"]],properties:{min:0,max:1,value:0,wcolor:"#AAF"},onExecute:function(){var a=this.getInputData(0);void 0!=a&&(this.properties.value=a)},onDrawForeground:function(a){a.lineWidth=1;a.fillStyle=this.properties.wcolor;var b=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),b=Math.min(1,b),b=Math.max(0,b);a.fillRect(2,2,(this.size[0]- 4)*b,this.size[1]-4)}}); -LiteGraph.registerNodeType("widget/text",{title:"Text",desc:"Shows the input value",widgets:[{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}],inputs:[["",0]],properties:{value:"...",font:"Arial",fontsize:18,color:"#AAA",align:"left",glowSize:0,decimals:1},onDrawBackground:function(a){a.fillStyle=this.properties.color;var b=this.properties.value;this.properties.glowSize?(a.shadowColor=this.properties.color, +LiteGraph.registerNodeType("widget/text",{title:"Text",desc:"Shows the input value",widgets:[{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}],inputs:[["",0]],properties:{value:"...",font:"Arial",fontsize:18,color:"#AAA",align:"left",glowSize:0,decimals:1},onDrawForeground:function(a){a.fillStyle=this.properties.color;var b=this.properties.value;this.properties.glowSize?(a.shadowColor=this.properties.color, a.shadowOffsetX=0,a.shadowOffsetY=0,a.shadowBlur=this.properties.glowSize):a.shadowColor="transparent";var c=this.properties.fontsize;a.textAlign=this.properties.align;a.font=c.toString()+"px "+this.properties.font;this.str="number"==typeof b?b.toFixed(this.properties.decimals):b;if("string"==typeof this.str){var b=this.str.split("\\n"),d;for(d in b)a.fillText(b[d],"left"==this.properties.align?15:this.size[0]-15,-0.15*c+c*(parseInt(d)+1))}a.shadowColor="transparent";this.last_ctx=a;a.textAlign="left"}, onExecute:function(){var a=this.getInputData(0);this.properties.value=null!=a?a:"";this.setDirtyCanvas(!0)},resize:function(){if(this.last_ctx){var a=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var b=0,c;for(c in a){var d=this.last_ctx.measureText(a[c]).width;bb&&(b=0);if(0!=a.length){var c=[0,0,0];if(0==b)c=a[0];else if(1==b)c=a[a.length-1];else{var d=(a.length-1)*b,b=a[Math.floor(d)],a=a[Math.floor(d)+1],d=d-Math.floor(d);c[0]=b[0]*(1-d)+a[0]*d;c[1]=b[1]*(1-d)+a[1]*d;c[2]=b[2]*(1-d)+a[2]*d}for(var e in c)c[e]/=255;this.boxcolor=colorToString(c);this.setOutputData(0,c)}}}); diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css new file mode 100644 index 000000000..4147d4b87 --- /dev/null +++ b/css/litegraph-editor.css @@ -0,0 +1,187 @@ +.litegraph-editor { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + + background-color: #333; + color: #EEE; + font: 14px Tahoma; + + position: relative; +} + +.litegraph-editor h1 { + font-family: "Metro Light",Tahoma; + color: #DDD; + font-size: 28px; + padding-left: 10px; + /*text-shadow: 0 1px 1px #333, 0 -1px 1px #777;*/ + margin: 0; + font-weight: normal; +} + +.litegraph-editor h1 span { + font-family: "Arial"; + font-size: 14px; + font-weight: normal; + color: #AAA; +} + +.litegraph-editor h2 { + font-family: "Metro Light"; + padding: 5px; + margin-left: 10px; +} + + +.litegraph-editor * { + box-sizing: border-box; + -moz-box-sizing: border-box; +} + +.litegraph-editor .content { + position: relative; + width: 100%; + height: calc( 100% - 80px ); + background-color: #292929; +} + +.litegraph-editor .header, .litegraph-editor .footer { + position: relative; + height: 40px; + background-color: #333; + /*border-radius: 10px 10px 0 0;*/ +} + +.litegraph-editor .tools, .litegraph-editor .tools-left, .litegraph-editor .tools-right { + position: absolute; + top: 2px; + right: 0px; + vertical-align: top; + + margin: 2px 5px 0 0px; +} + +.litegraph-editor .tools-left { + right: auto; + left: 4px; +} + +.litegraph-editor .footer { + height: 40px; + position: relative; + /*border-radius: 0 0 10px 10px;*/ +} + +.litegraph-editor .miniwindow { + background-color: #333; + border: 1px solid #111; +} + +.litegraph-editor .miniwindow .corner-button { + position: absolute; + top: 2px; + right: 2px; + font-family: "Tahoma"; + font-size: 14px; + color: #AAA; + cursor: pointer; +} + + +/* BUTTONS **********************/ + +.litegraph-editor button { + /*font-family: "Metro Light";*/ + color: #CCC; + font-size: 20px; + min-width: 30px; + /*border-radius: 0.3em;*/ + border: 0 solid #666; + background-color: #3F3F3F; + /*box-shadow: 0 0 3px black;*/ + padding: 4px 10px; + line-height: 20px; + cursor: pointer; + transition: all 1s; + -moz-transition: all 1s; + -webkit-transition: all 0.4s; +} + +.litegraph-editor button:hover { + background-color: #999; + color: #FFF; + transition: all 1s; + -moz-transition: all 1s; + -webkit-transition: all 0.4s; +} + +.litegraph-editor button:active { + background-color: white; +} + +.litegraph-editor button.fixed { + position: absolute; + top: 5px; + right: 5px; + font-size: 1.2em; +} + +.litegraph-editor button img { + margin: -4px; + vertical-align: top; +} + +.litegraph-editor .header button { + height: 32px; + vertical-align: top; +} + +.litegraph-editor .footer button { + /*font-size: 16px;*/ +} + +.litegraph-editor .toolbar-widget { + display: inline-block; +} + +.litegraph-editor .editor-area { + width: 100%; + height: 100%; +} + +/* METER *********************/ + +.litegraph-editor .loadmeter { + font-family: "Tahoma"; + color: #AAA; + font-size: 12px; + border-radius: 2px; + width: 130px; + vertical-align: top; +} + +.litegraph-editor .strong { + vertical-align: top; + padding: 3px; + width: 30px; + display: inline-block; + line-height: 8px; +} + +.litegraph-editor .cpuload .bgload, .litegraph-editor .gpuload .bgload { + display: inline-block; + width: 90px; + height: 15px; + background-image: url('../demo/imgs/load-progress-empty.png'); +} + +.litegraph-editor .cpuload .fgload, .litegraph-editor .gpuload .fgload { + display: inline-block; + width: 4px; + height: 15px; + max-width: 90px; + background-image: url('../demo/imgs/load-progress-full.png'); +} + diff --git a/demo/code.js b/demo/code.js index face0b02e..f0d4d6c4b 100644 --- a/demo/code.js +++ b/demo/code.js @@ -1,544 +1,9 @@ -var graph = null; -var graphcanvas = null; -$(window).load(function() { - - var id = null; - if ($.getUrlVar("id") != null) - id = parseInt($.getUrlVar("id")); - else if (self.document.location.hash) - id = parseInt( self.document.location.hash.substr(1) ); - - $("#settings_button").click( function() { $("#settings-panel").toggle(); }); - $("#addnode_button").click( function() { onShowNodes() }); - $("#deletenode_button").click( function() { onDeleteNode() }); - $("#clonenode_button").click( function() { onCloneNode() }); - - $("#playnode_button").click( function() { - if(graph.status == LGraph.STATUS_STOPPED) - { - $(this).html(" Stop"); - graph.start(1); - } - else - { - $(this).html(" Play"); - graph.stop(); - } - }); - - $("#playstepnode_button").click( function() { - graph.runStep(1); - graphcanvas.draw(true,true); - }); - - $("#playfastnode_button").click( function() { - graph.runStep(5000); - graphcanvas.draw(true,true); - }); - - $("#collapsenode_button").click( function() { - /* - for(var i in graphcanvas.nodes_selected) - graphcanvas.nodes_selected[i].collapse(); - */ - if( graphcanvas.node_in_panel ) - graphcanvas.node_in_panel.collapse(); - - graphcanvas.draw(); - }); - - $("#pinnode_button").click( function() { - if( graphcanvas.node_in_panel ) - graphcanvas.node_in_panel.pin(); - }); - - $("#sendtobacknode_button").click( function() { - if( graphcanvas.node_in_panel ) - graphcanvas.sendToBack( graphcanvas.node_in_panel ); - graphcanvas.draw(true); - }); - - - - $("#confirm-createnode_button").click(function() { - var element = $(".node-type.selected")[0]; - var name = element.data; - var n = LiteGraph.createNode(name); - graph.add(n); - n.pos = graphcanvas.convertOffsetToCanvas([30,30]); - graphcanvas.draw(true,true); - $("#modal-blocking-box").hide(); - $("#nodes-browser").hide(); - }); - - $("#cancel-createnode_button").click(function() { - $("#modal-blocking-box").hide(); - $("#nodes-browser").hide(); - }); - - $("#close-area_button").click(function() { - $("#modal-blocking-box").hide(); - $("#data-visor").hide(); - }); - - $("#confirm-loadsession_button").click(function() { - var element = $(".session-item.selected")[0]; - var info = element.data; - - var str = localStorage.getItem("graph_session_" + info.id ); - graph.stop(); - graph.unserialize(str); - - graphcanvas.draw(true,true); - $("#modal-blocking-box").hide(); - $("#sessions-browser").hide(); - }); - - $("#cancel-loadsession_button").click(function() { - $("#modal-blocking-box").hide(); - $("#sessions-browser").hide(); - }); - - $("#livemode_button").click( function() { - graphcanvas.switchLiveMode(); - graphcanvas.draw(); - var url = graphcanvas.live_mode ? "imgs/gauss_bg_medium.jpg" : "imgs/gauss_bg.jpg"; - $("#livemode_button").html(!graphcanvas.live_mode ? " Live" : " Edit" ); - //$("canvas").css("background-image","url('"+url+"')"); - }); - - $("#newsession_button").click( function() { - $("#main-area").hide(); - graph.clear(); - graphcanvas.draw(); - $("#main-area").show(); - }); - - $("#savesession_button").click( function() { - onSaveSession(); - }); - - $("#loadsession_button").click( function() { - onLoadSession(); - }); - - $("#cancelsession-dialog_button").click(function() - { - $("#modal-blocking-box").hide(); - $("#savesession-dialog").hide(); - }); - - $("#savesession-dialog_button").click(function() - { - var name = $("#session-name-input").val(); - var desc = $("#session-description-input").val(); - - saveSession(name,desc); - - $("#modal-blocking-box").hide(); - $("#savesession-dialog").hide(); - - }); - - $("#closepanel_button").click(function() - { - graphcanvas.showNodePanel(null); - }); - - $("#maximize_button").click(function() - { - if($("#main").width() != window.innerWidth) - { - $("#main").width( (window.innerWidth).toString() + "px"); - $("#main").height( (window.innerHeight - 40).toString() + "px"); - graphcanvas.resizeCanvas(window.innerWidth,window.innerHeight - 100); - } - else - { - $("#main").width("800px"); - $("#main").height("660px"); - graphcanvas.resizeCanvas(800,600); - } - }); - - $("#resetscale_button").click(function() - { - graph.config.canvas_scale = 1.0; - graphcanvas.draw(true,true); - }); - - $("#resetpos_button").click(function() - { - graph.config.canvas_offset = [0,0]; - graphcanvas.draw(true,true); - }); - - $(".nodecolorbutton").click(function() - { - if( graphcanvas.node_in_panel ) - { - graphcanvas.node_in_panel.color = this.getAttribute("data-color"); - graphcanvas.node_in_panel.bgcolor = this.getAttribute("data-bgcolor"); - } - graphcanvas.draw(true,true); - }); - - - if ("onhashchange" in window) // does the browser support the hashchange event? - { - window.onhashchange = function () { - var h = window.location.hash.substr(1); - //action - return false; - } - } - - LiteGraph.node_images_path = "../nodes_data/"; - graph = new LGraph(); - graphcanvas = new LGraphCanvas("graphcanvas",graph); - graphcanvas.background_image = "imgs/grid.png"; - - graph.onAfterExecute = function() { graphcanvas.draw(true) }; - demo(); - - graph.onPlayEvent = function() - { - $("#playnode_button").addClass("playing"); - $("#playnode_button").removeClass("stopped"); - } - - graph.onStopEvent = function() - { - $("#playnode_button").addClass("stopped"); - $("#playnode_button").removeClass("playing"); - } - - graphcanvas.draw(); - - //update load counter - setInterval(function() { - $("#cpuload .fgload").width( (2*graph.elapsed_time) * 90); - if(graph.status == LGraph.STATUS_RUNNING) - $("#gpuload .fgload").width( (graphcanvas.render_time*10) * 90); - else - $("#gpuload .fgload").width( 4 ); - },200); - - //LiteGraph.run(100); -}); - - -function onShowNodes() -{ - $("#nodes-list").empty(); - - for (var i in LiteGraph.registered_node_types) - { - var node = LiteGraph.registered_node_types[i]; - var categories = node.category.split("/"); - - //create categories and find the propper one - var root = $("#nodes-list")[0]; - for(var i in categories) - { - var result = $(root).find("#node-category_" + categories[i] + " .container"); - if (result.length == 0) - { - var element = document.createElement("div"); - element.id = "node-category_" + categories[i]; - element.className = "node-category"; - element.data = categories[i]; - element.innerHTML = ""+categories[i]+""; - root.appendChild(element); - - $(element).find(".title").click(function(e){ - var element = $("#node-category_" + this.parentNode.data + " .container"); - $(element[0]).toggle(); - }); - - - var container = document.createElement("div"); - container.className = "container"; - element.appendChild(container); - - root = container; - } - else - root = result[0]; - } - - //create entry - var type = node.type; - var element = document.createElement("div"); - element.innerHTML = ""+node.title+" " + (node.desc? node.desc : ""); - element.className = "node-type"; - element.id = "node-type-" + node.name; - element.data = type; - root.appendChild(element); - } - - $(".node-type").click( function() { - $(".node-type.selected").removeClass("selected"); - $(this).addClass("selected"); - $("#confirm-createnode_button").attr("disabled",false); - }); - - $(".node-type").dblclick( function() { - $("#confirm-createnode_button").click(); - }); - - $("#confirm-createnode_button").attr("disabled",true); - - $("#modal-blocking-box").show(); - $("#nodes-browser").show(); -} - -function onDeleteNode() -{ - if(!graphcanvas.node_in_panel) return; - - graph.remove( graphcanvas.node_in_panel ); - graphcanvas.draw(); - $("#node-panel").hide(); - graphcanvas.node_in_panel = null; -} - -function onCloneNode() -{ - if(!graphcanvas.node_in_panel) return; - - var n = graphcanvas.node_in_panel.clone(); - n.pos[0] += 10; - n.pos[1] += 10; - - graph.add(n); - graphcanvas.draw(); -} - -function onSaveSession() -{ - if(graph.session["name"]) - $("#session-name-input").val(graph.session["name"]); - - if(graph.session["description"]) - $("#session-desc-input").val(graph.session["description"]); - - $("#modal-blocking-box").show(); - $("#savesession-dialog").show(); - //var str = LiteGraph.serialize(); - //localStorage.setItem("graph_session",str); -} - -function saveSession(name,desc) -{ - desc = desc || ""; - - graph.session["name"] = name; - graph.session["description"] = desc; - if(!graph.session["id"]) - graph.session["id"] = new Date().getTime(); - - var str = graph.serializeSession(); - localStorage.setItem("graph_session_" + graph.session["id"],str); - - var sessions_str = localStorage.getItem("node_sessions"); - var sessions = []; - - if(sessions_str) - sessions = JSON.parse(sessions_str); - - var pos = -1; - for(var i = 0; i < sessions.length; i++) - if( sessions[i].id == graph.session["id"] && sessions[i].name == name) - { - pos = i; - break; - } - - if(pos != -1) - { - //already on the list - } - else - { - var current_session = {name:name, desc:desc, id:graph.session["id"]}; - sessions.unshift(current_session); - localStorage.setItem("graph_sessions", JSON.stringify(sessions)); - } -} - -function onLoadSession() -{ - $("#sessions-browser-list").empty(); - - $("#modal-blocking-box").show(); - $("#sessions-browser").show(); - - var sessions_str = localStorage.getItem("graph_sessions"); - var sessions = []; - - if(sessions_str) - sessions = JSON.parse(sessions_str); - - for(var i in sessions) - { - var element = document.createElement("div"); - element.className = "session-item"; - element.data = sessions[i]; - $(element).html(""+sessions[i].name+""+sessions[i].desc+"x"); - $("#sessions-browser-list").append(element); - } - - $(".session-item").click( function() { - $(".session-item.selected").removeClass("selected"); - $(this).addClass("selected"); - $("#confirm-loadsession_button").attr("disabled",false); - }); - - $(".session-item").dblclick( function() { - $("#confirm-loadsession_button").click(); - }); - - $(".delete_session").click(function(e) { - var root = $(this).parent(); - var info = root[0].data; - - var sessions_str = localStorage.getItem("graph_sessions"); - var sessions = []; - if(sessions_str) - sessions = JSON.parse(sessions_str); - var pos = -1; - for(var i = 0; i < sessions.length; i++) - if( sessions[i].id == info.id ) - { - pos = i; - break; - } - - if(pos != -1) - { - sessions.splice(pos,1); - localStorage.setItem("graph_sessions", JSON.stringify(sessions)); - } - - root.remove(); - }); - - $("#confirm-loadsession_button").attr("disabled",true); - - /* - LiteGraph.stop(); - var str = localStorage.getItem("graph_session"); - LiteGraph.unserialize(str); - LiteGraph.draw(); - */ -} - -function onShagraph() -{ - -} - -function showImage(data) -{ - var img = new Image(); - img.src = data; - $("#data-visor .content").empty(); - $("#data-visor .content").append(img); - $("#modal-blocking-box").show(); - $("#data-visor").show(); -} - -function showElement(data) -{ - setTimeout(function(){ - $("#data-visor .content").empty(); - $("#data-visor .content").append(data); - $("#modal-blocking-box").show(); - $("#data-visor").show(); - },100); -} - - -// ********* SEEDED RANDOM ****************************** -function RandomNumberGenerator(seed) -{ - if (typeof(seed) == 'undefined') - { - var d = new Date(); - this.seed = 2345678901 + (d.getSeconds() * 0xFFFFFF) + (d.getMinutes() * 0xFFFF); - } - else - this.seed = seed; - - this.A = 48271; - this.M = 2147483647; - this.Q = this.M / this.A; - this.R = this.M % this.A; - this.oneOverM = 1.0 / this.M; - this.next = nextRandomNumber; - return this; -} - -function nextRandomNumber(){ - var hi = this.seed / this.Q; - var lo = this.seed % this.Q; - var test = this.A * lo - this.R * hi; - if(test > 0){ - this.seed = test; - } else { - this.seed = test + this.M; - } - return (this.seed * this.oneOverM); -} - -var RAND_GEN = RandomNumberGenerator(0); - -function RandomSeed(s) { RAND_GEN = RandomNumberGenerator(s); }; - -function myrand(Min, Max){ - return Math.round((Max-Min) * RAND_GEN.next() + Min); -} - -function myrandom() { return myrand(0,100000) / 100000; } - -// @format (hex|rgb|null) : Format to return, default is integer -function random_color(format) -{ - var rint = Math.round(0xffffff * myrandom()); - switch(format) - { - case 'hex': - return ('#0' + rint.toString(16)).replace(/^#0([0-9a-f]{6})$/i, '#$1'); - break; - - case 'rgb': - return 'rgb(' + (rint >> 16) + ',' + (rint >> 8 & 255) + ',' + (rint & 255) + ')'; - break; - - default: - return rint; - break; - } -} - -$.extend({ - getUrlVars: function(){ - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for(var i = 0; i < hashes.length; i++) - { - hash = hashes[i].split('='); - vars.push(hash[0]); - vars[hash[0]] = hash[1]; - } - return vars; - }, - getUrlVar: function(name){ - return $.getUrlVars()[name]; - } -}); +LiteGraph.node_images_path = "../nodes_data/"; +var editor = new LiteGraph.Editor("main"); +window.graph = editor.graph; +window.addEventListener("resize", function() { editor.graphcanvas.resize(); } ); +demo(); function trace(a) { diff --git a/demo/code_old.js b/demo/code_old.js new file mode 100644 index 000000000..7f00dea51 --- /dev/null +++ b/demo/code_old.js @@ -0,0 +1,547 @@ +var graph = null; +var graphcanvas = null; + +$(window).load(function() { + + var id = null; + if ($.getUrlVar("id") != null) + id = parseInt($.getUrlVar("id")); + else if (self.document.location.hash) + id = parseInt( self.document.location.hash.substr(1) ); + + $("#settings_button").click( function() { $("#settings-panel").toggle(); }); + $("#addnode_button").click( function() { onShowNodes() }); + $("#deletenode_button").click( function() { onDeleteNode() }); + $("#clonenode_button").click( function() { onCloneNode() }); + + $("#playnode_button").click( function() { + if(graph.status == LGraph.STATUS_STOPPED) + { + $(this).html(" Stop"); + graph.start(1); + } + else + { + $(this).html(" Play"); + graph.stop(); + } + }); + + $("#playstepnode_button").click( function() { + graph.runStep(1); + graphcanvas.draw(true,true); + }); + + $("#playfastnode_button").click( function() { + graph.runStep(5000); + graphcanvas.draw(true,true); + }); + + $("#collapsenode_button").click( function() { + /* + for(var i in graphcanvas.nodes_selected) + graphcanvas.nodes_selected[i].collapse(); + */ + if( graphcanvas.node_in_panel ) + graphcanvas.node_in_panel.collapse(); + + graphcanvas.draw(); + }); + + $("#pinnode_button").click( function() { + if( graphcanvas.node_in_panel ) + graphcanvas.node_in_panel.pin(); + }); + + $("#sendtobacknode_button").click( function() { + if( graphcanvas.node_in_panel ) + graphcanvas.sendToBack( graphcanvas.node_in_panel ); + graphcanvas.draw(true); + }); + + + + $("#confirm-createnode_button").click(function() { + var element = $(".node-type.selected")[0]; + var name = element.data; + var n = LiteGraph.createNode(name); + graph.add(n); + n.pos = graphcanvas.convertOffsetToCanvas([30,30]); + graphcanvas.draw(true,true); + $("#modal-blocking-box").hide(); + $("#nodes-browser").hide(); + }); + + $("#cancel-createnode_button").click(function() { + $("#modal-blocking-box").hide(); + $("#nodes-browser").hide(); + }); + + $("#close-area_button").click(function() { + $("#modal-blocking-box").hide(); + $("#data-visor").hide(); + }); + + $("#confirm-loadsession_button").click(function() { + var element = $(".session-item.selected")[0]; + var info = element.data; + + var str = localStorage.getItem("graph_session_" + info.id ); + graph.stop(); + graph.unserialize(str); + + graphcanvas.draw(true,true); + $("#modal-blocking-box").hide(); + $("#sessions-browser").hide(); + }); + + $("#cancel-loadsession_button").click(function() { + $("#modal-blocking-box").hide(); + $("#sessions-browser").hide(); + }); + + $("#livemode_button").click( function() { + graphcanvas.switchLiveMode(true); + graphcanvas.draw(); + var url = graphcanvas.live_mode ? "imgs/gauss_bg_medium.jpg" : "imgs/gauss_bg.jpg"; + $("#livemode_button").html(!graphcanvas.live_mode ? " Live" : " Edit" ); + //$("canvas").css("background-image","url('"+url+"')"); + }); + + $("#newsession_button").click( function() { + $("#main-area").hide(); + graph.clear(); + graphcanvas.draw(); + $("#main-area").show(); + }); + + $("#savesession_button").click( function() { + onSaveSession(); + }); + + $("#loadsession_button").click( function() { + onLoadSession(); + }); + + $("#cancelsession-dialog_button").click(function() + { + $("#modal-blocking-box").hide(); + $("#savesession-dialog").hide(); + }); + + $("#savesession-dialog_button").click(function() + { + var name = $("#session-name-input").val(); + var desc = $("#session-description-input").val(); + + saveSession(name,desc); + + $("#modal-blocking-box").hide(); + $("#savesession-dialog").hide(); + + }); + + $("#closepanel_button").click(function() + { + graphcanvas.showNodePanel(null); + }); + + $("#maximize_button").click(function() + { + if($("#main").width() != window.innerWidth) + { + $("#main").width( (window.innerWidth).toString() + "px"); + $("#main").height( (window.innerHeight - 40).toString() + "px"); + graphcanvas.resizeCanvas(window.innerWidth,window.innerHeight - 100); + } + else + { + $("#main").width("800px"); + $("#main").height("660px"); + graphcanvas.resizeCanvas(800,600); + } + }); + + $("#resetscale_button").click(function() + { + graph.config.canvas_scale = 1.0; + graphcanvas.draw(true,true); + }); + + $("#resetpos_button").click(function() + { + graph.config.canvas_offset = [0,0]; + graphcanvas.draw(true,true); + }); + + $(".nodecolorbutton").click(function() + { + if( graphcanvas.node_in_panel ) + { + graphcanvas.node_in_panel.color = this.getAttribute("data-color"); + graphcanvas.node_in_panel.bgcolor = this.getAttribute("data-bgcolor"); + } + graphcanvas.draw(true,true); + }); + + + if ("onhashchange" in window) // does the browser support the hashchange event? + { + window.onhashchange = function () { + var h = window.location.hash.substr(1); + //action + return false; + } + } + + LiteGraph.node_images_path = "../nodes_data/"; + graph = new LGraph(); + graphcanvas = new LGraphCanvas("graphcanvas",graph); + graphcanvas.background_image = "imgs/grid.png"; + + graph.onAfterExecute = function() { graphcanvas.draw(true) }; + demo(); + + graph.onPlayEvent = function() + { + $("#playnode_button").addClass("playing"); + $("#playnode_button").removeClass("stopped"); + } + + graph.onStopEvent = function() + { + $("#playnode_button").addClass("stopped"); + $("#playnode_button").removeClass("playing"); + } + + graphcanvas.draw(); + + //update load counter + setInterval(function() { + $("#cpuload .fgload").width( (2*graph.elapsed_time) * 90); + if(graph.status == LGraph.STATUS_RUNNING) + $("#gpuload .fgload").width( (graphcanvas.render_time*10) * 90); + else + $("#gpuload .fgload").width( 4 ); + },200); + + //LiteGraph.run(100); +}); + + +function onShowNodes() +{ + $("#nodes-list").empty(); + + for (var i in LiteGraph.registered_node_types) + { + var node = LiteGraph.registered_node_types[i]; + var categories = node.category.split("/"); + + //create categories and find the propper one + var root = $("#nodes-list")[0]; + for(var i in categories) + { + var result = $(root).find("#node-category_" + categories[i] + " .container"); + if (result.length == 0) + { + var element = document.createElement("div"); + element.id = "node-category_" + categories[i]; + element.className = "node-category"; + element.data = categories[i]; + element.innerHTML = ""+categories[i]+""; + root.appendChild(element); + + $(element).find(".title").click(function(e){ + var element = $("#node-category_" + this.parentNode.data + " .container"); + $(element[0]).toggle(); + }); + + + var container = document.createElement("div"); + container.className = "container"; + element.appendChild(container); + + root = container; + } + else + root = result[0]; + } + + //create entry + var type = node.type; + var element = document.createElement("div"); + element.innerHTML = ""+node.title+" " + (node.desc? node.desc : ""); + element.className = "node-type"; + element.id = "node-type-" + node.name; + element.data = type; + root.appendChild(element); + } + + $(".node-type").click( function() { + $(".node-type.selected").removeClass("selected"); + $(this).addClass("selected"); + $("#confirm-createnode_button").attr("disabled",false); + }); + + $(".node-type").dblclick( function() { + $("#confirm-createnode_button").click(); + }); + + $("#confirm-createnode_button").attr("disabled",true); + + $("#modal-blocking-box").show(); + $("#nodes-browser").show(); +} + +function onDeleteNode() +{ + if(!graphcanvas.node_in_panel) return; + + graph.remove( graphcanvas.node_in_panel ); + graphcanvas.draw(); + $("#node-panel").hide(); + graphcanvas.node_in_panel = null; +} + +function onCloneNode() +{ + if(!graphcanvas.node_in_panel) return; + + var n = graphcanvas.node_in_panel.clone(); + n.pos[0] += 10; + n.pos[1] += 10; + + graph.add(n); + graphcanvas.draw(); +} + +function onSaveSession() +{ + if(graph.session["name"]) + $("#session-name-input").val(graph.session["name"]); + + if(graph.session["description"]) + $("#session-desc-input").val(graph.session["description"]); + + $("#modal-blocking-box").show(); + $("#savesession-dialog").show(); + //var str = LiteGraph.serialize(); + //localStorage.setItem("graph_session",str); +} + +function saveSession(name,desc) +{ + desc = desc || ""; + + graph.session["name"] = name; + graph.session["description"] = desc; + if(!graph.session["id"]) + graph.session["id"] = new Date().getTime(); + + var str = graph.serializeSession(); + localStorage.setItem("graph_session_" + graph.session["id"],str); + + var sessions_str = localStorage.getItem("node_sessions"); + var sessions = []; + + if(sessions_str) + sessions = JSON.parse(sessions_str); + + var pos = -1; + for(var i = 0; i < sessions.length; i++) + if( sessions[i].id == graph.session["id"] && sessions[i].name == name) + { + pos = i; + break; + } + + if(pos != -1) + { + //already on the list + } + else + { + var current_session = {name:name, desc:desc, id:graph.session["id"]}; + sessions.unshift(current_session); + localStorage.setItem("graph_sessions", JSON.stringify(sessions)); + } +} + +function onLoadSession() +{ + $("#sessions-browser-list").empty(); + + $("#modal-blocking-box").show(); + $("#sessions-browser").show(); + + var sessions_str = localStorage.getItem("graph_sessions"); + var sessions = []; + + if(sessions_str) + sessions = JSON.parse(sessions_str); + + for(var i in sessions) + { + var element = document.createElement("div"); + element.className = "session-item"; + element.data = sessions[i]; + $(element).html(""+sessions[i].name+""+sessions[i].desc+"x"); + $("#sessions-browser-list").append(element); + } + + $(".session-item").click( function() { + $(".session-item.selected").removeClass("selected"); + $(this).addClass("selected"); + $("#confirm-loadsession_button").attr("disabled",false); + }); + + $(".session-item").dblclick( function() { + $("#confirm-loadsession_button").click(); + }); + + $(".delete_session").click(function(e) { + var root = $(this).parent(); + var info = root[0].data; + + var sessions_str = localStorage.getItem("graph_sessions"); + var sessions = []; + if(sessions_str) + sessions = JSON.parse(sessions_str); + var pos = -1; + for(var i = 0; i < sessions.length; i++) + if( sessions[i].id == info.id ) + { + pos = i; + break; + } + + if(pos != -1) + { + sessions.splice(pos,1); + localStorage.setItem("graph_sessions", JSON.stringify(sessions)); + } + + root.remove(); + }); + + $("#confirm-loadsession_button").attr("disabled",true); + + /* + LiteGraph.stop(); + var str = localStorage.getItem("graph_session"); + LiteGraph.unserialize(str); + LiteGraph.draw(); + */ +} + +function onShagraph() +{ + +} + +function showImage(data) +{ + var img = new Image(); + img.src = data; + $("#data-visor .content").empty(); + $("#data-visor .content").append(img); + $("#modal-blocking-box").show(); + $("#data-visor").show(); +} + +function showElement(data) +{ + setTimeout(function(){ + $("#data-visor .content").empty(); + $("#data-visor .content").append(data); + $("#modal-blocking-box").show(); + $("#data-visor").show(); + },100); +} + + +// ********* SEEDED RANDOM ****************************** +function RandomNumberGenerator(seed) +{ + if (typeof(seed) == 'undefined') + { + var d = new Date(); + this.seed = 2345678901 + (d.getSeconds() * 0xFFFFFF) + (d.getMinutes() * 0xFFFF); + } + else + this.seed = seed; + + this.A = 48271; + this.M = 2147483647; + this.Q = this.M / this.A; + this.R = this.M % this.A; + this.oneOverM = 1.0 / this.M; + this.next = nextRandomNumber; + return this; +} + +function nextRandomNumber(){ + var hi = this.seed / this.Q; + var lo = this.seed % this.Q; + var test = this.A * lo - this.R * hi; + if(test > 0){ + this.seed = test; + } else { + this.seed = test + this.M; + } + return (this.seed * this.oneOverM); +} + +var RAND_GEN = RandomNumberGenerator(0); + +function RandomSeed(s) { RAND_GEN = RandomNumberGenerator(s); }; + +function myrand(Min, Max){ + return Math.round((Max-Min) * RAND_GEN.next() + Min); +} + +function myrandom() { return myrand(0,100000) / 100000; } + +// @format (hex|rgb|null) : Format to return, default is integer +function random_color(format) +{ + var rint = Math.round(0xffffff * myrandom()); + switch(format) + { + case 'hex': + return ('#0' + rint.toString(16)).replace(/^#0([0-9a-f]{6})$/i, '#$1'); + break; + + case 'rgb': + return 'rgb(' + (rint >> 16) + ',' + (rint >> 8 & 255) + ',' + (rint & 255) + ')'; + break; + + default: + return rint; + break; + } +} + +$.extend({ + getUrlVars: function(){ + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for(var i = 0; i < hashes.length; i++) + { + hash = hashes[i].split('='); + vars.push(hash[0]); + vars[hash[0]] = hash[1]; + } + return vars; + }, + getUrlVar: function(name){ + return $.getUrlVars()[name]; + } +}); + +function trace(a) +{ + if(typeof(console) == "object") + console.log(a); +} \ No newline at end of file diff --git a/demo/index.html b/demo/index.html index 746bfc2f7..ef592e175 100644 --- a/demo/index.html +++ b/demo/index.html @@ -6,9 +6,11 @@ +
+
+ + diff --git a/demo/style.css b/demo/style.css index 76fdbb897..898865354 100644 --- a/demo/style.css +++ b/demo/style.css @@ -13,76 +13,18 @@ body { h1 { font-family: "Metro Light",Tahoma; - color: #DDD; - font-size: 28px; - padding-left: 10px; - /*text-shadow: 0 1px 1px #333, 0 -1px 1px #777;*/ - margin: 0; - font-weight: normal; -} - -h1 span { - font-family: "Arial"; - font-size: 14px; - font-weight: normal; - color: #AAA; } h2 { font-family: "Metro Light"; - padding: 5px; - margin-left: 10px; } #main { - width: 1000px; - min-height: 540px; - margin: auto; - background-color: #222; - /*border-radius: 10px;*/ -} - -#main.fullscreen { width: 100%; height: 100%; + background-color: #222; } -#header, #footer { - position: relative; - height: 40px; - background-color: #333; - /*border-radius: 10px 10px 0 0;*/ -} - -.tools, #tools-left, #tools-right { - position: absolute; - top: 2px; - right: 0px; - vertical-align: top; - - margin: 2px 5px 0 0px; -} - -#header button { - height: 32px; - vertical-align: top; -} - -#footer button { - /*font-size: 16px;*/ -} - -#tools-left { - right: auto; - left: 4px; -} - - -#footer { - height: 40px; - position: relative; - /*border-radius: 0 0 10px 10px;*/ -} #status { position: absolute; @@ -135,55 +77,6 @@ h2 { height: 100%; } -button { - /*font-family: "Metro Light";*/ - color: #CCC; - font-size: 20px; - min-width: 30px; - /*border-radius: 0.3em;*/ - border: 0 solid #666; - background-color: #3F3F3F; - /*box-shadow: 0 0 3px black;*/ - padding: 4px 10px; - line-height: 20px; - cursor: pointer; - transition: all 1s; - -moz-transition: all 1s; - -webkit-transition: all 0.4s; -} - -button:hover { - background-color: #999; - color: #FFF; - transition: all 1s; - -moz-transition: all 1s; - -webkit-transition: all 0.4s; -} - -button:active { - background-color: white; -} - -button.fixed { - position: absolute; - top: 5px; - right: 5px; - font-size: 1.2em; -} - -button img { - margin: -4px; - vertical-align: top; -} - -#play_button { - background-color: #446; - margin-right: 30px; -} - -#play_button:hover { - background-color: #AAF; -} .item-list .item { margin: 5px; @@ -348,40 +241,3 @@ textarea { margin: 10px; } -#loadmeter { - font-family: "Tahoma"; - display: inline-block; - position: absolute; - top: 0; - left: 300px; - color: #AAA; - font-size: 12px; - border-radius: 2px; - width: 130px; - margin: 3px; - padding: 2px; - vertical-align: top; -} - -#loadmeter strong { - vertical-align: top; - padding: 3px; - width: 30px; - display: inline-block; - line-height: 8px; -} - -#cpuload .bgload, #gpuload .bgload { - display: inline-block; - width: 90px; - height: 15px; - background-image: url('imgs/load-progress-empty.png'); -} - -#cpuload .fgload, #gpuload .fgload { - display: inline-block; - width: 4px; - height: 15px; - max-width: 90px; - background-image: url('imgs/load-progress-full.png'); -} diff --git a/doc/api.js b/doc/api.js index 8f212c487..18d6f246a 100644 --- a/doc/api.js +++ b/doc/api.js @@ -2,6 +2,7 @@ YUI.add("yuidoc-meta", function(Y) { Y.YUIDoc = { meta: { "classes": [ "LGraph", + "LGraphCanvas", "LGraphNode", "LiteGraph" ], diff --git a/doc/classes/LGraph.html b/doc/classes/LGraph.html index 9f8bfb531..561b624fb 100644 --- a/doc/classes/LGraph.html +++ b/doc/classes/LGraph.html @@ -43,6 +43,8 @@
  • LGraph
  • +
  • LGraphCanvas
  • +
  • LGraphNode
  • LiteGraph
  • @@ -94,7 +96,7 @@
    - Defined in: ../src/litegraph.js:302 + Defined in: ../src/litegraph.js:299
    @@ -145,7 +147,7 @@ - ../src/litegraph.js:302 + ../src/litegraph.js:299

    @@ -196,6 +198,13 @@ + + +
  • + attachCanvas + + +
  • @@ -203,6 +212,20 @@ +
  • + +
  • + configure + + + +
  • + +
  • + detachCanvas + + +
  • @@ -252,6 +275,13 @@ +
  • + +
  • + isLive + + +
  • @@ -304,14 +334,7 @@
  • - stop - - - -
  • - -
  • - unserialize + stop execution @@ -382,7 +405,7 @@ - ../src/litegraph.js:602 + ../src/litegraph.js:645

    @@ -426,6 +449,94 @@ + + + +
    +

    attachCanvas

    + + +
    + (
      + +
    • + + graph_canvas + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:361 + +

    + + + + + +
    + +
    +

    Attach Canvas to this graph

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + graph_canvas + GraphCanvas + + + + +
      + +
      + + +
    • + +
    +
    + + + + +
    @@ -461,7 +572,7 @@ - ../src/litegraph.js:321 + ../src/litegraph.js:317

    @@ -481,6 +592,183 @@ + + + +
    +

    configure

    + + +
    + (
      + +
    • + + str + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:920 + +

    + + + + + +
    + +
    +

    Configure a graph from a JSON string

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + str + String + + + + +
      +

      configure a graph from a JSON string

      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    detachCanvas

    + + +
    + (
      + +
    • + + graph_canvas + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:380 + +

    + + + + + +
    + +
    +

    Detach Canvas from this graph

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + graph_canvas + GraphCanvas + + + + +
      + +
      + + +
    • + +
    +
    + + + + +
    @@ -530,7 +818,7 @@ - ../src/litegraph.js:737 + ../src/litegraph.js:780

    @@ -637,7 +925,7 @@ - ../src/litegraph.js:721 + ../src/litegraph.js:764

    @@ -734,7 +1022,7 @@ - ../src/litegraph.js:575 + ../src/litegraph.js:605

    @@ -808,7 +1096,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:564 + ../src/litegraph.js:594

    @@ -887,7 +1175,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:708 + ../src/litegraph.js:751

    @@ -991,7 +1279,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:753 + ../src/litegraph.js:796

    @@ -1120,7 +1408,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:553 + ../src/litegraph.js:583

    @@ -1154,6 +1442,61 @@ if the nodes are using graphical actions

    + + + +
    +

    isLive

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:869 + +

    + + + + + +
    + +
    +

    returns if the graph is in live mode

    + +
    + + + + + +
    @@ -1199,7 +1542,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:647 + ../src/litegraph.js:690

    @@ -1288,7 +1631,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:413 + ../src/litegraph.js:443

    @@ -1383,7 +1726,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:587 + ../src/litegraph.js:617

    @@ -1455,7 +1798,7 @@ if the nodes are using graphical actions

    - String + Object @@ -1482,7 +1825,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:843 + ../src/litegraph.js:893

    @@ -1493,7 +1836,7 @@ if the nodes are using graphical actions

    -

    Creates a JSON String containing all the info about this graph

    +

    Creates a Object containing all the info about this graph, it can be serialized

    @@ -1506,7 +1849,7 @@ if the nodes are using graphical actions

    - String: + Object:

    value of the node

    @@ -1567,7 +1910,7 @@ if the nodes are using graphical actions

    - ../src/litegraph.js:774 + ../src/litegraph.js:817

    @@ -1677,7 +2020,7 @@ can be easily accesed from the outside of the graph

    - ../src/litegraph.js:789 + ../src/litegraph.js:832

    @@ -1780,7 +2123,7 @@ can be easily accesed from the outside of the graph

    - ../src/litegraph.js:364 + ../src/litegraph.js:394

    @@ -1827,8 +2170,8 @@ can be easily accesed from the outside of the graph

    -
    -

    stop

    +
    +

    stop execution

    () @@ -1859,7 +2202,7 @@ can be easily accesed from the outside of the graph

    - ../src/litegraph.js:391 + ../src/litegraph.js:421

    @@ -1879,95 +2222,6 @@ can be easily accesed from the outside of the graph

    -
    - - -
    -

    unserialize

    - - -
    - (
      - -
    • - - str - -
    • - -
    ) -
    - - - - - - - - - - - - - - - - -
    - - - -

    - - Defined in - - - - - ../src/litegraph.js:869 - -

    - - - - - -
    - -
    -

    Configure a graph from a JSON string

    - -
    - - -
    -

    Parameters:

    - -
      - -
    • - - str - String - - - - -
      -

      configure a graph from a JSON string

      - -
      - - -
    • - -
    -
    - - - - -
    @@ -2003,7 +2257,7 @@ can be easily accesed from the outside of the graph

    - ../src/litegraph.js:457 + ../src/litegraph.js:487

    diff --git a/doc/classes/LGraphCanvas.html b/doc/classes/LGraphCanvas.html new file mode 100644 index 000000000..fcb197b6b --- /dev/null +++ b/doc/classes/LGraphCanvas.html @@ -0,0 +1,277 @@ + + + + + LGraphCanvas + + + + + + + + +
    +
    +
    + +

    + +
    +
    + API Docs for: +
    +
    +
    + +
    + +
    +
    +
    + Show: + + + + + + + +
    + + +
    +
    +
    +

    LGraphCanvas Class

    +
    + + + + + +
    + Defined in: ../src/litegraph.js:1794 +
    + + + + + +
    + + + +
    +

    The Global Scope. It contains all the registered node classes.

    + +
    + + +
    +

    Constructor

    +
    +

    LGraphCanvas

    + + +
    + (
      + +
    • + + canvas + +
    • + +
    • + + graph + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1794 + +

    + + + + + +
    + +
    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + canvas + HTMLCanvas + + + + +
      +

      the canvas where you want to render (it accepts a selector in string format)

      + +
      + + +
    • + +
    • + + graph + LGraph + + + + +
      + +
      + + +
    • + +
    +
    + + + + + +
    + +
    + + +
    + + +
    +
    +

    Item Index

    + + + + + + + + +
    + + + + + + + + +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + diff --git a/doc/classes/LGraphNode.html b/doc/classes/LGraphNode.html index ea04b3c2e..7d966a3da 100644 --- a/doc/classes/LGraphNode.html +++ b/doc/classes/LGraphNode.html @@ -43,6 +43,8 @@
  • LGraph
  • +
  • LGraphCanvas
  • +
  • LGraphNode
  • LiteGraph
  • @@ -94,7 +96,7 @@
    - Defined in: ../src/litegraph.js:947 + Defined in: ../src/litegraph.js:997
    @@ -117,6 +119,8 @@
  • Index
  • +
  • Methods
  • + @@ -127,6 +131,182 @@

    Item Index

    +
    +

    Methods

    + + +
    + @@ -136,6 +316,2436 @@ +
    +

    Methods

    + + +
    +

    addConnection

    + + +
    + (
      + +
    • + + name + +
    • + +
    • + + type + +
    • + +
    • + + pos + +
    • + +
    • + + direction + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1320 + +

    + + + + + +
    + +
    +

    add an special connection to this node (used for special kinds of graphs)

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + name + String + + + + +
      + +
      + + +
    • + +
    • + + type + String + + + + +
      +

      string defining the input type ("vec3","number",...)

      + +
      + + +
    • + +
    • + + pos + x,y + + + + +
      +

      position of the connection inside the node

      + +
      + + +
    • + +
    • + + direction + String + + + + +
      +

      if is input or output

      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    addInput

    + + +
    + (
      + +
    • + + name + +
    • + +
    • + + type + +
    • + +
    • + + extra_info + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1289 + +

    + + + + + +
    + +
    +

    add a new input slot to use in this node

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + name + String + + + + +
      + +
      + + +
    • + +
    • + + type + String + + + + +
      +

      string defining the input type ("vec3","number",...)

      + +
      + + +
    • + +
    • + + extra_info + Object + + + + +
      +

      this can be used to have special properties of an input (special color, position, etc)

      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    addOutput

    + + +
    + (
      + +
    • + + name + +
    • + +
    • + + type + +
    • + +
    • + + extra_info + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1258 + +

    + + + + + +
    + +
    +

    add a new output slot to use in this node

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + name + String + + + + +
      + +
      + + +
    • + +
    • + + type + String + + + + +
      +

      string defining the output type ("vec3","number",...)

      + +
      + + +
    • + +
    • + + extra_info + Object + + + + +
      +

      this can be used to have special properties of an output (special color, position, etc)

      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    collapse

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1756 + +

    + + + + + +
    + +
    +

    Collapse the node to make it smaller on the canvas

    + +
    + + + + + + +
    + + +
    +

    computeSize

    + + +
    + (
      + +
    • + + minHeight + +
    • + +
    ) +
    + + + + + Number + + + + + + + + + + + + + + + +
    + + + +

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

    + + + + + +
    + +
    +

    computes the size of a node according to its inputs and output slots

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + minHeight + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Number: + +

    the total size

    + + +
    +
    + + + +
    + + +
    +

    configure

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1026 + +

    + + + + + +
    + +
    +

    configure a node from an object

    + +
    + + + + + + +
    + + +
    +

    connect

    + + +
    + (
      + +
    • + + slot + +
    • + +
    • + + node + +
    • + +
    • + + target_slot + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1401 + +

    + + + + + +
    + +
    +

    connect this node output to the input of another node

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number_or_string + + + + +
      +

      (could be the number of the slot or the string with the name of the slot)

      + +
      + + +
    • + +
    • + + node + LGraphNode + + + + +
      +

      the target node

      + +
      + + +
    • + +
    • + + target_slot + Number_or_string + + + + +
      +

      the input slot of the target node (could be the number of the slot or the string with the name of the slot)

      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + +

    if it was connected succesfully

    + + +
    +
    + + + +
    + + +
    +

    disconnectInput

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1545 + +

    + + + + + +
    + +
    +

    disconnect one input

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number_or_string + + + + +
      +

      (could be the number of the slot or the string with the name of the slot)

      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + +

    if it was disconnected succesfully

    + + +
    +
    + + + +
    + + +
    +

    disconnectOutput

    + + +
    + (
      + +
    • + + slot + +
    • + +
    • + + target_node + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1482 + +

    + + + + + +
    + +
    +

    disconnect one output to an specific node

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number_or_string + + + + +
      +

      (could be the number of the slot or the string with the name of the slot)

      + +
      + + +
    • + +
    • + + target_node + LGraphNode + + + + +
      +

      the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]

      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + +

    if it was disconnected succesfully

    + + +
    +
    + + + +
    + + +
    +

    getBounding

    + + + () + + + + + Float32Array4 + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1351 + +

    + + + + + +
    + +
    +

    returns the bounding of the object, used for rendering purposes

    + +
    + + + + +
    +

    Returns:

    + +
    + + + Float32Array4: + +

    the total size

    + + +
    +
    + + + +
    + + +
    +

    getConnectionPos

    + + +
    + (
      + +
    • + + is_input + +
    • + +
    • + + slot + +
    • + +
    ) +
    + + + + + x,y + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1599 + +

    + + + + + +
    + +
    +

    returns the center of a connection point in canvas coords

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + is_input + Boolean + + + + +
      +

      true if if a input slot, false if it is an output

      + +
      + + +
    • + +
    • + + slot + Number_or_string + + + + +
      +

      (could be the number of the slot or the string with the name of the slot)

      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + x,y: + +

    the position

    + + +
    +
    + + + +
    + + +
    +

    getInputData

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1161 + +

    + + + + + +
    + +
    +

    retrieves the input data from one slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + : + +

    data

    + + +
    +
    + + + +
    + + +
    +

    getInputInfo

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Object + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1187 + +

    + + + + + +
    + +
    +

    tells you info about an input connection (which node, type, etc)

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Object: + + +
    +
    + + + +
    + + +
    +

    getOutputInfo

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Object + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1202 + +

    + + + + + +
    + +
    +

    tells you info about an output connection (which node, type, etc)

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Object: + + +
    +
    + + + +
    + + +
    +

    getOutputNodes

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Array + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1229 + +

    + + + + + +
    + +
    +

    retrieves all the nodes connected to this output slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Array: + + +
    +
    + + + +
    + + +
    +

    isInputConnected

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1175 + +

    + + + + + +
    + +
    +

    tells you if there is a connection in one input slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + + +
    +
    + + + +
    + + +
    +

    isOutputConnected

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1217 + +

    + + + + + +
    + +
    +

    tells you if there is a connection in one output slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + + +
    +
    + + + +
    + + +
    +

    isPointInsideNode

    + + +
    + (
      + +
    • + + x + +
    • + +
    • + + y + +
    • + +
    ) +
    + + + + + Boolean + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1361 + +

    + + + + + +
    + +
    +

    checks if a point is inside the shape of a node

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + x + Number + + + + +
      + +
      + + +
    • + +
    • + + y + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + +
    +

    Returns:

    + +
    + + + Boolean: + + +
    +
    + + + +
    + + +
    +

    pin

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1769 + +

    + + + + + +
    + +
    +

    Forces the node to do not move or realign on Z

    + +
    + + + + + + +
    + + +
    +

    removeInput

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1308 + +

    + + + + + +
    + +
    +

    remove an existing input slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    removeOutput

    + + +
    + (
      + +
    • + + slot + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1277 + +

    + + + + + +
    + +
    +

    remove an existing output slot

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    serialize

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1073 + +

    + + + + + +
    + +
    +

    serialize the content

    + +
    + + + + + + +
    + + +
    +

    setOutputData

    + + +
    + (
      + +
    • + + slot + +
    • + +
    • + + data + +
    • + +
    ) +
    + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1145 + +

    + + + + + +
    + +
    +

    sets the output data

    + +
    + + +
    +

    Parameters:

    + +
      + +
    • + + slot + Number + + + + +
      + +
      + + +
    • + +
    • + + data + + + + + +
      + +
      + + +
    • + +
    +
    + + + + + +
    + + +
    +

    toString

    + + + () + + + + + + + + + + + + + + + + +
    + + + +

    + + Defined in + + + + + ../src/litegraph.js:1132 + +

    + + + + + +
    + +
    +

    serialize and stringify

    + +
    + + + + + + +
    + + +
    + diff --git a/doc/classes/LiteGraph.html b/doc/classes/LiteGraph.html index f7132dbab..e2709b8d7 100644 --- a/doc/classes/LiteGraph.html +++ b/doc/classes/LiteGraph.html @@ -43,6 +43,8 @@
  • LGraph
  • +
  • LGraphCanvas
  • +
  • LGraphNode
  • LiteGraph
  • diff --git a/doc/data.json b/doc/data.json index cfce2454f..ca7f52bba 100644 --- a/doc/data.json +++ b/doc/data.json @@ -7,7 +7,8 @@ "classes": { "LiteGraph": 1, "LGraph": 1, - "LGraphNode": 1 + "LGraphNode": 1, + "LGraphCanvas": 1 }, "fors": {}, "namespaces": {} @@ -37,7 +38,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 302, + "line": 299, "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 }, @@ -50,7 +51,7 @@ "plugin_for": [], "extension_for": [], "file": "../src/litegraph.js", - "line": 947, + "line": 997, "description": "Base Class for all the node type classes", "params": [ { @@ -59,6 +60,31 @@ "type": "String" } ] + }, + "LGraphCanvas": { + "name": "LGraphCanvas", + "shortname": "LGraphCanvas", + "classitems": [], + "plugins": [], + "extensions": [], + "plugin_for": [], + "extension_for": [], + "file": "../src/litegraph.js", + "line": 1794, + "description": "The Global Scope. It contains all the registered node classes.", + "is_constructor": 1, + "params": [ + { + "name": "canvas", + "description": "the canvas where you want to render (it accepts a selector in string format)", + "type": "HTMLCanvas" + }, + { + "name": "graph", + "description": "", + "type": "LGraph" + } + ] } }, "classitems": [ @@ -159,7 +185,7 @@ }, { "file": "../src/litegraph.js", - "line": 321, + "line": 317, "description": "Removes all nodes from this graph", "itemtype": "method", "name": "clear", @@ -167,7 +193,37 @@ }, { "file": "../src/litegraph.js", - "line": 364, + "line": 361, + "description": "Attach Canvas to this graph", + "itemtype": "method", + "name": "attachCanvas", + "params": [ + { + "name": "graph_canvas", + "description": "", + "type": "GraphCanvas" + } + ], + "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 380, + "description": "Detach Canvas from this graph", + "itemtype": "method", + "name": "detachCanvas", + "params": [ + { + "name": "graph_canvas", + "description": "", + "type": "GraphCanvas" + } + ], + "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 394, "description": "Starts running this graph every interval milliseconds.", "itemtype": "method", "name": "start", @@ -182,15 +238,15 @@ }, { "file": "../src/litegraph.js", - "line": 391, + "line": 421, "description": "Stops the execution loop of the graph", "itemtype": "method", - "name": "stop", + "name": "stop execution", "class": "LGraph" }, { "file": "../src/litegraph.js", - "line": 413, + "line": 443, "description": "Run N steps (cycles) of the graph", "itemtype": "method", "name": "runStep", @@ -205,7 +261,7 @@ }, { "file": "../src/litegraph.js", - "line": 457, + "line": 487, "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", @@ -213,7 +269,7 @@ }, { "file": "../src/litegraph.js", - "line": 553, + "line": 583, "description": "Returns the amount of time the graph has been running in milliseconds", "itemtype": "method", "name": "getTime", @@ -225,7 +281,7 @@ }, { "file": "../src/litegraph.js", - "line": 564, + "line": 594, "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", @@ -237,7 +293,7 @@ }, { "file": "../src/litegraph.js", - "line": 575, + "line": 605, "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", @@ -249,7 +305,7 @@ }, { "file": "../src/litegraph.js", - "line": 587, + "line": 617, "description": "Sends an event to all the nodes, useful to trigger stuff", "itemtype": "method", "name": "sendEventToAllNodes", @@ -269,7 +325,7 @@ }, { "file": "../src/litegraph.js", - "line": 602, + "line": 645, "description": "Adds a new node instasnce to this graph", "itemtype": "method", "name": "add", @@ -284,7 +340,7 @@ }, { "file": "../src/litegraph.js", - "line": 647, + "line": 690, "description": "Removes a node from the graph", "itemtype": "method", "name": "remove", @@ -299,7 +355,7 @@ }, { "file": "../src/litegraph.js", - "line": 708, + "line": 751, "description": "Returns a node by its id.", "itemtype": "method", "name": "getNodeById", @@ -314,7 +370,7 @@ }, { "file": "../src/litegraph.js", - "line": 721, + "line": 764, "description": "Returns a list of nodes that matches a type", "itemtype": "method", "name": "findNodesByType", @@ -333,7 +389,7 @@ }, { "file": "../src/litegraph.js", - "line": 737, + "line": 780, "description": "Returns a list of nodes that matches a name", "itemtype": "method", "name": "findNodesByName", @@ -352,7 +408,7 @@ }, { "file": "../src/litegraph.js", - "line": 753, + "line": 796, "description": "Returns the top-most node in this position of the canvas", "itemtype": "method", "name": "getNodeOnPos", @@ -381,7 +437,7 @@ }, { "file": "../src/litegraph.js", - "line": 774, + "line": 817, "description": "Assigns a value to all the nodes that matches this name. This is used to create global variables of the node that\ncan be easily accesed from the outside of the graph", "itemtype": "method", "name": "setInputData", @@ -401,7 +457,7 @@ }, { "file": "../src/litegraph.js", - "line": 789, + "line": 832, "description": "Returns the value of the first node with this name. This is used to access global variables of the graph from the outside", "itemtype": "method", "name": "setInputData", @@ -420,22 +476,30 @@ }, { "file": "../src/litegraph.js", - "line": 843, - "description": "Creates a JSON String containing all the info about this graph", + "line": 869, + "description": "returns if the graph is in live mode", + "itemtype": "method", + "name": "isLive", + "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 893, + "description": "Creates a Object containing all the info about this graph, it can be serialized", "itemtype": "method", "name": "serialize", "return": { "description": "value of the node", - "type": "String" + "type": "Object" }, "class": "LGraph" }, { "file": "../src/litegraph.js", - "line": 869, + "line": 920, "description": "Configure a graph from a JSON string", "itemtype": "method", - "name": "unserialize", + "name": "configure", "params": [ { "name": "str", @@ -444,6 +508,441 @@ } ], "class": "LGraph" + }, + { + "file": "../src/litegraph.js", + "line": 1026, + "description": "configure a node from an object", + "itemtype": "method", + "name": "configure", + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1073, + "description": "serialize the content", + "itemtype": "method", + "name": "serialize", + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1132, + "description": "serialize and stringify", + "itemtype": "method", + "name": "toString", + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1145, + "description": "sets the output data", + "itemtype": "method", + "name": "setOutputData", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + }, + { + "name": "data", + "description": "", + "type": "*" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1161, + "description": "retrieves the input data from one slot", + "itemtype": "method", + "name": "getInputData", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "data", + "type": "*" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1175, + "description": "tells you if there is a connection in one input slot", + "itemtype": "method", + "name": "isInputConnected", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1187, + "description": "tells you info about an input connection (which node, type, etc)", + "itemtype": "method", + "name": "getInputInfo", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Object" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1202, + "description": "tells you info about an output connection (which node, type, etc)", + "itemtype": "method", + "name": "getOutputInfo", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Object" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1217, + "description": "tells you if there is a connection in one output slot", + "itemtype": "method", + "name": "isOutputConnected", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1229, + "description": "retrieves all the nodes connected to this output slot", + "itemtype": "method", + "name": "getOutputNodes", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Array" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1258, + "description": "add a new output slot to use in this node", + "itemtype": "method", + "name": "addOutput", + "params": [ + { + "name": "name", + "description": "", + "type": "String" + }, + { + "name": "type", + "description": "string defining the output type (\"vec3\",\"number\",...)", + "type": "String" + }, + { + "name": "extra_info", + "description": "this can be used to have special properties of an output (special color, position, etc)", + "type": "Object" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1277, + "description": "remove an existing output slot", + "itemtype": "method", + "name": "removeOutput", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1289, + "description": "add a new input slot to use in this node", + "itemtype": "method", + "name": "addInput", + "params": [ + { + "name": "name", + "description": "", + "type": "String" + }, + { + "name": "type", + "description": "string defining the input type (\"vec3\",\"number\",...)", + "type": "String" + }, + { + "name": "extra_info", + "description": "this can be used to have special properties of an input (special color, position, etc)", + "type": "Object" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1308, + "description": "remove an existing input slot", + "itemtype": "method", + "name": "removeInput", + "params": [ + { + "name": "slot", + "description": "", + "type": "Number" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1320, + "description": "add an special connection to this node (used for special kinds of graphs)", + "itemtype": "method", + "name": "addConnection", + "params": [ + { + "name": "name", + "description": "", + "type": "String" + }, + { + "name": "type", + "description": "string defining the input type (\"vec3\",\"number\",...)", + "type": "String" + }, + { + "name": "pos", + "description": "position of the connection inside the node", + "type": "[x,y]" + }, + { + "name": "direction", + "description": "if is input or output", + "type": "String" + } + ], + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1333, + "description": "computes the size of a node according to its inputs and output slots", + "itemtype": "method", + "name": "computeSize", + "params": [ + { + "name": "minHeight", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "the total size", + "type": "Number" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1351, + "description": "returns the bounding of the object, used for rendering purposes", + "itemtype": "method", + "name": "getBounding", + "return": { + "description": "the total size", + "type": "Float32Array[4]" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1361, + "description": "checks if a point is inside the shape of a node", + "itemtype": "method", + "name": "isPointInsideNode", + "params": [ + { + "name": "x", + "description": "", + "type": "Number" + }, + { + "name": "y", + "description": "", + "type": "Number" + } + ], + "return": { + "description": "", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1401, + "description": "connect this node output to the input of another node", + "itemtype": "method", + "name": "connect", + "params": [ + { + "name": "slot", + "description": "(could be the number of the slot or the string with the name of the slot)", + "type": "Number_or_string" + }, + { + "name": "node", + "description": "the target node", + "type": "LGraphNode" + }, + { + "name": "target_slot", + "description": "the input slot of the target node (could be the number of the slot or the string with the name of the slot)", + "type": "Number_or_string" + } + ], + "return": { + "description": "if it was connected succesfully", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1482, + "description": "disconnect one output to an specific node", + "itemtype": "method", + "name": "disconnectOutput", + "params": [ + { + "name": "slot", + "description": "(could be the number of the slot or the string with the name of the slot)", + "type": "Number_or_string" + }, + { + "name": "target_node", + "description": "the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]", + "type": "LGraphNode" + } + ], + "return": { + "description": "if it was disconnected succesfully", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1545, + "description": "disconnect one input", + "itemtype": "method", + "name": "disconnectInput", + "params": [ + { + "name": "slot", + "description": "(could be the number of the slot or the string with the name of the slot)", + "type": "Number_or_string" + } + ], + "return": { + "description": "if it was disconnected succesfully", + "type": "Boolean" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1599, + "description": "returns the center of a connection point in canvas coords", + "itemtype": "method", + "name": "getConnectionPos", + "params": [ + { + "name": "is_input", + "description": "true if if a input slot, false if it is an output", + "type": "Boolean" + }, + { + "name": "slot", + "description": "(could be the number of the slot or the string with the name of the slot)", + "type": "Number_or_string" + } + ], + "return": { + "description": "the position", + "type": "[x,y]" + }, + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1756, + "description": "Collapse the node to make it smaller on the canvas", + "itemtype": "method", + "name": "collapse", + "class": "LGraphNode" + }, + { + "file": "../src/litegraph.js", + "line": 1769, + "description": "Forces the node to do not move or realign on Z", + "itemtype": "method", + "name": "pin", + "class": "LGraphNode" } ], "warnings": [] diff --git a/doc/files/.._src_litegraph.js.html b/doc/files/.._src_litegraph.js.html index 49442c502..50a66387c 100644 --- a/doc/files/.._src_litegraph.js.html +++ b/doc/files/.._src_litegraph.js.html @@ -43,6 +43,8 @@
  • LGraph
  • +
  • LGraphCanvas
  • +
  • LGraphNode
  • LiteGraph
  • @@ -109,9 +111,10 @@ var LiteGraph = { NODE_WIDTH: 140, NODE_MIN_WIDTH: 50, NODE_COLLAPSED_RADIUS: 10, + NODE_COLLAPSED_WIDTH: 80, CANVAS_GRID_SIZE: 10, - NODE_DEFAULT_COLOR: "#888", - NODE_DEFAULT_BGCOLOR: "#333", + NODE_DEFAULT_COLOR: "#999", + NODE_DEFAULT_BGCOLOR: "#444", NODE_DEFAULT_BOXCOLOR: "#AEF", NODE_DEFAULT_SHAPE: "box", MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops @@ -120,7 +123,6 @@ var LiteGraph = { debug: false, registered_node_types: {}, - graphs: [], /** * Register a node class so it can be listed when the user wants to create a new one @@ -207,7 +209,7 @@ var LiteGraph = { if( prototype[i].concat ) //array node[i] = prototype[i].concat(); else if (typeof(prototype[i]) == 'object') - node[i] = jQuery.extend({}, prototype[i]); + node[i] = LiteGraph.cloneObject(prototype[i]); //slow but safe else node[i] = prototype[i]; } @@ -323,22 +325,19 @@ var LiteGraph = { } } - for (var i in LiteGraph.graphs) - { - for (var j in LiteGraph.graphs[i].nodes) - { - var m = LiteGraph.graphs[i].nodes[j]; - var t = LiteGraph.getNodeType(n.type); - if(!t) continue; - - for (var k in t) - if( typeof(t[k]) == "function" ) - m[k] = t[k]; - } - } - if(LiteGraph.debug) console.log("Nodes reloaded"); + }, + + //separated just to improve if it doesnt work + cloneObject: function(obj, target) + { + var r = JSON.parse( JSON.stringify( obj ) ); + if(!target) return r; + + for(var i in r) + target[i] = r[i]; + return target; } /* @@ -347,11 +346,11 @@ var LiteGraph = { mode = mode || "all"; trace("Benchmarking " + mode + "..."); - trace(" Num. nodes: " + this.nodes.length ); + trace(" Num. nodes: " + this._nodes.length ); var links = 0; - for(var i in this.nodes) - for(var j in this.nodes[i].outputs) - if(this.nodes[i].outputs[j].node_id != null) + for(var i in this._nodes) + for(var j in this._nodes[i].outputs) + if(this._nodes[i].outputs[j].node_id != null) links++; trace(" Num. links: " + links ); @@ -402,8 +401,7 @@ function LGraph() { if (LiteGraph.debug) console.log("Graph created"); - this.canvas = null; - LiteGraph.graphs.push(this); + this.list_of_graphcanvas = null; this.clear(); } @@ -422,8 +420,8 @@ LGraph.prototype.clear = function() this.last_node_id = 0; //nodes - this.nodes = []; - this.nodes_by_id = {}; + this._nodes = []; + this._nodes_by_id = {}; //links this.last_link_id = 0; @@ -433,8 +431,6 @@ LGraph.prototype.clear = function() this.iteration = 0; this.config = { - canvas_offset: [0,0], - canvas_scale: 1.0 }; //timing @@ -445,12 +441,48 @@ LGraph.prototype.clear = function() this.elapsed_time = 0.01; this.starttime = 0; + //globals + this.globals = {}; + this.graph = {}; this.debug = true; this.change(); - if(this.canvas) - this.canvas.clear(); + + this.sendActionToCanvas("clear"); +} + +/** +* Attach Canvas to this graph +* @method attachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.attachCanvas = function(graphcanvas) +{ + if(graphcanvas.constructor != LGraphCanvas) + throw("attachCanvas expects a LGraphCanvas instance"); + if(graphcanvas.graph && graphcanvas.graph != this) + graphcanvas.graph.detachCanvas( graphcanvas ); + + graphcanvas.graph = this; + if(!this.list_of_graphcanvas) + this.list_of_graphcanvas = []; + this.list_of_graphcanvas.push(graphcanvas); +} + +/** +* Detach Canvas from this graph +* @method detachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.detachCanvas = function(graphcanvas) +{ + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if(pos == -1) return; + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos,1); } /** @@ -470,7 +502,7 @@ LGraph.prototype.start = function(interval) this.sendEventToAllNodes("onStart"); //launch - this.starttime = new Date().getTime(); + this.starttime = window.performance.now(); interval = interval || 1; var that = this; @@ -482,7 +514,7 @@ LGraph.prototype.start = function(interval) /** * Stops the execution loop of the graph -* @method stop +* @method stop execution */ LGraph.prototype.stop = function() @@ -512,7 +544,7 @@ LGraph.prototype.runStep = function(num) { num = num || 1; - var start = new Date().getTime(); + var start = window.performance.now(); this.globaltime = 0.001 * (start - this.starttime); try @@ -539,7 +571,7 @@ LGraph.prototype.runStep = function(num) this.stop(); } - var elapsed = (new Date().getTime()) - start; + var elapsed = window.performance.now() - start; if (elapsed == 0) elapsed = 1; this.elapsed_time = 0.001 * elapsed; this.globaltime += 0.001 * elapsed; @@ -554,7 +586,7 @@ LGraph.prototype.runStep = function(num) LGraph.prototype.updateExecutionOrder = function() { - this.nodes_in_order = this.computeExecutionOrder(); + this._nodes_in_order = this.computeExecutionOrder(); } //This is more internal, it computes the order and returns it @@ -567,9 +599,9 @@ LGraph.prototype.computeExecutionOrder = function() var remaining_links = {}; //to a //search for the nodes without inputs (starting nodes) - for (var i in this.nodes) + for (var i in this._nodes) { - var n = this.nodes[i]; + var n = this._nodes[i]; M[n.id] = n; //add to pending nodes var num = 0; //num of input connections @@ -631,7 +663,7 @@ LGraph.prototype.computeExecutionOrder = function() for(var i in M) L.push(M[i]); - if(L.length != this.nodes.length && LiteGraph.debug) + if(L.length != this._nodes.length && LiteGraph.debug) console.log("something went wrong, nodes missing"); //save order number in the node @@ -685,12 +717,25 @@ LGraph.prototype.getElapsedTime = function() LGraph.prototype.sendEventToAllNodes = function(eventname, param) { - var M = this.nodes_in_order ? this.nodes_in_order : this.nodes; + var M = this._nodes_in_order ? this._nodes_in_order : this._nodes; for(var j in M) if(M[j][eventname]) M[j][eventname](param); } +LGraph.prototype.sendActionToCanvas = function(action, params) +{ + if(!this.list_of_graphcanvas) + return; + + for(var i in this.list_of_graphcanvas) + { + var c = this.list_of_graphcanvas[i]; + if( c[action] ) + c[action].apply(c, params); + } +} + /** * Adds a new node instasnce to this graph * @method add @@ -699,10 +744,10 @@ LGraph.prototype.sendEventToAllNodes = function(eventname, param) LGraph.prototype.add = function(node) { - if(!node || (node.id != -1 && this.nodes_by_id[node.id] != null)) + if(!node || (node.id != -1 && this._nodes_by_id[node.id] != null)) return; //already added - if(this.nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) + if(this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) throw("LiteGraph: max number of nodes in a graph reached"); //give him an id @@ -711,8 +756,8 @@ LGraph.prototype.add = function(node) node.graph = this; - this.nodes.push(node); - this.nodes_by_id[node.id] = node; + this._nodes.push(node); + this._nodes_by_id[node.id] = node; /* // rendering stuf... @@ -744,7 +789,7 @@ LGraph.prototype.add = function(node) LGraph.prototype.remove = function(node) { - if(this.nodes_by_id[node.id] == null) + if(this._nodes_by_id[node.id] == null) return; //not found if(node.ignore_remove) @@ -784,10 +829,10 @@ LGraph.prototype.remove = function(node) } //remove from containers - var pos = this.nodes.indexOf(node); + var pos = this._nodes.indexOf(node); if(pos != -1) - this.nodes.splice(pos,1); - delete this.nodes_by_id[node.id]; + this._nodes.splice(pos,1); + delete this._nodes_by_id[node.id]; if(this.canvas) this.canvas.setDirty(true,true); @@ -806,7 +851,7 @@ LGraph.prototype.remove = function(node) LGraph.prototype.getNodeById = function(id) { if(id==null) return null; - return this.nodes_by_id[id]; + return this._nodes_by_id[id]; } @@ -820,9 +865,9 @@ LGraph.prototype.getNodeById = function(id) LGraph.prototype.findNodesByType = function(type) { var r = []; - for(var i in this.nodes) - if(this.nodes[i].type == type) - r.push(this.nodes[i]); + for(var i in this._nodes) + if(this._nodes[i].type == type) + r.push(this._nodes[i]); return r; } @@ -836,9 +881,9 @@ LGraph.prototype.findNodesByType = function(type) LGraph.prototype.findNodesByName = function(name) { var result = []; - for (var i in this.nodes) - if(this.nodes[i].name == name) - result.push(this.nodes[i]); + for (var i in this._nodes) + if(this._nodes[i].name == name) + result.push(this._nodes[i]); return result; } @@ -853,7 +898,7 @@ LGraph.prototype.findNodesByName = function(name) LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) { - nodes_list = nodes_list || this.nodes; + nodes_list = nodes_list || this._nodes; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; @@ -909,39 +954,46 @@ LGraph.prototype.setCallback = function(name,func) m[i].setTrigger(func); } -//********** - LGraph.prototype.onConnectionChange = function() { this.updateExecutionOrder(); } +/** +* returns if the graph is in live mode +* @method isLive +*/ + LGraph.prototype.isLive = function() { if(!this.canvas) return false; return this.canvas.live_mode; } +/* Called when something visually changed */ LGraph.prototype.change = function() { if(LiteGraph.debug) console.log("Graph changed"); + + this.sendActionToCanvas("setDirty",[true,true]); + if(this.on_change) this.on_change(this); } //save and recover app state *************************************** /** -* Creates a JSON String containing all the info about this graph +* Creates a Object containing all the info about this graph, it can be serialized * @method serialize -* @return {String} value of the node +* @return {Object} value of the node */ LGraph.prototype.serialize = function() { var nodes_info = []; - for (var i in this.nodes) - nodes_info.push( this.nodes[i].objectivize() ); + for (var i in this._nodes) + nodes_info.push( this._nodes[i].serialize() ); var data = { graph: this.graph, @@ -955,20 +1007,20 @@ LGraph.prototype.serialize = function() nodes: nodes_info }; - return JSON.stringify(data); + return data; } + /** * Configure a graph from a JSON string -* @method unserialize +* @method configure * @param {String} str configure a graph from a JSON string */ -LGraph.prototype.unserialize = function(str, keep_old) +LGraph.prototype.configure = function(data, keep_old) { if(!keep_old) this.clear(); - var data = JSON.parse(str); var nodes = data.nodes; //copy all stored fields @@ -978,7 +1030,7 @@ LGraph.prototype.unserialize = function(str, keep_old) var error = false; //create nodes - this.nodes = []; + this._nodes = []; for (var i in nodes) { var n_info = nodes[i]; //stored info @@ -991,7 +1043,7 @@ LGraph.prototype.unserialize = function(str, keep_old) continue; } - n.copyFromObject(n_info); + n.configure(n_info); this.add(n); } @@ -1065,9 +1117,59 @@ function LGraphNode(name) }; } -//serialization ************************* +/** +* configure a node from an object +* @method configure +*/ +LGraphNode.prototype.configure = function(info) +{ + for (var j in info) + { + if(j == "console") continue; -LGraphNode.prototype.objectivize = function() + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j], this[j] || {} ); + else //value + this[j] = info[j]; + } +} + +/* Copy all the info from one object to this node (used for serialization) */ +LGraphNode.prototype.copyFromObject = function(info, ignore_connections) +{ + var outputs = null; + var inputs = null; + var properties = null; + var local_data = null; + + for (var j in info) + { + if(ignore_connections && (j == "outputs" || j == "inputs")) + continue; + + if(j == "console") continue; + + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j]); + else //value + this[j] = info[j]; + } +} + +/** +* serialize the content +* @method serialize +*/ + +LGraphNode.prototype.serialize = function() { var o = { id: this.id, @@ -1076,8 +1178,8 @@ LGraphNode.prototype.objectivize = function() pos: this.pos, size: this.size, data: this.data, - properties: jQuery.extend({}, this.properties), - flags: jQuery.extend({}, this.flags), + properties: LiteGraph.cloneObject(this.properties), + flags: LiteGraph.cloneObject(this.flags), inputs: this.inputs, outputs: this.outputs }; @@ -1094,10 +1196,14 @@ LGraphNode.prototype.objectivize = function() if(this.shape) o.shape = this.shape; + if(this.onSerialize) + this.onSerialize(o); + return o; } //reduced version of objectivize: NOT FINISHED +/* LGraphNode.prototype.reducedObjectivize = function() { var o = this.objectivize(); @@ -1115,19 +1221,27 @@ LGraphNode.prototype.reducedObjectivize = function() return o; } +*/ +/** +* serialize and stringify +* @method toString +*/ -LGraphNode.prototype.serialize = function() +LGraphNode.prototype.toString = function() { - if(this.onSerialize) - this.onSerialize(); - return JSON.stringify( this.reducedObjectivize() ); + return JSON.stringify( this.serialize() ); } //LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph // Execution ************************* - +/** +* sets the output data +* @method setOutputData +* @param {number} slot +* @param {*} data +*/ LGraphNode.prototype.setOutputData = function(slot,data) { if(!this.outputs) return; @@ -1138,6 +1252,12 @@ LGraphNode.prototype.setOutputData = function(slot,data) } } +/** +* retrieves the input data from one slot +* @method getInputData +* @param {number} slot +* @return {*} data +*/ LGraphNode.prototype.getInputData = function(slot) { if(!this.inputs) return null; @@ -1146,12 +1266,24 @@ LGraphNode.prototype.getInputData = function(slot) return null; } +/** +* tells you if there is a connection in one input slot +* @method isInputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isInputConnected = function(slot) { if(!this.inputs) return null; return (slot < this.inputs.length && this.inputs[slot].link != null); } +/** +* tells you info about an input connection (which node, type, etc) +* @method getInputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getInputInfo = function(slot) { if(!this.inputs) return null; @@ -1161,6 +1293,12 @@ LGraphNode.prototype.getInputInfo = function(slot) } +/** +* tells you info about an output connection (which node, type, etc) +* @method getOutputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getOutputInfo = function(slot) { if(!this.outputs) return null; @@ -1169,12 +1307,25 @@ LGraphNode.prototype.getOutputInfo = function(slot) return null; } + +/** +* tells you if there is a connection in one output slot +* @method isOutputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isOutputConnected = function(slot) { if(!this.outputs) return null; return (slot < this.outputs.length && this.outputs[slot].links && this.outputs[slot].links.length); } +/** +* retrieves all the nodes connected to this output slot +* @method getOutputNodes +* @param {number} slot +* @return {array} +*/ LGraphNode.prototype.getOutputNodes = function(slot) { if(!this.outputs || this.outputs.length == 0) return null; @@ -1198,6 +1349,13 @@ LGraphNode.prototype.triggerOutput = function(slot,param) //connections +/** +* add a new output slot to use in this node +* @method addOutput +* @param {string} name +* @param {string} type string defining the output type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an output (special color, position, etc) +*/ LGraphNode.prototype.addOutput = function(name,type,extra_info) { var o = {name:name,type:type,links:null}; @@ -1210,6 +1368,11 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing output slot +* @method removeOutput +* @param {number} slot +*/ LGraphNode.prototype.removeOutput = function(slot) { this.disconnectOutput(slot); @@ -1217,6 +1380,13 @@ LGraphNode.prototype.removeOutput = function(slot) this.size = this.computeSize(); } +/** +* add a new input slot to use in this node +* @method addInput +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an input (special color, position, etc) +*/ LGraphNode.prototype.addInput = function(name,type,extra_info) { var o = {name:name,type:type,link:null}; @@ -1229,6 +1399,11 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing input slot +* @method removeInput +* @param {number} slot +*/ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); @@ -1236,13 +1411,25 @@ LGraphNode.prototype.removeInput = function(slot) this.size = this.computeSize(); } -//trigger connection +/** +* add an special connection to this node (used for special kinds of graphs) +* @method addConnection +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {[x,y]} pos position of the connection inside the node +* @param {string} direction if is input or output +*/ LGraphNode.prototype.addConnection = function(name,type,pos,direction) { this.connections.push( {name:name,type:type,pos:pos,direction:direction,links:null}); } - +/** +* computes the size of a node according to its inputs and output slots +* @method computeSize +* @param {number} minHeight +* @return {number} the total size +*/ LGraphNode.prototype.computeSize = function(minHeight) { var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); @@ -1255,19 +1442,30 @@ LGraphNode.prototype.computeSize = function(minHeight) return size; } -//returns the bounding of the object, used for rendering purposes +/** +* returns the bounding of the object, used for rendering purposes +* @method getBounding +* @return {Float32Array[4]} the total size +*/ LGraphNode.prototype.getBounding = function() { - return new Float32Array([this.pos[0] - 4, this.pos[1] - LGraph.NODE_TITLE_HEIGHT, this.pos[0] + this.size[0] + 4, this.pos[1] + this.size[1] + LGraph.NODE_TITLE_HEIGHT]); + return new Float32Array([this.pos[0] - 4, this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, this.pos[0] + this.size[0] + 4, this.pos[1] + this.size[1] + LGraph.NODE_TITLE_HEIGHT]); } -//checks if a point is inside the shape of a node +/** +* checks if a point is inside the shape of a node +* @method isPointInsideNode +* @param {number} x +* @param {number} y +* @return {boolean} +*/ LGraphNode.prototype.isPointInsideNode = function(x,y) { var margin_top = this.graph.isLive() ? 0 : 20; if(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) + //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + if( isInsideRectangle(x,y, this.pos[0], this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_COLLAPSED_WIDTH, LiteGraph.NODE_TITLE_HEIGHT) ) return true; } else if (this.pos[0] - 4 < x && (this.pos[0] + this.size[0] + 4) > x @@ -1294,7 +1492,14 @@ LGraphNode.prototype.findOutputSlot = function(name) return -1; } -//connect this node output to the input of another node +/** +* connect this node output to the input of another node +* @method connect +* @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) +* @return {boolean} if it was connected succesfully +*/ LGraphNode.prototype.connect = function(slot, node, target_slot) { target_slot = target_slot || 0; @@ -1368,6 +1573,13 @@ LGraphNode.prototype.connect = function(slot, node, target_slot) return true; } +/** +* disconnect one output to an specific node +* @method disconnectOutput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectOutput = function(slot, target_node) { if( slot.constructor === String ) @@ -1424,6 +1636,12 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node) return true; } +/** +* disconnect one input +* @method disconnectInput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectInput = function(slot) { //seek for the output slot @@ -1472,11 +1690,23 @@ LGraphNode.prototype.disconnectInput = function(slot) return true; } -//returns the center of a connection point in canvas coords +/** +* returns the center of a connection point in canvas coords +* @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) +* @return {[x,y]} the position +**/ LGraphNode.prototype.getConnectionPos = function(is_input,slot_number) { if(this.flags.collapsed) - return [this.pos[0] + this.size[0] * 0.5, this.pos[1] + this.size[1] * 0.5]; + { + if(is_input) + return [this.pos[0], this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5]; + else + return [this.pos[0] + LiteGraph.NODE_COLLAPSED_WIDTH, this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5]; + //return [this.pos[0] + this.size[0] * 0.5, this.pos[1] + this.size[1] * 0.5]; + } if(is_input && slot_number == -1) { @@ -1493,331 +1723,6 @@ LGraphNode.prototype.getConnectionPos = function(is_input,slot_number) return [this.pos[0] , this.pos[1] + 10 + slot_number * LiteGraph.NODE_SLOT_HEIGHT]; } -/* Renders the LGraphNode on the canvas */ -LGraphNode.prototype.draw = function(ctx, canvasrender) -{ - var glow = false; - - var color = this.color || LiteGraph.NODE_DEFAULT_COLOR; - //if (this.selected) color = "#88F"; - - var render_title = true; - if(this.flags.skip_title_render || this.graph.isLive()) - render_title = false; - if(this.mouseOver) - render_title = true; - - //shadow and glow - if (this.mouseOver) glow = true; - - if(this.selected) - { - /* - ctx.shadowColor = "#EEEEFF";//glow ? "#AAF" : "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = 1; - */ - } - else if(canvasrender.render_shadows) - { - ctx.shadowColor = "#111"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 4; - } - else - ctx.shadowColor = "transparent"; - - //only render if it forces it to do it - if(canvasrender.live_mode) - { - if(!this.flags.collapsed) - { - ctx.shadowColor = "transparent"; - if(this.onDrawBackground) - this.onDrawBackground(ctx); - if(this.onDrawForeground) - this.onDrawForeground(ctx); - } - - return; - } - - //draw in collapsed form - if(this.flags.collapsed) - { - if(!this.onDrawCollapsed || this.onDrawCollapsed(ctx) == false) - this.drawNodeCollapsed(ctx,color,this.bgcolor); - return; - } - - //clip if required (mask) - if(this.flags.clip_area) - { - ctx.save(); - if(this.shape == null || this.shape == "box") - { - ctx.beginPath(); - ctx.rect(0,0,this.size[0], this.size[1]); - } - else if (this.shape == "round") - { - ctx.roundRect(0,0,this.size[0], this.size[1],10); - } - else if (this.shape == "circle") - { - ctx.beginPath(); - ctx.arc(this.size[0] * 0.5, this.size[1] * 0.5,this.size[0] * 0.5, 0, Math.PI*2); - } - ctx.clip(); - } - - //draw shape - this.drawNodeShape(ctx,color, this.bgcolor, !render_title, this.selected ); - ctx.shadowColor = "transparent"; - - //connection slots - ctx.textAlign = "left"; - ctx.font = "12px Arial"; - - var render_text = this.graph.config.canvas_scale > 0.6; - - //input connection slots - if(this.inputs) - for(var i = 0; i < this.inputs.length; i++) - { - var slot = this.inputs[i]; - - ctx.globalAlpha = 1.0; - if (canvasrender.connecting_node != null && canvasrender.connecting_output.type != 0 && this.inputs[i].type != 0 && canvasrender.connecting_output.type != this.inputs[i].type) - ctx.globalAlpha = 0.4; - - ctx.fillStyle = slot.link != null ? "#7F7" : "#AAA"; - - var pos = this.getConnectionPos(true,i); - pos[0] -= this.pos[0]; - pos[1] -= this.pos[1]; - - ctx.beginPath(); - - if (1 || slot.round) - ctx.arc(pos[0],pos[1],4,0,Math.PI*2); - //else - // ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5,14,10); - - ctx.fill(); - - //render name - if(render_text) - { - var text = slot.label != null ? slot.label : slot.name; - if(text) - { - ctx.fillStyle = color; - ctx.fillText(text,pos[0] + 10,pos[1] + 5); - } - } - } - - //output connection slots - if(canvasrender.connecting_node) - ctx.globalAlpha = 0.4; - - ctx.lineWidth = 1; - - ctx.textAlign = "right"; - ctx.strokeStyle = "black"; - if(this.outputs) - for(var i = 0; i < this.outputs.length; i++) - { - var slot = this.outputs[i]; - - var pos = this.getConnectionPos(false,i); - pos[0] -= this.pos[0]; - pos[1] -= this.pos[1]; - - ctx.fillStyle = slot.links && slot.links.length ? "#7F7" : "#AAA"; - ctx.beginPath(); - //ctx.rect( this.size[0] - 14,i*14,10,10); - - if (1 || slot.round) - ctx.arc(pos[0],pos[1],4,0,Math.PI*2); - //else - // ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10); - - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); - ctx.stroke(); - - //render output name - if(render_text) - { - var text = slot.label != null ? slot.label : slot.name; - if(text) - { - ctx.fillStyle = color; - ctx.fillText(text, pos[0] - 10,pos[1] + 5); - } - } - } - - ctx.textAlign = "left"; - ctx.globalAlpha = 1.0; - - if(this.onDrawForeground) - this.onDrawForeground(ctx); - - if(this.flags.clip_area) - ctx.restore(); -} - -/* Renders the node shape */ -LGraphNode.prototype.drawNodeShape = function(ctx, fgcolor, bgcolor, no_title, selected ) -{ - //bg rect - ctx.strokeStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; - ctx.fillStyle = bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; - - /* gradient test - var grad = ctx.createLinearGradient(0,0,0,this.size[1]); - grad.addColorStop(0, "#AAA"); - grad.addColorStop(0.5, fgcolor || LiteGraph.NODE_DEFAULT_COLOR); - grad.addColorStop(1, bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR); - ctx.fillStyle = grad; - */ - - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - - //render depending on shape - if(this.shape == null || this.shape == "box") - { - if(selected) - { - ctx.strokeStyle = "#CCC"; - ctx.strokeRect(-0.5,no_title ? -0.5 : -title_height + -0.5,this.size[0]+2, no_title ? (this.size[1]+2) : (this.size[1] + title_height+2) ); - ctx.strokeStyle = fgcolor; - } - - ctx.beginPath(); - ctx.rect(0.5,no_title ? 0.5 : -title_height + 0.5,this.size[0], no_title ? this.size[1] : this.size[1] + title_height); - } - else if (this.shape == "round") - { - ctx.roundRect(0,no_title ? 0 : -title_height,this.size[0], no_title ? this.size[1] : this.size[1] + title_height, 10); - } - else if (this.shape == "circle") - { - ctx.beginPath(); - ctx.arc(this.size[0] * 0.5, this.size[1] * 0.5,this.size[0] * 0.5, 0, Math.PI*2); - } - - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); - - //image - if (this.bgImage && this.bgImage.width) - ctx.drawImage( this.bgImage, (this.size[0] - this.bgImage.width) * 0.5 , (this.size[1] - this.bgImage.height) * 0.5); - - if(this.bgImageUrl && !this.bgImage) - this.bgImage = this.loadImage(this.bgImageUrl); - - if(this.onDrawBackground) - this.onDrawBackground(ctx); - - //title bg - if(!no_title) - { - ctx.fillStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; - - if(this.shape == null || this.shape == "box") - { - ctx.fillRect(0,-title_height,this.size[0],title_height); - ctx.stroke(); - } - else if (this.shape == "round") - { - ctx.roundRect(0,-title_height,this.size[0], title_height,10,0); - //ctx.fillRect(0,8,this.size[0],NODE_TITLE_HEIGHT - 12); - ctx.fill(); - ctx.stroke(); - } - - //box - ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.beginPath(); - if (this.shape == "round") - ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 6) *0.5,0,Math.PI*2); - else - ctx.rect(3,-title_height + 3,title_height - 6,title_height - 6); - ctx.fill(); - - //title text - ctx.font = "bold 12px Arial"; - if(this.name != "" && this.graph.config.canvas_scale > 0.8) - { - ctx.fillStyle = "#222"; - ctx.fillText(this.name,16,13-title_height ); - } - } -} - -/* Renders the node when collapsed */ -LGraphNode.prototype.drawNodeCollapsed = function(ctx, fgcolor, bgcolor) -{ - //draw default collapsed shape - ctx.strokeStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; - ctx.fillStyle = bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; - - var collapsed_radius = LiteGraph.NODE_COLLAPSED_RADIUS; - - //circle shape - if(this.shape == "circle") - { - ctx.beginPath(); - ctx.arc(this.size[0] * 0.5, this.size[1] * 0.5, collapsed_radius,0,Math.PI * 2); - ctx.fill(); - ctx.shadowColor = "rgba(0,0,0,0)"; - ctx.stroke(); - - ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.beginPath(); - ctx.arc(this.size[0] * 0.5, this.size[1] * 0.5, collapsed_radius * 0.5,0,Math.PI * 2); - ctx.fill(); - } - else if(this.shape == "round") //rounded box - { - ctx.beginPath(); - ctx.roundRect(this.size[0] * 0.5 - collapsed_radius, this.size[1] * 0.5 - collapsed_radius, 2*collapsed_radius,2*collapsed_radius,5); - ctx.fill(); - ctx.shadowColor = "rgba(0,0,0,0)"; - ctx.stroke(); - - ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.beginPath(); - ctx.roundRect(this.size[0] * 0.5 - collapsed_radius*0.5, this.size[1] * 0.5 - collapsed_radius*0.5, collapsed_radius,collapsed_radius,2); - ctx.fill(); - } - else //flat box - { - ctx.beginPath(); - ctx.rect(this.size[0] * 0.5 - collapsed_radius, this.size[1] * 0.5 - collapsed_radius, 2*collapsed_radius,2*collapsed_radius); - ctx.fill(); - ctx.shadowColor = "rgba(0,0,0,0)"; - ctx.stroke(); - - ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.beginPath(); - ctx.rect(this.size[0] * 0.5 - collapsed_radius*0.5, this.size[1] * 0.5 - collapsed_radius*0.5, collapsed_radius,collapsed_radius); - ctx.fill(); - } -} - /* Force align to grid */ LGraphNode.prototype.alignToGrid = function() { @@ -1825,50 +1730,6 @@ LGraphNode.prototype.alignToGrid = function() this.pos[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); } -/* Copy all the info from one object to this node (used for serialization) */ -LGraphNode.prototype.copyFromObject = function(info, ignore_connections) -{ - var outputs = null; - var inputs = null; - var properties = null; - var local_data = null; - - for (var j in info) - { - if(ignore_connections && (j == "outputs" || j == "inputs")) - continue; - - if(j == "console") continue; - - if(info[j] == null) - continue; - else if( info[j].concat ) //array - this[j] = info[j].concat(); - else if (typeof(info[j]) == 'object') //object - this[j] = jQuery.extend({}, info[j]); - else //value - this[j] = info[j]; - } - - //redo the connections - /* - if(outputs) - this.outputs = outputs.concat(); - if(inputs) - this.inputs = inputs.concat(); - - if(local_data) - this.data = local_data; - if(properties) - { - //copy only the ones defined - for (var j in properties) - if (this.properties[j] != null) - this.properties[j] = properties[j]; - } - */ -} - /* Creates a clone of this node */ LGraphNode.prototype.clone = function() { @@ -1908,13 +1769,9 @@ LGraphNode.prototype.trace = function(msg) /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function(dirty_foreground, dirty_background) { - if(!this.graph || !this.graph.canvas) + if(!this.graph) return; - - if(dirty_foreground) - this.graph.canvas.dirty_canvas = true; - if(dirty_background) - this.graph.canvas.dirty_bgcanvas = true; + this.graph.sendActionToCanvas("setDirty",[dirty_foreground, dirty_background]); } LGraphNode.prototype.loadImage = function(url) @@ -1971,20 +1828,29 @@ LGraphNode.prototype.executeAction = function(action) /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ LGraphNode.prototype.captureInput = function(v) { - if(!this.graph || !this.graph.canvas) + if(!this.graph || !this.graph.list_of_graphcanvas) return; - //releasing somebody elses capture?! - if(!v && this.graph.canvas.node_capturing_input != this) - return; + var list = this.graph.list_of_graphcanvas; - //change - this.graph.canvas.node_capturing_input = v ? this : null; - if(this.graph.debug) - console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + for(var i in list) + { + var c = list[i]; + //releasing somebody elses capture?! + if(!v && c.node_capturing_input != this) + continue; + + //change + c.node_capturing_input = v ? this : null; + if(this.graph.debug) + console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + } } -/* Collapse the node */ +/** +* Collapse the node to make it smaller on the canvas +* @method collapse +**/ LGraphNode.prototype.collapse = function() { if(!this.flags.collapsed) @@ -1994,19 +1860,23 @@ LGraphNode.prototype.collapse = function() this.setDirtyCanvas(true,true); } -/* Forces the node to do not move or realign on Z */ -LGraphNode.prototype.pin = function() +/** +* Forces the node to do not move or realign on Z +* @method pin +**/ + +LGraphNode.prototype.pin = function(v) { - if(!this.flags.pinned) - this.flags.pinned = true; + if(v === undefined) + this.flags.pinned = !this.flags.pinned; else - this.flags.pinned = false; + this.flags.pinned = v; } -LGraphNode.prototype.localToScreen = function(x,y) +LGraphNode.prototype.localToScreen = function(x,y, graphcanvas) { - return [(x + this.pos[0]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[0], - (y + this.pos[1]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[1]]; + return [(x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]]; } @@ -2015,27 +1885,28 @@ LGraphNode.prototype.localToScreen = function(x,y) // LGraphCanvas: LGraph renderer CLASS //********************************************************************************* +/** +* The Global Scope. It contains all the registered node classes. +* +* @class LGraphCanvas +* @constructor +* @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format) +* @param {LGraph} graph +*/ function LGraphCanvas(canvas, graph) { - if(graph === undefined) - throw ("No graph assigned"); + //if(graph === undefined) + // throw ("No graph assigned"); - if( typeof(window) != "undefined" ) - { - window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function( callback ){ - window.setTimeout(callback, 1000 / 60); - }; - })(); - } + if(typeof(canvas) == "string") + canvas = document.querySelector(canvas); + + if(!canvas) + throw("no canvas found"); //link canvas and graph - this.graph = graph; if(graph) - graph.canvas = this; + graph.attachCanvas(this); this.setCanvas(canvas); this.clear(); @@ -2044,7 +1915,6 @@ function LGraphCanvas(canvas, graph) } LGraphCanvas.link_type_colors = {'number':"#AAC",'node':"#DCA"}; -LGraphCanvas.link_width = 2; LGraphCanvas.prototype.clear = function() { @@ -2053,6 +1923,9 @@ LGraphCanvas.prototype.clear = function() this.render_time = 0; this.fps = 0; + this.scale = 1; + this.offset = [0,0]; + this.selected_nodes = {}; this.node_dragged = null; this.node_over = null; @@ -2060,6 +1933,7 @@ LGraphCanvas.prototype.clear = function() this.connecting_node = null; this.highquality_render = true; + this.editor_alpha = 1; //used for transition this.pause_rendering = false; this.render_shadows = true; this.dirty_canvas = true; @@ -2077,6 +1951,16 @@ LGraphCanvas.prototype.clear = function() this.last_mouse = [0,0]; this.last_mouseclick = 0; + this.title_text_font = "bold 14px Arial"; + this.inner_text_font = "normal 12px Arial"; + + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = true; + this.render_connection_arrows = true; + + this.connections_width = 4; + if(this.onClear) this.onClear(); //this.UIinit(); } @@ -2084,29 +1968,25 @@ LGraphCanvas.prototype.clear = function() LGraphCanvas.prototype.setGraph = function(graph) { if(this.graph == graph) return; - this.clear(); + + if(!graph && this.graph) + { + this.graph.detachCanvas(this); + return; + } + + /* if(this.graph) this.graph.canvas = null; //remove old graph link to the canvas this.graph = graph; if(this.graph) this.graph.canvas = this; + */ + graph.attachCanvas(this); this.setDirty(true,true); } -LGraphCanvas.prototype.resize = function(width, height) -{ - if(this.canvas.width == width && this.canvas.height == height) - return; - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true,true); -} - - LGraphCanvas.prototype.setCanvas = function(canvas) { var that = this; @@ -2231,6 +2111,13 @@ LGraphCanvas.prototype.setDirty = function(fgcanvas,bgcanvas) this.dirty_bgcanvas = true; } +//Used to attach the canvas in a popup +LGraphCanvas.prototype.getCanvasWindow = function() +{ + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; +} + LGraphCanvas.prototype.startRendering = function() { if(this.is_rendering) return; //already rendering @@ -2243,8 +2130,9 @@ LGraphCanvas.prototype.startRendering = function() if(!this.pause_rendering) this.draw(); + var window = this.getCanvasWindow(); if(this.is_rendering) - window.requestAnimFrame( renderFrame.bind(this) ); + window.requestAnimationFrame( renderFrame.bind(this) ); } @@ -2276,9 +2164,12 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.adjustMouseEvent(e); + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + this.canvas.removeEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mouseup", this._mouseup_callback ); + ref_window.document.addEventListener("mousemove", this._mousemove_callback ); //catch for the entire window + ref_window.document.addEventListener("mouseup", this._mouseup_callback ); var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); var skip_dragging = false; @@ -2359,7 +2250,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) var block_drag_node = false; //double clicking - var now = new Date().getTime(); + var now = window.performance.now(); if ((now - this.last_mouseclick) < 300 && this.selected_nodes[n.id]) { //double click node @@ -2414,7 +2305,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.last_mouse[0] = e.localX; this.last_mouse[1] = e.localY; - this.last_mouseclick = new Date().getTime(); + this.last_mouseclick = window.performance.now(); this.canvas_mouse = [e.canvasX, e.canvasY]; /* @@ -2425,7 +2316,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.graph.change(); //this is to ensure to defocus(blur) if a text input element is on focus - if(!document.activeElement || (document.activeElement.nodeName.toLowerCase() != "input" && document.activeElement.nodeName.toLowerCase() != "textarea")) + if(!ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != "input" && ref_window.document.activeElement.nodeName.toLowerCase() != "textarea")) e.preventDefault(); e.stopPropagation(); return false; @@ -2443,8 +2334,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.dragging_canvas) { - this.graph.config.canvas_offset[0] += delta[0] / this.graph.config.canvas_scale; - this.graph.config.canvas_offset[1] += delta[1] / this.graph.config.canvas_scale; + this.offset[0] += delta[0] / this.scale; + this.offset[1] += delta[1] / this.scale; this.dirty_canvas = true; this.dirty_bgcanvas = true; } @@ -2457,12 +2348,12 @@ LGraphCanvas.prototype.processMouseMove = function(e) var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); //remove mouseover flag - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - if(this.graph.nodes[i].mouseOver && n != this.graph.nodes[i]) + if(this.graph._nodes[i].mouseOver && n != this.graph._nodes[i]) { //mouse leave - this.graph.nodes[i].mouseOver = false; + this.graph._nodes[i].mouseOver = false; if(this.node_over && this.node_over.onMouseLeave) this.node_over.onMouseLeave(e); this.node_over = null; @@ -2519,8 +2410,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.node_dragged && !this.live_mode) { /* - this.node_dragged.pos[0] += delta[0] / this.graph.config.canvas_scale; - this.node_dragged.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); */ @@ -2529,8 +2420,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) { var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.graph.config.canvas_scale; - n.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); } @@ -2541,8 +2432,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.resizing_node && !this.live_mode) { - this.resizing_node.size[0] += delta[0] / this.graph.config.canvas_scale; - this.resizing_node.size[1] += delta[1] / this.graph.config.canvas_scale; + this.resizing_node.size[0] += delta[0] / this.scale; + this.resizing_node.size[1] += delta[1] / this.scale; var max_slots = Math.max( this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); if(this.resizing_node.size[1] < max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4) this.resizing_node.size[1] = max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4; @@ -2571,6 +2462,9 @@ LGraphCanvas.prototype.processMouseUp = function(e) { if(!this.graph) return; + var window = this.getCanvasWindow(); + var document = window.document; + document.removeEventListener("mousemove", this._mousemove_callback, true ); this.canvas.addEventListener("mousemove", this._mousemove_callback, true); document.removeEventListener("mouseup", this._mouseup_callback, true ); @@ -2736,7 +2630,7 @@ LGraphCanvas.prototype.processMouseWheel = function(e) this.adjustMouseEvent(e); - var zoom = this.graph.config.canvas_scale; + var zoom = this.scale; if (delta > 0) zoom *= 1.1; @@ -2821,13 +2715,13 @@ LGraphCanvas.prototype.selectNode = function(node) LGraphCanvas.prototype.selectAllNodes = function() { - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; if(!n.selected && n.onSelected) n.onSelected(); n.selected = true; - this.selected_nodes[this.graph.nodes[i].id] = n; + this.selected_nodes[this.graph._nodes[i].id] = n; } this.setDirty(true); @@ -2860,8 +2754,8 @@ LGraphCanvas.prototype.deleteSelectedNodes = function() LGraphCanvas.prototype.centerOnNode = function(node) { - this.graph.config.canvas_offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.graph.config.canvas_scale); - this.graph.config.canvas_offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.graph.config.canvas_scale); + 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.setDirty(true,true); } @@ -2871,8 +2765,8 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) e.localX = e.pageX - b.left; e.localY = e.pageY - b.top; - e.canvasX = e.localX / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0]; - e.canvasY = e.localY / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]; + e.canvasX = e.localX / this.scale - this.offset[0]; + e.canvasY = e.localY / this.scale - this.offset[1]; } LGraphCanvas.prototype.setZoom = function(value, zooming_center) @@ -2882,18 +2776,18 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) var center = this.convertOffsetToCanvas( zooming_center ); - this.graph.config.canvas_scale = value; + this.scale = value; - if(this.graph.config.canvas_scale > 4) - this.graph.config.canvas_scale = 4; - else if(this.graph.config.canvas_scale < 0.1) - this.graph.config.canvas_scale = 0.1; + if(this.scale > 4) + this.scale = 4; + else if(this.scale < 0.1) + this.scale = 0.1; var new_center = this.convertOffsetToCanvas( zooming_center ); var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - this.graph.config.canvas_offset[0] += delta_offset[0]; - this.graph.config.canvas_offset[1] += delta_offset[1]; + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -2901,13 +2795,13 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) LGraphCanvas.prototype.convertOffsetToCanvas = function(pos) { - return [pos[0] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0], pos[1] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]]; + return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; } LGraphCanvas.prototype.convertCanvasToOffset = function(pos) { - return [(pos[0] + this.graph.config.canvas_offset[0]) * this.graph.config.canvas_scale, - (pos[1] + this.graph.config.canvas_offset[1]) * this.graph.config.canvas_scale ]; + return [(pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale ]; } LGraphCanvas.prototype.convertEventToCanvas = function(e) @@ -2918,20 +2812,20 @@ LGraphCanvas.prototype.convertEventToCanvas = function(e) LGraphCanvas.prototype.bringToFront = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.push(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.push(n); } LGraphCanvas.prototype.sendToBack = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.unshift(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.unshift(n); } /* Interaction */ @@ -2943,9 +2837,9 @@ LGraphCanvas.prototype.sendToBack = function(n) LGraphCanvas.prototype.computeVisibleNodes = function() { var visible_nodes = []; - for (var i in this.graph.nodes) + for (var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; //skip rendering nodes in live mode if(this.live_mode && !n.onDrawBackground && !n.onDrawForeground) @@ -2962,14 +2856,14 @@ LGraphCanvas.prototype.computeVisibleNodes = function() LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { //fps counting - var now = new Date().getTime(); + var now = window.performance.now(); this.render_time = (now - this.last_draw_time)*0.001; this.last_draw_time = now; if(this.graph) { - var start = [-this.graph.config.canvas_offset[0], -this.graph.config.canvas_offset[1] ]; - var end = [start[0] + this.canvas.width / this.graph.config.canvas_scale, start[1] + this.canvas.height / this.graph.config.canvas_scale]; + 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],end[1]]); } @@ -3028,8 +2922,8 @@ LGraphCanvas.prototype.drawFrontCanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //draw nodes var drawn_nodes = 0; @@ -3038,14 +2932,14 @@ LGraphCanvas.prototype.drawFrontCanvas = function() for (var i in visible_nodes) { - var n = visible_nodes[i]; + var node = visible_nodes[i]; //transform coords system ctx.save(); - ctx.translate( n.pos[0], n.pos[1] ); + ctx.translate( node.pos[0], node.pos[1] ); //Draw - n.draw(ctx,this); + this.drawNode(node, ctx ); drawn_nodes += 1; //Restore @@ -3060,10 +2954,9 @@ LGraphCanvas.prototype.drawFrontCanvas = function() //current connection if(this.connecting_pos != null) { - ctx.lineWidth = LGraphCanvas.link_width; - ctx.fillStyle = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; - ctx.strokeStyle = ctx.fillStyle; - this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]] ); + ctx.lineWidth = this.connections_width; + var link_color = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; + this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]], link_color ); ctx.beginPath(); ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2); @@ -3112,13 +3005,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //render BG - if(this.background_image && this.graph.config.canvas_scale > 0.5) + if(this.background_image && this.scale > 0.5) { - ctx.globalAlpha = 1.0 - 0.5 / this.graph.config.canvas_scale; + ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha; ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false if(!this._bg_img || this._bg_img.name != this.background_image) { @@ -3158,8 +3051,7 @@ LGraphCanvas.prototype.drawBgcanvas = function() ctx.strokeStyle = "#235"; ctx.strokeRect(0,0,canvas.width,canvas.height); - /* - if(this.render_shadows) + if(this.render_connections_shadows) { ctx.shadowColor = "#000"; ctx.shadowOffsetX = 0; @@ -3168,12 +3060,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() } else ctx.shadowColor = "rgba(0,0,0,0)"; - */ //draw connections if(!this.live_mode) this.drawConnections(ctx); + ctx.shadowColor = "rgba(0,0,0,0)"; + //restore state ctx.restore(); } @@ -3182,19 +3075,370 @@ LGraphCanvas.prototype.drawBgcanvas = function() this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas } +/* Renders the LGraphNode on the canvas */ +LGraphCanvas.prototype.drawNode = function(node, ctx ) +{ + var glow = false; + + var color = node.color || LiteGraph.NODE_DEFAULT_COLOR; + //if (this.selected) color = "#88F"; + + var render_title = true; + if(node.flags.skip_title_render || node.graph.isLive()) + render_title = false; + if(node.mouseOver) + render_title = true; + + //shadow and glow + 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 = "#111"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 4; + } + else + ctx.shadowColor = "transparent"; + + //only render if it forces it to do it + if(this.live_mode) + { + if(!node.flags.collapsed) + { + ctx.shadowColor = "transparent"; + //if(node.onDrawBackground) + // node.onDrawBackground(ctx); + if(node.onDrawForeground) + node.onDrawForeground(ctx); + } + + return; + } + + //draw in collapsed form + /* + if(node.flags.collapsed) + { + if(!node.onDrawCollapsed || node.onDrawCollapsed(ctx) == false) + this.drawNodeCollapsed(node, ctx, color, node.bgcolor); + return; + } + */ + + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + + //clip if required (mask) + var shape = node.shape || "box"; + var size = new Float32Array(node.size); + if(node.flags.collapsed) + size.set([LiteGraph.NODE_COLLAPSED_WIDTH, 0]); + + //Start clipping + if(node.flags.clip_area) + { + ctx.save(); + if(shape == "box") + { + ctx.beginPath(); + ctx.rect(0,0,size[0], size[1]); + } + else if (shape == "round") + { + ctx.roundRect(0,0,size[0], size[1],10); + } + else if (shape == "circle") + { + ctx.beginPath(); + ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI*2); + } + ctx.clip(); + } + + //draw shape + this.drawNodeShape(node, ctx, size, color, node.bgcolor, !render_title, node.selected ); + ctx.shadowColor = "transparent"; + + //connection slots + ctx.textAlign = "left"; + ctx.font = this.inner_text_font; + + var render_text = this.scale > 0.6; + + //render inputs and outputs + if(!node.flags.collapsed) + { + //input connection slots + if(node.inputs) + for(var i = 0; i < node.inputs.length; i++) + { + var slot = node.inputs[i]; + + ctx.globalAlpha = editor_alpha; + if (this.connecting_node != null && this.connecting_output.type != 0 && node.inputs[i].type != 0 && this.connecting_output.type != node.inputs[i].type) + ctx.globalAlpha = 0.4 * editor_alpha; + + ctx.fillStyle = slot.link != null ? "#7F7" : "#AAA"; + + var pos = node.getConnectionPos(true,i); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + + ctx.beginPath(); + + if (1 || slot.round) + ctx.arc(pos[0],pos[1],4,0,Math.PI*2); + //else + // ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5,14,10); + + ctx.fill(); + + //render name + if(render_text) + { + var text = slot.label != null ? slot.label : slot.name; + if(text) + { + ctx.fillStyle = color; + ctx.fillText(text,pos[0] + 10,pos[1] + 5); + } + } + } + + //output connection slots + if(this.connecting_node) + ctx.globalAlpha = 0.4 * editor_alpha; + + ctx.lineWidth = 1; + + ctx.textAlign = "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); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + + ctx.fillStyle = slot.links && slot.links.length ? "#7F7" : "#AAA"; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); + + if (1 || slot.round) + ctx.arc(pos[0],pos[1],4,0,Math.PI*2); + //else + // ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10); + + //trigger + //if(slot.node_id != null && slot.slot == -1) + // ctx.fillStyle = "#F85"; + + //if(slot.links != null && slot.links.length) + ctx.fill(); + ctx.stroke(); + + //render output name + if(render_text) + { + var text = slot.label != null ? slot.label : slot.name; + if(text) + { + ctx.fillStyle = color; + ctx.fillText(text, pos[0] - 10,pos[1] + 5); + } + } + } + + ctx.textAlign = "left"; + ctx.globalAlpha = 1; + + if(node.onDrawForeground) + node.onDrawForeground(ctx); + }//!collapsed + + if(node.flags.clip_area) + ctx.restore(); + + ctx.globalAlpha = 1.0; +} + +/* Renders the node shape */ +LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolor, no_title, selected ) +{ + //bg rect + ctx.strokeStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; + ctx.fillStyle = bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; + + /* gradient test + var grad = ctx.createLinearGradient(0,0,0,node.size[1]); + grad.addColorStop(0, "#AAA"); + grad.addColorStop(0.5, fgcolor || LiteGraph.NODE_DEFAULT_COLOR); + grad.addColorStop(1, bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR); + ctx.fillStyle = grad; + //*/ + + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + + //render depending on shape + var shape = node.shape || "box"; + if(shape == "box") + { + if(selected) + { + ctx.strokeStyle = "#CCC"; + ctx.strokeRect(-0.5,no_title ? -0.5 : -title_height + -0.5, size[0]+2, no_title ? (size[1]+2) : (size[1] + title_height+2) ); + ctx.strokeStyle = fgcolor; + } + + ctx.beginPath(); + ctx.rect(0,no_title ? 0.5 : -title_height + 0.5,size[0]+1, no_title ? size[1] : size[1] + title_height); + } + else if (node.shape == "round") + { + ctx.roundRect(0,no_title ? 0 : -title_height,size[0], no_title ? size[1] : size[1] + title_height, 10); + } + else if (node.shape == "circle") + { + ctx.beginPath(); + ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI*2); + } + + ctx.fill(); + ctx.shadowColor = "transparent"; + + //ctx.stroke(); + + //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) + node.onDrawBackground(ctx); + + //title bg + if(!no_title) + { + ctx.fillStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; + var old_alpha = ctx.globalAlpha; + ctx.globalAlpha = 0.5 * old_alpha; + if(shape == "box") + { + ctx.beginPath(); + ctx.fillRect(0,-title_height,size[0]+1,title_height); + ctx.stroke(); + } + else if (shape == "round") + { + ctx.roundRect(0,-title_height,size[0], title_height,10,0); + //ctx.fillRect(0,8,size[0],NODE_TITLE_HEIGHT - 12); + ctx.fill(); + ctx.stroke(); + } + + //box + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.beginPath(); + if (shape == "round") + ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 6) *0.5,0,Math.PI*2); + else + ctx.rect(3,-title_height + 3,title_height - 6,title_height - 6); + ctx.fill(); + ctx.globalAlpha = old_alpha; + + //title text + ctx.font = this.title_text_font; + if(node.name != "" && this.scale > 0.8) + { + ctx.fillStyle = "#222"; + ctx.fillText(node.name,16,13-title_height ); + } + } +} + +/* Renders the node when collapsed */ +LGraphCanvas.prototype.drawNodeCollapsed = function(node, ctx, fgcolor, bgcolor) +{ + //draw default collapsed shape + ctx.strokeStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; + ctx.fillStyle = bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; + + var collapsed_radius = LiteGraph.NODE_COLLAPSED_RADIUS; + + //circle shape + var shape = node.shape || "box"; + if(shape == "circle") + { + ctx.beginPath(); + ctx.arc(node.size[0] * 0.5, node.size[1] * 0.5, collapsed_radius,0,Math.PI * 2); + ctx.fill(); + ctx.shadowColor = "rgba(0,0,0,0)"; + ctx.stroke(); + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.beginPath(); + ctx.arc(node.size[0] * 0.5, node.size[1] * 0.5, collapsed_radius * 0.5,0,Math.PI * 2); + ctx.fill(); + } + else if(shape == "round") //rounded box + { + ctx.beginPath(); + ctx.roundRect(node.size[0] * 0.5 - collapsed_radius, node.size[1] * 0.5 - collapsed_radius, 2*collapsed_radius,2*collapsed_radius,5); + ctx.fill(); + ctx.shadowColor = "rgba(0,0,0,0)"; + ctx.stroke(); + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.beginPath(); + ctx.roundRect(node.size[0] * 0.5 - collapsed_radius*0.5, node.size[1] * 0.5 - collapsed_radius*0.5, collapsed_radius,collapsed_radius,2); + ctx.fill(); + } + else //flat box + { + ctx.beginPath(); + //ctx.rect(node.size[0] * 0.5 - collapsed_radius, node.size[1] * 0.5 - collapsed_radius, 2*collapsed_radius, 2*collapsed_radius); + ctx.rect(0, 0, node.size[0], collapsed_radius * 2 ); + ctx.fill(); + ctx.shadowColor = "rgba(0,0,0,0)"; + ctx.stroke(); + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.beginPath(); + //ctx.rect(node.size[0] * 0.5 - collapsed_radius*0.5, node.size[1] * 0.5 - collapsed_radius*0.5, collapsed_radius,collapsed_radius); + ctx.rect(collapsed_radius*0.5, collapsed_radius*0.5, collapsed_radius, collapsed_radius); + ctx.fill(); + } +} + LGraphCanvas.link_colors = ["#AAC","#ACA","#CAA"]; LGraphCanvas.prototype.drawConnections = function(ctx) { //draw connections - ctx.lineWidth = LGraphCanvas.link_width; + ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; //for every node - for (var n in this.graph.nodes) + for (var n in this.graph._nodes) { - var node = this.graph.nodes[n]; + var node = this.graph._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 in node.inputs) @@ -3216,16 +3460,14 @@ LGraphCanvas.prototype.drawConnections = function(ctx) var color = LGraphCanvas.link_type_colors[node.inputs[i].type]; if(color == null) color = LGraphCanvas.link_colors[node.id % LGraphCanvas.link_colors.length]; - ctx.fillStyle = ctx.strokeStyle = color; - this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i) ); + this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i), color ); } } + ctx.globalAlpha = 1; } -LGraphCanvas.prototype.renderLink = function(ctx,a,b) +LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) { - var curved_lines = true; - if(!this.highquality_render) { ctx.beginPath(); @@ -3237,32 +3479,44 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b) var dist = distance(a,b); + if(this.render_connections_border) + ctx.lineWidth = this.connections_width + 4; + ctx.beginPath(); - if(curved_lines) + 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 + 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.render_connections_border) + { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); //render arrow - if(this.graph.config.canvas_scale > 0.6) + if(this.render_connection_arrows && this.scale > 0.6) { //get two points in the bezier curve var pos = this.computeConnectionPoint(a,b,0.5); var pos2 = this.computeConnectionPoint(a,b,0.51); var angle = 0; - if(curved_lines) + if(this.render_curved_connections) angle = -Math.atan2( pos2[0] - pos[0], pos2[1] - pos[1]); else angle = b[1] > a[1] ? 0 : Math.PI; @@ -3297,6 +3551,7 @@ LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t) return [x,y]; } +/* LGraphCanvas.prototype.resizeCanvas = function(width,height) { this.canvas.width = width; @@ -3307,12 +3562,63 @@ LGraphCanvas.prototype.resizeCanvas = function(width,height) this.bgcanvas.height = this.canvas.height; this.draw(true,true); } +*/ -LGraphCanvas.prototype.switchLiveMode = function() +LGraphCanvas.prototype.resize = function(width, height) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; + if(!width && !height) + { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if(this.canvas.width == width && this.canvas.height == height) + return; + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true,true); +} + + +LGraphCanvas.prototype.switchLiveMode = function(transition) +{ + if(!transition) + { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if(this.live_mode) + { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if(delta < 1 && self.editor_alpha < 0.01) + { + clearInterval(t); + if(delta < 1) + self.live_mode = true; + } + if(delta > 1 && self.editor_alpha > 0.99) + { + clearInterval(t); + self.editor_alpha = 1; + } + },1); } LGraphCanvas.prototype.onNodeSelectionChange = function(node) @@ -3339,6 +3645,9 @@ LGraphCanvas.prototype.touchHandler = function(event) //initMouseEvent(type, canBubble, cancelable, view, clickCount, // screenX, screenY, clientX, clientY, ctrlKey, // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); simulatedEvent.initMouseEvent(type, true, true, window, 1, @@ -3353,13 +3662,15 @@ LGraphCanvas.prototype.touchHandler = function(event) LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) { + var window = canvas.getCanvasWindow(); + var values = LiteGraph.getNodeTypesCategories(); var entries = {}; for(var i in values) if(values[i]) entries[ i ] = { value: values[i], content: values[i] , is_menu: true }; - var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, window); function inner_clicked(v, e) { @@ -3369,7 +3680,7 @@ LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) for(var i in node_types) values.push( { content: node_types[i].title, value: node_types[i].type }); - LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}); + LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}, window); return false; } @@ -3450,7 +3761,19 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu) function inner_clicked(v) { if(!node) return; - node.addOutput(v.value[0],v.value[1]); + + var value = v.value[1]; + + if(value && (value.constructor === Object || value.constructor === Array)) //submenu + { + var entries = []; + for(var i in value) + entries.push({content: i, value: value[i]}); + LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + return false; + } + else + node.addOutput(v.value[0],v.value[1]); } return false; @@ -3462,6 +3785,11 @@ LGraphCanvas.onMenuNodeCollapse = function(node) node.graph.canvas.setDirty(true,true); } +LGraphCanvas.onMenuNodePin = function(node) +{ + node.pin(); +} + LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) { var values = []; @@ -3490,7 +3818,7 @@ LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) LGraphCanvas.onMenuNodeShapes = function(node,e) { - LiteGraph.createContextualMenu(["box","round","circle"], {event: e, callback: inner_clicked}); + LiteGraph.createContextualMenu(["box","round"], {event: e, callback: inner_clicked}); function inner_clicked(v) { @@ -3541,6 +3869,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) {content:"Outputs", is_menu: true, disabled:true, callback: LGraphCanvas.onMenuNodeOutputs }, null, {content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse }, + {content:"Pin", callback: LGraphCanvas.onMenuNodePin }, {content:"Colors", is_menu: true, callback: LGraphCanvas.onMenuNodeColors }, {content:"Shapes", is_menu: true, callback: LGraphCanvas.onMenuNodeShapes }, null, @@ -3565,7 +3894,9 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) LGraphCanvas.prototype.processContextualMenu = function(node,event) { var that = this; - var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}); + var win = this.getCanvasWindow(); + + var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}, win); function inner_option_clicked(v,e) { @@ -3701,15 +4032,18 @@ function num2hex(triplet) { /* LiteGraph GUI elements *************************************/ -LiteGraph.createContextualMenu = function(values,options) +LiteGraph.createContextualMenu = function(values,options, ref_window) { options = options || {}; this.options = options; + //allows to create graph canvas in separate window + ref_window = ref_window || window; + if(!options.from) LiteGraph.closeAllContextualMenus(); - var root = document.createElement("div"); + var root = ref_window.document.createElement("div"); root.className = "litecontextualmenu litemenubar-panel"; this.root = root; var style = root.style; @@ -3731,7 +4065,7 @@ LiteGraph.createContextualMenu = function(values,options) for(var i in values) { var item = values[i]; - var element = document.createElement("div"); + var element = ref_window.document.createElement("div"); element.className = "litemenu-entry"; if(item == null) @@ -3766,7 +4100,7 @@ LiteGraph.createContextualMenu = function(values,options) root.addEventListener("mouseout", function(e) { //console.log("OUT!"); var aux = e.toElement; - while(aux != this && aux != document) + while(aux != this && aux != ref_window.document) aux = aux.parentNode; if(aux == this) return; @@ -3775,17 +4109,8 @@ LiteGraph.createContextualMenu = function(values,options) this.closeMenu(); }); - /* MS specific - root.addEventListener("mouseleave", function(e) { - - this.mouse_inside = false; - if(!this.block_close) - this.closeMenu(); - }); - */ - //insert before checking position - document.body.appendChild(root); + ref_window.document.body.appendChild(root); var root_rect = root.getClientRects()[0]; @@ -3804,7 +4129,7 @@ LiteGraph.createContextualMenu = function(values,options) if(options.left) left = options.left; - var rect = document.body.getClientRects()[0]; + var rect = ref_window.document.body.getClientRects()[0]; if(options.from) { @@ -3845,7 +4170,7 @@ LiteGraph.createContextualMenu = function(values,options) options.from.closeMenu(); } if(this.parentNode) - document.body.removeChild(this); + ref_window.document.body.removeChild(this); }; return root; @@ -3872,7 +4197,27 @@ LiteGraph.extendClass = function(origin, target) if(origin.prototype) //copy prototype properties for(var i in origin.prototype) target.prototype[i] = origin.prototype[i]; -} +} + +/* +LiteGraph.createNodetypeWrapper = function( class_object ) +{ + //create Nodetype object +} +//LiteGraph.registerNodeType("scene/global", LGraphGlobal ); +*/ + +if( !window["requestAnimationFrame"] ) +{ + window.requestAnimationFrame = window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + (function( callback ){ + window.setTimeout(callback, 1000 / 60); + }); +} + + + diff --git a/doc/index.html b/doc/index.html index b60c37e4b..0d9a3cbe7 100644 --- a/doc/index.html +++ b/doc/index.html @@ -43,6 +43,8 @@
  • LGraph
  • +
  • LGraphCanvas
  • +
  • LGraphNode
  • LiteGraph
  • diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js new file mode 100644 index 000000000..ef027c804 --- /dev/null +++ b/src/litegraph-editor.js @@ -0,0 +1,165 @@ +//NOT FINISHED + +function Editor(container_id, options) +{ + //fill container + var html = "
    "; + html += "
    "; + html += ""; + + var root = document.createElement("div"); + this.root = root; + root.className = "litegraph-editor"; + root.innerHTML = html; + + var canvas = root.querySelector(".graphcanvas"); + + //create graph + var graph = this.graph = new LGraph(); + var graphcanvas = this.graphcanvas = new LGraphCanvas(canvas,graph); + graphcanvas.background_image = "imgs/grid.png"; + graph.onAfterExecute = function() { graphcanvas.draw(true) }; + + //add stuff + this.addLoadCounter(); + this.addToolsButton("playnode_button","Play","imgs/icon-play.png", this.onPlayButton.bind(this), ".tools-right" ); + this.addToolsButton("playstepnode_button","Step","imgs/icon-playstep.png", this.onPlayStepButton.bind(this), ".tools-right" ); + + this.addToolsButton("livemode_button","Live","imgs/icon-record.png", this.onLiveButton.bind(this), ".tools-right" ); + this.addToolsButton("maximize_button","","imgs/icon-maximize.png", this.onFullscreenButton.bind(this), ".tools-right" ); + + this.addMiniWindow(300,200); + + //append to DOM + var parent = document.getElementById(container_id); + if(parent) + parent.appendChild(root); + + graphcanvas.resize(); + //graphcanvas.draw(true,true); +} + +Editor.prototype.addLoadCounter = function() +{ + var meter = document.createElement("div"); + meter.className = 'headerpanel loadmeter toolbar-widget'; + + var html = "
    CPU
    "; + html += "
    GFX
    "; + + meter.innerHTML = html; + this.root.querySelector(".header .tools-left").appendChild(meter); + var self = this; + + setInterval(function() { + meter.querySelector(".cpuload .fgload").style.width = ((2*self.graph.elapsed_time) * 90) + "px"; + if(self.graph.status == LGraph.STATUS_RUNNING) + meter.querySelector(".gpuload .fgload").style.width = ((self.graphcanvas.render_time*10) * 90) + "px"; + else + meter.querySelector(".gpuload .fgload").style.width = 4 + "px"; + },200); +} + +Editor.prototype.addToolsButton = function(id,name,icon_url, callback, container) +{ + if(!container) container = ".tools"; + + var button = document.createElement("button"); + button.id = id; + button.innerHTML = " "+name+""; + button.addEventListener("click", callback); + + this.root.querySelector(container).appendChild(button); + +} + +Editor.prototype.goFullscreen = function() +{ + if(this.root.requestFullscreen) + this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + else if(this.root.mozRequestFullscreen) + this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + else if(this.root.webkitRequestFullscreen) + this.root.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + else + throw("Fullscreen not supported"); + + var self = this; + setTimeout(function() { + self.graphcanvas.resize(); + },100); +} + +Editor.prototype.onPlayButton = function() +{ + var graph = this.graph; + var button = this.root.querySelector("#playnode_button"); + + if(graph.status == LGraph.STATUS_STOPPED) + { + button.innerHTML = " Stop"; + graph.start(1); + } + else + { + button.innerHTML = " Play"; + graph.stop(); + } +} + +Editor.prototype.onPlayStepButton = function() +{ + var graph = this.graph; + graph.runStep(1); + this.graphcanvas.draw(true,true); +} + +Editor.prototype.onLiveButton = function() +{ + var is_live_mode = !this.graphcanvas.live_mode; + this.graphcanvas.switchLiveMode(true); + this.graphcanvas.draw(); + var url = this.graphcanvas.live_mode ? "imgs/gauss_bg_medium.jpg" : "imgs/gauss_bg.jpg"; + var button = this.root.querySelector("#livemode_button"); + button.innerHTML = !is_live_mode ? " Live" : " Edit" ; +} + +Editor.prototype.onFullscreenButton = function() +{ + this.goFullscreen(); +} + +Editor.prototype.onMaximizeButton = function() +{ + this.maximize(); +} + +Editor.prototype.addMiniWindow = function(w,h) +{ + var miniwindow = document.createElement("div"); + miniwindow.className = "litegraph miniwindow"; + miniwindow.innerHTML = ""; + var canvas = miniwindow.querySelector("canvas"); + + var graphcanvas = new LGraphCanvas(canvas, this.graph); + graphcanvas.background_image = "imgs/grid.png"; + graphcanvas.scale = 0.5; + + miniwindow.style.position = "absolute"; + miniwindow.style.top = "4px"; + miniwindow.style.right = "4px"; + + var close_button = document.createElement("div"); + close_button.className = "corner-button"; + close_button.innerHTML = "X"; + close_button.addEventListener("click",function(e) { + graphcanvas.setGraph(null); + miniwindow.parentNode.removeChild(miniwindow); + }); + miniwindow.appendChild(close_button); + + + this.root.querySelector(".content").appendChild(miniwindow); +} + +LiteGraph.Editor = Editor; \ No newline at end of file diff --git a/src/litegraph.js b/src/litegraph.js index 70ed409eb..43e71b165 100644 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -29,7 +29,6 @@ var LiteGraph = { debug: false, registered_node_types: {}, - graphs: [], /** * Register a node class so it can be listed when the user wants to create a new one @@ -232,28 +231,19 @@ var LiteGraph = { } } - for (var i in LiteGraph.graphs) - { - for (var j in LiteGraph.graphs[i].nodes) - { - var m = LiteGraph.graphs[i].nodes[j]; - var t = LiteGraph.getNodeType(n.type); - if(!t) continue; - - for (var k in t) - if( typeof(t[k]) == "function" ) - m[k] = t[k]; - } - } - if(LiteGraph.debug) console.log("Nodes reloaded"); }, //separated just to improve if it doesnt work - cloneObject: function(obj) + cloneObject: function(obj, target) { - return JSON.parse( JSON.stringify( obj ) ); + var r = JSON.parse( JSON.stringify( obj ) ); + if(!target) return r; + + for(var i in r) + target[i] = r[i]; + return target; } /* @@ -262,11 +252,11 @@ var LiteGraph = { mode = mode || "all"; trace("Benchmarking " + mode + "..."); - trace(" Num. nodes: " + this.nodes.length ); + trace(" Num. nodes: " + this._nodes.length ); var links = 0; - for(var i in this.nodes) - for(var j in this.nodes[i].outputs) - if(this.nodes[i].outputs[j].node_id != null) + for(var i in this._nodes) + for(var j in this._nodes[i].outputs) + if(this._nodes[i].outputs[j].node_id != null) links++; trace(" Num. links: " + links ); @@ -317,8 +307,7 @@ function LGraph() { if (LiteGraph.debug) console.log("Graph created"); - this.canvas = null; - LiteGraph.graphs.push(this); + this.list_of_graphcanvas = null; this.clear(); } @@ -337,8 +326,8 @@ LGraph.prototype.clear = function() this.last_node_id = 0; //nodes - this.nodes = []; - this.nodes_by_id = {}; + this._nodes = []; + this._nodes_by_id = {}; //links this.last_link_id = 0; @@ -348,8 +337,6 @@ LGraph.prototype.clear = function() this.iteration = 0; this.config = { - canvas_offset: [0,0], - canvas_scale: 1.0 }; //timing @@ -360,12 +347,48 @@ LGraph.prototype.clear = function() this.elapsed_time = 0.01; this.starttime = 0; + //globals + this.globals = {}; + this.graph = {}; this.debug = true; this.change(); - if(this.canvas) - this.canvas.clear(); + + this.sendActionToCanvas("clear"); +} + +/** +* Attach Canvas to this graph +* @method attachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.attachCanvas = function(graphcanvas) +{ + if(graphcanvas.constructor != LGraphCanvas) + throw("attachCanvas expects a LGraphCanvas instance"); + if(graphcanvas.graph && graphcanvas.graph != this) + graphcanvas.graph.detachCanvas( graphcanvas ); + + graphcanvas.graph = this; + if(!this.list_of_graphcanvas) + this.list_of_graphcanvas = []; + this.list_of_graphcanvas.push(graphcanvas); +} + +/** +* Detach Canvas from this graph +* @method detachCanvas +* @param {GraphCanvas} graph_canvas +*/ + +LGraph.prototype.detachCanvas = function(graphcanvas) +{ + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if(pos == -1) return; + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos,1); } /** @@ -385,7 +408,7 @@ LGraph.prototype.start = function(interval) this.sendEventToAllNodes("onStart"); //launch - this.starttime = new Date().getTime(); + this.starttime = window.performance.now(); interval = interval || 1; var that = this; @@ -397,7 +420,7 @@ LGraph.prototype.start = function(interval) /** * Stops the execution loop of the graph -* @method stop +* @method stop execution */ LGraph.prototype.stop = function() @@ -427,7 +450,7 @@ LGraph.prototype.runStep = function(num) { num = num || 1; - var start = new Date().getTime(); + var start = window.performance.now(); this.globaltime = 0.001 * (start - this.starttime); try @@ -454,7 +477,7 @@ LGraph.prototype.runStep = function(num) this.stop(); } - var elapsed = (new Date().getTime()) - start; + var elapsed = window.performance.now() - start; if (elapsed == 0) elapsed = 1; this.elapsed_time = 0.001 * elapsed; this.globaltime += 0.001 * elapsed; @@ -469,7 +492,7 @@ LGraph.prototype.runStep = function(num) LGraph.prototype.updateExecutionOrder = function() { - this.nodes_in_order = this.computeExecutionOrder(); + this._nodes_in_order = this.computeExecutionOrder(); } //This is more internal, it computes the order and returns it @@ -482,9 +505,9 @@ LGraph.prototype.computeExecutionOrder = function() var remaining_links = {}; //to a //search for the nodes without inputs (starting nodes) - for (var i in this.nodes) + for (var i in this._nodes) { - var n = this.nodes[i]; + var n = this._nodes[i]; M[n.id] = n; //add to pending nodes var num = 0; //num of input connections @@ -546,7 +569,7 @@ LGraph.prototype.computeExecutionOrder = function() for(var i in M) L.push(M[i]); - if(L.length != this.nodes.length && LiteGraph.debug) + if(L.length != this._nodes.length && LiteGraph.debug) console.log("something went wrong, nodes missing"); //save order number in the node @@ -600,12 +623,25 @@ LGraph.prototype.getElapsedTime = function() LGraph.prototype.sendEventToAllNodes = function(eventname, param) { - var M = this.nodes_in_order ? this.nodes_in_order : this.nodes; + var M = this._nodes_in_order ? this._nodes_in_order : this._nodes; for(var j in M) if(M[j][eventname]) M[j][eventname](param); } +LGraph.prototype.sendActionToCanvas = function(action, params) +{ + if(!this.list_of_graphcanvas) + return; + + for(var i in this.list_of_graphcanvas) + { + var c = this.list_of_graphcanvas[i]; + if( c[action] ) + c[action].apply(c, params); + } +} + /** * Adds a new node instasnce to this graph * @method add @@ -614,10 +650,10 @@ LGraph.prototype.sendEventToAllNodes = function(eventname, param) LGraph.prototype.add = function(node) { - if(!node || (node.id != -1 && this.nodes_by_id[node.id] != null)) + if(!node || (node.id != -1 && this._nodes_by_id[node.id] != null)) return; //already added - if(this.nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) + if(this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) throw("LiteGraph: max number of nodes in a graph reached"); //give him an id @@ -626,8 +662,8 @@ LGraph.prototype.add = function(node) node.graph = this; - this.nodes.push(node); - this.nodes_by_id[node.id] = node; + this._nodes.push(node); + this._nodes_by_id[node.id] = node; /* // rendering stuf... @@ -659,7 +695,7 @@ LGraph.prototype.add = function(node) LGraph.prototype.remove = function(node) { - if(this.nodes_by_id[node.id] == null) + if(this._nodes_by_id[node.id] == null) return; //not found if(node.ignore_remove) @@ -699,10 +735,10 @@ LGraph.prototype.remove = function(node) } //remove from containers - var pos = this.nodes.indexOf(node); + var pos = this._nodes.indexOf(node); if(pos != -1) - this.nodes.splice(pos,1); - delete this.nodes_by_id[node.id]; + this._nodes.splice(pos,1); + delete this._nodes_by_id[node.id]; if(this.canvas) this.canvas.setDirty(true,true); @@ -721,7 +757,7 @@ LGraph.prototype.remove = function(node) LGraph.prototype.getNodeById = function(id) { if(id==null) return null; - return this.nodes_by_id[id]; + return this._nodes_by_id[id]; } @@ -735,9 +771,9 @@ LGraph.prototype.getNodeById = function(id) LGraph.prototype.findNodesByType = function(type) { var r = []; - for(var i in this.nodes) - if(this.nodes[i].type == type) - r.push(this.nodes[i]); + for(var i in this._nodes) + if(this._nodes[i].type == type) + r.push(this._nodes[i]); return r; } @@ -751,9 +787,9 @@ LGraph.prototype.findNodesByType = function(type) LGraph.prototype.findNodesByName = function(name) { var result = []; - for (var i in this.nodes) - if(this.nodes[i].name == name) - result.push(this.nodes[i]); + for (var i in this._nodes) + if(this._nodes[i].name == name) + result.push(this._nodes[i]); return result; } @@ -768,7 +804,7 @@ LGraph.prototype.findNodesByName = function(name) LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) { - nodes_list = nodes_list || this.nodes; + nodes_list = nodes_list || this._nodes; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; @@ -824,39 +860,46 @@ LGraph.prototype.setCallback = function(name,func) m[i].setTrigger(func); } -//********** - LGraph.prototype.onConnectionChange = function() { this.updateExecutionOrder(); } +/** +* returns if the graph is in live mode +* @method isLive +*/ + LGraph.prototype.isLive = function() { if(!this.canvas) return false; return this.canvas.live_mode; } +/* Called when something visually changed */ LGraph.prototype.change = function() { if(LiteGraph.debug) console.log("Graph changed"); + + this.sendActionToCanvas("setDirty",[true,true]); + if(this.on_change) this.on_change(this); } //save and recover app state *************************************** /** -* Creates a JSON String containing all the info about this graph +* Creates a Object containing all the info about this graph, it can be serialized * @method serialize -* @return {String} value of the node +* @return {Object} value of the node */ LGraph.prototype.serialize = function() { var nodes_info = []; - for (var i in this.nodes) - nodes_info.push( this.nodes[i].objectivize() ); + for (var i in this._nodes) + nodes_info.push( this._nodes[i].serialize() ); var data = { graph: this.graph, @@ -870,20 +913,20 @@ LGraph.prototype.serialize = function() nodes: nodes_info }; - return JSON.stringify(data); + return data; } + /** * Configure a graph from a JSON string -* @method unserialize +* @method configure * @param {String} str configure a graph from a JSON string */ -LGraph.prototype.unserialize = function(str, keep_old) +LGraph.prototype.configure = function(data, keep_old) { if(!keep_old) this.clear(); - var data = JSON.parse(str); var nodes = data.nodes; //copy all stored fields @@ -893,7 +936,7 @@ LGraph.prototype.unserialize = function(str, keep_old) var error = false; //create nodes - this.nodes = []; + this._nodes = []; for (var i in nodes) { var n_info = nodes[i]; //stored info @@ -906,7 +949,7 @@ LGraph.prototype.unserialize = function(str, keep_old) continue; } - n.copyFromObject(n_info); + n.configure(n_info); this.add(n); } @@ -980,9 +1023,59 @@ function LGraphNode(name) }; } -//serialization ************************* +/** +* configure a node from an object +* @method configure +*/ +LGraphNode.prototype.configure = function(info) +{ + for (var j in info) + { + if(j == "console") continue; -LGraphNode.prototype.objectivize = function() + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j], this[j] || {} ); + else //value + this[j] = info[j]; + } +} + +/* Copy all the info from one object to this node (used for serialization) */ +LGraphNode.prototype.copyFromObject = function(info, ignore_connections) +{ + var outputs = null; + var inputs = null; + var properties = null; + var local_data = null; + + for (var j in info) + { + if(ignore_connections && (j == "outputs" || j == "inputs")) + continue; + + if(j == "console") continue; + + if(info[j] == null) + continue; + else if( info[j].concat ) //array + this[j] = info[j].concat(); + else if (typeof(info[j]) == 'object') //object + this[j] = LiteGraph.cloneObject(info[j]); + else //value + this[j] = info[j]; + } +} + +/** +* serialize the content +* @method serialize +*/ + +LGraphNode.prototype.serialize = function() { var o = { id: this.id, @@ -1009,10 +1102,14 @@ LGraphNode.prototype.objectivize = function() if(this.shape) o.shape = this.shape; + if(this.onSerialize) + this.onSerialize(o); + return o; } //reduced version of objectivize: NOT FINISHED +/* LGraphNode.prototype.reducedObjectivize = function() { var o = this.objectivize(); @@ -1030,19 +1127,27 @@ LGraphNode.prototype.reducedObjectivize = function() return o; } +*/ +/** +* serialize and stringify +* @method toString +*/ -LGraphNode.prototype.serialize = function() +LGraphNode.prototype.toString = function() { - if(this.onSerialize) - this.onSerialize(); - return JSON.stringify( this.reducedObjectivize() ); + return JSON.stringify( this.serialize() ); } //LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph // Execution ************************* - +/** +* sets the output data +* @method setOutputData +* @param {number} slot +* @param {*} data +*/ LGraphNode.prototype.setOutputData = function(slot,data) { if(!this.outputs) return; @@ -1053,6 +1158,12 @@ LGraphNode.prototype.setOutputData = function(slot,data) } } +/** +* retrieves the input data from one slot +* @method getInputData +* @param {number} slot +* @return {*} data +*/ LGraphNode.prototype.getInputData = function(slot) { if(!this.inputs) return null; @@ -1061,12 +1172,24 @@ LGraphNode.prototype.getInputData = function(slot) return null; } +/** +* tells you if there is a connection in one input slot +* @method isInputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isInputConnected = function(slot) { if(!this.inputs) return null; return (slot < this.inputs.length && this.inputs[slot].link != null); } +/** +* tells you info about an input connection (which node, type, etc) +* @method getInputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getInputInfo = function(slot) { if(!this.inputs) return null; @@ -1076,6 +1199,12 @@ LGraphNode.prototype.getInputInfo = function(slot) } +/** +* tells you info about an output connection (which node, type, etc) +* @method getOutputInfo +* @param {number} slot +* @return {Object} +*/ LGraphNode.prototype.getOutputInfo = function(slot) { if(!this.outputs) return null; @@ -1084,12 +1213,25 @@ LGraphNode.prototype.getOutputInfo = function(slot) return null; } + +/** +* tells you if there is a connection in one output slot +* @method isOutputConnected +* @param {number} slot +* @return {boolean} +*/ LGraphNode.prototype.isOutputConnected = function(slot) { if(!this.outputs) return null; return (slot < this.outputs.length && this.outputs[slot].links && this.outputs[slot].links.length); } +/** +* retrieves all the nodes connected to this output slot +* @method getOutputNodes +* @param {number} slot +* @return {array} +*/ LGraphNode.prototype.getOutputNodes = function(slot) { if(!this.outputs || this.outputs.length == 0) return null; @@ -1113,6 +1255,13 @@ LGraphNode.prototype.triggerOutput = function(slot,param) //connections +/** +* add a new output slot to use in this node +* @method addOutput +* @param {string} name +* @param {string} type string defining the output type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an output (special color, position, etc) +*/ LGraphNode.prototype.addOutput = function(name,type,extra_info) { var o = {name:name,type:type,links:null}; @@ -1125,6 +1274,11 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing output slot +* @method removeOutput +* @param {number} slot +*/ LGraphNode.prototype.removeOutput = function(slot) { this.disconnectOutput(slot); @@ -1132,6 +1286,13 @@ LGraphNode.prototype.removeOutput = function(slot) this.size = this.computeSize(); } +/** +* add a new input slot to use in this node +* @method addInput +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {Object} extra_info this can be used to have special properties of an input (special color, position, etc) +*/ LGraphNode.prototype.addInput = function(name,type,extra_info) { var o = {name:name,type:type,link:null}; @@ -1144,6 +1305,11 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* remove an existing input slot +* @method removeInput +* @param {number} slot +*/ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); @@ -1151,13 +1317,25 @@ LGraphNode.prototype.removeInput = function(slot) this.size = this.computeSize(); } -//trigger connection +/** +* add an special connection to this node (used for special kinds of graphs) +* @method addConnection +* @param {string} name +* @param {string} type string defining the input type ("vec3","number",...) +* @param {[x,y]} pos position of the connection inside the node +* @param {string} direction if is input or output +*/ LGraphNode.prototype.addConnection = function(name,type,pos,direction) { this.connections.push( {name:name,type:type,pos:pos,direction:direction,links:null}); } - +/** +* computes the size of a node according to its inputs and output slots +* @method computeSize +* @param {number} minHeight +* @return {number} the total size +*/ LGraphNode.prototype.computeSize = function(minHeight) { var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); @@ -1170,13 +1348,23 @@ LGraphNode.prototype.computeSize = function(minHeight) return size; } -//returns the bounding of the object, used for rendering purposes +/** +* returns the bounding of the object, used for rendering purposes +* @method getBounding +* @return {Float32Array[4]} the total size +*/ LGraphNode.prototype.getBounding = function() { return new Float32Array([this.pos[0] - 4, this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, this.pos[0] + this.size[0] + 4, this.pos[1] + this.size[1] + LGraph.NODE_TITLE_HEIGHT]); } -//checks if a point is inside the shape of a node +/** +* checks if a point is inside the shape of a node +* @method isPointInsideNode +* @param {number} x +* @param {number} y +* @return {boolean} +*/ LGraphNode.prototype.isPointInsideNode = function(x,y) { var margin_top = this.graph.isLive() ? 0 : 20; @@ -1210,7 +1398,14 @@ LGraphNode.prototype.findOutputSlot = function(name) return -1; } -//connect this node output to the input of another node +/** +* connect this node output to the input of another node +* @method connect +* @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) +* @return {boolean} if it was connected succesfully +*/ LGraphNode.prototype.connect = function(slot, node, target_slot) { target_slot = target_slot || 0; @@ -1284,6 +1479,13 @@ LGraphNode.prototype.connect = function(slot, node, target_slot) return true; } +/** +* disconnect one output to an specific node +* @method disconnectOutput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectOutput = function(slot, target_node) { if( slot.constructor === String ) @@ -1340,6 +1542,12 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node) return true; } +/** +* disconnect one input +* @method disconnectInput +* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) +* @return {boolean} if it was disconnected succesfully +*/ LGraphNode.prototype.disconnectInput = function(slot) { //seek for the output slot @@ -1388,7 +1596,13 @@ LGraphNode.prototype.disconnectInput = function(slot) return true; } -//returns the center of a connection point in canvas coords +/** +* returns the center of a connection point in canvas coords +* @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) +* @return {[x,y]} the position +**/ LGraphNode.prototype.getConnectionPos = function(is_input,slot_number) { if(this.flags.collapsed) @@ -1422,50 +1636,6 @@ LGraphNode.prototype.alignToGrid = function() this.pos[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); } -/* Copy all the info from one object to this node (used for serialization) */ -LGraphNode.prototype.copyFromObject = function(info, ignore_connections) -{ - var outputs = null; - var inputs = null; - var properties = null; - var local_data = null; - - for (var j in info) - { - if(ignore_connections && (j == "outputs" || j == "inputs")) - continue; - - if(j == "console") continue; - - if(info[j] == null) - continue; - else if( info[j].concat ) //array - this[j] = info[j].concat(); - else if (typeof(info[j]) == 'object') //object - this[j] = LiteGraph.cloneObject(info[j]); - else //value - this[j] = info[j]; - } - - //redo the connections - /* - if(outputs) - this.outputs = outputs.concat(); - if(inputs) - this.inputs = inputs.concat(); - - if(local_data) - this.data = local_data; - if(properties) - { - //copy only the ones defined - for (var j in properties) - if (this.properties[j] != null) - this.properties[j] = properties[j]; - } - */ -} - /* Creates a clone of this node */ LGraphNode.prototype.clone = function() { @@ -1505,13 +1675,9 @@ LGraphNode.prototype.trace = function(msg) /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function(dirty_foreground, dirty_background) { - if(!this.graph || !this.graph.canvas) + if(!this.graph) return; - - if(dirty_foreground) - this.graph.canvas.dirty_canvas = true; - if(dirty_background) - this.graph.canvas.dirty_bgcanvas = true; + this.graph.sendActionToCanvas("setDirty",[dirty_foreground, dirty_background]); } LGraphNode.prototype.loadImage = function(url) @@ -1568,20 +1734,29 @@ LGraphNode.prototype.executeAction = function(action) /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ LGraphNode.prototype.captureInput = function(v) { - if(!this.graph || !this.graph.canvas) + if(!this.graph || !this.graph.list_of_graphcanvas) return; - //releasing somebody elses capture?! - if(!v && this.graph.canvas.node_capturing_input != this) - return; + var list = this.graph.list_of_graphcanvas; - //change - this.graph.canvas.node_capturing_input = v ? this : null; - if(this.graph.debug) - console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + for(var i in list) + { + var c = list[i]; + //releasing somebody elses capture?! + if(!v && c.node_capturing_input != this) + continue; + + //change + c.node_capturing_input = v ? this : null; + if(this.graph.debug) + console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + } } -/* Collapse the node */ +/** +* Collapse the node to make it smaller on the canvas +* @method collapse +**/ LGraphNode.prototype.collapse = function() { if(!this.flags.collapsed) @@ -1591,19 +1766,23 @@ LGraphNode.prototype.collapse = function() this.setDirtyCanvas(true,true); } -/* Forces the node to do not move or realign on Z */ -LGraphNode.prototype.pin = function() +/** +* Forces the node to do not move or realign on Z +* @method pin +**/ + +LGraphNode.prototype.pin = function(v) { - if(!this.flags.pinned) - this.flags.pinned = true; + if(v === undefined) + this.flags.pinned = !this.flags.pinned; else - this.flags.pinned = false; + this.flags.pinned = v; } -LGraphNode.prototype.localToScreen = function(x,y) +LGraphNode.prototype.localToScreen = function(x,y, graphcanvas) { - return [(x + this.pos[0]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[0], - (y + this.pos[1]) * this.graph.config.canvas_scale + this.graph.config.canvas_offset[1]]; + return [(x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]]; } @@ -1612,27 +1791,28 @@ LGraphNode.prototype.localToScreen = function(x,y) // LGraphCanvas: LGraph renderer CLASS //********************************************************************************* +/** +* The Global Scope. It contains all the registered node classes. +* +* @class LGraphCanvas +* @constructor +* @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format) +* @param {LGraph} graph +*/ function LGraphCanvas(canvas, graph) { - if(graph === undefined) - throw ("No graph assigned"); + //if(graph === undefined) + // throw ("No graph assigned"); - if( typeof(window) != "undefined" ) - { - window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function( callback ){ - window.setTimeout(callback, 1000 / 60); - }; - })(); - } + if(typeof(canvas) == "string") + canvas = document.querySelector(canvas); + + if(!canvas) + throw("no canvas found"); //link canvas and graph - this.graph = graph; if(graph) - graph.canvas = this; + graph.attachCanvas(this); this.setCanvas(canvas); this.clear(); @@ -1641,7 +1821,6 @@ function LGraphCanvas(canvas, graph) } LGraphCanvas.link_type_colors = {'number':"#AAC",'node':"#DCA"}; -LGraphCanvas.link_width = 2; LGraphCanvas.prototype.clear = function() { @@ -1650,6 +1829,9 @@ LGraphCanvas.prototype.clear = function() this.render_time = 0; this.fps = 0; + this.scale = 1; + this.offset = [0,0]; + this.selected_nodes = {}; this.node_dragged = null; this.node_over = null; @@ -1657,6 +1839,7 @@ LGraphCanvas.prototype.clear = function() this.connecting_node = null; this.highquality_render = true; + this.editor_alpha = 1; //used for transition this.pause_rendering = false; this.render_shadows = true; this.dirty_canvas = true; @@ -1674,6 +1857,16 @@ LGraphCanvas.prototype.clear = function() this.last_mouse = [0,0]; this.last_mouseclick = 0; + this.title_text_font = "bold 14px Arial"; + this.inner_text_font = "normal 12px Arial"; + + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = true; + this.render_connection_arrows = true; + + this.connections_width = 4; + if(this.onClear) this.onClear(); //this.UIinit(); } @@ -1681,29 +1874,25 @@ LGraphCanvas.prototype.clear = function() LGraphCanvas.prototype.setGraph = function(graph) { if(this.graph == graph) return; - this.clear(); + + if(!graph && this.graph) + { + this.graph.detachCanvas(this); + return; + } + + /* if(this.graph) this.graph.canvas = null; //remove old graph link to the canvas this.graph = graph; if(this.graph) this.graph.canvas = this; + */ + graph.attachCanvas(this); this.setDirty(true,true); } -LGraphCanvas.prototype.resize = function(width, height) -{ - if(this.canvas.width == width && this.canvas.height == height) - return; - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true,true); -} - - LGraphCanvas.prototype.setCanvas = function(canvas) { var that = this; @@ -1828,6 +2017,13 @@ LGraphCanvas.prototype.setDirty = function(fgcanvas,bgcanvas) this.dirty_bgcanvas = true; } +//Used to attach the canvas in a popup +LGraphCanvas.prototype.getCanvasWindow = function() +{ + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; +} + LGraphCanvas.prototype.startRendering = function() { if(this.is_rendering) return; //already rendering @@ -1840,8 +2036,9 @@ LGraphCanvas.prototype.startRendering = function() if(!this.pause_rendering) this.draw(); + var window = this.getCanvasWindow(); if(this.is_rendering) - window.requestAnimFrame( renderFrame.bind(this) ); + window.requestAnimationFrame( renderFrame.bind(this) ); } @@ -1873,9 +2070,12 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.adjustMouseEvent(e); + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + this.canvas.removeEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mousemove", this._mousemove_callback ); - document.addEventListener("mouseup", this._mouseup_callback ); + ref_window.document.addEventListener("mousemove", this._mousemove_callback ); //catch for the entire window + ref_window.document.addEventListener("mouseup", this._mouseup_callback ); var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); var skip_dragging = false; @@ -1956,7 +2156,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) var block_drag_node = false; //double clicking - var now = new Date().getTime(); + var now = window.performance.now(); if ((now - this.last_mouseclick) < 300 && this.selected_nodes[n.id]) { //double click node @@ -2011,7 +2211,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.last_mouse[0] = e.localX; this.last_mouse[1] = e.localY; - this.last_mouseclick = new Date().getTime(); + this.last_mouseclick = window.performance.now(); this.canvas_mouse = [e.canvasX, e.canvasY]; /* @@ -2022,7 +2222,7 @@ LGraphCanvas.prototype.processMouseDown = function(e) this.graph.change(); //this is to ensure to defocus(blur) if a text input element is on focus - if(!document.activeElement || (document.activeElement.nodeName.toLowerCase() != "input" && document.activeElement.nodeName.toLowerCase() != "textarea")) + if(!ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != "input" && ref_window.document.activeElement.nodeName.toLowerCase() != "textarea")) e.preventDefault(); e.stopPropagation(); return false; @@ -2040,8 +2240,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.dragging_canvas) { - this.graph.config.canvas_offset[0] += delta[0] / this.graph.config.canvas_scale; - this.graph.config.canvas_offset[1] += delta[1] / this.graph.config.canvas_scale; + this.offset[0] += delta[0] / this.scale; + this.offset[1] += delta[1] / this.scale; this.dirty_canvas = true; this.dirty_bgcanvas = true; } @@ -2054,12 +2254,12 @@ LGraphCanvas.prototype.processMouseMove = function(e) var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes); //remove mouseover flag - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - if(this.graph.nodes[i].mouseOver && n != this.graph.nodes[i]) + if(this.graph._nodes[i].mouseOver && n != this.graph._nodes[i]) { //mouse leave - this.graph.nodes[i].mouseOver = false; + this.graph._nodes[i].mouseOver = false; if(this.node_over && this.node_over.onMouseLeave) this.node_over.onMouseLeave(e); this.node_over = null; @@ -2116,8 +2316,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.node_dragged && !this.live_mode) { /* - this.node_dragged.pos[0] += delta[0] / this.graph.config.canvas_scale; - this.node_dragged.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); */ @@ -2126,8 +2326,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) { var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.graph.config.canvas_scale; - n.pos[1] += delta[1] / this.graph.config.canvas_scale; + 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]); } @@ -2138,8 +2338,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) if(this.resizing_node && !this.live_mode) { - this.resizing_node.size[0] += delta[0] / this.graph.config.canvas_scale; - this.resizing_node.size[1] += delta[1] / this.graph.config.canvas_scale; + this.resizing_node.size[0] += delta[0] / this.scale; + this.resizing_node.size[1] += delta[1] / this.scale; var max_slots = Math.max( this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); if(this.resizing_node.size[1] < max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4) this.resizing_node.size[1] = max_slots * LiteGraph.NODE_SLOT_HEIGHT + 4; @@ -2168,6 +2368,9 @@ LGraphCanvas.prototype.processMouseUp = function(e) { if(!this.graph) return; + var window = this.getCanvasWindow(); + var document = window.document; + document.removeEventListener("mousemove", this._mousemove_callback, true ); this.canvas.addEventListener("mousemove", this._mousemove_callback, true); document.removeEventListener("mouseup", this._mouseup_callback, true ); @@ -2333,7 +2536,7 @@ LGraphCanvas.prototype.processMouseWheel = function(e) this.adjustMouseEvent(e); - var zoom = this.graph.config.canvas_scale; + var zoom = this.scale; if (delta > 0) zoom *= 1.1; @@ -2418,13 +2621,13 @@ LGraphCanvas.prototype.selectNode = function(node) LGraphCanvas.prototype.selectAllNodes = function() { - for(var i in this.graph.nodes) + for(var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; if(!n.selected && n.onSelected) n.onSelected(); n.selected = true; - this.selected_nodes[this.graph.nodes[i].id] = n; + this.selected_nodes[this.graph._nodes[i].id] = n; } this.setDirty(true); @@ -2457,8 +2660,8 @@ LGraphCanvas.prototype.deleteSelectedNodes = function() LGraphCanvas.prototype.centerOnNode = function(node) { - this.graph.config.canvas_offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.graph.config.canvas_scale); - this.graph.config.canvas_offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.graph.config.canvas_scale); + 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.setDirty(true,true); } @@ -2468,8 +2671,8 @@ LGraphCanvas.prototype.adjustMouseEvent = function(e) e.localX = e.pageX - b.left; e.localY = e.pageY - b.top; - e.canvasX = e.localX / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0]; - e.canvasY = e.localY / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]; + e.canvasX = e.localX / this.scale - this.offset[0]; + e.canvasY = e.localY / this.scale - this.offset[1]; } LGraphCanvas.prototype.setZoom = function(value, zooming_center) @@ -2479,18 +2682,18 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) var center = this.convertOffsetToCanvas( zooming_center ); - this.graph.config.canvas_scale = value; + this.scale = value; - if(this.graph.config.canvas_scale > 4) - this.graph.config.canvas_scale = 4; - else if(this.graph.config.canvas_scale < 0.1) - this.graph.config.canvas_scale = 0.1; + if(this.scale > 4) + this.scale = 4; + else if(this.scale < 0.1) + this.scale = 0.1; var new_center = this.convertOffsetToCanvas( zooming_center ); var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - this.graph.config.canvas_offset[0] += delta_offset[0]; - this.graph.config.canvas_offset[1] += delta_offset[1]; + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -2498,13 +2701,13 @@ LGraphCanvas.prototype.setZoom = function(value, zooming_center) LGraphCanvas.prototype.convertOffsetToCanvas = function(pos) { - return [pos[0] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[0], pos[1] / this.graph.config.canvas_scale - this.graph.config.canvas_offset[1]]; + return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; } LGraphCanvas.prototype.convertCanvasToOffset = function(pos) { - return [(pos[0] + this.graph.config.canvas_offset[0]) * this.graph.config.canvas_scale, - (pos[1] + this.graph.config.canvas_offset[1]) * this.graph.config.canvas_scale ]; + return [(pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale ]; } LGraphCanvas.prototype.convertEventToCanvas = function(e) @@ -2515,20 +2718,20 @@ LGraphCanvas.prototype.convertEventToCanvas = function(e) LGraphCanvas.prototype.bringToFront = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.push(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.push(n); } LGraphCanvas.prototype.sendToBack = function(n) { - var i = this.graph.nodes.indexOf(n); + var i = this.graph._nodes.indexOf(n); if(i == -1) return; - this.graph.nodes.splice(i,1); - this.graph.nodes.unshift(n); + this.graph._nodes.splice(i,1); + this.graph._nodes.unshift(n); } /* Interaction */ @@ -2540,9 +2743,9 @@ LGraphCanvas.prototype.sendToBack = function(n) LGraphCanvas.prototype.computeVisibleNodes = function() { var visible_nodes = []; - for (var i in this.graph.nodes) + for (var i in this.graph._nodes) { - var n = this.graph.nodes[i]; + var n = this.graph._nodes[i]; //skip rendering nodes in live mode if(this.live_mode && !n.onDrawBackground && !n.onDrawForeground) @@ -2559,14 +2762,14 @@ LGraphCanvas.prototype.computeVisibleNodes = function() LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { //fps counting - var now = new Date().getTime(); + var now = window.performance.now(); this.render_time = (now - this.last_draw_time)*0.001; this.last_draw_time = now; if(this.graph) { - var start = [-this.graph.config.canvas_offset[0], -this.graph.config.canvas_offset[1] ]; - var end = [start[0] + this.canvas.width / this.graph.config.canvas_scale, start[1] + this.canvas.height / this.graph.config.canvas_scale]; + 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],end[1]]); } @@ -2625,8 +2828,8 @@ LGraphCanvas.prototype.drawFrontCanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //draw nodes var drawn_nodes = 0; @@ -2657,10 +2860,9 @@ LGraphCanvas.prototype.drawFrontCanvas = function() //current connection if(this.connecting_pos != null) { - ctx.lineWidth = LGraphCanvas.link_width; - ctx.fillStyle = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; - ctx.strokeStyle = ctx.fillStyle; - this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]] ); + ctx.lineWidth = this.connections_width; + var link_color = this.connecting_output.type == 'node' ? "#F85" : "#AFA"; + this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]], link_color ); ctx.beginPath(); ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2); @@ -2709,13 +2911,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() { //apply transformations ctx.save(); - ctx.scale(this.graph.config.canvas_scale,this.graph.config.canvas_scale); - ctx.translate(this.graph.config.canvas_offset[0],this.graph.config.canvas_offset[1]); + ctx.scale(this.scale,this.scale); + ctx.translate(this.offset[0],this.offset[1]); //render BG - if(this.background_image && this.graph.config.canvas_scale > 0.5) + if(this.background_image && this.scale > 0.5) { - ctx.globalAlpha = 1.0 - 0.5 / this.graph.config.canvas_scale; + ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha; ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false if(!this._bg_img || this._bg_img.name != this.background_image) { @@ -2755,8 +2957,7 @@ LGraphCanvas.prototype.drawBgcanvas = function() ctx.strokeStyle = "#235"; ctx.strokeRect(0,0,canvas.width,canvas.height); - /* - if(this.render_shadows) + if(this.render_connections_shadows) { ctx.shadowColor = "#000"; ctx.shadowOffsetX = 0; @@ -2765,12 +2966,13 @@ LGraphCanvas.prototype.drawBgcanvas = function() } else ctx.shadowColor = "rgba(0,0,0,0)"; - */ //draw connections if(!this.live_mode) this.drawConnections(ctx); + ctx.shadowColor = "rgba(0,0,0,0)"; + //restore state ctx.restore(); } @@ -2821,8 +3023,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(!node.flags.collapsed) { ctx.shadowColor = "transparent"; - if(node.onDrawBackground) - node.onDrawBackground(ctx); + //if(node.onDrawBackground) + // node.onDrawBackground(ctx); if(node.onDrawForeground) node.onDrawForeground(ctx); } @@ -2840,13 +3042,16 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } */ + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + //clip if required (mask) var shape = node.shape || "box"; var size = new Float32Array(node.size); if(node.flags.collapsed) size.set([LiteGraph.NODE_COLLAPSED_WIDTH, 0]); - //Start cliping + //Start clipping if(node.flags.clip_area) { ctx.save(); @@ -2873,9 +3078,9 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) //connection slots ctx.textAlign = "left"; - ctx.font = "12px Arial"; + ctx.font = this.inner_text_font; - var render_text = node.graph.config.canvas_scale > 0.6; + var render_text = this.scale > 0.6; //render inputs and outputs if(!node.flags.collapsed) @@ -2886,9 +3091,9 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) { var slot = node.inputs[i]; - ctx.globalAlpha = 1.0; + ctx.globalAlpha = editor_alpha; if (this.connecting_node != null && this.connecting_output.type != 0 && node.inputs[i].type != 0 && this.connecting_output.type != node.inputs[i].type) - ctx.globalAlpha = 0.4; + ctx.globalAlpha = 0.4 * editor_alpha; ctx.fillStyle = slot.link != null ? "#7F7" : "#AAA"; @@ -2919,7 +3124,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) //output connection slots if(this.connecting_node) - ctx.globalAlpha = 0.4; + ctx.globalAlpha = 0.4 * editor_alpha; ctx.lineWidth = 1; @@ -2964,7 +3169,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } ctx.textAlign = "left"; - ctx.globalAlpha = 1.0; + ctx.globalAlpha = 1; if(node.onDrawForeground) node.onDrawForeground(ctx); @@ -2972,6 +3177,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) if(node.flags.clip_area) ctx.restore(); + + ctx.globalAlpha = 1.0; } /* Renders the node shape */ @@ -2987,7 +3194,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo grad.addColorStop(0.5, fgcolor || LiteGraph.NODE_DEFAULT_COLOR); grad.addColorStop(1, bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR); ctx.fillStyle = grad; - */ + //*/ var title_height = LiteGraph.NODE_TITLE_HEIGHT; @@ -3003,7 +3210,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo } ctx.beginPath(); - ctx.rect(0,no_title ? 0.5 : -title_height + 1,size[0]+1, no_title ? size[1] : size[1] + title_height); + ctx.rect(0,no_title ? 0.5 : -title_height + 0.5,size[0]+1, no_title ? size[1] : size[1] + title_height); } else if (node.shape == "round") { @@ -3034,7 +3241,8 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo if(!no_title) { ctx.fillStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR; - + var old_alpha = ctx.globalAlpha; + ctx.globalAlpha = 0.5 * old_alpha; if(shape == "box") { ctx.beginPath(); @@ -3057,10 +3265,11 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo else ctx.rect(3,-title_height + 3,title_height - 6,title_height - 6); ctx.fill(); + ctx.globalAlpha = old_alpha; //title text - ctx.font = "bold 12px Arial"; - if(node.name != "" && node.graph.config.canvas_scale > 0.8) + ctx.font = this.title_text_font; + if(node.name != "" && this.scale > 0.8) { ctx.fillStyle = "#222"; ctx.fillText(node.name,16,13-title_height ); @@ -3127,14 +3336,15 @@ LGraphCanvas.link_colors = ["#AAC","#ACA","#CAA"]; LGraphCanvas.prototype.drawConnections = function(ctx) { //draw connections - ctx.lineWidth = LGraphCanvas.link_width; + ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; //for every node - for (var n in this.graph.nodes) + for (var n in this.graph._nodes) { - var node = this.graph.nodes[n]; + var node = this.graph._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 in node.inputs) @@ -3156,16 +3366,14 @@ LGraphCanvas.prototype.drawConnections = function(ctx) var color = LGraphCanvas.link_type_colors[node.inputs[i].type]; if(color == null) color = LGraphCanvas.link_colors[node.id % LGraphCanvas.link_colors.length]; - ctx.fillStyle = ctx.strokeStyle = color; - this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i) ); + this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i), color ); } } + ctx.globalAlpha = 1; } -LGraphCanvas.prototype.renderLink = function(ctx,a,b) +LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) { - var curved_lines = true; - if(!this.highquality_render) { ctx.beginPath(); @@ -3177,32 +3385,44 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b) var dist = distance(a,b); + if(this.render_connections_border) + ctx.lineWidth = this.connections_width + 4; + ctx.beginPath(); - if(curved_lines) + 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 + 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.render_connections_border) + { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); //render arrow - if(this.graph.config.canvas_scale > 0.6) + if(this.render_connection_arrows && this.scale > 0.6) { //get two points in the bezier curve var pos = this.computeConnectionPoint(a,b,0.5); var pos2 = this.computeConnectionPoint(a,b,0.51); var angle = 0; - if(curved_lines) + if(this.render_curved_connections) angle = -Math.atan2( pos2[0] - pos[0], pos2[1] - pos[1]); else angle = b[1] > a[1] ? 0 : Math.PI; @@ -3237,6 +3457,7 @@ LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t) return [x,y]; } +/* LGraphCanvas.prototype.resizeCanvas = function(width,height) { this.canvas.width = width; @@ -3247,12 +3468,63 @@ LGraphCanvas.prototype.resizeCanvas = function(width,height) this.bgcanvas.height = this.canvas.height; this.draw(true,true); } +*/ -LGraphCanvas.prototype.switchLiveMode = function() +LGraphCanvas.prototype.resize = function(width, height) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; + if(!width && !height) + { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if(this.canvas.width == width && this.canvas.height == height) + return; + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true,true); +} + + +LGraphCanvas.prototype.switchLiveMode = function(transition) +{ + if(!transition) + { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if(this.live_mode) + { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if(delta < 1 && self.editor_alpha < 0.01) + { + clearInterval(t); + if(delta < 1) + self.live_mode = true; + } + if(delta > 1 && self.editor_alpha > 0.99) + { + clearInterval(t); + self.editor_alpha = 1; + } + },1); } LGraphCanvas.prototype.onNodeSelectionChange = function(node) @@ -3279,6 +3551,9 @@ LGraphCanvas.prototype.touchHandler = function(event) //initMouseEvent(type, canBubble, cancelable, view, clickCount, // screenX, screenY, clientX, clientY, ctrlKey, // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); simulatedEvent.initMouseEvent(type, true, true, window, 1, @@ -3293,13 +3568,15 @@ LGraphCanvas.prototype.touchHandler = function(event) LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) { + var window = canvas.getCanvasWindow(); + var values = LiteGraph.getNodeTypesCategories(); var entries = {}; for(var i in values) if(values[i]) entries[ i ] = { value: values[i], content: values[i] , is_menu: true }; - var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, window); function inner_clicked(v, e) { @@ -3309,7 +3586,7 @@ LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event ) for(var i in node_types) values.push( { content: node_types[i].title, value: node_types[i].type }); - LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}); + LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}, window); return false; } @@ -3390,7 +3667,19 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu) function inner_clicked(v) { if(!node) return; - node.addOutput(v.value[0],v.value[1]); + + var value = v.value[1]; + + if(value && (value.constructor === Object || value.constructor === Array)) //submenu + { + var entries = []; + for(var i in value) + entries.push({content: i, value: value[i]}); + LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}); + return false; + } + else + node.addOutput(v.value[0],v.value[1]); } return false; @@ -3402,6 +3691,11 @@ LGraphCanvas.onMenuNodeCollapse = function(node) node.graph.canvas.setDirty(true,true); } +LGraphCanvas.onMenuNodePin = function(node) +{ + node.pin(); +} + LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) { var values = []; @@ -3481,6 +3775,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) {content:"Outputs", is_menu: true, disabled:true, callback: LGraphCanvas.onMenuNodeOutputs }, null, {content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse }, + {content:"Pin", callback: LGraphCanvas.onMenuNodePin }, {content:"Colors", is_menu: true, callback: LGraphCanvas.onMenuNodeColors }, {content:"Shapes", is_menu: true, callback: LGraphCanvas.onMenuNodeShapes }, null, @@ -3505,7 +3800,9 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node) LGraphCanvas.prototype.processContextualMenu = function(node,event) { var that = this; - var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}); + var win = this.getCanvasWindow(); + + var menu = LiteGraph.createContextualMenu(node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions(), {event: event, callback: inner_option_clicked}, win); function inner_option_clicked(v,e) { @@ -3641,15 +3938,18 @@ function num2hex(triplet) { /* LiteGraph GUI elements *************************************/ -LiteGraph.createContextualMenu = function(values,options) +LiteGraph.createContextualMenu = function(values,options, ref_window) { options = options || {}; this.options = options; + //allows to create graph canvas in separate window + ref_window = ref_window || window; + if(!options.from) LiteGraph.closeAllContextualMenus(); - var root = document.createElement("div"); + var root = ref_window.document.createElement("div"); root.className = "litecontextualmenu litemenubar-panel"; this.root = root; var style = root.style; @@ -3671,7 +3971,7 @@ LiteGraph.createContextualMenu = function(values,options) for(var i in values) { var item = values[i]; - var element = document.createElement("div"); + var element = ref_window.document.createElement("div"); element.className = "litemenu-entry"; if(item == null) @@ -3706,7 +4006,7 @@ LiteGraph.createContextualMenu = function(values,options) root.addEventListener("mouseout", function(e) { //console.log("OUT!"); var aux = e.toElement; - while(aux != this && aux != document) + while(aux != this && aux != ref_window.document) aux = aux.parentNode; if(aux == this) return; @@ -3715,17 +4015,8 @@ LiteGraph.createContextualMenu = function(values,options) this.closeMenu(); }); - /* MS specific - root.addEventListener("mouseleave", function(e) { - - this.mouse_inside = false; - if(!this.block_close) - this.closeMenu(); - }); - */ - //insert before checking position - document.body.appendChild(root); + ref_window.document.body.appendChild(root); var root_rect = root.getClientRects()[0]; @@ -3744,7 +4035,7 @@ LiteGraph.createContextualMenu = function(values,options) if(options.left) left = options.left; - var rect = document.body.getClientRects()[0]; + var rect = ref_window.document.body.getClientRects()[0]; if(options.from) { @@ -3785,7 +4076,7 @@ LiteGraph.createContextualMenu = function(values,options) options.from.closeMenu(); } if(this.parentNode) - document.body.removeChild(this); + ref_window.document.body.removeChild(this); }; return root; @@ -3812,4 +4103,23 @@ LiteGraph.extendClass = function(origin, target) if(origin.prototype) //copy prototype properties for(var i in origin.prototype) target.prototype[i] = origin.prototype[i]; -} \ No newline at end of file +} + +/* +LiteGraph.createNodetypeWrapper = function( class_object ) +{ + //create Nodetype object +} +//LiteGraph.registerNodeType("scene/global", LGraphGlobal ); +*/ + +if( !window["requestAnimationFrame"] ) +{ + window.requestAnimationFrame = window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + (function( callback ){ + window.setTimeout(callback, 1000 / 60); + }); +} + + diff --git a/src/nodes/uinodes.js b/src/nodes/uinodes.js index cede31931..9eb0d2529 100644 --- a/src/nodes/uinodes.js +++ b/src/nodes/uinodes.js @@ -79,7 +79,7 @@ ctx.textAlign = "left"; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.onDrawImageKnob(ctx); }, @@ -227,7 +227,7 @@ ctx.drawImage(this.imgfg, 2+(this.size[0]-4)*this.value - this.imgfg.width*0.5,-this.imgfg.height*0.5 + 10); }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.onDrawImage(ctx); }, @@ -451,7 +451,7 @@ ctx.textAlign = "left"; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { this.drawBevelShape(ctx); }, @@ -515,7 +515,7 @@ if( v != undefined ) this.properties["value"] = v; }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { //border ctx.lineWidth = 1; @@ -535,7 +535,7 @@ inputs: [["",0]], properties:{value:"...",font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { //ctx.fillStyle="#000"; //ctx.fillRect(0,0,100,60); @@ -645,7 +645,7 @@ this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); }, - onDrawBackground: function(ctx) + onDrawForeground: function(ctx) { if(this.lineargradient == null) this.createGradient(ctx);